diff --git a/CMakeLists.txt b/CMakeLists.txt index fa92f1f85de304f0f6f4261986ff95daea7b234d..7d274925345a16b91830a2e16bda710521d61a55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -160,6 +160,10 @@ if( DEFINED ENV{CI_JOB_NAME} OR ${CMAKE_GENERATOR} STREQUAL "Ninja" ) endif() endif() +# add the filesystem library to all targets +# https://en.cppreference.com/w/cpp/filesystem +link_libraries( stdc++fs ) + # gtest has to be built before we add the MPI flags if( ${WITH_TESTS} OR ${WITH_MATRIX_TESTS} ) enable_testing() diff --git a/cmake/FindMETIS.cmake b/cmake/FindMETIS.cmake new file mode 100644 index 0000000000000000000000000000000000000000..bfaf1abd8be7edf2fbff48eca71ef3c2c343fcad --- /dev/null +++ b/cmake/FindMETIS.cmake @@ -0,0 +1,23 @@ +# This module will try to find METIS and define the following: +# METIS_FOUND - True iff METIS was found +# METIS_INCLUDE_DIRS - METIS include directories +# METIS_LIBRARIES - METIS library paths + +find_path(METIS_INCLUDE_DIR metis.h + PATHS /usr/include /usr/local/include + DOC "METIS include directory") + +find_library(METIS_LIBRARY metis + PATHS /usr/lib /usr/local/lib ${PROJECT_SOURCE_DIR}/metis + DOC "Path to METIS library") + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set METIS_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args(METIS DEFAULT_MSG + METIS_LIBRARY METIS_INCLUDE_DIR) + +mark_as_advanced(METIS_INCLUDE_DIR METIS_LIBRARY) + +set(METIS_LIBRARIES ${METIS_LIBRARY}) +set(METIS_INCLUDE_DIRS ${METIS_INCLUDE_DIR}) diff --git a/src/Python/pytnl/tnl/Mesh.cpp b/src/Python/pytnl/tnl/Mesh.cpp index c267aeade67b136d2b425444bc4f010ce3acc12d..4c81843041eec389516178c35da0650cc2f128dd 100644 --- a/src/Python/pytnl/tnl/Mesh.cpp +++ b/src/Python/pytnl/tnl/Mesh.cpp @@ -13,18 +13,18 @@ void export_Meshes( py::module & m ) using Reader = TNL::Meshes::Readers::VTKReader; py::class_< Reader >( m, "VTKReader" ) - .def(py::init<>()) - .def("readMesh", &Reader::template readMesh< MeshOfEdges >) - .def("readMesh", &Reader::template readMesh< MeshOfTriangles >) - .def("readMesh", &Reader::template readMesh< MeshOfTetrahedrons >) -// .def("readMesh", []( Reader& reader, const std::string& name, MeshOfEdges & mesh ) { -// return reader.readMesh( name.c_str(), mesh ); + .def(py::init()) + .def("loadMesh", &Reader::template loadMesh< MeshOfEdges >) + .def("loadMesh", &Reader::template loadMesh< MeshOfTriangles >) + .def("loadMesh", &Reader::template loadMesh< MeshOfTetrahedrons >) +// .def("loadMesh", []( Reader& reader, const std::string& name, MeshOfEdges & mesh ) { +// return reader.loadMesh( name.c_str(), mesh ); // } ) -// .def("readMesh", []( Reader& reader, const std::string& name, MeshOfTriangles & mesh ) { -// return reader.readMesh( name.c_str(), mesh ); +// .def("loadMesh", []( Reader& reader, const std::string& name, MeshOfTriangles & mesh ) { +// return reader.loadMesh( name.c_str(), mesh ); // } ) -// .def("readMesh", []( Reader& reader, const std::string& name, MeshOfTetrahedrons & mesh ) { -// return reader.readMesh( name.c_str(), mesh ); +// .def("loadMesh", []( Reader& reader, const std::string& name, MeshOfTetrahedrons & mesh ) { +// return reader.loadMesh( name.c_str(), mesh ); // } ) ; } diff --git a/src/Python/pytnl/tnl/Mesh.h b/src/Python/pytnl/tnl/Mesh.h index c0207e243ceae80613ea634b73ff429b94d697d2..21fa015fc94d967146cc2e8f046ac0f70fcbdb39 100644 --- a/src/Python/pytnl/tnl/Mesh.h +++ b/src/Python/pytnl/tnl/Mesh.h @@ -13,22 +13,12 @@ namespace py = pybind11; #include -template< typename MeshEntity > -typename MeshEntity::MeshTraitsType::GlobalIndexType -getIndex( const MeshEntity& entity ) -{ - return entity.getIndex(); -}; - -struct _general {}; -struct _special : _general {}; - template< typename MeshEntity, int Superdimension, typename Scope, - typename = typename std::enable_if< Superdimension <= MeshEntity::MeshTraitsType::meshDimension >::type > + std::enable_if_t< Superdimension <= MeshEntity::MeshType::getMeshDimension(), bool > = true > // && MeshEntity::template SuperentityTraits< Superdimension >::storageEnabled >::type > -void export_getSuperentityIndex( Scope & m, _special ) +void export_getSuperentityIndex( Scope & m ) { m.def("getSuperentityIndex", []( const MeshEntity& entity, const typename MeshEntity::LocalIndexType& i ) { return entity.template getSuperentityIndex< Superdimension >( i ); @@ -38,8 +28,9 @@ void export_getSuperentityIndex( Scope & m, _special ) template< typename MeshEntity, int Superdimension, - typename Scope > -void export_getSuperentityIndex( Scope &, _general ) + typename Scope, + std::enable_if_t< ! ( Superdimension <= MeshEntity::MeshType::getMeshDimension() ), bool > = true > +void export_getSuperentityIndex( Scope & ) { } @@ -47,9 +38,9 @@ void export_getSuperentityIndex( Scope &, _general ) template< typename MeshEntity, int Subdimension, typename Scope, - typename = typename std::enable_if< Subdimension <= MeshEntity::MeshTraitsType::meshDimension - && (Subdimension < MeshEntity::getEntityDimension()) >::type > -void export_getSubentityIndex( Scope & m, const char* name, _special ) + std::enable_if_t< Subdimension <= MeshEntity::MeshType::getMeshDimension() + && (Subdimension < MeshEntity::getEntityDimension()), bool > = true > +void export_getSubentityIndex( Scope & m, const char* name ) { m.def(name, []( const MeshEntity& entity, const typename MeshEntity::LocalIndexType& i ) { return entity.template getSubentityIndex< Subdimension >( i ); @@ -58,17 +49,20 @@ void export_getSubentityIndex( Scope & m, const char* name, _special ) } template< typename MeshEntity, - int Superdimension, - typename Scope > -void export_getSubentityIndex( Scope &, const char*, _general ) + int Subdimension, + typename Scope, + std::enable_if_t< ! ( Subdimension <= MeshEntity::MeshType::getMeshDimension() + && (Subdimension < MeshEntity::getEntityDimension()) + ), bool > = true > +void export_getSubentityIndex( Scope &, const char* ) { } template< typename MeshEntity, typename Scope, - typename = typename std::enable_if< MeshEntity::getEntityDimension() == 0 >::type > -void export_getPoint( Scope & scope, _special ) + std::enable_if_t< MeshEntity::getEntityDimension() == 0, bool > = true > +void export_getPoint( Scope & scope ) { scope.def("getPoint", []( const MeshEntity& entity ) { return entity.getPoint(); @@ -77,8 +71,9 @@ void export_getPoint( Scope & scope, _special ) } template< typename MeshEntity, - typename Scope > -void export_getPoint( Scope &, _general ) + typename Scope, + std::enable_if_t< MeshEntity::getEntityDimension() != 0, bool > = true > +void export_getPoint( Scope & ) { } @@ -88,24 +83,22 @@ void export_MeshEntity( Scope & scope, const char* name ) { auto entity = py::class_< MeshEntity >( scope, name ) .def_static("getEntityDimension", &MeshEntity::getEntityDimension) - // FIXME: boost chokes on this -// .def("getIndex", &MeshEntity::getIndex, py::return_internal_reference<>()) - .def("getIndex", getIndex< MeshEntity >) + .def("getIndex", &MeshEntity::getIndex) // TODO ; - export_getSuperentityIndex< MeshEntity, MeshEntity::getEntityDimension() + 1 >( entity, _special() ); - export_getSubentityIndex< MeshEntity, 0 >( entity, "getSubvertexIndex", _special() ); - export_getPoint< MeshEntity >( entity, _special() ); + export_getSuperentityIndex< MeshEntity, MeshEntity::getEntityDimension() + 1 >( entity ); + export_getSubentityIndex< MeshEntity, 0 >( entity, "getSubvertexIndex" ); + export_getPoint< MeshEntity >( entity ); } template< typename Mesh > void export_Mesh( py::module & m, const char* name ) { // there are two templates - const and non-const - take only the const - auto (Mesh::* getEntity_cell)(const typename Mesh::GlobalIndexType&) const = &Mesh::template getEntity; - auto (Mesh::* getEntity_face)(const typename Mesh::GlobalIndexType&) const = &Mesh::template getEntity; - auto (Mesh::* getEntity_vertex)(const typename Mesh::GlobalIndexType&) const = &Mesh::template getEntity; + auto (Mesh::* getEntity_cell)(const typename Mesh::GlobalIndexType) const = &Mesh::template getEntity; + auto (Mesh::* getEntity_face)(const typename Mesh::GlobalIndexType) const = &Mesh::template getEntity; + auto (Mesh::* getEntity_vertex)(const typename Mesh::GlobalIndexType) const = &Mesh::template getEntity; export_EntityTypes(m); @@ -116,9 +109,9 @@ void export_Mesh( py::module & m, const char* name ) .def("getSerializationTypeVirtual", &Mesh::getSerializationTypeVirtual) .def("getEntitiesCount", &mesh_getEntitiesCount< Mesh >) // TODO: if combined, the return type would depend on the runtime parameter (entity) - .def("getEntity_cell", getEntity_cell, py::return_value_policy::reference_internal) - .def("getEntity_face", getEntity_face, py::return_value_policy::reference_internal) - .def("getEntity_vertex", getEntity_vertex, py::return_value_policy::reference_internal) + .def("getEntity_cell", getEntity_cell) + .def("getEntity_face", getEntity_face) + .def("getEntity_vertex", getEntity_vertex) .def("getEntityCenter", []( const Mesh& mesh, const typename Mesh::Cell& cell ){ return getEntityCenter( mesh, cell ); } ) .def("getEntityCenter", []( const Mesh& mesh, const typename Mesh::Face& face ){ return getEntityCenter( mesh, face ); } ) .def("getEntityCenter", []( const Mesh& mesh, const typename Mesh::Vertex& vertex ){ return getEntityCenter( mesh, vertex ); } ) diff --git a/src/Python/pytnl/typedefs.h b/src/Python/pytnl/typedefs.h index b06693ab66ee0afb901aea469a347231bf68beae..7a74237f02b6bb150a02b28e1b42bc9e343ea47c 100644 --- a/src/Python/pytnl/typedefs.h +++ b/src/Python/pytnl/typedefs.h @@ -24,19 +24,16 @@ using MeshOfEdges = TNL::Meshes::Mesh< TNL::Meshes::DefaultConfig< EdgeTopology::dimension, RealType, IndexType, - LocalIndexType, - IndexType > >; + LocalIndexType > >; using MeshOfTriangles = TNL::Meshes::Mesh< TNL::Meshes::DefaultConfig< TriangleTopology, TriangleTopology::dimension, RealType, IndexType, - LocalIndexType, - IndexType > >; + LocalIndexType > >; using MeshOfTetrahedrons = TNL::Meshes::Mesh< TNL::Meshes::DefaultConfig< TetrahedronTopology, TetrahedronTopology::dimension, RealType, IndexType, - LocalIndexType, - IndexType > >; + LocalIndexType > >; diff --git a/src/TNL/Containers/Multimaps/EllpackIndexMultimap.h b/src/TNL/Containers/Multimaps/EllpackIndexMultimap.h deleted file mode 100644 index 5e575cc21ce8292ba3f9d3d4c8ed4b189b056936..0000000000000000000000000000000000000000 --- a/src/TNL/Containers/Multimaps/EllpackIndexMultimap.h +++ /dev/null @@ -1,110 +0,0 @@ -/*************************************************************************** - EllpackIndexMultimap.h - description - ------------------- - begin : Sep 9, 2015 - copyright : (C) 2015 by Tomas Oberhuber et al. - email : tomas.oberhuber@fjfi.cvut.cz - ***************************************************************************/ - -/* See Copyright Notice in tnl/Copyright */ - -#pragma once - -#include -#include - -namespace TNL { -namespace Containers { -namespace Multimaps { - -template< typename Device > -struct EllpackIndexMultimapSliceSizeGetter -{ - static constexpr int SliceSize = 1; -}; - -template<> -struct EllpackIndexMultimapSliceSizeGetter< Devices::Cuda > -{ - static constexpr int SliceSize = 32; -}; - -template< typename Index = int, - typename Device = Devices::Host, - typename LocalIndex = Index, - int SliceSize = EllpackIndexMultimapSliceSizeGetter< Device >::SliceSize > -class EllpackIndexMultimap - : public Object -{ - public: - using DeviceType = Device; - using IndexType = Index; - using LocalIndexType = LocalIndex; - using ValuesAccessorType = EllpackIndexMultimapValues< IndexType, DeviceType, LocalIndexType, SliceSize >; - using ConstValuesAccessorType = EllpackIndexMultimapValues< const IndexType, DeviceType, LocalIndexType, SliceSize >; - using ValuesAllocationVectorType = Vector< LocalIndexType, DeviceType, IndexType >; - - EllpackIndexMultimap() = default; - - template< typename Device_ > - EllpackIndexMultimap( const EllpackIndexMultimap< Index, Device_, LocalIndex, SliceSize >& other ); - - template< typename Device_ > - EllpackIndexMultimap& operator=( const EllpackIndexMultimap< Index, Device_, LocalIndex, SliceSize >& other ); - - void setKeysRange( const IndexType& keysRange ); - - __cuda_callable__ - const IndexType getKeysRange() const; - - void allocate( const LocalIndexType& maxValuesCount ); - - void allocate( const ValuesAllocationVectorType& valuesCounts ); - - template< typename Device_, int SliceSize_ > - void setLike( const EllpackIndexMultimap< Index, Device_, LocalIndex, SliceSize_ >& other ); - - __cuda_callable__ - ValuesAccessorType getValues( const IndexType& inputIndex ); - - __cuda_callable__ - ConstValuesAccessorType getValues( const IndexType& inputIndex ) const; - - bool operator==( const EllpackIndexMultimap& other ) const; - - void save( File& file ) const; - - void load( File& file ); - - using Object::load; - - using Object::save; - - void print( std::ostream& str ) const; - - protected: - Vector< IndexType, DeviceType, IndexType > values; - ValuesAllocationVectorType valuesCounts; - - IndexType keysRange = 0; - LocalIndexType maxValuesCount = 0; - - __cuda_callable__ - IndexType getAllocationKeysRange( IndexType keysRange ) const; - - // friend class is needed for templated assignment operators - template< typename Index_, typename Device_, typename LocalIndex_, int SliceSize_ > - friend class EllpackIndexMultimap; -}; - -template< typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -std::ostream& operator << ( std::ostream& str, const EllpackIndexMultimap< Index, Device, LocalIndex, SliceSize >& multimap ); - -} // namespace Multimaps -} // namespace Containers -} // namespace TNL - -#include diff --git a/src/TNL/Containers/Multimaps/EllpackIndexMultimap.hpp b/src/TNL/Containers/Multimaps/EllpackIndexMultimap.hpp deleted file mode 100644 index c75250029ec0bdd0dc1db06b89681c9ae9f1190e..0000000000000000000000000000000000000000 --- a/src/TNL/Containers/Multimaps/EllpackIndexMultimap.hpp +++ /dev/null @@ -1,284 +0,0 @@ -/*************************************************************************** - EllpackIndexMultimap_impl.h - description - ------------------- - begin : Sep 9, 2015 - copyright : (C) 2015 by Tomas Oberhuber et al. - email : tomas.oberhuber@fjfi.cvut.cz - ***************************************************************************/ - -/* See Copyright Notice in tnl/Copyright */ - -#pragma once - -#include - -namespace TNL { -namespace Containers { -namespace Multimaps { - -template< typename Index, - typename Device, - typename LocalIndex, - int SliceSize > - template< typename Device_ > -EllpackIndexMultimap< Index, Device, LocalIndex, SliceSize >:: -EllpackIndexMultimap( const EllpackIndexMultimap< Index, Device_, LocalIndex, SliceSize >& other ) -{ - operator=( other ); -} - -template< typename Index, - typename Device, - typename LocalIndex, - int SliceSize > - template< typename Device_ > -EllpackIndexMultimap< Index, Device, LocalIndex, SliceSize >& -EllpackIndexMultimap< Index, Device, LocalIndex, SliceSize >:: -operator=( const EllpackIndexMultimap< Index, Device_, LocalIndex, SliceSize >& other ) -{ - values = other.values; - valuesCounts = other.valuesCounts; - keysRange = other.keysRange; - maxValuesCount = other.maxValuesCount; - return *this; -} - -template< typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -void -EllpackIndexMultimap< Index, Device, LocalIndex, SliceSize >:: -setKeysRange( const IndexType& keysRange ) -{ - TNL_ASSERT( keysRange >= 0, ); - this->keysRange = keysRange; -} - -template< typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -__cuda_callable__ -const Index -EllpackIndexMultimap< Index, Device, LocalIndex, SliceSize >:: -getKeysRange() const -{ - return this->keysRange; -} - -template< typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -void -EllpackIndexMultimap< Index, Device, LocalIndex, SliceSize >:: -allocate( const LocalIndexType& maxValuesCount ) -{ - TNL_ASSERT( maxValuesCount >= 0, ); - this->maxValuesCount = maxValuesCount; - const IndexType ldSize = getAllocationKeysRange( this->getKeysRange() ); - this->values.setSize( ldSize * this->maxValuesCount ); - this->valuesCounts.setSize( this->getKeysRange() ); - if( this->valuesCounts.getSize() > 0 ) - this->valuesCounts.setValue( maxValuesCount ); - - // extra cost at initialization, which allows to have much simpler operator== - if( this->values.getSize() > 0 ) - values.setValue( 0 ); -} - -template< typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -void -EllpackIndexMultimap< Index, Device, LocalIndex, SliceSize >:: -allocate( const ValuesAllocationVectorType& valuesCounts ) -{ - TNL_ASSERT( valuesCounts.getSize() == this->keysRange, - std::cerr << "valuesCounts.getSize() = " << valuesCounts.getSize() - << "this->keysRange = " << this->keysRange - << std::endl; ); - this->maxValuesCount = max( valuesCounts ); - - TNL_ASSERT( this->maxValuesCount >= 0, - std::cerr << "this->maxValuesCount = " << this->maxValuesCount << std::endl; ); - const IndexType ldSize = getAllocationKeysRange( this->getKeysRange() ); - this->values.setSize( ldSize * this->maxValuesCount ); - this->valuesCounts.setSize( this->getKeysRange() ); - this->valuesCounts = valuesCounts; - - // extra cost at initialization, which allows to have much simpler operator== - if( this->values.getSize() > 0 ) - values.setValue( 0 ); -} - -template< typename Index, - typename Device, - typename LocalIndex, - int SliceSize > - template< typename Device_, int SliceSize_ > -void -EllpackIndexMultimap< Index, Device, LocalIndex, SliceSize >:: -setLike( const EllpackIndexMultimap< Index, Device_, LocalIndex, SliceSize_ >& other ) -{ - const IndexType ldSize = getAllocationKeysRange( other.getKeysRange() ); - values.setSize( ldSize * other.maxValuesCount ); - valuesCounts.setLike( other.valuesCounts ); - valuesCounts = other.valuesCounts; - keysRange = other.keysRange; - maxValuesCount = other.maxValuesCount; - - // extra cost at initialization, which allows to have much simpler operator== - if( this->values.getSize() > 0 ) - values.setValue( 0 ); -} - -template< typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -__cuda_callable__ -typename EllpackIndexMultimap< Index, Device, LocalIndex, SliceSize >::ValuesAccessorType -EllpackIndexMultimap< Index, Device, LocalIndex, SliceSize >:: -getValues( const IndexType& inputIndex ) -{ - TNL_ASSERT( inputIndex < this->getKeysRange(), - std::cerr << "inputIndex = " << inputIndex << std::endl - << "this->getKeysRange() = " << this->getKeysRange() - << std::endl; ); - TNL_ASSERT( getAllocationKeysRange( this->getKeysRange() ) * this->maxValuesCount == this->values.getSize() && this->getKeysRange() == this->valuesCounts.getSize(), - std::cerr << "The map has not been reallocated after calling setKeysRange()." << std::endl - << "this->getKeysRange() = " << this->getKeysRange() << std::endl - << "this->maxValuesCount = " << this->maxValuesCount << std::endl - << "this->values.getSize() = " << this->values.getSize() << std::endl - << "this->valuesCounts.getSize() = " << this->valuesCounts.getSize() << std::endl; ); - const IndexType sliceIdx = inputIndex / SliceSize; - const IndexType sliceOffset = sliceIdx * SliceSize * this->maxValuesCount; - const IndexType offset = sliceOffset + inputIndex - sliceIdx * SliceSize; - return ValuesAccessorType( &this->values[ offset ], &this->valuesCounts[ inputIndex ], this->maxValuesCount ); -} - -template< typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -__cuda_callable__ -typename EllpackIndexMultimap< Index, Device, LocalIndex, SliceSize >::ConstValuesAccessorType -EllpackIndexMultimap< Index, Device, LocalIndex, SliceSize >:: -getValues( const IndexType& inputIndex ) const -{ - TNL_ASSERT( inputIndex < this->getKeysRange(), - std::cerr << "inputIndex = " << inputIndex << std::endl - << "this->getKeysRange() = " << this->getKeysRange() - << std::endl; ); - TNL_ASSERT( getAllocationKeysRange( this->getKeysRange() ) * this->maxValuesCount == this->values.getSize() && this->getKeysRange() == this->valuesCounts.getSize(), - std::cerr << "The map has not been reallocated after calling setKeysRange()." << std::endl - << "this->getKeysRange() = " << this->getKeysRange() << std::endl - << "this->maxValuesCount = " << this->maxValuesCount << std::endl - << "this->values.getSize() = " << this->values.getSize() << std::endl - << "this->valuesCounts.getSize() = " << this->valuesCounts.getSize() << std::endl; ); - const IndexType sliceIdx = inputIndex / SliceSize; - const IndexType sliceOffset = sliceIdx * SliceSize * this->maxValuesCount; - const IndexType offset = sliceOffset + inputIndex - sliceIdx * SliceSize; - return ConstValuesAccessorType( &this->values[ offset ], &this->valuesCounts[ inputIndex ], this->maxValuesCount ); -} - -template< typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -bool -EllpackIndexMultimap< Index, Device, LocalIndex, SliceSize >:: -operator==( const EllpackIndexMultimap& other ) const -{ -// if( ! ( this->keysRange == other.keysRange && -// this->maxValuesCount == other.maxValuesCount && -// this->valuesCounts == other.valuesCounts ) ) -// return false; -// // compare values for each key separately - the sizes may vary -// for( IndexType i = 0; i < this->keysRange; i++ ) -// if( this->getValues( i ) != other.getValues( i ) ) -// return false; -// return true; - - // we assume that invalid entries in the ellpack format are always 0 - return this->keysRange == other.keysRange && - this->maxValuesCount == other.maxValuesCount && - this->valuesCounts == other.valuesCounts && - this->values == other.values; -} - -template< typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -void -EllpackIndexMultimap< Index, Device, LocalIndex, SliceSize >:: -save( File& file ) const -{ - Object::save( file ); - file.save( &this->keysRange ); - file.save( &this->maxValuesCount ); - file << this->values << this->valuesCounts; -} - -template< typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -void -EllpackIndexMultimap< Index, Device, LocalIndex, SliceSize >:: -load( File& file ) -{ - Object::load( file ); - file.load( &this->keysRange ); - file.load( &this->maxValuesCount ); - file >> this->values >> this->valuesCounts; -} - -template< typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -void -EllpackIndexMultimap< Index, Device, LocalIndex, SliceSize >:: -print( std::ostream& str ) const -{ - str << "[ "; - if( this->getKeysRange() > 0 ) - { - str << this->getValues( 0 ); - for( Index i = 1; i < this->getKeysRange(); i++ ) - str << ",\n " << this->getValues( i ); - } - str << " ]"; -} - -template< typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -__cuda_callable__ -Index -EllpackIndexMultimap< Index, Device, LocalIndex, SliceSize >:: -getAllocationKeysRange( IndexType keysRange ) const -{ - return SliceSize * roundUpDivision( keysRange, SliceSize ); -} - - -template< typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -std::ostream& operator << ( std::ostream& str, const EllpackIndexMultimap< Index, Device, LocalIndex, SliceSize >& multimap ) -{ - multimap.print( str ); - return str; -} - -} // namespace Multimaps -} // namespace Containers -} // namespace TNL diff --git a/src/TNL/Containers/Multimaps/EllpackIndexMultimapValues.h b/src/TNL/Containers/Multimaps/EllpackIndexMultimapValues.h deleted file mode 100644 index 9be47980d1dbef78af8891ff50837d70fb851c22..0000000000000000000000000000000000000000 --- a/src/TNL/Containers/Multimaps/EllpackIndexMultimapValues.h +++ /dev/null @@ -1,121 +0,0 @@ -/*************************************************************************** - EllpackIndexMultimapValues.h - description - ------------------- - begin : Sep 10, 2015 - copyright : (C) 2015 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 Containers { -namespace Multimaps { - -template< typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -class EllpackIndexMultimap; - -template< typename Index, - typename Device, - typename LocalIndex, - int step = 1 > -class EllpackIndexMultimapValues -{ - public: - using DeviceType = Device; - using IndexType = Index; - using LocalIndexType = LocalIndex; - using NetworkType = EllpackIndexMultimap< IndexType, DeviceType, LocalIndexType, step >; - - __cuda_callable__ - EllpackIndexMultimapValues(); - - __cuda_callable__ - EllpackIndexMultimapValues( EllpackIndexMultimapValues&& other ); - - __cuda_callable__ - EllpackIndexMultimapValues& operator=( const EllpackIndexMultimapValues& ); - - // converting assignment, needed for 'const int' -> 'int' etc. - template< typename Index_, typename LocalIndex_, int step_ > - __cuda_callable__ - EllpackIndexMultimapValues& operator=( const EllpackIndexMultimapValues< Index_, Device, LocalIndex_, step_ >& other ); - - __cuda_callable__ - EllpackIndexMultimapValues& operator=( EllpackIndexMultimapValues&& other ); - - __cuda_callable__ - void bind( const EllpackIndexMultimapValues& other ); - - __cuda_callable__ - void setSize( const LocalIndexType& portsCount ); - - __cuda_callable__ - LocalIndexType getSize() const; - - __cuda_callable__ - LocalIndexType getAllocatedSize() const; - - __cuda_callable__ - void setValue( const LocalIndexType& portIndex, - const IndexType& value ); - - __cuda_callable__ - IndexType getValue( const LocalIndexType& portIndex ) const; - - __cuda_callable__ - IndexType& operator[]( const LocalIndexType& portIndex ); - - __cuda_callable__ - const IndexType& operator[]( const LocalIndexType& portIndex ) const; - - __cuda_callable__ - bool operator==( const EllpackIndexMultimapValues& other ) const; - - __cuda_callable__ - bool operator!=( const EllpackIndexMultimapValues& other ) const; - - void print( std::ostream& str ) const; - - protected: - using ValuesCountType = typename std::conditional< std::is_const< IndexType >::value, - std::add_const_t< LocalIndexType >, - LocalIndexType >::type; - - __cuda_callable__ - EllpackIndexMultimapValues( IndexType* values, - ValuesCountType* valuesCount, - const LocalIndexType& allocatedSize ); - - IndexType* values; - - ValuesCountType* valuesCount; - - // TODO: this is useless for a const-accessor (without setSize etc.) - LocalIndexType allocatedSize; - - friend EllpackIndexMultimap< IndexType, DeviceType, LocalIndexType, step >; - friend EllpackIndexMultimap< typename std::remove_const< IndexType >::type, DeviceType, LocalIndexType, step >; -}; - -template< typename Index, - typename Device, - typename LocalIndex, - int step > -std::ostream& operator << ( std::ostream& str, const EllpackIndexMultimapValues< Index, Device, LocalIndex, step >& ports ); - -} // namespace Multimaps -} // namespace Containers -} // namespace TNL - -#include diff --git a/src/TNL/Containers/Multimaps/EllpackIndexMultimapValues.hpp b/src/TNL/Containers/Multimaps/EllpackIndexMultimapValues.hpp deleted file mode 100644 index 6b98927fb8082a500da4817f68470b33b32b8ce0..0000000000000000000000000000000000000000 --- a/src/TNL/Containers/Multimaps/EllpackIndexMultimapValues.hpp +++ /dev/null @@ -1,293 +0,0 @@ -/*************************************************************************** - EllpackIndexMultimapValues_impl.h - description - ------------------- - begin : Sep 10, 2015 - copyright : (C) 2015 by Tomas Oberhuber et al. - email : tomas.oberhuber@fjfi.cvut.cz - ***************************************************************************/ - -/* See Copyright Notice in tnl/Copyright */ - -#pragma once - -#include "EllpackIndexMultimapValues.h" - -#include - -namespace TNL { -namespace Containers { -namespace Multimaps { - -template< typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -EllpackIndexMultimapValues< Index, Device, LocalIndex, step >:: -EllpackIndexMultimapValues() -: values( nullptr ), valuesCount( nullptr ), allocatedSize( 0 ) -{ -} - -template< typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -EllpackIndexMultimapValues< Index, Device, LocalIndex, step >:: -EllpackIndexMultimapValues( EllpackIndexMultimapValues&& other ) -: values( other.values ), valuesCount( other.valuesCount ), allocatedSize( other.allocatedSize ) -{ - other.values = nullptr; - other.valuesCount = nullptr; - other.allocatedSize = 0; -} - -template< typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -EllpackIndexMultimapValues< Index, Device, LocalIndex, step >:: -EllpackIndexMultimapValues( IndexType* values, - ValuesCountType* valuesCount, - const LocalIndexType& allocatedSize ) -: values( values ), valuesCount( valuesCount ), allocatedSize( allocatedSize ) -{ - TNL_ASSERT( *(this->valuesCount) <= allocatedSize, ); -} - -template< typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -EllpackIndexMultimapValues< Index, Device, LocalIndex, step >& -EllpackIndexMultimapValues< Index, Device, LocalIndex, step >:: -operator=( const EllpackIndexMultimapValues& other ) -{ - TNL_ASSERT( this->getAllocatedSize() >= other.getSize(), ); - this->setSize( other.getSize() ); - if( this->values != other.values ) { - for( LocalIndexType i = 0; i < this->getSize(); i++ ) - this->setValue( i, other[ i ] ); - } - return *this; -} - -template< typename Index, - typename Device, - typename LocalIndex, - int step > - template< typename Index_, typename LocalIndex_, int step_ > -__cuda_callable__ -EllpackIndexMultimapValues< Index, Device, LocalIndex, step >& -EllpackIndexMultimapValues< Index, Device, LocalIndex, step >:: -operator=( const EllpackIndexMultimapValues< Index_, Device, LocalIndex_, step_ >& other ) -{ - TNL_ASSERT( this->getAllocatedSize() >= other.getSize(), ); - this->setSize( other.getSize() ); - for( LocalIndexType i = 0; i < this->getSize(); i++ ) - this->setValue( i, other[ i ] ); - return *this; -} - -template< typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -EllpackIndexMultimapValues< Index, Device, LocalIndex, step >& -EllpackIndexMultimapValues< Index, Device, LocalIndex, step >:: -operator=( EllpackIndexMultimapValues&& other ) -{ - this->values = other.values; - this->valuesCount = other.valuesCount; - this->allocatedSize = other.allocatedSize; - other.values = nullptr; - other.valuesCount = nullptr; - other.allocatedSize = 0; - return *this; -} - -template< typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -void -EllpackIndexMultimapValues< Index, Device, LocalIndex, step >:: -bind( const EllpackIndexMultimapValues& other ) -{ - this->values = other.values; - this->valuesCount = other.valuesCount; - this->allocatedSize = other.allocatedSize; -} - -template< typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -void -EllpackIndexMultimapValues< Index, Device, LocalIndex, step >:: -setSize( const LocalIndexType& size ) -{ - TNL_ASSERT( this->valuesCount, - std::cerr << "Uninitialized 'valuesCount' pointer in EllpackIndexMultimapValues." << std::endl; ); - TNL_ASSERT( size >= 0 && size <= this->allocatedSize, - std::cerr << "size = " << size << ", allocatedSize = " << this->allocatedSize << std::endl; ); - *valuesCount = size; -} - -template< typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -LocalIndex -EllpackIndexMultimapValues< Index, Device, LocalIndex, step >:: -getSize() const -{ - if( ! valuesCount ) - return 0; - return *valuesCount; -} - -template< typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -LocalIndex -EllpackIndexMultimapValues< Index, Device, LocalIndex, step >:: -getAllocatedSize() const -{ - return this->allocatedSize; -} - -template< typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -void -EllpackIndexMultimapValues< Index, Device, LocalIndex, step >:: -setValue( const LocalIndexType& portIndex, - const IndexType& value ) -{ - TNL_ASSERT( portIndex < this->getSize(), - std::cerr << " portIndex = " << portIndex - << " getSize() = " << this->getSize() - << std::endl ); - this->values[ portIndex * step ] = value; -} - -template< typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -Index -EllpackIndexMultimapValues< Index, Device, LocalIndex, step >:: -getValue( const LocalIndexType& portIndex ) const -{ - TNL_ASSERT( portIndex < this->getSize(), - std::cerr << " portIndex = " << portIndex - << " getSize() = " << this->getSize() - << std::endl ); - return this->values[ portIndex * step ]; -} - -template< typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -Index& -EllpackIndexMultimapValues< Index, Device, LocalIndex, step >:: -operator[]( const LocalIndexType& portIndex ) -{ - TNL_ASSERT( portIndex < this->getSize(), - std::cerr << " portIndex = " << portIndex - << " getSize() = " << this->getSize() - << std::endl ); - return this->values[ portIndex * step ]; -} - -template< typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -const Index& -EllpackIndexMultimapValues< Index, Device, LocalIndex, step >:: -operator[]( const LocalIndexType& portIndex ) const -{ - TNL_ASSERT( portIndex < this->getSize(), - std::cerr << " portIndex = " << portIndex - << " getSize() = " << this->getSize() - << std::endl ); - return this->values[ portIndex * step ]; -} - -template< typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -bool -EllpackIndexMultimapValues< Index, Device, LocalIndex, step >:: -operator==( const EllpackIndexMultimapValues& other ) const -{ - if( this->getSize() != other.getSize() ) - return false; - for( LocalIndexType i = 0; i < this->getSize(); i++ ) - if( this->operator[]( i ) != other[ i ] ) - return false; - return true; -} - -template< typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -bool -EllpackIndexMultimapValues< Index, Device, LocalIndex, step >:: -operator!=( const EllpackIndexMultimapValues& other ) const -{ - return ! ( *this == other ); -} - -template< typename Index, - typename Device, - typename LocalIndex, - int step > -void -EllpackIndexMultimapValues< Index, Device, LocalIndex, step >:: -print( std::ostream& str ) const -{ - str << "[ "; - if( this->getSize() > 0 ) - { - str << this->getValue( 0 ); - for( typename std::remove_const< Index >::type i = 1; i < this->getSize(); i++ ) - str << ", " << this->getValue( i ); - } - str << " ]"; -} - -template< typename Index, - typename Device, - typename LocalIndex, - int step > -std::ostream& operator << ( std::ostream& str, const EllpackIndexMultimapValues< Index, Device, LocalIndex, step >& ports ) -{ - ports.print( str ); - return str; -} - -} // namespace Multimaps -} // namespace Containers -} // namespace TNL diff --git a/src/TNL/Containers/Multimaps/MultimapPermutationApplier.h b/src/TNL/Containers/Multimaps/MultimapPermutationApplier.h deleted file mode 100644 index 9533393059255bc7151e803e18cec2f1829ea4b7..0000000000000000000000000000000000000000 --- a/src/TNL/Containers/Multimaps/MultimapPermutationApplier.h +++ /dev/null @@ -1,90 +0,0 @@ -/*************************************************************************** - IndexPermutationApplier.h - description - ------------------- - begin : Mar 10, 2017 - copyright : (C) 2017 by Tomas Oberhuber et al. - email : tomas.oberhuber@fjfi.cvut.cz - ***************************************************************************/ - -/* See Copyright Notice in tnl/Copyright */ - -#pragma once - -#include -#include - -namespace TNL { -namespace Containers { -namespace Multimaps { - -template< typename Multimap, - typename PermutationVector > -void permuteMultimapKeys( Multimap& multimap, const PermutationVector& perm ) -{ - static_assert( std::is_same< typename Multimap::DeviceType, typename PermutationVector::DeviceType >::value, - "The multimap and permutation vector must be stored on the same device." ); - using IndexType = typename Multimap::IndexType; - using DeviceType = typename Multimap::DeviceType; - TNL_ASSERT( multimap.getKeysRange() == perm.getSize(), - std::cerr << "multimap keys range is " << multimap.getKeysRange() - << ", permutation size is " << perm.getSize() << std::endl; ); - - // create temporary multimap for the permuted data - Multimap multimapCopy; - multimapCopy.setLike( multimap ); - - // kernel to permute the rows of multimap into multimapCopy - auto kernel = [] __cuda_callable__ - ( IndexType i, - const Multimap* multimap, - Multimap* multimapCopy, - const typename PermutationVector::RealType* perm ) - { - const auto srcValues = multimap->getValues( perm[ i ] ); - auto destValues = multimapCopy->getValues( i ); - destValues = srcValues; - }; - - Pointers::DevicePointer< Multimap > multimapPointer( multimap ); - Pointers::DevicePointer< Multimap > multimapCopyPointer( multimapCopy ); - - Algorithms::ParallelFor< DeviceType >::exec( (IndexType) 0, multimap.getKeysRange(), - kernel, - &multimapPointer.template getData< DeviceType >(), - &multimapCopyPointer.template modifyData< DeviceType >(), - perm.getData() ); - - // copy the permuted data back into the multimap - multimap = multimapCopy; -} - -template< typename Multimap, - typename PermutationVector > -void permuteMultimapValues( Multimap& multimap, const PermutationVector& iperm ) -{ - static_assert( std::is_same< typename Multimap::DeviceType, typename PermutationVector::DeviceType >::value, - "The multimap and permutation vector must be stored on the same device." ); - using IndexType = typename Multimap::IndexType; - using DeviceType = typename Multimap::DeviceType; - - // kernel to permute the multimap values - auto kernel = [] __cuda_callable__ - ( IndexType i, - Multimap* multimap, - const typename PermutationVector::RealType* iperm ) - { - auto values = multimap->getValues( i ); - for( typename Multimap::LocalIndexType v = 0; v < values.getSize(); v++ ) - values[ v ] = iperm[ values[ v ] ]; - }; - - Pointers::DevicePointer< Multimap > multimapPointer( multimap ); - Algorithms::ParallelFor< DeviceType >::exec( (IndexType) 0, multimap.getKeysRange(), - kernel, - &multimapPointer.template modifyData< DeviceType >(), - iperm.getData() ); -} - -} // namespace Multimaps -} // namespace Containers -} // namespace TNL diff --git a/src/TNL/Containers/Multimaps/StaticEllpackIndexMultimap.h b/src/TNL/Containers/Multimaps/StaticEllpackIndexMultimap.h deleted file mode 100644 index f816cabd2c37626b978a03dbdf6a6ed63076036d..0000000000000000000000000000000000000000 --- a/src/TNL/Containers/Multimaps/StaticEllpackIndexMultimap.h +++ /dev/null @@ -1,107 +0,0 @@ -/*************************************************************************** - StaticEllpackIndexMultimap.h - description - ------------------- - begin : Sep 9, 2015 - copyright : (C) 2015 by Tomas Oberhuber et al. - email : tomas.oberhuber@fjfi.cvut.cz - ***************************************************************************/ - -/* See Copyright Notice in tnl/Copyright */ - -#pragma once - -#include -#include - -namespace TNL { -namespace Containers { -namespace Multimaps { - -template< typename Device > -struct StaticEllpackIndexMultimapSliceSizeGetter -{ - static constexpr int SliceSize = 1; -}; - -template<> -struct StaticEllpackIndexMultimapSliceSizeGetter< Devices::Cuda > -{ - static constexpr int SliceSize = 32; -}; - -template< int ValuesCount, - typename Index = int, - typename Device = Devices::Host, - typename LocalIndex = Index, - int SliceSize = StaticEllpackIndexMultimapSliceSizeGetter< Device >::SliceSize > -class StaticEllpackIndexMultimap - : public Object -{ - public: - using DeviceType = Device; - using IndexType = Index; - using LocalIndexType = LocalIndex; - using ValuesAccessorType = StaticEllpackIndexMultimapValues< ValuesCount, IndexType, DeviceType, LocalIndexType, SliceSize >; - using ConstValuesAccessorType = StaticEllpackIndexMultimapValues< ValuesCount, const IndexType, DeviceType, LocalIndexType, SliceSize >; - - StaticEllpackIndexMultimap() = default; - - template< typename Device_ > - StaticEllpackIndexMultimap( const StaticEllpackIndexMultimap< ValuesCount, Index, Device_, LocalIndex, SliceSize >& other ); - - template< typename Device_ > - StaticEllpackIndexMultimap& operator=( const StaticEllpackIndexMultimap< ValuesCount, Index, Device_, LocalIndex, SliceSize >& other ); - - void setKeysRange( const IndexType& keysRange ); - - __cuda_callable__ - const IndexType getKeysRange() const; - - void allocate(); - - template< typename Device_ > - void setLike( const StaticEllpackIndexMultimap< ValuesCount, Index, Device_, LocalIndex, SliceSize >& other ); - - __cuda_callable__ - ValuesAccessorType getValues( const IndexType& inputIndex ); - - __cuda_callable__ - ConstValuesAccessorType getValues( const IndexType& inputIndex ) const; - - bool operator==( const StaticEllpackIndexMultimap& other ) const; - - void save( File& file ) const; - - void load( File& file ); - - using Object::load; - - using Object::save; - - void print( std::ostream& str ) const; - - protected: - Vector< IndexType, DeviceType, IndexType > values; - - IndexType keysRange = 0; - - __cuda_callable__ - IndexType getAllocationKeysRange( IndexType keysRange ) const; - - // friend class is needed for templated assignment operators - template< int ValuesCount_, typename Index_, typename Device_, typename LocalIndex_, int SliceSize_ > - friend class StaticEllpackIndexMultimap; -}; - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -std::ostream& operator << ( std::ostream& str, const StaticEllpackIndexMultimap< ValuesCount, Index, Device, LocalIndex, SliceSize >& multimap ); - -} // namespace Multimaps -} // namespace Containers -} // namespace TNL - -#include diff --git a/src/TNL/Containers/Multimaps/StaticEllpackIndexMultimap.hpp b/src/TNL/Containers/Multimaps/StaticEllpackIndexMultimap.hpp deleted file mode 100644 index 7af150615a682a5a215e448b4e7e0f85efd0079a..0000000000000000000000000000000000000000 --- a/src/TNL/Containers/Multimaps/StaticEllpackIndexMultimap.hpp +++ /dev/null @@ -1,241 +0,0 @@ -/*************************************************************************** - StaticEllpackIndexMultimap_impl.h - description - ------------------- - begin : Sep 9, 2015 - copyright : (C) 2015 by Tomas Oberhuber et al. - email : tomas.oberhuber@fjfi.cvut.cz - ***************************************************************************/ - -/* See Copyright Notice in tnl/Copyright */ - -#pragma once - -#include - -namespace TNL { -namespace Containers { -namespace Multimaps { - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int SliceSize > - template< typename Device_ > -StaticEllpackIndexMultimap< ValuesCount, Index, Device, LocalIndex, SliceSize >:: -StaticEllpackIndexMultimap( const StaticEllpackIndexMultimap< ValuesCount, Index, Device_, LocalIndex, SliceSize >& other ) -{ - operator=( other ); -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int SliceSize > - template< typename Device_ > -StaticEllpackIndexMultimap< ValuesCount, Index, Device, LocalIndex, SliceSize >& -StaticEllpackIndexMultimap< ValuesCount, Index, Device, LocalIndex, SliceSize >:: -operator=( const StaticEllpackIndexMultimap< ValuesCount, Index, Device_, LocalIndex, SliceSize >& other ) -{ - values = other.values; - keysRange = other.keysRange; - return *this; -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -void -StaticEllpackIndexMultimap< ValuesCount, Index, Device, LocalIndex, SliceSize >:: -setKeysRange( const IndexType& keysRange ) -{ - TNL_ASSERT( keysRange >= 0, ); - this->keysRange = keysRange; -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -__cuda_callable__ -const Index -StaticEllpackIndexMultimap< ValuesCount, Index, Device, LocalIndex, SliceSize >:: -getKeysRange() const -{ - return this->keysRange; -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -void -StaticEllpackIndexMultimap< ValuesCount, Index, Device, LocalIndex, SliceSize >:: -allocate() -{ - const IndexType ldSize = getAllocationKeysRange( this->getKeysRange() ); - values.setSize( ldSize * ValuesCount ); - - // extra cost at initialization, which allows to have much simpler operator== - if( ldSize > 0 ) - values.setValue( 0 ); -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int SliceSize > - template< typename Device_ > -void -StaticEllpackIndexMultimap< ValuesCount, Index, Device, LocalIndex, SliceSize >:: -setLike( const StaticEllpackIndexMultimap< ValuesCount, Index, Device_, LocalIndex, SliceSize >& other ) -{ - const IndexType ldSize = getAllocationKeysRange( other.getKeysRange() ); - values.setSize( ldSize * ValuesCount ); - - // extra cost at initialization, which allows to have much simpler operator== - if( ldSize > 0 ) - values.setValue( 0 ); - - keysRange = other.keysRange; -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -__cuda_callable__ -typename StaticEllpackIndexMultimap< ValuesCount, Index, Device, LocalIndex, SliceSize >::ValuesAccessorType -StaticEllpackIndexMultimap< ValuesCount, Index, Device, LocalIndex, SliceSize >:: -getValues( const IndexType& inputIndex ) -{ - TNL_ASSERT( inputIndex < this->getKeysRange(), - std::cerr << "inputIndex = " << inputIndex << std::endl - << "this->getKeysRange() = " << this->getKeysRange() << std::endl; ); - TNL_ASSERT( getAllocationKeysRange( this->getKeysRange() ) * ValuesCount == this->values.getSize(), - std::cerr << "The map has not been reallocated after calling setKeysRange()." << std::endl - << "this->getKeysRange() = " << this->getKeysRange() << std::endl - << "this->values.getSize() = " << this->values.getSize() << std::endl; ); - const IndexType sliceIdx = inputIndex / SliceSize; - const IndexType sliceOffset = sliceIdx * SliceSize * ValuesCount; - const IndexType offset = sliceOffset + inputIndex - sliceIdx * SliceSize; - return ValuesAccessorType( &this->values[ offset ] ); -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -__cuda_callable__ -typename StaticEllpackIndexMultimap< ValuesCount, Index, Device, LocalIndex, SliceSize >::ConstValuesAccessorType -StaticEllpackIndexMultimap< ValuesCount, Index, Device, LocalIndex, SliceSize >:: -getValues( const IndexType& inputIndex ) const -{ - TNL_ASSERT( inputIndex < this->getKeysRange(), - std::cerr << "inputIndex = " << inputIndex << std::endl - << "this->getKeysRange() = " << this->getKeysRange() << std::endl; ); - TNL_ASSERT( getAllocationKeysRange( this->getKeysRange() ) * ValuesCount == this->values.getSize(), - std::cerr << "The map has not been reallocated after calling setKeysRange()." << std::endl - << "this->getKeysRange() = " << this->getKeysRange() << std::endl - << "this->values.getSize() = " << this->values.getSize() << std::endl; ); - const IndexType sliceIdx = inputIndex / SliceSize; - const IndexType sliceOffset = sliceIdx * SliceSize * ValuesCount; - const IndexType offset = sliceOffset + inputIndex - sliceIdx * SliceSize; - return ConstValuesAccessorType( &this->values[ offset ] ); -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -bool -StaticEllpackIndexMultimap< ValuesCount, Index, Device, LocalIndex, SliceSize >:: -operator==( const StaticEllpackIndexMultimap& other ) const -{ - return ( this->keysRange == other.keysRange && this->values == other.values ); -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -void -StaticEllpackIndexMultimap< ValuesCount, Index, Device, LocalIndex, SliceSize >:: -save( File& file ) const -{ - Object::save( file ); - file.save( &this->keysRange ); - file << this->values; -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -void -StaticEllpackIndexMultimap< ValuesCount, Index, Device, LocalIndex, SliceSize >:: -load( File& file ) -{ - Object::load( file ); - file.load( &this->keysRange ); - file >> this->values; -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -void -StaticEllpackIndexMultimap< ValuesCount, Index, Device, LocalIndex, SliceSize >:: -print( std::ostream& str ) const -{ - str << "[ "; - if( this->getKeysRange() > 0 ) - { - str << this->getValues( 0 ); - for( Index i = 1; i < this->getKeysRange(); i++ ) - str << ",\n " << this->getValues( i ); - } - str << " ]"; -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -__cuda_callable__ -Index -StaticEllpackIndexMultimap< ValuesCount, Index, Device, LocalIndex, SliceSize >:: -getAllocationKeysRange( IndexType keysRange ) const -{ - return SliceSize * roundUpDivision( keysRange, SliceSize ); -} - - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -std::ostream& operator << ( std::ostream& str, const StaticEllpackIndexMultimap< ValuesCount, Index, Device, LocalIndex, SliceSize >& multimap ) -{ - multimap.print( str ); - return str; -} - -} // namespace Multimaps -} // namespace Containers -} // namespace TNL diff --git a/src/TNL/Containers/Multimaps/StaticEllpackIndexMultimapValues.h b/src/TNL/Containers/Multimaps/StaticEllpackIndexMultimapValues.h deleted file mode 100644 index efae4f05173b9f0531cc12e96dd5644a5c72fefe..0000000000000000000000000000000000000000 --- a/src/TNL/Containers/Multimaps/StaticEllpackIndexMultimapValues.h +++ /dev/null @@ -1,108 +0,0 @@ -/*************************************************************************** - StaticEllpackIndexMultimapValues.h - description - ------------------- - begin : Sep 10, 2015 - copyright : (C) 2015 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 Containers { -namespace Multimaps { - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int step > -class StaticEllpackIndexMultimap; - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int step > -class StaticEllpackIndexMultimapValues -{ - public: - using DeviceType = Device; - using IndexType = Index; - using LocalIndexType = LocalIndex; - using NetworkType = StaticEllpackIndexMultimap< ValuesCount, IndexType, DeviceType, LocalIndexType, step >; - - __cuda_callable__ - StaticEllpackIndexMultimapValues(); - - __cuda_callable__ - StaticEllpackIndexMultimapValues( StaticEllpackIndexMultimapValues&& other ); - - __cuda_callable__ - StaticEllpackIndexMultimapValues& operator=( const StaticEllpackIndexMultimapValues& other ); - - // converting assignment, needed for 'const int' -> 'int' etc. - template< typename Index_, typename LocalIndex_, int step_ > - __cuda_callable__ - StaticEllpackIndexMultimapValues& operator=( const StaticEllpackIndexMultimapValues< ValuesCount, Index_, Device, LocalIndex_, step_ >& other ); - - __cuda_callable__ - StaticEllpackIndexMultimapValues& operator=( StaticEllpackIndexMultimapValues&& other ); - - __cuda_callable__ - void bind( const StaticEllpackIndexMultimapValues& other ); - - constexpr LocalIndexType getSize() const; - - constexpr LocalIndexType getAllocatedSize() const; - - __cuda_callable__ - void setValue( const LocalIndexType& portIndex, - const IndexType& value ); - - __cuda_callable__ - IndexType getValue( const LocalIndexType& portIndex ) const; - - __cuda_callable__ - IndexType& operator[]( const LocalIndexType& portIndex ); - - __cuda_callable__ - const IndexType& operator[]( const LocalIndexType& portIndex ) const; - - __cuda_callable__ - bool operator==( const StaticEllpackIndexMultimapValues& other ) const; - - __cuda_callable__ - bool operator!=( const StaticEllpackIndexMultimapValues& other ) const; - - void print( std::ostream& str ) const; - - protected: - __cuda_callable__ - StaticEllpackIndexMultimapValues( IndexType* values ); - - IndexType* values; - - friend StaticEllpackIndexMultimap< ValuesCount, IndexType, DeviceType, LocalIndexType, step >; - friend StaticEllpackIndexMultimap< ValuesCount, typename std::remove_const< IndexType >::type, DeviceType, LocalIndexType, step >; -}; - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int step > -std::ostream& operator << ( std::ostream& str, const StaticEllpackIndexMultimapValues< ValuesCount, Index, Device, LocalIndex, step >& ports ); - -} // namespace Multimaps -} // namespace Containers -} // namespace TNL - -#include diff --git a/src/TNL/Containers/Multimaps/StaticEllpackIndexMultimapValues.hpp b/src/TNL/Containers/Multimaps/StaticEllpackIndexMultimapValues.hpp deleted file mode 100644 index 2f3bd4df9f83eddf2430676c83f12b95a4e471b8..0000000000000000000000000000000000000000 --- a/src/TNL/Containers/Multimaps/StaticEllpackIndexMultimapValues.hpp +++ /dev/null @@ -1,285 +0,0 @@ -/*************************************************************************** - StaticEllpackIndexMultimapValues_impl.h - description - ------------------- - begin : Sep 10, 2015 - copyright : (C) 2015 by Tomas Oberhuber et al. - email : tomas.oberhuber@fjfi.cvut.cz - ***************************************************************************/ - -/* See Copyright Notice in tnl/Copyright */ - -#pragma once - -#include "StaticEllpackIndexMultimapValues.h" - -#include - -namespace TNL { -namespace Containers { -namespace Multimaps { - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -StaticEllpackIndexMultimapValues< ValuesCount, Index, Device, LocalIndex, step >:: -StaticEllpackIndexMultimapValues() -: values( nullptr ) -{ -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -StaticEllpackIndexMultimapValues< ValuesCount, Index, Device, LocalIndex, step >:: -StaticEllpackIndexMultimapValues( StaticEllpackIndexMultimapValues&& other ) -: values( other.values ) -{ - other.values = nullptr; -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -StaticEllpackIndexMultimapValues< ValuesCount, Index, Device, LocalIndex, step >:: -StaticEllpackIndexMultimapValues( IndexType* values ) -: values( values ) -{ -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -StaticEllpackIndexMultimapValues< ValuesCount, Index, Device, LocalIndex, step >& -StaticEllpackIndexMultimapValues< ValuesCount, Index, Device, LocalIndex, step >:: -operator=( const StaticEllpackIndexMultimapValues& other ) -{ - if( this->values != other.values ) { - for( LocalIndexType i = 0; i < this->getSize(); i++ ) - this->setValue( i, other[ i ] ); - } - return *this; -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int step > - template< typename Index_, typename LocalIndex_, int step_ > -__cuda_callable__ -StaticEllpackIndexMultimapValues< ValuesCount, Index, Device, LocalIndex, step >& -StaticEllpackIndexMultimapValues< ValuesCount, Index, Device, LocalIndex, step >:: -operator=( const StaticEllpackIndexMultimapValues< ValuesCount, Index_, Device, LocalIndex_, step_ >& other ) -{ - for( LocalIndexType i = 0; i < this->getSize(); i++ ) - this->setValue( i, other[ i ] ); - return *this; -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -StaticEllpackIndexMultimapValues< ValuesCount, Index, Device, LocalIndex, step >& -StaticEllpackIndexMultimapValues< ValuesCount, Index, Device, LocalIndex, step >:: -operator=( StaticEllpackIndexMultimapValues&& other ) -{ - this->values = other.values; - other.values = nullptr; - return *this; -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -void -StaticEllpackIndexMultimapValues< ValuesCount, Index, Device, LocalIndex, step >:: -bind( const StaticEllpackIndexMultimapValues& other ) -{ - this->values = other.values; -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int step > -constexpr LocalIndex -StaticEllpackIndexMultimapValues< ValuesCount, Index, Device, LocalIndex, step >:: -getSize() const -{ - return ValuesCount; -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int step > -constexpr LocalIndex -StaticEllpackIndexMultimapValues< ValuesCount, Index, Device, LocalIndex, step >:: -getAllocatedSize() const -{ - return ValuesCount; -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -void -StaticEllpackIndexMultimapValues< ValuesCount, Index, Device, LocalIndex, step >:: -setValue( const LocalIndexType& portIndex, - const IndexType& value ) -{ - TNL_ASSERT( this->values, - std::cerr << "This instance is not bound to any multimap." << std::endl; ); - TNL_ASSERT( portIndex < this->getSize(), - std::cerr << " portIndex = " << portIndex - << " getSize() = " << this->getSize() - << std::endl ); - this->values[ portIndex * step ] = value; -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -Index -StaticEllpackIndexMultimapValues< ValuesCount, Index, Device, LocalIndex, step >:: -getValue( const LocalIndexType& portIndex ) const -{ - TNL_ASSERT( this->values, - std::cerr << "This instance is not bound to any multimap." << std::endl; ); - TNL_ASSERT( portIndex < this->getSize(), - std::cerr << " portIndex = " << portIndex - << " getSize() = " << this->getSize() - << std::endl ); - return this->values[ portIndex * step ]; -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -Index& -StaticEllpackIndexMultimapValues< ValuesCount, Index, Device, LocalIndex, step >:: -operator[]( const LocalIndexType& portIndex ) -{ - TNL_ASSERT( this->values, - std::cerr << "This instance is not bound to any multimap." << std::endl; ); - TNL_ASSERT( portIndex < this->getSize(), - std::cerr << " portIndex = " << portIndex - << " getSize() = " << this->getSize() - << std::endl ); - return this->values[ portIndex * step ]; -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -const Index& -StaticEllpackIndexMultimapValues< ValuesCount, Index, Device, LocalIndex, step >:: -operator[]( const LocalIndexType& portIndex ) const -{ - TNL_ASSERT( this->values, - std::cerr << "This instance is not bound to any multimap." << std::endl; ); - TNL_ASSERT( portIndex < this->getSize(), - std::cerr << " portIndex = " << portIndex - << " getSize() = " << this->getSize() - << std::endl ); - return this->values[ portIndex * step ]; -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -bool -StaticEllpackIndexMultimapValues< ValuesCount, Index, Device, LocalIndex, step >:: -operator==( const StaticEllpackIndexMultimapValues& other ) const -{ - if( this->values == other.values ) - return true; - if( ! this->values || ! other.values ) - return false; - for( LocalIndexType i = 0; i < this->getSize(); i++ ) - if( this->operator[]( i ) != other[ i ] ) - return false; - return true; -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int step > -__cuda_callable__ -bool -StaticEllpackIndexMultimapValues< ValuesCount, Index, Device, LocalIndex, step >:: -operator!=( const StaticEllpackIndexMultimapValues& other ) const -{ - return ! ( *this == other ); -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int step > -void -StaticEllpackIndexMultimapValues< ValuesCount, Index, Device, LocalIndex, step >:: -print( std::ostream& str ) const -{ - str << "[ "; - if( this->getSize() > 0 ) - { - str << this->getValue( 0 ); - for( typename std::remove_const< Index >::type i = 1; i < this->getSize(); i++ ) - str << ", " << this->getValue( i ); - } - str << " ]"; -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int step > -std::ostream& operator << ( std::ostream& str, const StaticEllpackIndexMultimapValues< ValuesCount, Index, Device, LocalIndex, step >& ports ) -{ - ports.print( str ); - return str; -} - -} // namespace Multimaps -} // namespace Containers -} // namespace TNL diff --git a/src/TNL/Endianness.h b/src/TNL/Endianness.h new file mode 100644 index 0000000000000000000000000000000000000000..c2cfc19d90b60218a54d1707b9bb542864aaac10 --- /dev/null +++ b/src/TNL/Endianness.h @@ -0,0 +1,70 @@ +/*************************************************************************** + Endianness.h - description + ------------------- + begin : Mar 11, 2020 + copyright : (C) 2020 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +// Implemented by: Jakub Klinkovský + +#pragma once + +#include +#include + +namespace TNL { + +/** + * \brief Function takes a value and swaps its endianness. + * + * Reference: https://stackoverflow.com/a/4956493 + */ +template< typename T > +T swapEndianness(T u) +{ + static_assert( CHAR_BIT == 8, "CHAR_BIT != 8" ); + static_assert( std::is_fundamental< T >::value, "swap_endian works only for fundamental types" ); + + union + { + T u; + unsigned char u8[sizeof(T)]; + } source, dest; + + source.u = u; + + for (std::size_t k = 0; k < sizeof(T); k++) + dest.u8[k] = source.u8[sizeof(T) - k - 1]; + + return dest.u; +} + +/** + * \brief Function returns `true` iff the system executing the program is little endian. + */ +inline bool +isLittleEndian() +{ + const unsigned int tmp1 = 1; + const unsigned char *tmp2 = reinterpret_cast(&tmp1); + if (*tmp2 != 0) + return true; + return false; +} + +/** + * \brief Function takes a value and returns its big endian representation. + */ +template< typename T > +T forceBigEndian(T value) +{ + static bool swap = isLittleEndian(); + if( swap ) + return swapEndianness(value); + return value; +} + +} diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalProblem.h b/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalProblem.h index fff0e35dd4835dfd93112c749841d418dbf603eb..26d96d1218ba22aa8d421111bb25e3f3c6a4e326 100644 --- a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalProblem.h +++ b/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalProblem.h @@ -80,7 +80,7 @@ class tnlDirectEikonalProblem protected: - using DistributedMeshSynchronizerType = Meshes::DistributedMeshes::DistributedMeshSynchronizer< MeshFunctionType >; + using DistributedMeshSynchronizerType = Meshes::DistributedMeshes::DistributedMeshSynchronizer< Meshes::DistributedMeshes::DistributedMesh< typename MeshFunctionType::MeshType > >; DistributedMeshSynchronizerType synchronizer; MeshFunctionPointer u; diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod.h b/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod.h index 7023c09a967deb83dc31ac16c018ff09599e6f75..2f933cbecd67398cd16b5ae65a9c78e5880942fd 100644 --- a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod.h +++ b/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod.h @@ -113,7 +113,7 @@ class FastSweepingMethod< Meshes::Grid< 2, Real, Device, Index >, Communicator, protected: - using DistributedMeshSynchronizerType = Meshes::DistributedMeshes::DistributedMeshSynchronizer< MeshFunctionType >; + using DistributedMeshSynchronizerType = Meshes::DistributedMeshes::DistributedMeshSynchronizer< Meshes::DistributedMeshes::DistributedMesh< typename MeshFunctionType::MeshType > >; DistributedMeshSynchronizerType synchronizer; const IndexType maxIterations; @@ -174,7 +174,7 @@ class FastSweepingMethod< Meshes::Grid< 3, Real, Device, Index >, Communicator, protected: - using DistributedMeshSynchronizerType = Meshes::DistributedMeshes::DistributedMeshSynchronizer< MeshFunctionType >; + using DistributedMeshSynchronizerType = Meshes::DistributedMeshes::DistributedMeshSynchronizer< Meshes::DistributedMeshes::DistributedMesh< typename MeshFunctionType::MeshType > >; DistributedMeshSynchronizerType synchronizer; const IndexType maxIterations; diff --git a/src/TNL/Functions/MeshFunction.hpp b/src/TNL/Functions/MeshFunction.hpp index 028bc19dfafb94ce26fc247a86e9892b73294a77..6d8f91ebccbd1c80e53bf3fe8a9c13dfded18d0b 100644 --- a/src/TNL/Functions/MeshFunction.hpp +++ b/src/TNL/Functions/MeshFunction.hpp @@ -14,7 +14,8 @@ #include #include #include -#include +#include +#include #pragma once @@ -405,8 +406,22 @@ write( const String& fileName, std::cerr << "Unable to open a file " << fileName << "." << std::endl; return false; } - if( format == "vtk" ) - return MeshFunctionVTKWriter< MeshFunction >::write( *this, file ); + if( format == "vtk" ) { + Meshes::Writers::VTKWriter< Mesh > writer( file ); + writer.template writeEntities< getEntitiesDimension() >( *meshPointer ); + if( MeshFunction::getEntitiesDimension() == 0 ) + writer.writePointData( getData(), "cellFunctionValues", 1 ); + else + writer.writeCellData( getData(), "pointFunctionValues", 1 ); + } + else if( format == "vtu" ) { + Meshes::Writers::VTUWriter< Mesh > writer( file ); + writer.template writeEntities< getEntitiesDimension() >( *meshPointer ); + if( MeshFunction::getEntitiesDimension() == 0 ) + writer.writePointData( getData(), "cellFunctionValues", 1 ); + else + writer.writeCellData( getData(), "pointFunctionValues", 1 ); + } else if( format == "gnuplot" ) return MeshFunctionGnuplotWriter< MeshFunction >::write( *this, file ); else { diff --git a/src/TNL/Functions/MeshFunctionVTKWriter.h b/src/TNL/Functions/MeshFunctionVTKWriter.h deleted file mode 100644 index 10dd997a810c4b190c911631fd418ecfc940e7e4..0000000000000000000000000000000000000000 --- a/src/TNL/Functions/MeshFunctionVTKWriter.h +++ /dev/null @@ -1,56 +0,0 @@ -/*************************************************************************** - MeshFunctionVTKWriter.h - description - ------------------- - begin : Jan 28, 2016 - copyright : (C) 2016 by Tomas Oberhuber et al. - email : tomas.oberhuber@fjfi.cvut.cz - ***************************************************************************/ - -/* See Copyright Notice in tnl/Copyright */ - -#pragma once - -#include - -namespace TNL { -namespace Functions { - -template< typename MeshFunction > -class MeshFunctionVTKWriter -{ - using MeshType = typename MeshFunction::MeshType; - using MeshWriter = Meshes::Writers::VTKWriter< MeshType >; - using EntityType = typename MeshType::template EntityType< MeshFunction::getEntitiesDimension() >; - using GlobalIndex = typename MeshType::GlobalIndexType; - -public: - static bool write( const MeshFunction& function, - std::ostream& str, - const String& functionName = "cellFunctionValues" ) - { - const MeshType& mesh = function.getMesh(); - MeshWriter::template writeEntities< MeshFunction::getEntitiesDimension() >( mesh, str ); - appendFunction( function, str, functionName ); - return true; - } - - // VTK supports writing multiple functions into the same file. - // You can call this after 'write', which initializes the mesh entities, - // with different function name. - static void appendFunction( const MeshFunction& function, - std::ostream& str, - const String& functionName ) - { - const MeshType& mesh = function.getMesh(); - const GlobalIndex entitiesCount = mesh.template getEntitiesCount< EntityType >(); - str << std::endl << "CELL_DATA " << entitiesCount << std::endl; - str << "SCALARS " << functionName << " " << getType< typename MeshFunction::RealType >() << " 1" << std::endl; - str << "LOOKUP_TABLE default" << std::endl; - for( GlobalIndex i = 0; i < entitiesCount; i++ ) { - str << function.getData().getElement( i ) << "\n"; - } - } -}; - -} // namespace Functions -} // namespace TNL diff --git a/src/TNL/Functions/MeshFunctionView.hpp b/src/TNL/Functions/MeshFunctionView.hpp index b1845362effba9a90b4ee8f28cb84e8d2dc83e20..15b70c4edf8da7343489b145a1222ec1319d3712 100644 --- a/src/TNL/Functions/MeshFunctionView.hpp +++ b/src/TNL/Functions/MeshFunctionView.hpp @@ -14,7 +14,8 @@ #include #include #include -#include +#include +#include #pragma once @@ -490,8 +491,22 @@ write( const String& fileName, std::cerr << "Unable to open a file " << fileName << "." << std::endl; return false; } - if( format == "vtk" ) - return MeshFunctionVTKWriter< MeshFunctionView >::write( *this, file ); + if( format == "vtk" ) { + Meshes::Writers::VTKWriter< Mesh > writer( file ); + writer.template writeEntities< getEntitiesDimension() >( *meshPointer ); + if( MeshFunctionView::getEntitiesDimension() == 0 ) + writer.writePointData( getData(), "cellFunctionValues", 1 ); + else + writer.writeCellData( getData(), "pointFunctionValues", 1 ); + } + else if( format == "vtu" ) { + Meshes::Writers::VTUWriter< Mesh > writer( file ); + writer.template writeEntities< getEntitiesDimension() >( *meshPointer ); + if( MeshFunctionView::getEntitiesDimension() == 0 ) + writer.writePointData( getData(), "cellFunctionValues", 1 ); + else + writer.writeCellData( getData(), "pointFunctionValues", 1 ); + } else if( format == "gnuplot" ) return MeshFunctionGnuplotWriter< MeshFunctionView >::write( *this, file ); else { diff --git a/src/TNL/Functions/VectorField.h b/src/TNL/Functions/VectorField.h index e9a99113eef3673018ea4fc70ae9fb2a62741567..f0e875608bf16ef49b1ff3c88ad546c7b8633973 100644 --- a/src/TNL/Functions/VectorField.h +++ b/src/TNL/Functions/VectorField.h @@ -16,7 +16,8 @@ #include #include #include -#include +#include +#include namespace TNL { namespace Functions { @@ -259,8 +260,36 @@ class VectorField< Size, MeshFunction< Mesh, MeshEntityDimension, Real > > std::cerr << "Unable to open a file " << fileName << "." << std::endl; return false; } - if( format == "vtk" ) - return VectorFieldVTKWriter< VectorField >::write( *this, file ); + if( format == "vtk" || format == "vtu" ) { + // copy all values from the vector field into a contiguous array + using BufferType = Containers::Array< typename VectorField::RealType, Devices::Host, IndexType >; + const IndexType entitiesCount = getMeshPointer()->template getEntitiesCount< getEntitiesDimension() >(); + BufferType buffer( 3 * entitiesCount ); + IndexType k = 0; + for( IndexType i = 0; i < entitiesCount; i++ ) { + const VectorType vector = getElement( i ); + static_assert( getVectorDimension() <= 3, "VTK formats support only up to 3D vector fields." ); + for( int j = 0; j < 3; j++ ) + buffer[ k++ ] = ( j < vector.getSize() ? vector[ j ] : 0 ); + } + + if( format == "vtk" ) { + Meshes::Writers::VTKWriter< Mesh > writer( file ); + writer.template writeEntities< getEntitiesDimension() >( *getMeshPointer() ); + if( getEntitiesDimension() == 0 ) + writer.writePointData( buffer, "cellVectorFieldValues", 3 ); + else + writer.writeCellData( buffer, "pointVectorFieldValues", 3 ); + } + else if( format == "vtu" ) { + Meshes::Writers::VTUWriter< Mesh > writer( file ); + writer.template writeEntities< getEntitiesDimension() >( *getMeshPointer() ); + if( getEntitiesDimension() == 0 ) + writer.writePointData( buffer, "cellVectorFieldValues", 3 ); + else + writer.writeCellData( buffer, "pointVectorFieldValues", 3 ); + } + } else if( format == "gnuplot" ) return VectorFieldGnuplotWriter< VectorField >::write( *this, file ); else { @@ -504,8 +533,36 @@ class VectorField< Size, MeshFunctionView< Mesh, MeshEntityDimension, Real > > std::cerr << "Unable to open a file " << fileName << "." << std::endl; return false; } - if( format == "vtk" ) - return VectorFieldVTKWriter< VectorField >::write( *this, file ); + if( format == "vtk" || format == "vtu" ) { + // copy all values from the vector field into a contiguous array + using BufferType = Containers::Array< typename VectorField::RealType, Devices::Host, IndexType >; + const IndexType entitiesCount = getMeshPointer()->template getEntitiesCount< getEntitiesDimension() >(); + BufferType buffer( 3 * entitiesCount ); + IndexType k = 0; + for( IndexType i = 0; i < entitiesCount; i++ ) { + const VectorType vector = getElement( i ); + static_assert( getVectorDimension() <= 3, "VTK formats support only up to 3D vector fields." ); + for( int j = 0; j < 3; j++ ) + buffer[ k++ ] = ( j < vector.getSize() ? vector[ j ] : 0 ); + } + + if( format == "vtk" ) { + Meshes::Writers::VTKWriter< Mesh > writer( file ); + writer.template writeEntities< getEntitiesDimension() >( *getMeshPointer() ); + if( getEntitiesDimension() == 0 ) + writer.writePointData( buffer, "cellVectorFieldValues", 3 ); + else + writer.writeCellData( buffer, "pointVectorFieldValues", 3 ); + } + else if( format == "vtu" ) { + Meshes::Writers::VTUWriter< Mesh > writer( file ); + writer.template writeEntities< getEntitiesDimension() >( *getMeshPointer() ); + if( getEntitiesDimension() == 0 ) + writer.writePointData( buffer, "cellVectorFieldValues", 3 ); + else + writer.writeCellData( buffer, "pointVectorFieldValues", 3 ); + } + } else if( format == "gnuplot" ) return VectorFieldGnuplotWriter< VectorField >::write( *this, file ); else { @@ -514,15 +571,15 @@ class VectorField< Size, MeshFunctionView< Mesh, MeshEntityDimension, Real > > } return true; } - + using Object::save; - + using Object::load; - + protected: - + Containers::StaticArray< Size, FunctionPointer > vectorField; - + }; template< int Dimension, diff --git a/src/TNL/Functions/VectorFieldVTKWriter.h b/src/TNL/Functions/VectorFieldVTKWriter.h deleted file mode 100644 index c3ccc4b945aa1d942c6988e2a854cd2b2a9486da..0000000000000000000000000000000000000000 --- a/src/TNL/Functions/VectorFieldVTKWriter.h +++ /dev/null @@ -1,59 +0,0 @@ -/*************************************************************************** - VectorFieldVTKWriter.h - description - ------------------- - begin : Jan 10, 2018 - copyright : (C) 2018 by Tomas Oberhuber et al. - email : tomas.oberhuber@fjfi.cvut.cz - ***************************************************************************/ - -/* See Copyright Notice in tnl/Copyright */ - -#pragma once - -#include - -namespace TNL { -namespace Functions { - -template< typename VectorField > -class VectorFieldVTKWriter -{ - using MeshType = typename VectorField::MeshType; - using MeshWriter = Meshes::Writers::VTKWriter< MeshType >; - using EntityType = typename MeshType::template EntityType< VectorField::getEntitiesDimension() >; - using GlobalIndex = typename MeshType::GlobalIndexType; - -public: - static bool write( const VectorField& field, - std::ostream& str, - const String& fieldName = "cellVectorFieldValues" ) - { - const MeshType& mesh = field.getMesh(); - MeshWriter::template writeEntities< VectorField::getEntitiesDimension() >( mesh, str ); - appendField( field, str, fieldName ); - return true; - } - - // VTK supports writing multiple fields into the same file. - // You can call this after 'write', which initializes the mesh entities, - // with different field name. - static void appendField( const VectorField& field, - std::ostream& str, - const String& fieldName ) - { - const MeshType& mesh = field.getMesh(); - const GlobalIndex entitiesCount = mesh.template getEntitiesCount< EntityType >(); - str << std::endl << "CELL_DATA " << entitiesCount << std::endl; - str << "VECTORS " << fieldName << " " << getType< typename VectorField::RealType >() << " 1" << std::endl; - for( GlobalIndex i = 0; i < entitiesCount; i++ ) { - const typename VectorField::VectorType vector = field.getElement( i ); - static_assert( VectorField::getVectorDimension() <= 3, "The VTK format supports only up to 3D vector fields." ); - for( int i = 0; i < 3; i++ ) - str << ( i < vector.getSize() ? vector[ i ] : 0.0 ) << " "; - str << "\n"; - } - } -}; - -} // namespace Functions -} // namespace TNL diff --git a/src/TNL/Matrices/MatrixPermutationApplier.h b/src/TNL/Matrices/MatrixPermutationApplier.h new file mode 100644 index 0000000000000000000000000000000000000000..91cfbe54f548e745ced25cc69cdec6c3c4f6baf6 --- /dev/null +++ b/src/TNL/Matrices/MatrixPermutationApplier.h @@ -0,0 +1,97 @@ +/*************************************************************************** + MatrixPermutationApplier.h - description + ------------------- + begin : May 7, 2020 + copyright : (C) 2020 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +// Implemented by: Jakub Klinkovský + +#pragma once + +#include + +namespace TNL { +namespace Matrices { + +template< typename Matrix, + typename PermutationArray > +void permuteMatrixRows( Matrix& matrix, const PermutationArray& perm ) +{ + static_assert( std::is_same< typename Matrix::DeviceType, typename PermutationArray::DeviceType >::value, + "The matrix and permutation vector must be stored on the same device." ); + using IndexType = typename Matrix::IndexType; + using DeviceType = typename Matrix::DeviceType; + TNL_ASSERT_EQ( matrix.getRows(), perm.getSize(), "permutation size does not match the matrix size" ); + + // FIXME: getConstView does not work +// const auto matrix_view = matrix.getConstView(); + const auto matrix_view = matrix.getView(); + const auto perm_view = perm.getConstView(); + + // create temporary matrix for the permuted data + Matrix matrixCopy; + matrixCopy.setLike( matrix ); + + // permute the row capacities + typename Matrix::RowsCapacitiesType capacities( matrix.getRows() ); + auto capacities_view = capacities.getView(); + + auto kernel_capacities = [=] __cuda_callable__ ( IndexType i ) mutable + { + capacities_view[ i ] = matrix_view.getRowCapacity( perm_view[ i ] ); + }; + Algorithms::ParallelFor< DeviceType >::exec( (IndexType) 0, matrix.getRows(), kernel_capacities ); + + matrixCopy.setRowCapacities( capacities ); + auto copy_view = matrixCopy.getView(); + + auto kernel = [=] __cuda_callable__ ( IndexType i ) mutable + { + const auto srcRow = matrix_view.getRow( perm_view[ i ] ); + auto destRow = copy_view.getRow( i ); + for( IndexType c = 0; c < srcRow.getSize(); c++ ) + if( srcRow.isBinary() ) + destRow.setElement( c, srcRow.getColumnIndex( c ), true ); // the value does not matter + else + destRow.setElement( c, srcRow.getColumnIndex( c ), srcRow.getValue( c ) ); + }; + Algorithms::ParallelFor< DeviceType >::exec( (IndexType) 0, matrix.getRows(), kernel ); + + // copy the permuted data back into the matrix + matrix = matrixCopy; +} + +template< typename Matrix, + typename PermutationArray > +void permuteMatrixColumns( Matrix& matrix, const PermutationArray& iperm ) +{ + static_assert( std::is_same< typename Matrix::DeviceType, typename PermutationArray::DeviceType >::value, + "The matrix and permutation vector must be stored on the same device." ); + using IndexType = typename Matrix::IndexType; + using DeviceType = typename Matrix::DeviceType; + + auto matrix_view = matrix.getView(); + const auto iperm_view = iperm.getConstView(); + + auto kernel = [=] __cuda_callable__ ( IndexType i ) mutable + { + auto row = matrix_view.getRow( i ); + for( IndexType c = 0; c < row.getSize(); c++ ) { + const IndexType col = row.getColumnIndex( c ); + if( col == matrix_view.getPaddingIndex() ) + break; + if( row.isBinary() ) + row.setElement( c, iperm_view[ col ], true ); // the value does not matter + else + row.setElement( c, iperm_view[ col ], row.getValue( c ) ); + } + }; + Algorithms::ParallelFor< DeviceType >::exec( (IndexType) 0, matrix.getRows(), kernel ); +} + +} // namespace Matrices +} // namespace TNL diff --git a/src/TNL/Meshes/BuildConfigTags.h b/src/TNL/Meshes/BuildConfigTags.h index 35aa2b2f89041575587e3f9ac291db02475dd0fa..9539ef5dfc8595903b92d09fc67e1cabcdaefa1b 100644 --- a/src/TNL/Meshes/BuildConfigTags.h +++ b/src/TNL/Meshes/BuildConfigTags.h @@ -31,8 +31,10 @@ namespace BuildConfigTags { // 1, 2, and 3 dimensions are enabled by default template< typename ConfigTag, int Dimension > struct GridDimensionTag { enum { enabled = ( Dimension > 0 && Dimension <= 3 ) }; }; -// Grids are enabled for all real types by default. -template< typename ConfigTag, typename Real > struct GridRealTag { enum { enabled = true }; }; +// Grids are enabled only for the `float` and `double` real types by default. +template< typename ConfigTag, typename Real > struct GridRealTag { enum { enabled = false }; }; +template< typename ConfigTag > struct GridRealTag< ConfigTag, float > { enum { enabled = true }; }; +template< typename ConfigTag > struct GridRealTag< ConfigTag, double > { enum { enabled = true }; }; // Grids are enabled on all available devices by default. template< typename ConfigTag, typename Device > struct GridDeviceTag { enum { enabled = true }; }; @@ -40,8 +42,10 @@ template< typename ConfigTag, typename Device > struct GridDeviceTag { enum { en template< typename ConfigTag > struct GridDeviceTag< ConfigTag, Devices::Cuda > { enum { enabled = false }; }; #endif -// Grids are enabled for all index types by default. -template< typename ConfigTag, typename Index > struct GridIndexTag { enum { enabled = true }; }; +// Grids are enabled only for the `int` and `long int` index types by default. +template< typename ConfigTag, typename Index > struct GridIndexTag { enum { enabled = false }; }; +template< typename ConfigTag > struct GridIndexTag< ConfigTag, int > { enum { enabled = true }; }; +template< typename ConfigTag > struct GridIndexTag< ConfigTag, long int > { enum { enabled = true }; }; // The Grid is enabled for allowed dimensions and Real, Device and Index types. // @@ -85,26 +89,26 @@ template< typename ConfigTag, typename CellTopology > struct MeshCellTopologyTag // All sensible world dimensions are enabled by default. template< typename ConfigTag, typename CellTopology, int WorldDimension > struct MeshWorldDimensionTag { enum { enabled = ( WorldDimension >= CellTopology::dimension && WorldDimension <= 3 ) }; }; -// Meshes are enabled for all real types by default. -template< typename ConfigTag, typename Real > struct MeshRealTag { enum { enabled = true }; }; +// Meshes are enabled only for the `float` and `double` real types by default. +template< typename ConfigTag, typename Real > struct MeshRealTag { enum { enabled = false }; }; +template< typename ConfigTag > struct MeshRealTag< ConfigTag, float > { enum { enabled = true }; }; +template< typename ConfigTag > struct MeshRealTag< ConfigTag, double > { enum { enabled = true }; }; -// Meshes are enabled for all global index types by default. -template< typename ConfigTag, typename GlobalIndex > struct MeshGlobalIndexTag { enum { enabled = true }; }; +// Meshes are enabled only for the `int` and `long int` global index types by default. +template< typename ConfigTag, typename GlobalIndex > struct MeshGlobalIndexTag { enum { enabled = false }; }; +template< typename ConfigTag > struct MeshGlobalIndexTag< ConfigTag, int > { enum { enabled = true }; }; +template< typename ConfigTag > struct MeshGlobalIndexTag< ConfigTag, long int > { enum { enabled = true }; }; -// Meshes are enabled for all local index types by default. -template< typename ConfigTag, typename LocalIndex > struct MeshLocalIndexTag { enum { enabled = true }; }; - -// Meshes are enabled for 'GlobalIndex' and 'void' id types by default. -template< typename ConfigTag, typename GlobalIndex, typename Id > struct MeshIdTag { enum { enabled = false }; }; -template< typename ConfigTag, typename GlobalIndex > struct MeshIdTag< ConfigTag, GlobalIndex, void > { enum { enabled = true }; }; -template< typename ConfigTag, typename GlobalIndex > struct MeshIdTag< ConfigTag, GlobalIndex, GlobalIndex > { enum { enabled = true }; }; +// Meshes are enabled only for the `short int` local index type by default. +template< typename ConfigTag, typename LocalIndex > struct MeshLocalIndexTag { enum { enabled = false }; }; +template< typename ConfigTag > struct MeshLocalIndexTag< ConfigTag, short int > { enum { enabled = true }; }; // Config tag specifying the MeshConfig to use. template< typename ConfigTag > struct MeshConfigTemplateTag { - template< typename Cell, int WorldDimension, typename Real, typename GlobalIndex, typename LocalIndex, typename Id > - using MeshConfig = DefaultConfig< Cell, WorldDimension, Real, GlobalIndex, LocalIndex, Id >; + template< typename Cell, int WorldDimension, typename Real, typename GlobalIndex, typename LocalIndex > + using MeshConfig = DefaultConfig< Cell, WorldDimension, Real, GlobalIndex, LocalIndex >; }; // The Mesh is enabled for allowed Device, CellTopology, WorldDimension, Real, @@ -121,9 +125,9 @@ struct MeshConfigTemplateTag // // struct MeshTag< ConfigTag, // Mesh< typename MeshConfigTemplateTag< ConfigTag >:: -// template MeshConfig< CellTopology, WorldDimension, Real, GlobalIndex, LocalIndex, Id > > > +// template MeshConfig< CellTopology, WorldDimension, Real, GlobalIndex, LocalIndex > > > // -template< typename ConfigTag, typename Device, typename CellTopology, int WorldDimension, typename Real, typename GlobalIndex, typename LocalIndex, typename Id > +template< typename ConfigTag, typename Device, typename CellTopology, int WorldDimension, typename Real, typename GlobalIndex, typename LocalIndex > struct MeshTag { enum { enabled = @@ -132,8 +136,7 @@ struct MeshTag MeshWorldDimensionTag< ConfigTag, CellTopology, WorldDimension >::enabled && MeshRealTag< ConfigTag, Real >::enabled && MeshGlobalIndexTag< ConfigTag, GlobalIndex >::enabled && - MeshLocalIndexTag< ConfigTag, LocalIndex >::enabled && - MeshIdTag< ConfigTag, GlobalIndex, Id >::enabled + MeshLocalIndexTag< ConfigTag, LocalIndex >::enabled }; }; diff --git a/src/TNL/Meshes/DefaultConfig.h b/src/TNL/Meshes/DefaultConfig.h index 36635647477ff7b3d70a3dd0d92b836cea6da12b..e4eaba905d4fc9adbbb61223c487eac68be83092 100644 --- a/src/TNL/Meshes/DefaultConfig.h +++ b/src/TNL/Meshes/DefaultConfig.h @@ -16,8 +16,6 @@ #pragma once -#include -#include #include namespace TNL { @@ -25,51 +23,32 @@ namespace Meshes { /**** * Basic structure for mesh configuration. - * Setting Id to GlobalIndex enables storage of entity Id. - * It means that each mesh entity stores its index in its - * mesh storage layer. */ template< typename Cell, int WorldDimension = Cell::dimension, typename Real = double, typename GlobalIndex = int, - typename LocalIndex = GlobalIndex, - typename Id = void > + typename LocalIndex = short int > struct DefaultConfig { using CellTopology = Cell; using RealType = Real; using GlobalIndexType = GlobalIndex; using LocalIndexType = LocalIndex; - using IdType = Id; static constexpr int worldDimension = WorldDimension; static constexpr int meshDimension = Cell::dimension; - /**** - * Storage of mesh entities. - */ - static constexpr bool entityStorage( int dimension ) - { - /**** - * Vertices and cells must always be stored. - */ - return true; - //return ( dimension == 0 || dimension == cellDimension ); - } - /**** * Storage of subentities of mesh entities. */ template< typename EntityTopology > static constexpr bool subentityStorage( EntityTopology, int SubentityDimension ) { - /**** - * Subvertices of all stored entities must always be stored - */ - return entityStorage( EntityTopology::dimension ); - //return entityStorage( EntityTopology::dimension ) && - // SubentityDimension == 0; + return true; + // Subvertices must be stored for all entities which appear in other + // subentity or superentity mappings. + //return SubentityDimension == 0; } /**** @@ -79,7 +58,7 @@ struct DefaultConfig template< typename EntityTopology > static constexpr bool subentityOrientationStorage( EntityTopology, int SubentityDimension ) { - return ( SubentityDimension > 0 ); + return SubentityDimension > 0 && SubentityDimension < meshDimension; } /**** @@ -88,30 +67,42 @@ struct DefaultConfig template< typename EntityTopology > static constexpr bool superentityStorage( EntityTopology, int SuperentityDimension ) { - return entityStorage( EntityTopology::dimension ); - //return false; + return true; } /**** - * Storage of boundary tags of mesh entities. Necessary for the mesh traverser. + * Storage of mesh entity tags. Boundary tags are necessary for the mesh traverser. * * The configuration must satisfy the following necessary conditions in * order to provide boundary tags: - * - faces must be stored * - faces must store the cell indices in the superentity layer * - if dim(entity) < dim(face), the entities on which the tags are stored * must be stored as subentities of faces - * TODO: check this in the ConfigValidator */ template< typename EntityTopology > - static constexpr bool boundaryTagsStorage( EntityTopology ) + static constexpr bool entityTagsStorage( EntityTopology ) { using FaceTopology = typename Topologies::Subtopology< CellTopology, meshDimension - 1 >::Topology; - return entityStorage( meshDimension - 1 ) && - superentityStorage( FaceTopology(), meshDimension ) && + return superentityStorage( FaceTopology(), meshDimension ) && ( EntityTopology::dimension >= meshDimension - 1 || subentityStorage( FaceTopology(), EntityTopology::dimension ) ); //return false; } + + /**** + * Storage of the dual graph. + * + * If enabled, links from vertices to cells must be stored. + */ + static constexpr bool dualGraphStorage() + { + return true; + } + + /**** + * Cells must have at least this number of common vertices to be considered + * as neighbors in the dual graph. + */ + static constexpr int dualGraphMinCommonVertices = meshDimension; }; } // namespace Meshes diff --git a/src/TNL/Meshes/DistributedMeshes/DistributedGridSynchronizer.h b/src/TNL/Meshes/DistributedMeshes/DistributedGridSynchronizer.h index e5d467615d7410d75adc1d5366d5c0455922ea5f..5a11502403ca54be090c350b802e80d102926392 100644 --- a/src/TNL/Meshes/DistributedMeshes/DistributedGridSynchronizer.h +++ b/src/TNL/Meshes/DistributedMeshes/DistributedGridSynchronizer.h @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -19,25 +20,22 @@ #include namespace TNL { -namespace Meshes { -namespace DistributedMeshes { +namespace Meshes { +namespace DistributedMeshes { -template< typename MeshFunction, - int MeshDimension, +// NOTE: this specialization works only for synchronizations on cells +template< int MeshDimension, typename Index, typename Device, typename GridReal > -class DistributedMeshSynchronizer< MeshFunction, Grid< MeshDimension, GridReal, Device, Index > > +class DistributedMeshSynchronizer< DistributedMesh< Grid< MeshDimension, GridReal, Device, Index > >, MeshDimension > { - using RealType = typename MeshFunction::RealType; public: static constexpr int getMeshDimension() { return MeshDimension; }; static constexpr int getNeighborCount() {return DirectionCount::get();}; typedef typename Grid< MeshDimension, GridReal, Device, Index >::Cell Cell; - // FIXME: clang does not like this (incomplete type error) -// typedef typename Functions::MeshFunction< Grid< 3, GridReal, Device, Index >,EntityDimension, RealType> MeshFunctionType; - typedef typename Grid< MeshDimension, GridReal, Device, Index >::DistributedMeshType DistributedGridType; + typedef typename Grid< MeshDimension, GridReal, Device, Index >::DistributedMeshType DistributedGridType; typedef typename DistributedGridType::CoordinatesType CoordinatesType; using SubdomainOverlapsType = typename DistributedGridType::SubdomainOverlapsType; @@ -68,12 +66,12 @@ class DistributedMeshSynchronizer< MeshFunction, Grid< MeshDimension, GridReal, isSet = true; this->distributedGrid = distributedGrid; - + const SubdomainOverlapsType& lowerOverlap = this->distributedGrid->getLowerOverlap(); const SubdomainOverlapsType& upperOverlap = this->distributedGrid->getUpperOverlap(); - - const CoordinatesType& localBegin = this->distributedGrid->getLocalBegin(); - const CoordinatesType& localSize = this->distributedGrid->getLocalSize(); + + const CoordinatesType& localBegin = this->distributedGrid->getLocalBegin(); + const CoordinatesType& localSize = this->distributedGrid->getLocalSize(); const int *neighbors = distributedGrid->getNeighbors(); @@ -107,8 +105,6 @@ class DistributedMeshSynchronizer< MeshFunction, Grid< MeshDimension, GridReal, } sendSizes[ i ] = sendSize; - sendBuffers[ i ].setSize( sendSize ); - recieveBuffers[ i ].setSize( sendSize); if( this->periodicBoundariesCopyDirection == OverlapToBoundary && neighbors[ i ] == -1 ) @@ -123,16 +119,25 @@ class DistributedMeshSynchronizer< MeshFunction, Grid< MeshDimension, GridReal, bool periodicBoundaries = false, const PeriodicBoundariesMaskPointer& mask = PeriodicBoundariesMaskPointer( nullptr ) ) { + using RealType = typename MeshFunctionType::RealType; + static_assert( MeshFunctionType::getEntitiesDimension() == MeshFunctionType::getMeshDimension(), + "this specialization works only for synchronizations on cells" ); TNL_ASSERT_TRUE( isSet, "Synchronizer is not set, but used to synchronize" ); - if( !distributedGrid->isDistributed() ) return; - + if( !distributedGrid->isDistributed() ) return; + + // allocate buffers (setSize does nothing if the array size is already correct) + for( int i=0; igetNeighborCount(); i++ ) { + sendBuffers[ i ].setSize( sendSizes[ i ] * sizeof(RealType) ); + recieveBuffers[ i ].setSize( sendSizes[ i ] * sizeof(RealType)); + } + const int *neighbors = distributedGrid->getNeighbors(); const int *periodicNeighbors = distributedGrid->getPeriodicNeighbors(); - + //fill send buffers - copyBuffers( meshFunction, + copyBuffers( meshFunction, sendBuffers, sendBegin,sendDimensions, true, neighbors, @@ -145,25 +150,25 @@ class DistributedMeshSynchronizer< MeshFunction, Grid< MeshDimension, GridReal, group=*((typename CommunicatorType::CommunicationGroup *)(distributedGrid->getCommunicationGroup())); int requestsCount( 0 ); - //send everything, recieve everything + //send everything, recieve everything for( int i=0; igetNeighborCount(); i++ ) { - /*TNL_MPI_PRINT( "Sending data... " << i << " sizes -> " + /*TNL_MPI_PRINT( "Sending data... " << i << " sizes -> " << sendSizes[ i ] << "sendDimensions -> " << sendDimensions[ i ] << " upperOverlap -> " << this->distributedGrid->getUpperOverlap() );*/ if( neighbors[ i ] != -1 ) { //TNL_MPI_PRINT( "Sending data to node " << neighbors[ i ] ); - requests[ requestsCount++ ] = CommunicatorType::ISend( sendBuffers[ i ].getData(), sendSizes[ i ], neighbors[ i ], 0, group ); + requests[ requestsCount++ ] = CommunicatorType::ISend( reinterpret_cast( sendBuffers[ i ].getData() ), sendSizes[ i ], neighbors[ i ], 0, group ); //TNL_MPI_PRINT( "Receiving data from node " << neighbors[ i ] ); - requests[ requestsCount++ ] = CommunicatorType::IRecv( recieveBuffers[ i ].getData(), sendSizes[ i ], neighbors[ i ], 0, group ); + requests[ requestsCount++ ] = CommunicatorType::IRecv( reinterpret_cast( recieveBuffers[ i ].getData() ), sendSizes[ i ], neighbors[ i ], 0, group ); } else if( periodicBoundaries && sendSizes[ i ] !=0 ) { //TNL_MPI_PRINT( "Sending data to node " << periodicNeighbors[ i ] ); - requests[ requestsCount++ ] = CommunicatorType::ISend( sendBuffers[ i ].getData(), sendSizes[ i ], periodicNeighbors[ i ], 1, group ); + requests[ requestsCount++ ] = CommunicatorType::ISend( reinterpret_cast( sendBuffers[ i ].getData() ), sendSizes[ i ], periodicNeighbors[ i ], 1, group ); //TNL_MPI_PRINT( "Receiving data to node " << periodicNeighbors[ i ] ); - requests[ requestsCount++ ] = CommunicatorType::IRecv( recieveBuffers[ i ].getData(), sendSizes[ i ], periodicNeighbors[ i ], 1, group ); + requests[ requestsCount++ ] = CommunicatorType::IRecv( reinterpret_cast( recieveBuffers[ i ].getData() ), sendSizes[ i ], periodicNeighbors[ i ], 1, group ); } } @@ -182,12 +187,11 @@ class DistributedMeshSynchronizer< MeshFunction, Grid< MeshDimension, GridReal, } private: - template< typename Real_, - typename MeshFunctionType, + template< typename MeshFunctionType, typename PeriodicBoundariesMaskPointer > void copyBuffers( MeshFunctionType& meshFunction, - Containers::Array* buffers, + Containers::Array* buffers, CoordinatesType* begins, CoordinatesType* sizes, bool toBuffer, @@ -195,23 +199,24 @@ class DistributedMeshSynchronizer< MeshFunction, Grid< MeshDimension, GridReal, bool periodicBoundaries, const PeriodicBoundariesMaskPointer& mask ) { - using Helper = BufferEntitiesHelper< MeshFunctionType, PeriodicBoundariesMaskPointer, getMeshDimension(), Real_, Device >; - + using RealType = typename MeshFunctionType::RealType; + using Helper = BufferEntitiesHelper< MeshFunctionType, PeriodicBoundariesMaskPointer, getMeshDimension(), RealType, Device >; + for(int i=0;igetNeighborCount();i++) { bool isBoundary=( neighbor[ i ] == -1 ); if( ! isBoundary || periodicBoundaries ) { - Helper::BufferEntities( meshFunction, mask, buffers[ i ].getData(), isBoundary, begins[i], sizes[i], toBuffer ); + Helper::BufferEntities( meshFunction, mask, reinterpret_cast( buffers[ i ].getData() ), isBoundary, begins[i], sizes[i], toBuffer ); } } } private: - Containers::Array sendBuffers[getNeighborCount()]; - Containers::Array recieveBuffers[getNeighborCount()]; Containers::StaticArray< getNeighborCount(), int > sendSizes; + Containers::Array< std::uint8_t, Device, Index > sendBuffers[getNeighborCount()]; + Containers::Array< std::uint8_t, Device, Index > recieveBuffers[getNeighborCount()]; PeriodicBoundariesCopyDirection periodicBoundariesCopyDirection = BoundaryToOverlap; diff --git a/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h b/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h index a6e7c01f9887522deccc6165accab950e44f5c37..2ea6d8539d285304320a0a72a718f84e2dcb7e10 100644 --- a/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h +++ b/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h @@ -1,38 +1,243 @@ /*************************************************************************** DistributedMesh.h - description ------------------- - begin : March 17, 2017 - copyright : (C) 2017 by Tomas Oberhuber + begin : April 9, 2020 + copyright : (C) 2020 by Tomas Oberhuber et al. email : tomas.oberhuber@fjfi.cvut.cz ***************************************************************************/ /* See Copyright Notice in tnl/Copyright */ +// Implemented by: Jakub Klinkovský + #pragma once -#include +#include +#include +#include +#include namespace TNL { -namespace Meshes { +namespace Meshes { namespace DistributedMeshes { -template< typename MeshType > +template< typename Mesh > class DistributedMesh +: protected GlobalIndexStorageFamily< Mesh > { public: - // FIXME: this is not going to work - using SubdomainOverlapsType = Containers::StaticVector< MeshType::getMeshDimension(), typename MeshType::GlobalIndexType >; + using MeshType = Mesh; + using Config = typename Mesh::Config; + using DeviceType = typename Mesh::DeviceType; + using GlobalIndexType = typename Mesh::GlobalIndexType; + using LocalIndexType = typename Mesh::LocalIndexType; + using PointType = typename Mesh::PointType; + using RealType = typename PointType::RealType; + using GlobalIndexArray = typename Mesh::GlobalIndexArray; + using CommunicatorType = Communicators::MpiCommunicator; + using CommunicationGroup = typename CommunicatorType::CommunicationGroup; + using VTKTypesArrayType = Containers::Array< std::uint8_t, Devices::Sequential, GlobalIndexType >; + + DistributedMesh() = default; + + DistributedMesh( MeshType&& localMesh ) + : localMesh( std::move(localMesh) ) + {} + + DistributedMesh( const DistributedMesh& ) = default; + + DistributedMesh( DistributedMesh&& ) = default; + + DistributedMesh& operator=( const DistributedMesh& ) = default; + + DistributedMesh& operator=( DistributedMesh&& ) = default; + + template< typename Mesh_ > + DistributedMesh& operator=( const Mesh_& other ) + { + GlobalIndexStorageFamily< Mesh >::operator=( other ); + localMesh = other.getLocalMesh(); + group = other.getCommunicationGroup(); + ghostLevels = other.getGhostLevels(); + vtkPointGhostTypesArray = other.vtkPointGhostTypes(); + vtkCellGhostTypesArray = other.vtkCellGhostTypes(); + return *this; + } + + bool operator==( const DistributedMesh& other ) const + { + return ( GlobalIndexStorageFamily< Mesh, DeviceType >::operator==( other ) && + localMesh == other.getLocalMesh() && + group == other.getCommunicationGroup() && + ghostLevels == other.getGhostLevels() && + vtkPointGhostTypesArray == other.vtkPointGhostTypes() && + vtkCellGhostTypesArray == other.vtkCellGhostTypes() ); + } + + bool operator!=( const DistributedMesh& other ) const + { + return ! operator==( other ); + } + + /** + * Common methods redirected to the local mesh + */ + static constexpr int getMeshDimension() + { + return MeshType::getMeshDimension(); + } + + // types of common entities + using Cell = typename MeshType::template EntityType< getMeshDimension() >; + using Face = typename MeshType::template EntityType< getMeshDimension() - 1 >; + using Vertex = typename MeshType::template EntityType< 0 >; + + static_assert( Mesh::Config::entityTagsStorage( typename Cell::EntityTopology{} ), + "DistributedMesh must store entity tags on cells" ); + static_assert( Mesh::Config::entityTagsStorage( typename Vertex::EntityTopology{} ), + "DistributedMesh must store entity tags on vertices" ); + + + /** + * Methods specific to the distributed mesh + */ + void setCommunicationGroup( CommunicationGroup group ) + { + this->group = group; + } + + CommunicationGroup getCommunicationGroup() const + { + return group; + } + + const MeshType& getLocalMesh() const + { + return localMesh; + } - bool IsDistributed(void) + MeshType& getLocalMesh() { - return false; - }; + return localMesh; + } + + void setGhostLevels( int levels ) + { + ghostLevels = levels; + } + + int getGhostLevels() const + { + return ghostLevels; + } + + template< int Dimension > + const GlobalIndexArray& + getGlobalIndices() const + { + return GlobalIndexStorage< MeshType, DeviceType, Dimension >::getGlobalIndices(); + } + + template< int Dimension > + GlobalIndexArray& + getGlobalIndices() + { + return GlobalIndexStorage< MeshType, DeviceType, Dimension >::getGlobalIndices(); + } + + VTKTypesArrayType& + vtkCellGhostTypes() + { + return vtkCellGhostTypesArray; + } - bool setup( const Config::ParameterContainer& parameters, - const String& prefix ) + const VTKTypesArrayType& + vtkCellGhostTypes() const { - return false; + return vtkCellGhostTypesArray; } + + VTKTypesArrayType& + vtkPointGhostTypes() + { + return vtkPointGhostTypesArray; + } + + const VTKTypesArrayType& + vtkPointGhostTypes() const + { + return vtkPointGhostTypesArray; + } + + // wrapper for MeshType::reorderEntities - reorders the local mesh, global indices and, + // if applicable, the vtkCellGhostTypes/vtkPointGhostTypes arrays + template< int Dimension > + void reorderEntities( const GlobalIndexArray& perm, + const GlobalIndexArray& iperm ) + { + localMesh.template reorderEntities< Dimension >( perm, iperm ); + if( getGlobalIndices< Dimension >().getSize() > 0 ) + IndexPermutationApplier< MeshType, Dimension >::permuteArray( getGlobalIndices< Dimension >(), perm ); + if( Dimension == 0 && vtkPointGhostTypes().getSize() > 0 ) + IndexPermutationApplier< MeshType, Dimension >::permuteArray( vtkPointGhostTypes(), perm ); + if( Dimension == getMeshDimension() && vtkCellGhostTypes().getSize() > 0 ) + IndexPermutationApplier< MeshType, Dimension >::permuteArray( vtkCellGhostTypes(), perm ); + } + + void + printInfo( std::ostream& str ) const + { + const GlobalIndexType verticesCount = localMesh.template getEntitiesCount< 0 >(); + const GlobalIndexType cellsCount = localMesh.template getEntitiesCount< Mesh::getMeshDimension() >(); + + CommunicatorType::Barrier(); + for( int i = 0; i < CommunicatorType::GetSize(); i++ ) { + if( i == CommunicatorType::GetRank() ) { + str << "MPI rank:\t" << CommunicatorType::GetRank() << "\n" + << "\tMesh dimension:\t" << getMeshDimension() << "\n" + << "\tCell topology:\t" << getType( typename Cell::EntityTopology{} ) << "\n" + << "\tCells count:\t" << cellsCount << "\n" + << "\tvertices count:\t" << verticesCount << "\n" + << "\tGhost levels:\t" << getGhostLevels() << "\n" + << "\tGhost cells count:\t" << localMesh.template getGhostEntitiesCount< Mesh::getMeshDimension() >() << "\n" + << "\tGhost vertices count:\t" << localMesh.template getGhostEntitiesCount< 0 >() << "\n" + << "\tBoundary cells count:\t" << localMesh.template getBoundaryIndices< Mesh::getMeshDimension() >().getSize() << "\n" + << "\tBoundary vertices count:\t" << localMesh.template getBoundaryIndices< 0 >().getSize() << "\n"; + const GlobalIndexType globalPointIndices = getGlobalIndices< 0 >().getSize(); + const GlobalIndexType globalCellIndices = getGlobalIndices< Mesh::getMeshDimension() >().getSize(); + if( getGhostLevels() > 0 ) { + if( globalPointIndices != verticesCount ) + str << "ERROR: array of global point indices has wrong size: " << globalPointIndices << "\n"; + if( globalCellIndices != cellsCount ) + str << "ERROR: array of global cell indices has wrong size: " << globalCellIndices << "\n"; + if( vtkPointGhostTypesArray.getSize() != verticesCount ) + str << "ERROR: array of VTK point ghost types has wrong size: " << vtkPointGhostTypesArray.getSize() << "\n"; + if( vtkCellGhostTypesArray.getSize() != cellsCount ) + str << "ERROR: array of VTK cell ghost types has wrong size: " << vtkCellGhostTypesArray.getSize() << "\n"; + } + else { + if( globalPointIndices > 0 ) + str << "WARNING: mesh has 0 ghost levels, but array of global point indices has non-zero size: " << globalPointIndices << "\n"; + if( globalCellIndices > 0 ) + str << "WARNING: mesh has 0 ghost levels, but array of global cell indices has non-zero size: " << globalCellIndices << "\n"; + if( vtkPointGhostTypesArray.getSize() > 0 ) + str << "WARNING: mesh has 0 ghost levels, but array of VTK point ghost types has non-zero size: " << vtkPointGhostTypesArray.getSize() << "\n"; + if( vtkCellGhostTypesArray.getSize() > 0 ) + str << "WARNING: mesh has 0 ghost levels, but array of VTK cell ghost types has non-zero size: " << vtkCellGhostTypesArray.getSize() << "\n"; + } + str.flush(); + } + CommunicatorType::Barrier(); + } + } + +protected: + MeshType localMesh; + CommunicationGroup group = CommunicatorType::NullGroup; + int ghostLevels = 0; + + // vtkGhostType arrays for points and cells (cached for output into VTK formats) + VTKTypesArrayType vtkPointGhostTypesArray, vtkCellGhostTypesArray; }; } // namespace DistributedMeshes diff --git a/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h b/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h index b1f9d51ef564461e828c319036f455570ab88c64..724510bf4a6c576eafd5ba58d5d1065d4733a674 100644 --- a/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h +++ b/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h @@ -1,36 +1,494 @@ /*************************************************************************** DistributedMeshSynchronizer.h - description ------------------- - begin : January 8, 2017 - copyright : (C) 2017 by Tomas Oberhuber + begin : April 12, 2020 + copyright : (C) 2020 by Tomas Oberhuber et al. email : tomas.oberhuber@fjfi.cvut.cz ***************************************************************************/ /* See Copyright Notice in tnl/Copyright */ +// Implemented by: Jakub Klinkovský + #pragma once -#include +#include +#include namespace TNL { namespace Meshes { namespace DistributedMeshes { -template< typename MeshFunction, - // Mesh is used only for DistributedGrid specializations - typename Mesh = typename MeshFunction::MeshType > +template< typename T, typename Enable = void > +struct HasMeshType +: public std::false_type +{}; + +template< typename T > +struct HasMeshType< T, typename Containers::Expressions::enable_if_type< typename T::MeshType >::type > +: public std::true_type +{}; + +template< typename DistributedMesh, + int EntityDimension = DistributedMesh::getMeshDimension() > class DistributedMeshSynchronizer { - public: +public: + using DeviceType = typename DistributedMesh::DeviceType; + using GlobalIndexType = typename DistributedMesh::GlobalIndexType; + using CommunicatorType = typename DistributedMesh::CommunicatorType; + + DistributedMeshSynchronizer() = default; + + void initialize( const DistributedMesh& mesh ) + { + if( mesh.getGhostLevels() <= 0 ) + throw std::logic_error( "There are no ghost levels on the distributed mesh." ); + TNL_ASSERT_EQ( mesh.template getGlobalIndices< EntityDimension >().getSize(), mesh.getLocalMesh().template getEntitiesCount< EntityDimension >(), + "Global indices are not allocated properly." ); + + // GOTCHA: https://devblogs.nvidia.com/cuda-pro-tip-always-set-current-device-avoid-multithreading-bugs/ + #ifdef HAVE_CUDA + if( std::is_same< DeviceType, Devices::Cuda >::value ) + cudaGetDevice(&this->gpu_id); + #endif + + group = mesh.getCommunicationGroup(); + const int rank = CommunicatorType::GetRank( group ); + const int nproc = CommunicatorType::GetSize( group ); + + // exchange the global index offsets so that each rank can determine the + // owner of every entity by its global index + const GlobalIndexType ownStart = mesh.template getGlobalIndices< EntityDimension >().getElement( 0 ); + globalOffsets.setSize( nproc ); + { + Containers::Array< GlobalIndexType, Devices::Host, int > sendbuf( nproc ); + sendbuf.setValue( ownStart ); + CommunicatorType::Alltoall( sendbuf.getData(), 1, + globalOffsets.getData(), 1, + group ); + } + + // count local ghost entities for each rank + Containers::Array< GlobalIndexType, Devices::Host, int > localGhostCounts( nproc ); + { + localGhostCounts.setValue( 0 ); + Containers::Array< GlobalIndexType, Devices::Host, GlobalIndexType > hostGlobalIndices; + hostGlobalIndices = mesh.template getGlobalIndices< EntityDimension >(); + GlobalIndexType prev_global_idx = 0; + mesh.getLocalMesh().template forGhost< EntityDimension, Devices::Sequential >( + [&] ( GlobalIndexType local_idx ) + { + if( ! std::is_same< DeviceType, Devices::Cuda >::value ) + if( ! mesh.getLocalMesh().template isGhostEntity< EntityDimension >( local_idx ) ) + throw std::runtime_error( "encountered local entity while iterating over ghost entities - the mesh is probably inconsistent or there is a bug in the DistributedMeshSynchronizer" ); + const GlobalIndexType global_idx = hostGlobalIndices[ local_idx ]; + if( global_idx < prev_global_idx ) + throw std::runtime_error( "ghost indices are not sorted - the mesh is probably inconsistent or there is a bug in the DistributedMeshSynchronizer" ); + prev_global_idx = global_idx; + const int owner = getEntityOwner( global_idx ); + if( owner == rank ) + throw std::runtime_error( "the owner of a ghost entity cannot be the local rank - the mesh is probably inconsistent or there is a bug in the DistributedMeshSynchronizer" ); + ++localGhostCounts[ owner ]; + } + ); + } + + // exchange the ghost counts + ghostEntitiesCounts.setDimensions( nproc, nproc ); + { + Matrices::DenseMatrix< GlobalIndexType, Devices::Host, int > sendbuf; + sendbuf.setDimensions( nproc, nproc ); + // copy the local ghost counts into all rows of the sendbuf + for( int j = 0; j < nproc; j++ ) + for( int i = 0; i < nproc; i++ ) + sendbuf.setElement( j, i, localGhostCounts[ i ] ); + CommunicatorType::Alltoall( &sendbuf(0, 0), nproc, + &ghostEntitiesCounts(0, 0), nproc, + group ); + } + + // allocate ghost offsets + ghostOffsets.setSize( nproc + 1 ); + + // set ghost neighbor offsets + ghostNeighborOffsets.setSize( nproc + 1 ); + ghostNeighborOffsets[ 0 ] = 0; + for( int i = 0; i < nproc; i++ ) + ghostNeighborOffsets[ i + 1 ] = ghostNeighborOffsets[ i ] + ghostEntitiesCounts( i, rank ); + + // allocate ghost neighbors + ghostNeighbors.setSize( ghostNeighborOffsets[ nproc ] ); + + // send indices of ghost entities - set them as ghost neighbors on the target rank + { + std::vector< typename CommunicatorType::Request > requests; + + // send our ghost indices to the neighboring ranks + GlobalIndexType ghostOffset = mesh.getLocalMesh().template getGhostEntitiesOffset< EntityDimension >(); + ghostOffsets[ 0 ] = ghostOffset; + for( int i = 0; i < nproc; i++ ) { + if( ghostEntitiesCounts( rank, i ) > 0 ) { + requests.push_back( CommunicatorType::ISend( + mesh.template getGlobalIndices< EntityDimension >().getData() + ghostOffset, + ghostEntitiesCounts( rank, i ), + i, 0, group ) ); + ghostOffset += ghostEntitiesCounts( rank, i ); + } + // update ghost offsets + ghostOffsets[ i + 1 ] = ghostOffset; + } + TNL_ASSERT_EQ( ghostOffsets[ nproc ], mesh.getLocalMesh().template getEntitiesCount< EntityDimension >(), + "bug in setting ghost offsets" ); + + // receive ghost indices from the neighboring ranks + for( int j = 0; j < nproc; j++ ) { + if( ghostEntitiesCounts( j, rank ) > 0 ) { + requests.push_back( CommunicatorType::IRecv( + ghostNeighbors.getData() + ghostNeighborOffsets[ j ], + ghostEntitiesCounts( j, rank ), + j, 0, group ) ); + } + } + + // wait for all communications to finish + CommunicatorType::WaitAll( requests.data(), requests.size() ); + + // convert received ghost indices from global to local + ghostNeighbors -= ownStart; + } + } + + template< typename MeshFunction, + std::enable_if_t< HasMeshType< MeshFunction >::value, bool > = true > + void synchronize( MeshFunction& function ) + { + static_assert( MeshFunction::getEntitiesDimension() == EntityDimension, + "the mesh function's entity dimension does not match" ); + static_assert( std::is_same< typename MeshFunction::MeshType, typename DistributedMesh::MeshType >::value, + "The type of the mesh function's mesh does not match the local mesh." ); + + synchronize( function.getData() ); + } + + template< typename Array, + std::enable_if_t< ! HasMeshType< Array >::value, bool > = true > + void synchronize( Array& array ) + { + // wrapped only because nvcc is fucked up and does not like __cuda_callable__ lambdas in enable_if methods + synchronizeArray( array ); + } + + template< typename Array > + void synchronizeArray( Array& array, int valuesPerElement = 1 ) + { + TNL_ASSERT_EQ( array.getSize(), valuesPerElement * ghostOffsets[ ghostOffsets.getSize() - 1 ], + "The array does not have the expected size." ); + using ValueType = typename Array::ValueType; + + // GOTCHA: https://devblogs.nvidia.com/cuda-pro-tip-always-set-current-device-avoid-multithreading-bugs/ + #ifdef HAVE_CUDA + if( std::is_same< DeviceType, Devices::Cuda >::value ) + cudaSetDevice(gpu_id); + #endif + + const int rank = CommunicatorType::GetRank( group ); + const int nproc = CommunicatorType::GetSize( group ); + + // allocate send buffers (setSize does nothing if the array size is already correct) + sendBuffers.setSize( valuesPerElement * ghostNeighborOffsets[ nproc ] * sizeof(ValueType) ); + + // buffer for asynchronous communication requests + std::vector< typename CommunicatorType::Request > requests; + + // issue all receive async operations + for( int j = 0; j < nproc; j++ ) { + if( ghostEntitiesCounts( rank, j ) > 0 ) { + requests.push_back( CommunicatorType::IRecv( + array.getData() + valuesPerElement * ghostOffsets[ j ], + valuesPerElement * ghostEntitiesCounts( rank, j ), + j, 0, group ) ); + } + } + + Containers::ArrayView< ValueType, DeviceType, GlobalIndexType > sendBuffersView; + sendBuffersView.bind( reinterpret_cast( sendBuffers.getData() ), valuesPerElement * ghostNeighborOffsets[ nproc ] ); + const auto ghostNeighborsView = ghostNeighbors.getConstView(); + const auto arrayView = array.getConstView(); + auto copy_kernel = [sendBuffersView, arrayView, ghostNeighborsView, valuesPerElement] __cuda_callable__ ( GlobalIndexType k, GlobalIndexType offset ) mutable + { + for( int i = 0; i < valuesPerElement; i++ ) + sendBuffersView[ i + valuesPerElement * (offset + k) ] = arrayView[ i + valuesPerElement * ghostNeighborsView[ offset + k ] ]; + }; - // FIXME: clang does not like this (incomplete type error) -// typedef typename MeshFunctionType::DistributedMeshType DistributedMeshType; + for( int i = 0; i < nproc; i++ ) { + if( ghostEntitiesCounts( i, rank ) > 0 ) { + const GlobalIndexType offset = ghostNeighborOffsets[ i ]; + // copy data to send buffers + Algorithms::ParallelFor< DeviceType >::exec( (GlobalIndexType) 0, ghostEntitiesCounts( i, rank ), copy_kernel, offset ); - template< typename DistributedMeshType > - void setDistributedGrid( DistributedMeshType *distributedGrid ) + // issue async send operation + requests.push_back( CommunicatorType::ISend( + sendBuffersView.getData() + valuesPerElement * ghostNeighborOffsets[ i ], + valuesPerElement * ghostEntitiesCounts( i, rank ), + i, 0, group ) ); + } + } + + // wait for all communications to finish + CommunicatorType::WaitAll( requests.data(), requests.size() ); + } + + // performs a synchronization of a sparse matrix + // - row indices -- ghost indices (recv), ghost neighbors (send) + // - column indices -- values to be synchronized (in a CSR-like format) + // NOTE: if all shared rows have the same capacity on all ranks that send/receive data + // from these rows (e.g. if all row capacities in the matrix are constant), + // set assumeConsistentRowCapacities to true + // returns: a tuple of the received rankOffsets, rowPointers and columnIndices arrays + template< typename SparsePattern > + auto + synchronizeSparse( const SparsePattern& pattern, bool assumeConsistentRowCapacities = false ) + { + TNL_ASSERT_EQ( pattern.getRows(), ghostOffsets[ ghostOffsets.getSize() - 1 ], "invalid sparse pattern matrix" ); + + const int rank = CommunicatorType::GetRank( group ); + const int nproc = CommunicatorType::GetSize( group ); + + // buffer for asynchronous communication requests + std::vector< typename CommunicatorType::Request > requests; + + Containers::Array< GlobalIndexType, Devices::Host, int > send_rankOffsets( nproc + 1 ), recv_rankOffsets( nproc + 1 ); + Containers::Array< GlobalIndexType, Devices::Host, GlobalIndexType > send_rowCapacities, send_rowPointers, send_columnIndices, recv_rowPointers, recv_columnIndices; + + // sending part { - throw Exceptions::NotImplementedError("Distributed version of this mesh type is not implemented."); + // set rank offsets + send_rankOffsets[ 0 ] = 0; + for( int i = 0; i < nproc; i++ ) + send_rankOffsets[ i + 1 ] = send_rankOffsets[ i ] + ghostNeighborOffsets[ i + 1 ] - ghostNeighborOffsets[ i ]; + + // allocate row pointers + send_rowPointers.setSize( send_rankOffsets[ nproc ] + 1 ); + if( ! assumeConsistentRowCapacities ) + send_rowCapacities.setSize( send_rankOffsets[ nproc ] + 1 ); + + // set row pointers + GlobalIndexType rowPtr = 0; + send_rowPointers[ rowPtr ] = 0; + for( int i = 0; i < nproc; i++ ) { + if( i == rank ) + continue; + for( GlobalIndexType r = ghostNeighborOffsets[ i ]; r < ghostNeighborOffsets[ i + 1 ]; r++ ) { + const GlobalIndexType rowIdx = ghostNeighbors[ r ]; + const auto row = pattern.getRow( rowIdx ); + send_rowPointers[ rowPtr + 1 ] = send_rowPointers[ rowPtr ] + row.getSize(); + if( ! assumeConsistentRowCapacities ) + send_rowCapacities[ rowPtr ] = row.getSize(); + rowPtr++; + } + // send our row sizes to the target rank + if( ! assumeConsistentRowCapacities ) + // issue async send operation + requests.push_back( CommunicatorType::ISend( + send_rowCapacities.getData() + send_rankOffsets[ i ], + ghostNeighborOffsets[ i + 1 ] - ghostNeighborOffsets[ i ], + i, 1, group ) ); + } + + // allocate column indices + send_columnIndices.setSize( send_rowPointers[ send_rowPointers.getSize() - 1 ] ); + + // set column indices + for( int i = 0; i < nproc; i++ ) { + if( i == rank ) + continue; + const GlobalIndexType rankOffset = send_rankOffsets[ i ]; + GlobalIndexType rowBegin = send_rowPointers[ rankOffset ]; + for( GlobalIndexType r = ghostNeighborOffsets[ i ]; r < ghostNeighborOffsets[ i + 1 ]; r++ ) { + const GlobalIndexType rowIdx = ghostNeighbors[ r ]; + const auto row = pattern.getRow( rowIdx ); + for( GlobalIndexType c = 0; c < row.getSize(); c++ ) + send_columnIndices[ rowBegin + c ] = row.getColumnIndex( c ); + rowBegin += row.getSize(); + } + } + + for( int i = 0; i < nproc; i++ ) { + if( send_rankOffsets[ i + 1 ] == send_rankOffsets[ i ] ) + continue; + // issue async send operation + requests.push_back( CommunicatorType::ISend( + send_columnIndices.getData() + send_rowPointers[ send_rankOffsets[ i ] ], + send_rowPointers[ send_rankOffsets[ i + 1 ] ] - send_rowPointers[ send_rankOffsets[ i ] ], + i, 0, group ) ); + } + } + + // receiving part + { + // set rank offsets + recv_rankOffsets[ 0 ] = 0; + for( int i = 0; i < nproc; i++ ) + recv_rankOffsets[ i + 1 ] = recv_rankOffsets[ i ] + ghostOffsets[ i + 1 ] - ghostOffsets[ i ]; + + // allocate row pointers + recv_rowPointers.setSize( recv_rankOffsets[ nproc ] + 1 ); + + std::vector< typename CommunicatorType::Request > row_lengths_requests; + + // set row pointers + GlobalIndexType rowPtr = 0; + recv_rowPointers[ rowPtr ] = 0; + for( int i = 0; i < nproc; i++ ) { + if( i == rank ) + continue; + if( assumeConsistentRowCapacities ) { + for( GlobalIndexType rowIdx = ghostOffsets[ i ]; rowIdx < ghostOffsets[ i + 1 ]; rowIdx++ ) { + const auto row = pattern.getRow( rowIdx ); + recv_rowPointers[ rowPtr + 1 ] = recv_rowPointers[ rowPtr ] + row.getSize(); + rowPtr++; + } + } + else { + // receive row sizes from the sender + // issue async recv operation + row_lengths_requests.push_back( CommunicatorType::IRecv( + recv_rowPointers.getData() + recv_rankOffsets[ i ], + ghostOffsets[ i + 1 ] - ghostOffsets[ i ], + i, 1, group ) ); + } + } + + if( ! assumeConsistentRowCapacities ) { + // wait for all row lengths + CommunicatorType::WaitAll( row_lengths_requests.data(), row_lengths_requests.size() ); + + // scan the rowPointers array to convert + Containers::VectorView< GlobalIndexType, Devices::Host, GlobalIndexType > rowPointersView; + rowPointersView.bind( recv_rowPointers ); + rowPointersView.template scan< Algorithms::ScanType::Exclusive >(); + } + + // allocate column indices + recv_columnIndices.setSize( recv_rowPointers[ recv_rowPointers.getSize() - 1 ] ); + + for( int i = 0; i < nproc; i++ ) { + if( recv_rankOffsets[ i + 1 ] == recv_rankOffsets[ i ] ) + continue; + // issue async recv operation + requests.push_back( CommunicatorType::IRecv( + recv_columnIndices.getData() + recv_rowPointers[ recv_rankOffsets[ i ] ], + recv_rowPointers[ recv_rankOffsets[ i + 1 ] ] - recv_rowPointers[ recv_rankOffsets[ i ] ], + i, 0, group ) ); + } } + + // wait for all communications to finish + CommunicatorType::WaitAll( requests.data(), requests.size() ); + + return std::make_tuple( recv_rankOffsets, recv_rowPointers, recv_columnIndices ); + } + + // get entity owner based on its global index - can be used only after running initialize() + int getEntityOwner( GlobalIndexType global_idx ) const + { + const int nproc = globalOffsets.getSize(); + for( int i = 0; i < nproc - 1; i++ ) + if( globalOffsets[ i ] <= global_idx && global_idx < globalOffsets[ i + 1 ] ) + return i; + return nproc - 1; + }; + + // public const accessors for the communication pattern matrix and index arrays which were + // created in the `initialize` method + const auto& getGlobalOffsets() const + { + return globalOffsets; + } + + const auto& getGhostEntitiesCounts() const + { + return ghostEntitiesCounts; + } + + const auto& getGhostOffsets() const + { + return ghostOffsets; + } + + const auto& getGhostNeighborOffsets() const + { + return ghostNeighborOffsets; + } + + const auto& getGhostNeighbors() const + { + return ghostNeighbors; + } + +protected: + // GOTCHA (see above) + int gpu_id = 0; + + // communication group taken from the distributed mesh + typename CommunicatorType::CommunicationGroup group; + + /** + * Global offsets: array of size nproc where the i-th value is the lowest + * global index of the entities owned by the i-th rank. This can be used + * to determine the owner of every entity based on its global index. + */ + Containers::Array< GlobalIndexType, Devices::Host, int > globalOffsets; + + /** + * Communication pattern: + * - an unsymmetric nproc x nproc matrix G such that G_ij represents the + * number of ghost entities on rank i that are owned by rank j + * - assembly of the i-th row involves traversal of the ghost entities on the + * local mesh and determining its owner based on the global index + * - assembly of the full matrix needs all-to-all communication + * - for the i-th rank, the i-th row determines the receive buffer sizes and + * the i-th column determines the send buffer sizes + */ + Matrices::DenseMatrix< GlobalIndexType, Devices::Host, int > ghostEntitiesCounts; + + /** + * Ghost offsets: array of size nproc + 1 where the i-th value is the local + * index of the first ghost entity owned by the i-th rank. The last value is + * equal to the entities count on the local mesh. All ghost entities owned by + * the i-th rank are assumed to be indexed contiguously in the local mesh. + */ + Containers::Array< GlobalIndexType, Devices::Host, int > ghostOffsets; + + /** + * Ghost neighbor offsets: array of size nproc + 1 where the i-th value is + * the offset of ghost neighbor indices requested by the i-th rank. The last + * value is the size of the ghostNeighbors and sendBuffers arrays (see + * below). + */ + Containers::Array< GlobalIndexType, Devices::Host, int > ghostNeighborOffsets; + + /** + * Ghost neighbor indices: array containing local indices of the entities + * which are ghosts on other ranks. The indices requested by the i-th rank + * are in the range starting at ghostNeighborOffsets[i] (inclusive) and + * ending at ghostNeighborOffsets[i+1] (exclusive). These indices are used + * for copying the mesh function values into the sendBuffers array. Note that + * ghost neighbor indices cannot be made contiguous in general so we need the + * send buffers. + */ + Containers::Vector< GlobalIndexType, DeviceType, GlobalIndexType > ghostNeighbors; + + /** + * Send buffers: array for buffering the mesh function values which will be + * sent to other ranks. We use std::uint8_t as the value type to make this + * class independent of the mesh function's real type. When cast to the real + * type values, the send buffer for the i-th rank is the part of the array + * starting at index ghostNeighborOffsets[i] (inclusive) and ending at index + * ghostNeighborOffsets[i+1] (exclusive). + */ + Containers::Array< std::uint8_t, DeviceType, GlobalIndexType > sendBuffers; }; } // namespace DistributedMeshes diff --git a/src/TNL/Meshes/DistributedMeshes/GlobalIndexStorage.h b/src/TNL/Meshes/DistributedMeshes/GlobalIndexStorage.h new file mode 100644 index 0000000000000000000000000000000000000000..cba45a9e798e743abe18c4c1a7afd3f747e60d3e --- /dev/null +++ b/src/TNL/Meshes/DistributedMeshes/GlobalIndexStorage.h @@ -0,0 +1,112 @@ +/*************************************************************************** + GlobalIndexStorage.h - description + ------------------- + begin : April 11, 2020 + copyright : (C) 2020 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +// Implemented by: Jakub Klinkovský + +#pragma once + +#include + +namespace TNL { +namespace Meshes { +namespace DistributedMeshes { + +template< typename Mesh, typename Device, int Dimension > +class GlobalIndexStorage +{ +public: + using GlobalIndexArray = typename Mesh::GlobalIndexArray; + + GlobalIndexStorage() = default; + GlobalIndexStorage( const GlobalIndexStorage& ) = default; + GlobalIndexStorage( GlobalIndexStorage&& ) = default; + GlobalIndexStorage& operator=( const GlobalIndexStorage& ) = default; + GlobalIndexStorage& operator=( GlobalIndexStorage&& ) = default; + + template< typename Mesh_ > + GlobalIndexStorage& operator=( const Mesh_& mesh ) + { + globalIndices = mesh.template getGlobalIndices< Dimension >(); + return *this; + } + + bool operator==( const GlobalIndexStorage& other ) const + { + return globalIndices == other.getGlobalIndices(); + } + + const GlobalIndexArray& + getGlobalIndices() const + { + return globalIndices; + } + + GlobalIndexArray& + getGlobalIndices() + { + return globalIndices; + } + +protected: + GlobalIndexArray globalIndices; +}; + +template< typename Mesh, typename Device = typename Mesh::DeviceType, typename DimensionTag = Meshes::DimensionTag< 0 > > +class GlobalIndexStorageFamily +: public GlobalIndexStorage< Mesh, Device, DimensionTag::value >, + public GlobalIndexStorageFamily< Mesh, Device, typename DimensionTag::Increment > +{ +public: + GlobalIndexStorageFamily() = default; + GlobalIndexStorageFamily( const GlobalIndexStorageFamily& ) = default; + GlobalIndexStorageFamily( GlobalIndexStorageFamily&& ) = default; + GlobalIndexStorageFamily& operator=( const GlobalIndexStorageFamily& ) = default; + GlobalIndexStorageFamily& operator=( GlobalIndexStorageFamily&& ) = default; + + template< typename Mesh_ > + GlobalIndexStorageFamily& operator=( const Mesh_& mesh ) + { + GlobalIndexStorage< Mesh, Device, DimensionTag::value >::operator=( mesh ); + GlobalIndexStorageFamily< Mesh, Device, typename DimensionTag::Increment >::operator=( mesh ); + return *this; + } + + bool operator==( const GlobalIndexStorageFamily& other ) const + { + return GlobalIndexStorage< Mesh, Device, DimensionTag::value >::operator==( other ) && + GlobalIndexStorageFamily< Mesh, Device, typename DimensionTag::Increment >::operator==( other ); + } +}; + +template< typename Mesh, typename Device > +class GlobalIndexStorageFamily< Mesh, Device, Meshes::DimensionTag< Mesh::getMeshDimension() + 1 > > +{ +public: + GlobalIndexStorageFamily() = default; + GlobalIndexStorageFamily( const GlobalIndexStorageFamily& ) = default; + GlobalIndexStorageFamily( GlobalIndexStorageFamily&& ) = default; + GlobalIndexStorageFamily& operator=( const GlobalIndexStorageFamily& ) = default; + GlobalIndexStorageFamily& operator=( GlobalIndexStorageFamily&& ) = default; + + template< typename Mesh_ > + GlobalIndexStorageFamily& operator=( const Mesh_& ) + { + return *this; + } + + bool operator==( const GlobalIndexStorageFamily& ) const + { + return true; + } +}; + +} // namespace DistributedMeshes +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/DistributedMeshes/distributeSubentities.h b/src/TNL/Meshes/DistributedMeshes/distributeSubentities.h new file mode 100644 index 0000000000000000000000000000000000000000..2135ffcee20369212ddcabfb0337cc4fd32af7f1 --- /dev/null +++ b/src/TNL/Meshes/DistributedMeshes/distributeSubentities.h @@ -0,0 +1,223 @@ +/*************************************************************************** + distributeSubentities.h - description + ------------------- + begin : July 13, 2020 + copyright : (C) 2020 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +// Implemented by: Jakub Klinkovský + +#pragma once + +#include // std::iota +#include + +#include +#include + +#include + +namespace TNL { +namespace Meshes { +namespace DistributedMeshes { + +template< int Dimension, typename DistributedMesh > +void +distributeSubentities( DistributedMesh& mesh ) +{ + using DeviceType = typename DistributedMesh::DeviceType; + using GlobalIndexType = typename DistributedMesh::GlobalIndexType; + using LocalIndexType = typename DistributedMesh::LocalIndexType; + using LocalMesh = typename DistributedMesh::MeshType; + using CommunicatorType = typename DistributedMesh::CommunicatorType; + + static_assert( ! std::is_same< DeviceType, Devices::Cuda >::value, + "this method can be called only for host meshes" ); + static_assert( 0 < Dimension && Dimension < DistributedMesh::getMeshDimension(), + "Vertices and cells cannot be distributed using this method." ); + if( mesh.getGhostLevels() <= 0 ) + throw std::logic_error( "There are no ghost levels on the distributed mesh." ); + + const int rank = CommunicatorType::GetRank( mesh.getCommunicationGroup() ); + const int nproc = CommunicatorType::GetSize( mesh.getCommunicationGroup() ); + + // 0. exchange vertex data to prepare getVertexOwner and later on synchronizeSparse + DistributedMeshSynchronizer< DistributedMesh, 0 > synchronizer; + synchronizer.initialize( mesh ); + + auto getVertexOwner = [&] ( GlobalIndexType local_idx ) -> int + { + const GlobalIndexType global_idx = mesh.template getGlobalIndices< 0 >()[ local_idx ]; + return synchronizer.getEntityOwner( global_idx ); + }; + + // find which rank owns all vertices of its local cells + int rankOwningAllLocalCellSubvertices = nproc; + { + std::atomic its_us( true ); + mesh.getLocalMesh().template forLocal< DistributedMesh::getMeshDimension() >( [&] ( GlobalIndexType i ) mutable { + for( LocalIndexType v = 0; v < mesh.getLocalMesh().template getSubentitiesCount< DistributedMesh::getMeshDimension(), 0 >( i ); v++ ) { + const GlobalIndexType gv = mesh.getLocalMesh().template getSubentityIndex< DistributedMesh::getMeshDimension(), 0 >( i, v ); + if( getVertexOwner( gv ) != rank ) + its_us = false; + } + }); + Containers::Array< bool, Devices::Host, int > recvbuf( nproc ), sendbuf( nproc ); + sendbuf.setValue( its_us ); + CommunicatorType::Alltoall( sendbuf.getData(), 1, + recvbuf.getData(), 1, + mesh.getCommunicationGroup() ); + for( int i = 0; i < nproc; i++ ) + if( recvbuf[ i ] ) { + rankOwningAllLocalCellSubvertices = i; + break; + } + } + if( rankOwningAllLocalCellSubvertices != 0 && rankOwningAllLocalCellSubvertices != nproc - 1 ) + throw std::runtime_error("Vertices are not distributed consistently. Shared vertices on the boundaries must be assigned " + "either to the highest or to the lowest rank. Thus, either the first or the last rank must " + "own all subvertices of its local cells."); + + auto getEntityOwner = [&] ( GlobalIndexType local_idx ) -> int + { + auto entity = mesh.getLocalMesh().template getEntity< Dimension >( local_idx ); + int owner = (rankOwningAllLocalCellSubvertices == 0) ? 0 : nproc; + if( rankOwningAllLocalCellSubvertices == 0 ) { + // this assumes that vertices at the boundaries were assigned to the subdomain with the lowest rank + // (this is used in DistributedMeshTest for simplicitty) + for( LocalIndexType v = 0; v < entity.template getSubentitiesCount< 0 >(); v++ ) { + const GlobalIndexType gv = entity.template getSubentityIndex< 0 >( v ); + owner = TNL::max( owner, getVertexOwner( gv ) ); + } + } + else { + // this assumes that vertices at the boundaries were assigned to the subdomain with the highest rank + // (this is what tnl-decompose-mesh does) + for( LocalIndexType v = 0; v < entity.template getSubentitiesCount< 0 >(); v++ ) { + const GlobalIndexType gv = entity.template getSubentityIndex< 0 >( v ); + owner = TNL::min( owner, getVertexOwner( gv ) ); + } + } + return owner; + }; + + // 1. identify local entities, set the GhostEntity tag on others + LocalMesh& localMesh = mesh.getLocalMesh(); + localMesh.template forAll< Dimension >( [&] ( GlobalIndexType i ) mutable { + if( getEntityOwner( i ) != rank ) + localMesh.template addEntityTag< Dimension >( i, EntityTags::GhostEntity ); + }); + + // 2. reorder the entities to make sure that all ghost entities are after local entities + // TODO: it would be nice if the mesh initializer could do this + { + // count local entities + GlobalIndexType localEntitiesCount = 0; + for( GlobalIndexType i = 0; i < localMesh.template getEntitiesCount< Dimension >(); i++ ) + if( ! localMesh.template isGhostEntity< Dimension >( i ) ) + ++localEntitiesCount; + + // create the permutation + typename LocalMesh::GlobalIndexArray perm, iperm; + perm.setSize( localMesh.template getEntitiesCount< Dimension >() ); + iperm.setSize( localMesh.template getEntitiesCount< Dimension >() ); + GlobalIndexType localsCount = 0; + GlobalIndexType ghostsCount = 0; + for( GlobalIndexType j = 0; j < iperm.getSize(); j++ ) { + if( localMesh.template isGhostEntity< Dimension >( j ) ) { + iperm[ j ] = localEntitiesCount + ghostsCount; + perm[ localEntitiesCount + ghostsCount ] = j; + ++ghostsCount; + } + else { + iperm[ j ] = localsCount; + perm[ localsCount ] = j; + ++localsCount; + } + } + + // reorder the local mesh + localMesh.template reorderEntities< Dimension >( perm, iperm ); + } + + // 3. update entity tags layer (this is actually done as part of the mesh reordering) + //localMesh.template updateEntityTagsLayer< Dimension >(); + + // 4. exchange the counts of local entities between ranks, compute offsets for global indices + Containers::Vector< GlobalIndexType, Devices::Host, int > globalOffsets( nproc ); + { + Containers::Array< GlobalIndexType, Devices::Host, int > sendbuf( nproc ); + sendbuf.setValue( localMesh.template getGhostEntitiesOffset< Dimension >() ); + CommunicatorType::Alltoall( sendbuf.getData(), 1, + globalOffsets.getData(), 1, + mesh.getCommunicationGroup() ); + } + globalOffsets.template scan< Algorithms::ScanType::Exclusive >(); + + // 5. assign global indices to the local entities + mesh.template getGlobalIndices< Dimension >().setSize( localMesh.template getEntitiesCount< Dimension >() ); + localMesh.template forLocal< Dimension >( [&] ( GlobalIndexType i ) mutable { + mesh.template getGlobalIndices< Dimension >()[ i ] = globalOffsets[ rank ] + i; + }); + + // 6. exchange local indices for ghost entities + // We have to synchronize the vertex-entity superentity matrix, synchronization based + // on the cell-entity subentity matrix is not general. For example, two subdomains can + // have a common face, but no common cell, even when ghost_levels > 0. On the other + // hand, if two subdomains have a common face, they have common all its subvertices, + // so it is ensured that we send/receive indices for all ghost entities (with a rather + // great redundancy). + const auto sparseResult = synchronizer.synchronizeSparse( localMesh.template getSuperentitiesMatrix< 0, Dimension >() ); + const auto& rankOffsets = std::get< 0 >( sparseResult ); + const auto& rowPointers = std::get< 1 >( sparseResult ); + const auto& columnIndices = std::get< 2 >( sparseResult ); + + // 7. set the global indices of our ghost entities + localMesh.template forGhost< Dimension >( [&] ( GlobalIndexType entityIndex ) mutable { + const int owner = getEntityOwner( entityIndex ); + for( LocalIndexType v = 0; v < localMesh.template getSubentitiesCount< Dimension, 0 >( entityIndex ); v++ ) { + const GlobalIndexType vertex = localMesh.template getSubentityIndex< Dimension, 0 >( entityIndex, v ); + const int vertexOwner = getVertexOwner( vertex ); + if( vertexOwner == owner ) { + const GlobalIndexType ghostOffset = vertex - synchronizer.getGhostOffsets()[ vertexOwner ]; + // global index = owner's local index + owner's offset + const GlobalIndexType globalEntityIndex = columnIndices[ rowPointers[ rankOffsets[ vertexOwner ] + ghostOffset ] + v ] + globalOffsets[ owner ]; + mesh.template getGlobalIndices< Dimension >()[ entityIndex ] = globalEntityIndex; + break; + } + } + }); + + // 8. reorder the entities to make sure that global indices are sorted + { + // prepare vector with an identity permutation + std::vector< GlobalIndexType > permutation( localMesh.template getEntitiesCount< Dimension >() ); + std::iota( permutation.begin(), permutation.end(), (GlobalIndexType) 0 ); + + // sort the subarray corresponding to ghost entities by the global index + std::stable_sort( permutation.begin() + mesh.getLocalMesh().template getGhostEntitiesOffset< Dimension >(), + permutation.end(), + [&mesh](auto& left, auto& right) { + return mesh.template getGlobalIndices< Dimension >()[ left ] < mesh.template getGlobalIndices< Dimension >()[ right ]; + }); + + // copy the permutation into TNL array and invert + typename LocalMesh::GlobalIndexArray perm, iperm; + perm.setSize( localMesh.template getEntitiesCount< Dimension >() ); + iperm.setSize( localMesh.template getEntitiesCount< Dimension >() ); + for( GlobalIndexType i = 0; i < localMesh.template getEntitiesCount< Dimension >(); i++ ) { + perm[ i ] = permutation[ i ]; + iperm[ perm[ i ] ] = i; + } + + // reorder the mesh + mesh.template reorderEntities< Dimension >( perm, iperm ); + } +} + +} // namespace DistributedMeshes +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/DistributedMeshes/loadDistributedMesh.h b/src/TNL/Meshes/DistributedMeshes/loadDistributedMesh.h new file mode 100644 index 0000000000000000000000000000000000000000..135e3c15a8c5f64b3032b2d2dba28a0b3d964bd9 --- /dev/null +++ b/src/TNL/Meshes/DistributedMeshes/loadDistributedMesh.h @@ -0,0 +1,208 @@ +/*************************************************************************** + loadDistributedMesh.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 +#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" ) +{ + std::cout << "Detecting distributed mesh from file " << fileName << " ..." << std::endl; + + auto wrapper = [&functor] ( Readers::MeshReader& reader, auto&& localMesh ) + { + using LocalMesh = std::decay_t< decltype(localMesh) >; + using DistributedMesh = DistributedMeshes::DistributedMesh< LocalMesh >; + return std::forward(functor)( reader, DistributedMesh{ std::move(localMesh) } ); + }; + + namespace fs = std::experimental::filesystem; + std::string format = fileFormat; + if( format == "auto" ) { + format = fs::path(fileName).extension(); + if( format.length() > 0 ) + // remove dot from the extension + format = format.substr(1); + } + + if( format == "pvtu" ) { + // FIXME: The XML VTK files don't store the local index type. + // The reader has some defaults, but they might be disabled by the BuildConfigTags - in + // this case we should use the first enabled type. + Readers::PVTUReader reader( fileName ); + reader.detectMesh(); + if( reader.getMeshType() == "Meshes::DistributedMesh" ) { + return MeshTypeResolver< ConfigTag, Device >::run( static_cast(reader), wrapper ); + } + else { + std::cerr << "The mesh type " << reader.getMeshType() << " is not supported in the VTK reader." << std::endl; + return false; + } + } + else { + if( fileFormat == "auto" ) + std::cerr << "File '" << fileName << "' has unsupported format (based on the file extension): " << format << "."; + else + std::cerr << "Unsupported fileFormat parameter: " << fileFormat << "."; + std::cerr << " Supported formats are 'pvtu'." << std::endl; + return false; + } +} + +template< typename ConfigTag, + typename Device, + typename Functor > +bool +resolveAndLoadDistributedMesh( Functor&& functor, + const std::string& fileName, + const std::string& fileFormat = "auto" ) +{ + auto wrapper = [&]( Readers::MeshReader& reader, auto&& mesh ) -> bool + { + using MeshType = std::decay_t< decltype(mesh) >; + std::cout << "Loading a mesh from the file " << fileName << " ..." << std::endl; + try { + dynamic_cast(reader).loadMesh( mesh ); + } + catch( const Meshes::Readers::MeshReaderError& e ) { + std::cerr << "Failed to load the mesh from the file " << fileName << ". The error is:\n" << e.what() << std::endl; + return false; + } + return functor( reader, std::forward(mesh) ); + }; + return resolveDistributedMeshType< ConfigTag, Device >( wrapper, fileName, fileFormat ); +} + +template< typename CommunicatorType, + 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" ) +{ + // TODO: simplify interface, pass only the distributed mesh + TNL_ASSERT_EQ( &mesh, &distributedMesh.getLocalMesh(), "mesh is not local mesh of the distributed mesh" ); + + namespace fs = std::experimental::filesystem; + std::string format = fileFormat; + if( format == "auto" ) { + format = fs::path(fileName).extension(); + if( format.length() > 0 ) + // remove dot from the extension + format = format.substr(1); + } + + if( format == "pvtu" ) { + Readers::PVTUReader reader( fileName ); + reader.loadMesh( distributedMesh ); + return true; + } + else { + if( fileFormat == "auto" ) + std::cerr << "File '" << fileName << "' has unsupported format (based on the file extension): " << format << "."; + else + std::cerr << "Unsupported fileFormat parameter: " << fileFormat << "."; + std::cerr << " Supported formats are 'pvtu'." << std::endl; + return false; + } +} + +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 ) +{ + std::cerr << "Distributed Mesh is not supported yet, only Distributed Grid is supported."; + return false; +} + +// overloads for grids +template< typename CommunicatorType, + 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" ) +{ + std::cout << "Loading a global mesh from the file " << fileName << "..."; + Grid< Dimension, Real, Device, Index > globalGrid; + try + { + globalGrid.load( fileName ); + } + catch(...) + { + std::cerr << std::endl; + std::cerr << "I am not able to load the global mesh from the file " << fileName << "." << std::endl; + return false; + } + std::cout << " [ OK ] " << std::endl; + + typename Meshes::DistributedMeshes::DistributedMesh>::SubdomainOverlapsType overlap; + distributedMesh.template setGlobalGrid< CommunicatorType >( globalGrid ); + distributedMesh.setupGrid(mesh); + return true; +} + +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 ) +{ + using GridType = Grid< Dimension, Real, Device, Index >; + using DistributedGridType = DistributedMeshes::DistributedMesh< GridType >; + using SubdomainOverlapsType = typename DistributedGridType::SubdomainOverlapsType; + using CommunicatorType = typename Problem::CommunicatorType; + + SubdomainOverlapsType lower( 0 ), upper( 0 ); + distributedMesh.setOverlaps( lower, upper ); + distributedMesh.setupGrid( mesh ); + + problem.getSubdomainOverlaps( parameters, prefix, mesh, lower, upper ); + distributedMesh.setOverlaps( lower, upper ); + distributedMesh.setupGrid( mesh ); + + return true; +} + +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/DummyMesh.h b/src/TNL/Meshes/DummyMesh.h index 04a49ed0118ac8d7d2bcf8534b6634d3ff3a49da..e0caf6d3c403fd6227f7b8ab269c0ea1cc9e5267 100644 --- a/src/TNL/Meshes/DummyMesh.h +++ b/src/TNL/Meshes/DummyMesh.h @@ -29,19 +29,6 @@ public: constexpr static int getMeshDimension() { return 1; } - const Real& getParametricStep(){ return 0.0; } - - String getSerializationType() const { return String( "DummyMesh" ); } - - template< typename GridFunction > - typename GridFunction::RealType getDifferenceAbsMax( const GridFunction& f1, - const GridFunction& f2 ) const { return 0.0; } - - template< typename GridFunction > - typename GridFunction::RealType getDifferenceLpNorm( const GridFunction& f1, - const GridFunction& f2, - const typename GridFunction::RealType& p ) const { return 0.0; } - void save( File& file ) const {} void load( File& file ) {} @@ -49,15 +36,6 @@ public: void save( const String& fileName ) const {} void load( const String& fileName ) {} - - bool writeMesh( const String& fileName, - const String& format ) const { return true; } - - - template< typename MeshFunction > - bool write( const MeshFunction& function, - const String& fileName, - const String& format ) const { return true; } }; } // namespace Meshes diff --git a/src/TNL/Meshes/Geometry/getEntityCenter.h b/src/TNL/Meshes/Geometry/getEntityCenter.h index 4cceff7067581a9ff4dc105ab254e34a4213c370..6e869f6ec2655797d56b11adee010160eeb2890f 100644 --- a/src/TNL/Meshes/Geometry/getEntityCenter.h +++ b/src/TNL/Meshes/Geometry/getEntityCenter.h @@ -50,14 +50,13 @@ getEntityCenter( const Mesh< MeshConfig, Device > & mesh, const MeshEntity< MeshConfig, Device, EntityTopology > & entity ) { using EntityType = MeshEntity< MeshConfig, Device, EntityTopology >; - constexpr typename MeshConfig::LocalIndexType subvertices = EntityType::template getSubentitiesCount< 0 >(); + constexpr typename MeshConfig::LocalIndexType subvertices = EntityType::template SubentityTraits< 0 >::count; typename MeshTraits< MeshConfig >::PointType c = 0; for( typename MeshConfig::LocalIndexType i = 0; i < subvertices; i++ ) { - const auto& v = mesh.template getEntity< 0 >( entity.template getSubentityIndex< 0 >( i ) ); - c += v.getPoint(); + c += mesh.getPoint( entity.template getSubentityIndex< 0 >( i ) ); } return ( 1.0 / subvertices ) * c; } diff --git a/src/TNL/Meshes/Geometry/getEntityMeasure.h b/src/TNL/Meshes/Geometry/getEntityMeasure.h index 070c28c583a916a789e49166ea90d10eadc9bd63..47394f293858ad8a84c96072156baece50c3e886 100644 --- a/src/TNL/Meshes/Geometry/getEntityMeasure.h +++ b/src/TNL/Meshes/Geometry/getEntityMeasure.h @@ -74,36 +74,36 @@ typename MeshConfig::RealType getEntityMeasure( const Mesh< MeshConfig, Device > & mesh, const MeshEntity< MeshConfig, Device, Topologies::Edge > & entity ) { - const auto& v0 = mesh.template getEntity< 0 >( entity.template getSubentityIndex< 0 >( 0 ) ); - const auto& v1 = mesh.template getEntity< 0 >( entity.template getSubentityIndex< 0 >( 1 ) ); - return getVectorLength( v1.getPoint() - v0.getPoint() ); + const auto& v0 = mesh.getPoint( entity.template getSubentityIndex< 0 >( 0 ) ); + const auto& v1 = mesh.getPoint( entity.template getSubentityIndex< 0 >( 1 ) ); + return getVectorLength( v1 - v0 ); } // Triangle -template< typename Real > +template< typename VectorExpression, + std::enable_if_t< VectorExpression::getSize() == 2, bool > = true > __cuda_callable__ -Real -getTriangleArea( const TNL::Containers::StaticVector< 2, Real > & v1, - const TNL::Containers::StaticVector< 2, Real > & v2 ) +typename VectorExpression::RealType +getTriangleArea( const VectorExpression & v1, + const VectorExpression & v2 ) { - return 0.5 * TNL::abs( v1.x() * v2.y() - v1.y() * v2.x() ); + using Real = typename VectorExpression::RealType; + return Real( 0.5 ) * TNL::abs( v1.x() * v2.y() - v1.y() * v2.x() ); } -template< typename Real > +template< typename VectorExpression, + std::enable_if_t< VectorExpression::getSize() == 3, bool > = true > __cuda_callable__ -Real -getTriangleArea( const TNL::Containers::StaticVector< 3, Real > & v1, - const TNL::Containers::StaticVector< 3, Real > & v2 ) +typename VectorExpression::RealType +getTriangleArea( const VectorExpression & v1, + const VectorExpression & v2 ) { + using Real = typename VectorExpression::RealType; // formula from http://math.stackexchange.com/a/128999 - Real S = 0.0; - Real aux = v1.y() * v2.z() - v1.z() * v2.y(); // first component of the cross product - S += aux * aux; - aux = v1.z() * v2.x() - v1.x() * v2.z(); // second component of the cross product - S += aux * aux; - aux = v1.x() * v2.y() - v1.y() * v2.x(); // third component of the cross product - S += aux * aux; - return 0.5 * ::sqrt( S ); + const Real c1 = v1.y() * v2.z() - v1.z() * v2.y(); // first component of the cross product + const Real c2 = v1.z() * v2.x() - v1.x() * v2.z(); // second component of the cross product + const Real c3 = v1.x() * v2.y() - v1.y() * v2.x(); // third component of the cross product + return Real( 0.5 ) * TNL::sqrt( c1 * c1 + c2 * c2 + c3 * c3 ); } template< typename MeshConfig, typename Device > @@ -112,13 +112,10 @@ typename MeshConfig::RealType getEntityMeasure( const Mesh< MeshConfig, Device > & mesh, const MeshEntity< MeshConfig, Device, Topologies::Triangle > & entity ) { - const auto& v0 = mesh.template getEntity< 0 >( entity.template getSubentityIndex< 0 >( 0 ) ); - const auto& v1 = mesh.template getEntity< 0 >( entity.template getSubentityIndex< 0 >( 1 ) ); - const auto& v2 = mesh.template getEntity< 0 >( entity.template getSubentityIndex< 0 >( 2 ) ); - using Point = decltype( v0.getPoint() ); - const Point p1 = v2.getPoint() - v0.getPoint(); - const Point p2 = v1.getPoint() - v0.getPoint(); - return getTriangleArea( p1, p2 ); + const auto& v0 = mesh.getPoint( entity.template getSubentityIndex< 0 >( 0 ) ); + const auto& v1 = mesh.getPoint( entity.template getSubentityIndex< 0 >( 1 ) ); + const auto& v2 = mesh.getPoint( entity.template getSubentityIndex< 0 >( 2 ) ); + return getTriangleArea( v2 - v0, v1 - v0 ); } // Quadrilateral @@ -130,14 +127,11 @@ getEntityMeasure( const Mesh< MeshConfig, Device > & mesh, { // measure = 0.5 * |AC x BD|, where AC and BD are the diagonals // Hence, we can use the same formula as for the triangle area. - const auto& v0 = mesh.template getEntity< 0 >( entity.template getSubentityIndex< 0 >( 0 ) ); - const auto& v1 = mesh.template getEntity< 0 >( entity.template getSubentityIndex< 0 >( 1 ) ); - const auto& v2 = mesh.template getEntity< 0 >( entity.template getSubentityIndex< 0 >( 2 ) ); - const auto& v3 = mesh.template getEntity< 0 >( entity.template getSubentityIndex< 0 >( 3 ) ); - using Point = decltype( v0.getPoint() ); - const Point p1 = v2.getPoint() - v0.getPoint(); - const Point p2 = v3.getPoint() - v1.getPoint(); - return getTriangleArea( p1, p2 ); + const auto& v0 = mesh.getPoint( entity.template getSubentityIndex< 0 >( 0 ) ); + const auto& v1 = mesh.getPoint( entity.template getSubentityIndex< 0 >( 1 ) ); + const auto& v2 = mesh.getPoint( entity.template getSubentityIndex< 0 >( 2 ) ); + const auto& v3 = mesh.getPoint( entity.template getSubentityIndex< 0 >( 3 ) ); + return getTriangleArea( v2 - v0, v3 - v1 ); } template< typename VectorExpression > @@ -155,7 +149,7 @@ getTetrahedronVolume( const VectorExpression& v1, ( v1.z() * v2.y() * v3.x() + v1.y() * v2.x() * v3.z() + v1.x() * v2.z() * v3.y() ); - return ( 1.0 / 6.0 ) * TNL::abs( det ); + return Real( 1.0 / 6.0 ) * TNL::abs( det ); } template< typename MeshConfig, typename Device > @@ -164,11 +158,11 @@ typename MeshConfig::RealType getEntityMeasure( const Mesh< MeshConfig, Device > & mesh, const MeshEntity< MeshConfig, Device, Topologies::Tetrahedron > & entity ) { - const auto& v0 = mesh.template getEntity< 0 >( entity.template getSubentityIndex< 0 >( 0 ) ); - const auto& v1 = mesh.template getEntity< 0 >( entity.template getSubentityIndex< 0 >( 1 ) ); - const auto& v2 = mesh.template getEntity< 0 >( entity.template getSubentityIndex< 0 >( 2 ) ); - const auto& v3 = mesh.template getEntity< 0 >( entity.template getSubentityIndex< 0 >( 3 ) ); - return getTetrahedronVolume( v3.getPoint() - v0.getPoint(), v2.getPoint() - v0.getPoint(), v1.getPoint() - v0.getPoint() ); + const auto& v0 = mesh.getPoint( entity.template getSubentityIndex< 0 >( 0 ) ); + const auto& v1 = mesh.getPoint( entity.template getSubentityIndex< 0 >( 1 ) ); + const auto& v2 = mesh.getPoint( entity.template getSubentityIndex< 0 >( 2 ) ); + const auto& v3 = mesh.getPoint( entity.template getSubentityIndex< 0 >( 3 ) ); + return getTetrahedronVolume( v3 - v0, v2 - v0, v1 - v0 ); } } // namespace Meshes diff --git a/src/TNL/Meshes/Geometry/getOutwardNormalVector.h b/src/TNL/Meshes/Geometry/getOutwardNormalVector.h new file mode 100644 index 0000000000000000000000000000000000000000..53680086264bd6a803ce26307264d6a28171a387 --- /dev/null +++ b/src/TNL/Meshes/Geometry/getOutwardNormalVector.h @@ -0,0 +1,91 @@ +/*************************************************************************** + getOutwardNormalVector.h - description + ------------------- + begin : Feb 14, 2020 + copyright : (C) 2020 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +#pragma once + +#include + +namespace TNL { +namespace Meshes { + +template< typename Grid, typename Config > +__cuda_callable__ +typename Grid::PointType +getOutwardNormalVector( const Grid & grid, + const GridEntity< Grid, 0, Config > & face, + const typename Grid::PointType cellCenter ) +{ + static_assert( Grid::getMeshDimension() == 1, "getOutwardNormalVector can be used only with faces." ); + const typename Grid::PointType faceCenter = getEntityCenter( grid, face ); + if( faceCenter.x() > cellCenter.x() ) + return {1}; + else + return {-1}; +} + +template< typename Grid, typename Config > +__cuda_callable__ +typename Grid::PointType +getOutwardNormalVector( const Grid & grid, + const GridEntity< Grid, 1, Config > & face, + const typename Grid::PointType cellCenter ) +{ + static_assert( Grid::getMeshDimension() == 2, "getOutwardNormalVector can be used only with faces." ); + const typename Grid::PointType faceCenter = getEntityCenter( grid, face ); + if( face.getOrientation().x() != 0 ) { + // x-normal face + if( faceCenter.x() > cellCenter.x() ) + return {1, 0}; + else + return {-1, 0}; + } + else { + // y-normal face + if( faceCenter.y() > cellCenter.y() ) + return {0, 1}; + else + return {0, -1}; + } +} + +template< typename Grid, typename Config > +__cuda_callable__ +typename Grid::PointType +getOutwardNormalVector( const Grid & grid, + const GridEntity< Grid, 2, Config > & face, + const typename Grid::PointType cellCenter ) +{ + static_assert( Grid::getMeshDimension() == 3, "getOutwardNormalVector can be used only with faces." ); + const typename Grid::PointType faceCenter = getEntityCenter( grid, face ); + if( face.getOrientation().x() != 0 ) { + // x-normal face + if( faceCenter.x() > cellCenter.x() ) + return {1, 0, 0}; + else + return {-1, 0, 0}; + } + else if( face.getOrientation().y() != 0 ) { + // y-normal face + if( faceCenter.y() > cellCenter.y() ) + return {0, 1, 0}; + else + return {0, -1, 0}; + } + else { + // z-normal face + if( faceCenter.z() > cellCenter.z() ) + return {0, 0, 1}; + else + return {0, 0, -1}; + } +} + +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/GridDetails/Grid2D.h b/src/TNL/Meshes/GridDetails/Grid2D.h index 3d7335053b062baa7e92514acd1cb360b8bade00..3fd9194bd74517085227c84a97a4d8061d3d8221 100644 --- a/src/TNL/Meshes/GridDetails/Grid2D.h +++ b/src/TNL/Meshes/GridDetails/Grid2D.h @@ -161,6 +161,12 @@ class Grid< 2, Real, Device, Index > : public Object __cuda_callable__ const RealType& getSpaceStepsProducts() const; + /** + * \brief Returns the number of x-normal faces. + */ + __cuda_callable__ + IndexType getNumberOfNxFaces() const; + /** * \breif Returns the measure (area) of a cell in this grid. */ diff --git a/src/TNL/Meshes/GridDetails/Grid2D_impl.h b/src/TNL/Meshes/GridDetails/Grid2D_impl.h index 4f9f8fdd580f17a8e88c596127216e57e916bfee..3d8a4c19400c807aa5adf0bc701daf28ec6a63a9 100644 --- a/src/TNL/Meshes/GridDetails/Grid2D_impl.h +++ b/src/TNL/Meshes/GridDetails/Grid2D_impl.h @@ -328,6 +328,17 @@ getSpaceStepsProducts() const return this->spaceStepsProducts[ xPow + 2 ][ yPow + 2 ]; } +template< typename Real, + typename Device, + typename Index > +__cuda_callable__ +Index +Grid< 2, Real, Device, Index >:: +getNumberOfNxFaces() const +{ + return numberOfNxFaces; +} + template< typename Real, typename Device, typename Index > diff --git a/src/TNL/Meshes/GridDetails/Grid3D.h b/src/TNL/Meshes/GridDetails/Grid3D.h index 7e3f0d5e86781d70a79bb7ef5ac83f19cd7404db..91357ba1265d120a8bd1d8a6918de9109c906201 100644 --- a/src/TNL/Meshes/GridDetails/Grid3D.h +++ b/src/TNL/Meshes/GridDetails/Grid3D.h @@ -167,6 +167,18 @@ class Grid< 3, Real, Device, Index > : public Object __cuda_callable__ const RealType& getSpaceStepsProducts() const; + /** + * \brief Returns the number of x-normal faces. + */ + __cuda_callable__ + IndexType getNumberOfNxFaces() const; + + /** + * \brief Returns the number of x-normal and y-normal faces. + */ + __cuda_callable__ + IndexType getNumberOfNxAndNyFaces() const; + /** * \breif Returns the measure (volume) of a cell in this grid. */ diff --git a/src/TNL/Meshes/GridDetails/Grid3D_impl.h b/src/TNL/Meshes/GridDetails/Grid3D_impl.h index dd91a7ed852c0d961be8c3cb0811d5c687735280..0b1f86d6b2d105fe68404803cfcbe322d1ffd8be 100644 --- a/src/TNL/Meshes/GridDetails/Grid3D_impl.h +++ b/src/TNL/Meshes/GridDetails/Grid3D_impl.h @@ -387,6 +387,28 @@ getSpaceStepsProducts() const return this->spaceStepsProducts[ xPow + 2 ][ yPow + 2 ][ zPow + 2 ]; } +template< typename Real, + typename Device, + typename Index > +__cuda_callable__ +Index +Grid< 3, Real, Device, Index >:: +getNumberOfNxFaces() const +{ + return numberOfNxFaces; +} + +template< typename Real, + typename Device, + typename Index > +__cuda_callable__ +Index +Grid< 3, Real, Device, Index >:: +getNumberOfNxAndNyFaces() const +{ + return numberOfNxAndNyFaces; +} + template< typename Real, typename Device, typename Index > diff --git a/src/TNL/Meshes/Mesh.h b/src/TNL/Meshes/Mesh.h index 4d71e3ac91231e3741ee61bafe06e12ff99320a2..e432d8ea6638e7f9d9b8603e9899c1d3b647a161 100644 --- a/src/TNL/Meshes/Mesh.h +++ b/src/TNL/Meshes/Mesh.h @@ -20,11 +20,10 @@ #include #include #include -#include #include #include -#include -#include +#include +#include #include @@ -34,6 +33,11 @@ namespace TNL { */ namespace Meshes { +template< typename MeshConfig, + typename Device, + typename EntityTopology_ > +class MeshEntity; + template< typename MeshConfig > class Initializer; template< typename Mesh > class EntityStorageRebinder; template< typename Mesh, int Dimension > struct IndexPermutationApplier; @@ -64,10 +68,10 @@ class Mesh public ConfigValidator< MeshConfig >, public MeshInitializableBase< MeshConfig, Device, Mesh< MeshConfig, Device > >, public StorageLayerFamily< MeshConfig, Device >, - public BoundaryTags::LayerFamily< MeshConfig, Device, Mesh< MeshConfig, Device > > + public EntityTags::LayerFamily< MeshConfig, Device, Mesh< MeshConfig, Device > > { using StorageBaseType = StorageLayerFamily< MeshConfig, Device >; - using BoundaryTagsLayerFamily = BoundaryTags::LayerFamily< MeshConfig, Device, Mesh >; + using EntityTagsLayerFamily = EntityTags::LayerFamily< MeshConfig, Device, Mesh >; public: using Config = MeshConfig; @@ -77,7 +81,7 @@ class Mesh using LocalIndexType = typename MeshTraitsType::LocalIndexType; using PointType = typename MeshTraitsType::PointType; using RealType = typename PointType::RealType; - using GlobalIndexVector = Containers::Vector< GlobalIndexType, DeviceType, GlobalIndexType >; + using GlobalIndexArray = Containers::Array< GlobalIndexType, DeviceType, GlobalIndexType >; template< int Dimension > using EntityTraits = typename MeshTraitsType::template EntityTraits< Dimension >; @@ -110,22 +114,16 @@ class Mesh virtual String getSerializationTypeVirtual() const; - - template< int Dimension > - static constexpr bool entitiesAvailable(); - + /** + * Entities + */ template< int Dimension > __cuda_callable__ GlobalIndexType getEntitiesCount() const; template< int Dimension > __cuda_callable__ - EntityType< Dimension >& getEntity( const GlobalIndexType& entityIndex ); - - template< int Dimension > - __cuda_callable__ - const EntityType< Dimension >& getEntity( const GlobalIndexType& entityIndex ) const; - + EntityType< Dimension > getEntity( const GlobalIndexType entityIndex ) const; // duplicated for compatibility with grids template< typename EntityType > @@ -134,11 +132,103 @@ class Mesh template< typename EntityType > __cuda_callable__ - EntityType& getEntity( const GlobalIndexType& entityIndex ); + EntityType getEntity( const GlobalIndexType entityIndex ) const; - template< typename EntityType > + /** + * Points + */ + __cuda_callable__ + const PointType& getPoint( const GlobalIndexType vertexIndex ) const; + + __cuda_callable__ + PointType& getPoint( const GlobalIndexType vertexIndex ); + + /** + * Subentities + */ + template< int EntityDimension, int SubentityDimension > __cuda_callable__ - const EntityType& getEntity( const GlobalIndexType& entityIndex ) const; + constexpr LocalIndexType getSubentitiesCount( const GlobalIndexType entityIndex ) const; + + template< int EntityDimension, int SubentityDimension > + __cuda_callable__ + GlobalIndexType getSubentityIndex( const GlobalIndexType entityIndex, const LocalIndexType subentityIndex ) const; + + /** + * Superentities + */ + template< int EntityDimension, int SuperentityDimension > + __cuda_callable__ + LocalIndexType getSuperentitiesCount( const GlobalIndexType entityIndex ) const; + + template< int EntityDimension, int SuperentityDimension > + __cuda_callable__ + GlobalIndexType getSuperentityIndex( const GlobalIndexType entityIndex, const LocalIndexType superentityIndex ) const; + + /** + * Cell neighbors - access the dual graph + */ + __cuda_callable__ + LocalIndexType getCellNeighborsCount( const GlobalIndexType cellIndex ) const; + + __cuda_callable__ + GlobalIndexType getCellNeighborIndex( const GlobalIndexType cellIndex, const LocalIndexType neighborIndex ) const; + + + /** + * \brief Execute function \e f in parallel for all mesh entities with dimension \e EntityDimension. + * + * The function \e f is executed as `f(i)`, where `GlobalIndexType i` is the global index of the + * mesh entity to be processed. The mesh itself is not passed to the function `f`, it is the user's + * responsibility to ensure proper access to the mesh if needed, e.g. by the means of lambda capture + * and/or using a \ref SharedPointer. + */ + template< int EntityDimension, typename Device2 = DeviceType, typename Func > + void forAll( Func f ) const; + + /** + * \brief Execute function \e f in parallel for all boundary mesh entities with dimension \e EntityDimension. + * + * The function \e f is executed as `f(i)`, where `GlobalIndexType i` is the global index of the + * mesh entity to be processed. The mesh itself is not passed to the function `f`, it is the user's + * responsibility to ensure proper access to the mesh if needed, e.g. by the means of lambda capture + * and/or using a \ref SharedPointer. + */ + template< int EntityDimension, typename Device2 = DeviceType, typename Func > + void forBoundary( Func f ) const; + + /** + * \brief Execute function \e f in parallel for all interior mesh entities with dimension \e EntityDimension. + * + * The function \e f is executed as `f(i)`, where `GlobalIndexType i` is the global index of the + * mesh entity to be processed. The mesh itself is not passed to the function `f`, it is the user's + * responsibility to ensure proper access to the mesh if needed, e.g. by the means of lambda capture + * and/or using a \ref SharedPointer. + */ + template< int EntityDimension, typename Device2 = DeviceType, typename Func > + void forInterior( Func f ) const; + + /** + * \brief Execute function \e f in parallel for all local mesh entities with dimension \e EntityDimension. + * + * The function \e f is executed as `f(i)`, where `GlobalIndexType i` is the global index of the + * mesh entity to be processed. The mesh itself is not passed to the function `f`, it is the user's + * responsibility to ensure proper access to the mesh if needed, e.g. by the means of lambda capture + * and/or using a \ref SharedPointer. + */ + template< int EntityDimension, typename Device2 = DeviceType, typename Func > + void forLocal( Func f ) const; + + /** + * \brief Execute function \e f in parallel for all ghost mesh entities with dimension \e EntityDimension. + * + * The function \e f is executed as `f(i)`, where `GlobalIndexType i` is the global index of the + * mesh entity to be processed. The mesh itself is not passed to the function `f`, it is the user's + * responsibility to ensure proper access to the mesh if needed, e.g. by the means of lambda capture + * and/or using a \ref SharedPointer. + */ + template< int EntityDimension, typename Device2 = DeviceType, typename Func > + void forGhost( Func f ) const; /* @@ -148,8 +238,8 @@ class Mesh * M is the entity with index iperm[j] in M'. */ template< int Dimension > - void reorderEntities( const GlobalIndexVector& perm, - const GlobalIndexVector& iperm ); + void reorderEntities( const GlobalIndexArray& perm, + const GlobalIndexArray& iperm ); void save( File& file ) const; @@ -167,21 +257,13 @@ class Mesh void writeProlog( Logger& logger ) const; - DistributedMeshes::DistributedMesh< Mesh >* getDistributedMesh(void) const - { - return NULL; - }; - protected: // Methods for the mesh initializer + using StorageBaseType::getPoints; using StorageBaseType::setEntitiesCount; - using StorageBaseType::getSubentityStorageNetwork; - using StorageBaseType::getSuperentityStorageNetwork; friend Initializer< MeshConfig >; - friend EntityStorageRebinder< Mesh >; - template< typename Mesh, int Dimension > friend struct IndexPermutationApplier; }; @@ -192,4 +274,6 @@ std::ostream& operator<<( std::ostream& str, const Mesh< MeshConfig, Device >& m } // namespace Meshes } // namespace TNL -#include +#include + +#include diff --git a/src/TNL/Meshes/Mesh.hpp b/src/TNL/Meshes/Mesh.hpp new file mode 100644 index 0000000000000000000000000000000000000000..278ef215dd033aa6062ae1dfaa32c5cd37f6a660 --- /dev/null +++ b/src/TNL/Meshes/Mesh.hpp @@ -0,0 +1,431 @@ +/*************************************************************************** + Mesh.hpp - description + ------------------- + begin : Sep 5, 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 +#include + +namespace TNL { +namespace Meshes { + +template< typename MeshConfig, typename Device, typename MeshType > +void +MeshInitializableBase< MeshConfig, Device, MeshType >:: +init( typename MeshTraitsType::PointArrayType& points, + typename MeshTraitsType::CellSeedArrayType& cellSeeds ) +{ + MeshType* mesh = static_cast< MeshType* >( this ); + Initializer< typename MeshType::Config > initializer; + initializer.createMesh( points, cellSeeds, *mesh ); + // init boundary tags + static_cast< EntityTags::LayerFamily< MeshConfig, Device, MeshType >* >( mesh )->initLayer(); + // init dual graph + mesh->initializeDualGraph( *mesh ); +} + + +template< typename MeshConfig, typename Device > +Mesh< MeshConfig, Device >:: +Mesh( const Mesh& mesh ) + : StorageBaseType( mesh ), + EntityTagsLayerFamily( mesh ) +{ +} + +template< typename MeshConfig, typename Device > + template< typename Device_ > +Mesh< MeshConfig, Device >:: +Mesh( const Mesh< MeshConfig, Device_ >& mesh ) + : StorageBaseType( mesh ), + EntityTagsLayerFamily( mesh ) +{ +} + +template< typename MeshConfig, typename Device > +Mesh< MeshConfig, Device >& +Mesh< MeshConfig, Device >:: +operator=( const Mesh& mesh ) +{ + StorageBaseType::operator=( mesh ); + EntityTagsLayerFamily::operator=( mesh ); + return *this; +} + +template< typename MeshConfig, typename Device > + template< typename Device_ > +Mesh< MeshConfig, Device >& +Mesh< MeshConfig, Device >:: +operator=( const Mesh< MeshConfig, Device_ >& mesh ) +{ + StorageBaseType::operator=( mesh ); + EntityTagsLayerFamily::operator=( mesh ); + return *this; +} + +template< typename MeshConfig, typename Device > +constexpr int +Mesh< MeshConfig, Device >:: +getMeshDimension() +{ + return MeshTraitsType::meshDimension; +} + +template< typename MeshConfig, typename Device > +String +Mesh< MeshConfig, Device >:: +getSerializationType() +{ + return String( "Meshes::Mesh< ") + TNL::getType< MeshConfig >() + " >"; +} + +template< typename MeshConfig, typename Device > +String +Mesh< MeshConfig, Device >:: +getSerializationTypeVirtual() const +{ + return this->getSerializationType(); +} + +template< typename MeshConfig, typename Device > + template< int Dimension > +__cuda_callable__ +typename Mesh< MeshConfig, Device >::GlobalIndexType +Mesh< MeshConfig, Device >:: +getEntitiesCount() const +{ + return StorageBaseType::getEntitiesCount( DimensionTag< Dimension >() ); +} + +template< typename MeshConfig, typename Device > + template< int Dimension > +__cuda_callable__ +typename Mesh< MeshConfig, Device >::template EntityType< Dimension > +Mesh< MeshConfig, Device >:: +getEntity( const GlobalIndexType entityIndex ) const +{ + TNL_ASSERT_LT( entityIndex, getEntitiesCount< Dimension >(), "invalid entity index" ); + return EntityType< Dimension >( *this, entityIndex ); +} + + +// duplicated for compatibility with grids +template< typename MeshConfig, typename Device > + template< typename Entity > +__cuda_callable__ +typename Mesh< MeshConfig, Device >::GlobalIndexType +Mesh< MeshConfig, Device >:: +getEntitiesCount() const +{ + return getEntitiesCount< Entity::getEntityDimension() >(); +} + +template< typename MeshConfig, typename Device > + template< typename Entity > +__cuda_callable__ +Entity +Mesh< MeshConfig, Device >:: +getEntity( const GlobalIndexType entityIndex ) const +{ + return getEntity< Entity::getEntityDimension() >( entityIndex ); +} + + +template< typename MeshConfig, typename Device > +__cuda_callable__ +const typename Mesh< MeshConfig, Device >::PointType& +Mesh< MeshConfig, Device >:: +getPoint( const GlobalIndexType vertexIndex ) const +{ + TNL_ASSERT_GE( vertexIndex, 0, "invalid vertex index" ); + TNL_ASSERT_LT( vertexIndex, getEntitiesCount< 0 >(), "invalid vertex index" ); + return this->points[ vertexIndex ]; +} + +template< typename MeshConfig, typename Device > +__cuda_callable__ +typename Mesh< MeshConfig, Device >::PointType& +Mesh< MeshConfig, Device >:: +getPoint( const GlobalIndexType vertexIndex ) +{ + TNL_ASSERT_GE( vertexIndex, 0, "invalid vertex index" ); + TNL_ASSERT_LT( vertexIndex, getEntitiesCount< 0 >(), "invalid vertex index" ); + return this->points[ vertexIndex ]; +} + + +template< typename MeshConfig, typename Device > + template< int EntityDimension, int SubentityDimension > +__cuda_callable__ +constexpr typename Mesh< MeshConfig, Device >::LocalIndexType +Mesh< MeshConfig, Device >:: +getSubentitiesCount( const GlobalIndexType entityIndex ) const +{ + return StorageBaseType::template getSubentitiesCount< EntityDimension, SubentityDimension >(); +} + +template< typename MeshConfig, typename Device > + template< int EntityDimension, int SubentityDimension > +__cuda_callable__ +typename Mesh< MeshConfig, Device >::GlobalIndexType +Mesh< MeshConfig, Device >:: +getSubentityIndex( const GlobalIndexType entityIndex, const LocalIndexType subentityIndex ) const +{ + const auto& row = this->template getSubentitiesMatrix< EntityDimension, SubentityDimension >().getRow( entityIndex ); + TNL_ASSERT_GE( row.getColumnIndex( subentityIndex ), 0, "padding index returned for given subentity index" ); + return row.getColumnIndex( subentityIndex ); +} + +template< typename MeshConfig, typename Device > + template< int EntityDimension, int SuperentityDimension > +__cuda_callable__ +typename Mesh< MeshConfig, Device >::LocalIndexType +Mesh< MeshConfig, Device >:: +getSuperentitiesCount( const GlobalIndexType entityIndex ) const +{ + return this->template getSuperentitiesCountsArray< EntityDimension, SuperentityDimension >()[ entityIndex ]; +} + +template< typename MeshConfig, typename Device > + template< int EntityDimension, int SuperentityDimension > +__cuda_callable__ +typename Mesh< MeshConfig, Device >::GlobalIndexType +Mesh< MeshConfig, Device >:: +getSuperentityIndex( const GlobalIndexType entityIndex, const LocalIndexType superentityIndex ) const +{ + const auto row = this->template getSuperentitiesMatrix< EntityDimension, SuperentityDimension >().getRow( entityIndex ); + TNL_ASSERT_GE( row.getColumnIndex( superentityIndex ), 0, "padding index returned for given superentity index" ); + return row.getColumnIndex( superentityIndex ); +} + +template< typename MeshConfig, typename Device > +__cuda_callable__ +typename Mesh< MeshConfig, Device >::LocalIndexType +Mesh< MeshConfig, Device >:: +getCellNeighborsCount( const GlobalIndexType cellIndex ) const +{ + static_assert( MeshConfig::dualGraphStorage(), + "You try to access the dual graph which is disabled in the mesh configuration." ); + return this->getNeighborCounts()[ cellIndex ]; +} + +template< typename MeshConfig, typename Device > +__cuda_callable__ +typename Mesh< MeshConfig, Device >::GlobalIndexType +Mesh< MeshConfig, Device >:: +getCellNeighborIndex( const GlobalIndexType cellIndex, const LocalIndexType neighborIndex ) const +{ + static_assert( MeshConfig::dualGraphStorage(), + "You try to access the dual graph which is disabled in the mesh configuration." ); + TNL_ASSERT_GE( neighborIndex, 0, "Invalid cell neighbor index." ); + TNL_ASSERT_LT( neighborIndex, getCellNeighborsCount( cellIndex ), "Invalid cell neighbor index." ); + const auto row = this->getDualGraph().getRow( cellIndex ); + TNL_ASSERT_GE( row.getColumnIndex( neighborIndex ), 0, "padding index returned for given neighbor index" ); + return row.getColumnIndex( neighborIndex ); +} + + +template< typename MeshConfig, typename Device > + template< int EntityDimension, typename Device2, typename Func > +void +Mesh< MeshConfig, Device >:: +forAll( Func f ) const +{ + const GlobalIndexType entitiesCount = getEntitiesCount< EntityDimension >(); + Algorithms::ParallelFor< Device2 >::exec( (GlobalIndexType) 0, entitiesCount, f ); +} + +template< typename MeshConfig, typename Device > + template< int EntityDimension, typename Device2, typename Func > +void +Mesh< MeshConfig, Device >:: +forBoundary( Func f ) const +{ + const auto boundaryIndices = this->template getBoundaryIndices< EntityDimension >(); + const GlobalIndexType entitiesCount = boundaryIndices.getSize(); + auto wrapper = [f, boundaryIndices] __cuda_callable__ ( const GlobalIndexType i ) mutable + { + f( boundaryIndices[ i ] ); + }; + Algorithms::ParallelFor< Device2 >::exec( (GlobalIndexType) 0, entitiesCount, wrapper ); +} + +template< typename MeshConfig, typename Device > + template< int EntityDimension, typename Device2, typename Func > +void +Mesh< MeshConfig, Device >:: +forInterior( Func f ) const +{ + const auto interiorIndices = this->template getInteriorIndices< EntityDimension >(); + const GlobalIndexType entitiesCount = interiorIndices.getSize(); + auto wrapper = [f, interiorIndices] __cuda_callable__ ( const GlobalIndexType i ) mutable + { + f( interiorIndices[ i ] ); + }; + Algorithms::ParallelFor< Device2 >::exec( (GlobalIndexType) 0, entitiesCount, wrapper ); +} + +template< typename MeshConfig, typename Device > + template< int EntityDimension, typename Device2, typename Func > +void +Mesh< MeshConfig, Device >:: +forLocal( Func f ) const +{ + const GlobalIndexType ghostsOffset = this->template getGhostEntitiesOffset< EntityDimension >(); + Algorithms::ParallelFor< DeviceType >::exec( (GlobalIndexType) 0, ghostsOffset, f ); +} + +template< typename MeshConfig, typename Device > + template< int EntityDimension, typename Device2, typename Func > +void +Mesh< MeshConfig, Device >:: +forGhost( Func f ) const +{ + const GlobalIndexType ghostsOffset = this->template getGhostEntitiesOffset< EntityDimension >(); + const GlobalIndexType entitiesCount = this->template getEntitiesCount< EntityDimension >(); + Algorithms::ParallelFor< Device2 >::exec( ghostsOffset, entitiesCount, f ); +} + + +template< typename MeshConfig, typename Device > + template< int Dimension > +void +Mesh< MeshConfig, Device >:: +reorderEntities( const GlobalIndexArray& perm, + const GlobalIndexArray& iperm ) +{ + const GlobalIndexType entitiesCount = getEntitiesCount< Dimension >(); + + // basic sanity check + if( perm.getSize() != entitiesCount || iperm.getSize() != entitiesCount ) { + throw std::logic_error( "Wrong size of permutation vectors: " + "perm size = " + std::to_string( perm.getSize() ) + ", " + "iperm size = " + std::to_string( iperm.getSize() ) ); + } +#ifndef NDEBUG + using View = Containers::VectorView< const GlobalIndexType, DeviceType, GlobalIndexType >; + const View perm_view = perm.getConstView(); + const View iperm_view = iperm.getConstView(); + TNL_ASSERT( min( perm_view ) == 0 && max( perm_view ) == entitiesCount - 1, + std::cerr << "Given array is not a permutation: min = " << min( perm_view ) + << ", max = " << max( perm_view ) + << ", number of entities = " << entitiesCount + << ", array = " << perm << std::endl; ); + TNL_ASSERT( min( iperm_view ) == 0 && max( iperm_view ) == entitiesCount - 1, + std::cerr << "Given array is not a permutation: min = " << min( iperm_view ) + << ", max = " << max( iperm_view ) + << ", number of entities = " << entitiesCount + << ", array = " << iperm << std::endl; ); +#endif + + IndexPermutationApplier< Mesh, Dimension >::exec( *this, perm, iperm ); + // permute entity tags + auto tags = this->template getEntityTagsView< Dimension >(); + IndexPermutationApplier< Mesh, Dimension >::permuteArray( tags, perm ); + // update the entity tags layer + this->template updateEntityTagsLayer< Dimension >(); +} + + +template< typename MeshConfig, typename Device > +void +Mesh< MeshConfig, Device >:: +save( File& file ) const +{ + // saving via host is necessary due to segment-based sparse matrices + if( std::is_same< Device, Devices::Cuda >::value ) { + Mesh< MeshConfig, Devices::Host > hostMesh; + hostMesh = *this; + hostMesh.save( file ); + } + else { + Object::save( file ); + StorageBaseType::save( file ); + EntityTagsLayerFamily::save( file ); + } +} + +template< typename MeshConfig, typename Device > +void +Mesh< MeshConfig, Device >:: +load( File& file ) +{ + // loading via host is necessary for the initialization of the dual graph (and due to segment-based sparse matrices) + if( std::is_same< Device, Devices::Cuda >::value ) { + Mesh< MeshConfig, Devices::Host > hostMesh; + hostMesh.load( file ); + *this = hostMesh; + } + else { + Object::load( file ); + StorageBaseType::load( file ); + EntityTagsLayerFamily::load( file ); + this->initializeDualGraph( *this ); + } +} + +template< typename MeshConfig, typename Device > +void +Mesh< MeshConfig, Device >:: +print( std::ostream& str ) const +{ + StorageBaseType::print( str ); + EntityTagsLayerFamily::print( str ); +} + +template< typename MeshConfig, typename Device > +bool +Mesh< MeshConfig, Device >:: +operator==( const Mesh& mesh ) const +{ + return StorageBaseType::operator==( mesh ) && + EntityTagsLayerFamily::operator==( mesh ); +} + +template< typename MeshConfig, typename Device > +bool +Mesh< MeshConfig, Device >:: +operator!=( const Mesh& mesh ) const +{ + return ! operator==( mesh ); +} + +template< typename MeshConfig, typename Device > +void +Mesh< MeshConfig, Device >:: +writeProlog( Logger& logger ) const +{ + logger.writeParameter( "Dimension:", getMeshDimension() ); + logger.writeParameter( "Cell topology:", getType( typename Cell::EntityTopology{} ) ); + logger.writeParameter( "Cells count:", getEntitiesCount< getMeshDimension() >() ); + if( getMeshDimension() > 1 ) + logger.writeParameter( "Faces count:", getEntitiesCount< getMeshDimension() - 1 >() ); + logger.writeParameter( "Vertices count:", getEntitiesCount< 0 >() ); + logger.writeParameter( "Boundary cells count:", this->template getBoundaryEntitiesCount< Mesh::getMeshDimension() >() ); + logger.writeParameter( "Boundary vertices count:", this->template getBoundaryEntitiesCount< 0 >() ); +} + + +template< typename MeshConfig, typename Device > +std::ostream& operator<<( std::ostream& str, const Mesh< MeshConfig, Device >& mesh ) +{ + mesh.print( str ); + return str; +} + +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/MeshBuilder.h b/src/TNL/Meshes/MeshBuilder.h index cc5ef2def0c88fb6b878cb5b686b985f7a44c519..8dc39063365603458e35d2db5590692b9f2ca11d 100644 --- a/src/TNL/Meshes/MeshBuilder.h +++ b/src/TNL/Meshes/MeshBuilder.h @@ -17,7 +17,6 @@ #pragma once #include -#include namespace TNL { namespace Meshes { diff --git a/src/TNL/Meshes/MeshDetails/ConfigValidator.h b/src/TNL/Meshes/MeshDetails/ConfigValidator.h index 9f27538383a012b2aebc2be2be78bf9817c0c039..fa35ba4d03e76bfa298150b351d0fca415af0701 100644 --- a/src/TNL/Meshes/MeshDetails/ConfigValidator.h +++ b/src/TNL/Meshes/MeshDetails/ConfigValidator.h @@ -29,11 +29,11 @@ class ConfigValidatorSubtopologyLayer : public ConfigValidatorSubtopologyLayer< MeshConfig, EntityTopology, typename DimensionTag::Decrement > { static_assert( ! MeshConfig::subentityStorage( EntityTopology(), DimensionTag::value ) || - MeshConfig::entityStorage( EntityTopology::dimension ), - "entities of which subentities are stored must be stored" ); + MeshConfig::subentityStorage( EntityTopology(), 0 ), + "entities of which subentities are stored must store their subvertices" ); static_assert( ! MeshConfig::subentityStorage( EntityTopology(), DimensionTag::value ) || - MeshConfig::entityStorage( DimensionTag::value ), - "entities that are stored as subentities must be stored"); + 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"); @@ -43,9 +43,6 @@ template< typename MeshConfig, typename EntityTopology > class ConfigValidatorSubtopologyLayer< MeshConfig, EntityTopology, DimensionTag< 0 > > { - static_assert( ! MeshConfig::subentityStorage( EntityTopology(), 0 ) || - MeshConfig::entityStorage( EntityTopology::dimension ), - "entities of which subvertices are stored must be stored" ); static_assert( ! MeshConfig::subentityOrientationStorage( EntityTopology(), 0 ), "storage of vertex orientation does not make sense" ); }; @@ -58,11 +55,11 @@ class ConfigValidatorSupertopologyLayer : public ConfigValidatorSupertopologyLayer< MeshConfig, EntityTopology, typename DimensionTag::Decrement > { static_assert( ! MeshConfig::superentityStorage( EntityTopology(), DimensionTag::value ) || - MeshConfig::entityStorage( EntityTopology::dimension ), - "entities of which superentities are stored must be stored"); + MeshConfig::subentityStorage( EntityTopology(), 0 ), + "entities of which superentities are stored must store their subvertices"); static_assert( ! MeshConfig::superentityStorage( EntityTopology(), DimensionTag::value ) || - MeshConfig::entityStorage( DimensionTag::value ), - "entities that are stored as superentities must be stored"); + MeshConfig::subentityStorage( EntityTopology(), 0 ), + "entities that are stored as superentities must store their subvertices"); }; template< typename MeshConfig, @@ -80,13 +77,7 @@ class ConfigValidatorLayer public ConfigValidatorSupertopologyLayer< MeshConfig, typename Topologies::Subtopology< typename MeshConfig::CellTopology, dimension >::Topology, DimensionTag< MeshConfig::CellTopology::dimension > > -{ - using Topology = typename Topologies::Subtopology< typename MeshConfig::CellTopology, dimension >::Topology; - - static_assert( ! MeshConfig::entityStorage( dimension ) || - MeshConfig::subentityStorage( Topology(), 0 ), - "subvertices of all stored entities must be stored"); -}; +{}; template< typename MeshConfig > class ConfigValidatorLayer< MeshConfig, 0 > @@ -101,11 +92,11 @@ class ConfigValidatorLayerCell DimensionTag< MeshConfig::CellTopology::dimension - 1 > > { using CellTopology = typename MeshConfig::CellTopology; - static constexpr int dimension = CellTopology::dimension; - static_assert( ! MeshConfig::entityStorage( dimension ) || - MeshConfig::subentityStorage( CellTopology(), 0 ), - "subvertices of all stored entities must be stored" ); + 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 > @@ -116,9 +107,6 @@ class ConfigValidator static_assert( 1 <= meshDimension, "zero dimensional meshes are not supported" ); static_assert( meshDimension <= MeshConfig::worldDimension, "world dimension must not be less than mesh dimension" ); - - static_assert( MeshConfig::entityStorage( 0 ), "mesh vertices must be stored" ); - static_assert( MeshConfig::entityStorage( meshDimension ), "mesh cells must be stored" ); }; } // namespace Meshes diff --git a/src/TNL/Meshes/MeshDetails/EntityLayers/SubentityAccess.h b/src/TNL/Meshes/MeshDetails/EntityLayers/SubentityAccess.h deleted file mode 100644 index 4adc400d450d26dff4af235f11334d58249a28c9..0000000000000000000000000000000000000000 --- a/src/TNL/Meshes/MeshDetails/EntityLayers/SubentityAccess.h +++ /dev/null @@ -1,419 +0,0 @@ -/*************************************************************************** - SubentityAccess.h - description - ------------------- - begin : Oct 26, 2016 - copyright : (C) 2014 by Tomas Oberhuber et al. - email : tomas.oberhuber@fjfi.cvut.cz - ***************************************************************************/ - -/* See Copyright Notice in tnl/Copyright */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace TNL { -namespace Meshes { - -template< typename MeshConfig, - typename Device, - typename EntityTopology, - typename DimensionTag, - bool SubentityStorage = - WeakSubentityStorageTrait< MeshConfig, Device, EntityTopology, DimensionTag >::storageEnabled, - bool SubentityOrientationStorage = - MeshConfig::subentityOrientationStorage( EntityTopology(), DimensionTag::value ) > -class SubentityAccessLayer; - - -template< typename MeshConfig, - typename Device, - typename EntityTopology > -class SubentityAccessLayerFamily - : public SubentityAccessLayer< MeshConfig, - Device, - EntityTopology, - Meshes::DimensionTag< 0 > > -{ - using BaseType = SubentityAccessLayer< MeshConfig, - Device, - EntityTopology, - Meshes::DimensionTag< 0 > >; - - using MeshTraitsType = MeshTraits< MeshConfig, Device >; - - template< int Subdimension > - using SubentityTraits = typename MeshTraitsType::template SubentityTraits< EntityTopology, Subdimension >; - -public: - template< int Subdimension > - __cuda_callable__ - void bindSubentitiesStorageNetwork( const typename SubentityTraits< Subdimension >::SubentityAccessorType& storage ) - { - static_assert( SubentityTraits< Subdimension >::storageEnabled, "You try to bind subentities which are not configured for storage." ); - BaseType::bindSubentitiesStorageNetwork( Meshes::DimensionTag< Subdimension >(), - storage ); - } - - template< int Subdimension > - static constexpr typename MeshTraitsType::LocalIndexType getSubentitiesCount() - { - return SubentityTraits< Subdimension >::count; - } - - template< int Subdimension > - __cuda_callable__ - void setSubentityIndex( const typename MeshTraitsType::LocalIndexType& localIndex, - const typename MeshTraitsType::GlobalIndexType& globalIndex ) - { - static_assert( SubentityTraits< Subdimension >::storageEnabled, "You try to set subentity which is not configured for storage." ); - BaseType::setSubentityIndex( Meshes::DimensionTag< Subdimension >(), - localIndex, - globalIndex ); - } - - template< int Subdimension > - __cuda_callable__ - typename MeshTraitsType::GlobalIndexType - getSubentityIndex( const typename MeshTraitsType::LocalIndexType localIndex ) const - { - static_assert( SubentityTraits< Subdimension >::storageEnabled, "You try to get subentity which is not configured for storage." ); - return BaseType::getSubentityIndex( Meshes::DimensionTag< Subdimension >(), - localIndex ); - } - - template< int Subdimension > - __cuda_callable__ - typename SubentityTraits< Subdimension >::OrientationArrayType& subentityOrientationsArray() - { - static_assert( SubentityTraits< Subdimension >::orientationEnabled, "You try to get subentity orientation which is not configured for storage." ); - return BaseType::subentityOrientationsArray( Meshes::DimensionTag< Subdimension >() ); - } - - template< int Subdimension > - __cuda_callable__ - typename SubentityTraits< Subdimension >::IdPermutationArrayType getSubentityOrientation( typename MeshTraitsType::LocalIndexType index ) const - { - static_assert( SubentityTraits< Subdimension >::orientationEnabled, "You try to get subentity orientation which is not configured for storage." ); - return BaseType::getSubentityOrientation( Meshes::DimensionTag< Subdimension >(), index ); - } - - __cuda_callable__ - bool operator==( const SubentityAccessLayerFamily& other ) const - { - return BaseType::operator==( other ); - } - - void print( std::ostream& str ) const - { - BaseType::print( str ); - } -}; - - -template< typename MeshConfig, - typename Device, - typename EntityTopology, - typename DimensionTag > -class SubentityAccessLayer< MeshConfig, - Device, - EntityTopology, - DimensionTag, - true, - true > - : public SubentityAccessLayer< MeshConfig, - Device, - EntityTopology, - typename DimensionTag::Increment > -{ - using BaseType = SubentityAccessLayer< MeshConfig, - Device, - EntityTopology, - typename DimensionTag::Increment >; - - using MeshTraitsType = MeshTraits< MeshConfig, Device >; - using SubentityTraitsType = typename MeshTraitsType::template SubentityTraits< EntityTopology, DimensionTag::value >; - -protected: - using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; - using LocalIndexType = typename MeshTraitsType::LocalIndexType; - using SubentityAccessorType = typename SubentityTraitsType::SubentityAccessorType; - using OrientationArrayType = typename SubentityTraitsType::OrientationArrayType; - using IdPermutationArrayType = typename SubentityTraitsType::IdPermutationArrayType; - - SubentityAccessLayer() = default; - - __cuda_callable__ - explicit SubentityAccessLayer( const SubentityAccessLayer& layer ) - : BaseType( layer ) - { - this->subentityIndices.bind( layer.subentityIndices ); - } - - __cuda_callable__ - SubentityAccessLayer& operator=( const SubentityAccessLayer& layer ) - { - BaseType::operator=( layer ); - this->subentityIndices.bind( layer.subentityIndices ); - return *this; - } - - void print( std::ostream& str ) const - { - BaseType::print( str ); - str << "\t Subentities with dimension " << DimensionTag::value << " are: " << subentityIndices << "." << std::endl; - } - - __cuda_callable__ - bool operator==( const SubentityAccessLayer& layer ) const - { - return ( BaseType::operator==( layer ) && - subentityIndices == layer.subentityIndices ); - } - - /**** - * Make visible setters and getters of the lower subentities - */ - using BaseType::bindSubentitiesStorageNetwork; - using BaseType::getSubentityIndex; - using BaseType::setSubentityIndex; - using BaseType::getSubentityIndices; - - /**** - * Define setter/getter for the current level of the subentities - */ - __cuda_callable__ - void bindSubentitiesStorageNetwork( DimensionTag, - const SubentityAccessorType& storage ) - { - this->subentityIndices.bind( storage ); - } - - __cuda_callable__ - void setSubentityIndex( DimensionTag, - const LocalIndexType localIndex, - const GlobalIndexType globalIndex ) - { - this->subentityIndices[ localIndex ] = globalIndex; - } - - __cuda_callable__ - GlobalIndexType getSubentityIndex( DimensionTag, - const LocalIndexType localIndex ) const - { - return this->subentityIndices[ localIndex ]; - } - - __cuda_callable__ - const SubentityAccessorType& getSubentityIndices( DimensionTag ) const - { - return this->subentityIndices; - } - - __cuda_callable__ - SubentityAccessorType& getSubentityIndices( DimensionTag ) - { - return this->subentityIndices; - } - - using BaseType::getSubentityOrientation; - __cuda_callable__ - const IdPermutationArrayType& getSubentityOrientation( DimensionTag, LocalIndexType index) const - { - TNL_ASSERT_GE( index, 0, "index must be non-negative" ); - TNL_ASSERT_LT( index, SubentityTraitsType::count, "index is out of bounds" ); - return this->subentityOrientations[ index ].getSubvertexPermutation(); - } - - using BaseType::subentityOrientationsArray; - __cuda_callable__ - OrientationArrayType& subentityOrientationsArray( DimensionTag ) { return this->subentityOrientations; } - -private: - SubentityAccessorType subentityIndices; - - OrientationArrayType subentityOrientations; -}; - -template< typename MeshConfig, - typename Device, - typename EntityTopology, - typename DimensionTag > -class SubentityAccessLayer< MeshConfig, - Device, - EntityTopology, - DimensionTag, - true, - false > - : public SubentityAccessLayer< MeshConfig, - Device, - EntityTopology, - typename DimensionTag::Increment > -{ - static_assert( DimensionTag::value < EntityTopology::dimension, "" ); - using BaseType = SubentityAccessLayer< MeshConfig, - Device, - EntityTopology, - typename DimensionTag::Increment >; - - using MeshTraitsType = MeshTraits< MeshConfig, Device >; - using SubentityTraitsType = typename MeshTraitsType::template SubentityTraits< EntityTopology, DimensionTag::value >; - -protected: - using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; - using LocalIndexType = typename MeshTraitsType::LocalIndexType; - using SubentityAccessorType = typename SubentityTraitsType::SubentityAccessorType; - - SubentityAccessLayer() = default; - - __cuda_callable__ - explicit SubentityAccessLayer( const SubentityAccessLayer& layer ) - : BaseType( layer ) - { - this->subentityIndices.bind( layer.subentityIndices ); - } - - __cuda_callable__ - SubentityAccessLayer& operator=( const SubentityAccessLayer& layer ) - { - BaseType::operator=( layer ); - this->subentityIndices.bind( layer.subentityIndices ); - return *this; - } - - void print( std::ostream& str ) const - { - BaseType::print( str ); - str << "\t Subentities with dimension " << DimensionTag::value << " are: " << subentityIndices << "." << std::endl; - } - - __cuda_callable__ - bool operator==( const SubentityAccessLayer& layer ) const - { - return ( BaseType::operator==( layer ) && - subentityIndices == layer.subentityIndices ); - } - - /**** - * Make visible setters and getters of the lower subentities - */ - using BaseType::bindSubentitiesStorageNetwork; - using BaseType::getSubentityIndex; - using BaseType::setSubentityIndex; - using BaseType::getSubentityIndices; - - /**** - * Define setter/getter for the current level of the subentities - */ - __cuda_callable__ - void bindSubentitiesStorageNetwork( DimensionTag, - const SubentityAccessorType& storage ) - { - this->subentityIndices.bind( storage ); - } - - __cuda_callable__ - void setSubentityIndex( DimensionTag, - const LocalIndexType localIndex, - const GlobalIndexType globalIndex ) - { - this->subentityIndices[ localIndex ] = globalIndex; - } - - __cuda_callable__ - GlobalIndexType getSubentityIndex( DimensionTag, - const LocalIndexType localIndex ) const - { - return this->subentityIndices[ localIndex ]; - } - - __cuda_callable__ - const SubentityAccessorType& getSubentityIndices( DimensionTag ) const - { - return this->subentityIndices; - } - - __cuda_callable__ - SubentityAccessorType& getSubentityIndices( DimensionTag ) - { - return this->subentityIndices; - } - -private: - SubentityAccessorType subentityIndices; -}; - -template< typename MeshConfig, - typename Device, - typename EntityTopology, - typename DimensionTag, - bool SubentityOrientationStorage > -class SubentityAccessLayer< MeshConfig, - Device, - EntityTopology, - DimensionTag, - false, - SubentityOrientationStorage > - : public SubentityAccessLayer< MeshConfig, - Device, - EntityTopology, - typename DimensionTag::Increment > -{ -}; - -// termination of recursive inheritance (everything is reduced to EntityStorage == false thanks to the WeakSubentityStorageTrait) -template< typename MeshConfig, - typename Device, - typename EntityTopology, - bool SubentityOrientationStorage > -class SubentityAccessLayer< MeshConfig, - Device, - EntityTopology, - Meshes::DimensionTag< EntityTopology::dimension >, - false, - SubentityOrientationStorage > -{ - using DimensionTag = Meshes::DimensionTag< EntityTopology::dimension >; - -protected: - using GlobalIndexType = typename MeshConfig::GlobalIndexType; - using LocalIndexType = typename MeshConfig::LocalIndexType; - - /*** - * Necessary because of 'using BaseType::...;' in the derived classes - */ - template< typename SubentityAccessorType > - __cuda_callable__ - void bindSubentitiesStorageNetwork( DimensionTag, - const SubentityAccessorType& storage ) {} - __cuda_callable__ - void getSubentityIndex( DimensionTag, - const LocalIndexType localIndex ) const {} - __cuda_callable__ - void setSubentityIndex( DimensionTag, - const LocalIndexType& localIndex, - const GlobalIndexType& globalIndex ) {} - __cuda_callable__ - void getSubentityIndices() {} - - template< typename LocalIndexType > - __cuda_callable__ - void getSubentityOrientation( DimensionTag, LocalIndexType index) const {} - __cuda_callable__ - void subentityOrientationsArray( DimensionTag ) {} - - __cuda_callable__ - bool operator==( const SubentityAccessLayer& other ) const - { - return true; - } - - void print( std::ostream& str ) const {} -}; - -} // namespace Meshes -} // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/EntityLayers/SuperentityAccess.h b/src/TNL/Meshes/MeshDetails/EntityLayers/SuperentityAccess.h deleted file mode 100644 index 47457181651a631d6ccce3bcba216c78be18e38d..0000000000000000000000000000000000000000 --- a/src/TNL/Meshes/MeshDetails/EntityLayers/SuperentityAccess.h +++ /dev/null @@ -1,288 +0,0 @@ -/*************************************************************************** - SuperentityAccess.h - description - ------------------- - begin : Aug 15, 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 Device, - typename EntityTopology, - typename DimensionTag, - bool SuperentityStorage = WeakSuperentityStorageTrait< MeshConfig, Device, EntityTopology, DimensionTag >::storageEnabled > -class SuperentityAccessLayer; - - -template< typename MeshConfig, - typename Device, - typename EntityTopology > -class SuperentityAccessLayerFamily - : public SuperentityAccessLayer< MeshConfig, - Device, - EntityTopology, - Meshes::DimensionTag< MeshTraits< MeshConfig, Device >::meshDimension > > -{ - using BaseType = SuperentityAccessLayer< MeshConfig, - Device, - EntityTopology, - Meshes::DimensionTag< MeshTraits< MeshConfig, Device >::meshDimension > >; - - using MeshTraitsType = MeshTraits< MeshConfig, Device >; - - template< int Superdimension > - using SuperentityTraits = typename MeshTraitsType::template SuperentityTraits< EntityTopology, Superdimension >; - -public: - template< int Superdimension > - __cuda_callable__ - void bindSuperentitiesStorageNetwork( const typename SuperentityTraits< Superdimension >::SuperentityAccessorType& storage ) - { - static_assert( SuperentityTraits< Superdimension >::storageEnabled, "You try to bind superentities which are not configured for storage." ); - BaseType::bindSuperentitiesStorageNetwork( Meshes::DimensionTag< Superdimension >(), - storage ); - } - - template< int Superdimension > - __cuda_callable__ - void setNumberOfSuperentities( const typename MeshTraitsType::LocalIndexType size ) - { - static_assert( SuperentityTraits< Superdimension >::storageEnabled, "You try to set number of superentities which are not configured for storage." ); - BaseType::setNumberOfSuperentities( Meshes::DimensionTag< Superdimension >(), - size ); - } - - template< int Superdimension > - __cuda_callable__ - typename MeshTraitsType::LocalIndexType - getSuperentitiesCount() const - { - static_assert( SuperentityTraits< Superdimension >::storageEnabled, "You try to get number of superentities which are not configured for storage." ); - return BaseType::getSuperentitiesCount( Meshes::DimensionTag< Superdimension >() ); - } - - template< int Superdimension > - __cuda_callable__ - void - setSuperentityIndex( const typename MeshTraitsType::LocalIndexType& localIndex, - const typename MeshTraitsType::GlobalIndexType& globalIndex ) - { - static_assert( SuperentityTraits< Superdimension >::storageEnabled, "You try to set superentities which are not configured for storage." ); - BaseType::setSuperentityIndex( Meshes::DimensionTag< Superdimension >(), - localIndex, - globalIndex ); - } - - template< int Superdimension > - __cuda_callable__ - typename MeshTraitsType::GlobalIndexType - getSuperentityIndex( const typename MeshTraitsType::LocalIndexType localIndex ) const - { - static_assert( SuperentityTraits< Superdimension >::storageEnabled, "You try to get superentities which are not configured for storage." ); - return BaseType::getSuperentityIndex( Meshes::DimensionTag< Superdimension >(), - localIndex ); - } - - __cuda_callable__ - bool operator==( const SuperentityAccessLayerFamily& other ) const - { - return BaseType::operator==( other ); - } - - void print( std::ostream& str ) const - { - BaseType::print( str ); - } -}; - -template< typename MeshConfig, - typename Device, - typename EntityTopology, - typename DimensionTag > -class SuperentityAccessLayer< MeshConfig, - Device, - EntityTopology, - DimensionTag, - true > - : public SuperentityAccessLayer< MeshConfig, Device, EntityTopology, typename DimensionTag::Decrement > -{ - using BaseType = SuperentityAccessLayer< MeshConfig, Device, EntityTopology, typename DimensionTag::Decrement >; - using MeshTraitsType = MeshTraits< MeshConfig, Device >; - using SuperentityTraitsType = typename MeshTraitsType::template SuperentityTraits< EntityTopology, DimensionTag::value >; - -public: - using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; - using LocalIndexType = typename MeshTraitsType::LocalIndexType; - using SuperentityAccessorType = typename SuperentityTraitsType::SuperentityAccessorType; - - /**** - * Make visible setters and getters of the lower superentities - */ - using BaseType::bindSuperentitiesStorageNetwork; - using BaseType::setNumberOfSuperentities; - using BaseType::getSuperentitiesCount; - using BaseType::setSuperentityIndex; - using BaseType::getSuperentityIndex; - using BaseType::getSuperentityIndices; - - SuperentityAccessLayer() = default; - - __cuda_callable__ - explicit SuperentityAccessLayer( const SuperentityAccessLayer& layer ) - : BaseType( layer ) - { - this->superentityIndices.bind( layer.superentityIndices ); - } - - __cuda_callable__ - SuperentityAccessLayer& operator=( const SuperentityAccessLayer& layer ) - { - BaseType::operator=( layer ); - this->superentityIndices.bind( layer.superentityIndices ); - return *this; - } - - /**** - * Define setter/getter for the current level of the superentities - */ - __cuda_callable__ - void bindSuperentitiesStorageNetwork( DimensionTag, - const SuperentityAccessorType& storage ) - { - this->superentityIndices.bind( storage ); - } - - __cuda_callable__ - void setNumberOfSuperentities( DimensionTag, - const LocalIndexType size ) - { - this->superentityIndices.setSize( size ); - } - - __cuda_callable__ - LocalIndexType getSuperentitiesCount( DimensionTag ) const - { - return this->superentityIndices.getSize(); - } - - __cuda_callable__ - void setSuperentityIndex( DimensionTag, - const LocalIndexType& localIndex, - const GlobalIndexType& globalIndex ) - { - this->superentityIndices[ localIndex ] = globalIndex; - } - - __cuda_callable__ - GlobalIndexType getSuperentityIndex( DimensionTag, - const LocalIndexType localIndex ) const - { - return this->superentityIndices[ localIndex ]; - } - - __cuda_callable__ - const SuperentityAccessorType& getSuperentityIndices( DimensionTag ) const - { - return this->superentityIndices; - } - - __cuda_callable__ - SuperentityAccessorType& getSuperentityIndices( DimensionTag ) - { - return this->superentityIndices; - } - - __cuda_callable__ - bool operator==( const SuperentityAccessLayer& other ) const - { - return ( BaseType::operator==( other ) && superentityIndices == other.superentityIndices ); - } - - void print( std::ostream& str ) const - { - BaseType::print( str ); - str << "\t Superentities with dimension " << DimensionTag::value << " are: " << this->superentityIndices << "." << std::endl; - } - -private: - SuperentityAccessorType superentityIndices; -}; - -template< typename MeshConfig, - typename Device, - typename EntityTopology, - typename DimensionTag > -class SuperentityAccessLayer< MeshConfig, - Device, - EntityTopology, - DimensionTag, - false > - : public SuperentityAccessLayer< MeshConfig, Device, EntityTopology, typename DimensionTag::Decrement > -{ -}; - -// termination of recursive inheritance (everything is reduced to EntityStorage == false thanks to the WeakSuperentityStorageTrait) -template< typename MeshConfig, - typename Device, - typename EntityTopology > -class SuperentityAccessLayer< MeshConfig, - Device, - EntityTopology, - Meshes::DimensionTag< EntityTopology::dimension >, - false > -{ - using DimensionTag = Meshes::DimensionTag< EntityTopology::dimension >; - -protected: - using GlobalIndexType = typename MeshConfig::GlobalIndexType; - using LocalIndexType = typename MeshConfig::LocalIndexType; - - /*** - * Necessary because of 'using BaseType::...;' in the derived classes - */ - template< typename SuperentityAccessorType > - __cuda_callable__ - void bindSuperentitiesStorageNetwork( DimensionTag, - const SuperentityAccessorType& storage ) {} - __cuda_callable__ - void setNumberOfSuperentities( DimensionTag, - const LocalIndexType size ) {} - __cuda_callable__ - void getSuperentitiesCount( DimensionTag ) const {} - __cuda_callable__ - void getSuperentityIndex( DimensionTag, - const LocalIndexType localIndex ) const {} - __cuda_callable__ - void setSuperentityIndex( DimensionTag, - const LocalIndexType& localIndex, - const GlobalIndexType& globalIndex ) {} - __cuda_callable__ - void getSuperentityIndices() {} - - __cuda_callable__ - bool operator==( const SuperentityAccessLayer& other ) const - { - return true; - } - - void print( std::ostream& str ) const {} -}; - -} // namespace Meshes -} // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/EntityStorageRebinder.h b/src/TNL/Meshes/MeshDetails/EntityStorageRebinder.h deleted file mode 100644 index c956d3169a659dbb106a2a04e83ce39984a84646..0000000000000000000000000000000000000000 --- a/src/TNL/Meshes/MeshDetails/EntityStorageRebinder.h +++ /dev/null @@ -1,159 +0,0 @@ -/*************************************************************************** - EntityStorageRebinder.h - description - ------------------- - begin : Oct 22, 2016 - copyright : (C) 2014 by Tomas Oberhuber et al. - email : tomas.oberhuber@fjfi.cvut.cz - ***************************************************************************/ - -/* See Copyright Notice in tnl/Copyright */ - -#pragma once - -/* - * Everything in this file is basically just a templatized version of the - * following pseudo-code (which does not work because normal variables are not - * usable in template arguments): - * - * for( int dimension = 0; dimension < MeshTraitsType::meshDimension; dimension++ ) - * for( int superdimension = dimension + 1; superdimension <= MeshTraitsType::meshDimension; superdimension++ ) - * if( EntityTraits< dimension >::SuperentityTraits< superdimension >::storageEnabled ) - * for( GlobalIndexType i = 0; i < mesh.template getEntitiesCount< dimension >(); i++ ) - * { - * auto& entity = mesh.template getEntity< dimension >( i ); - * entity.template bindSuperentitiesStorageNetwork< superdimension >( mesh.template getSuperentityStorageNetwork< superdimension >().getValues( i ) ); - * } - */ - -#include -#include -#include -#include -#include - -namespace TNL { -namespace Meshes { - -template< typename Mesh > -class EntityStorageRebinder -{ - using IndexType = typename Mesh::GlobalIndexType; - using DeviceType = typename Mesh::DeviceType; - -// nvcc does not allow __cuda_callable__ lambdas inside private or protected sections -#ifdef __NVCC__ -public: -#endif - template< typename DimensionTag, - typename SuperdimensionTag, - bool Enabled = - Mesh::MeshTraitsType::template SuperentityTraits< typename Mesh::template EntityType< DimensionTag::value >::EntityTopology, - SuperdimensionTag::value >::storageEnabled - > - struct SuperentityWorker - { - static void bindSuperentities( Mesh& mesh ) - { - const IndexType entitiesCount = mesh.template getEntitiesCount< DimensionTag::value >(); - auto& superentitiesStorage = mesh.template getSuperentityStorageNetwork< DimensionTag::value, SuperdimensionTag::value >(); - using Multimap = typename std::remove_reference< decltype(superentitiesStorage) >::type; - Pointers::DevicePointer< Mesh > meshPointer( mesh ); - Pointers::DevicePointer< Multimap > superentitiesStoragePointer( superentitiesStorage ); - - auto kernel = [] __cuda_callable__ - ( IndexType i, - Mesh* mesh, - Multimap* superentitiesStorage ) - { - auto& subentity = mesh->template getEntity< DimensionTag::value >( i ); - subentity.template bindSuperentitiesStorageNetwork< SuperdimensionTag::value >( superentitiesStorage->getValues( i ) ); - }; - - Algorithms::ParallelFor< DeviceType >::exec( (IndexType) 0, entitiesCount, - kernel, - &meshPointer.template modifyData< DeviceType >(), - &superentitiesStoragePointer.template modifyData< DeviceType >() ); - } - }; - - template< typename DimensionTag, - typename SuperdimensionTag > - struct SuperentityWorker< DimensionTag, SuperdimensionTag, false > - { - static void bindSuperentities( Mesh& mesh ) {} - }; - - - template< typename DimensionTag, - typename SuperdimensionTag, - bool Enabled = - Mesh::MeshTraitsType::template SubentityTraits< typename Mesh::template EntityType< SuperdimensionTag::value >::EntityTopology, - DimensionTag::value >::storageEnabled - > - struct SubentityWorker - { - static void bindSubentities( Mesh& mesh ) - { - const IndexType entitiesCount = mesh.template getEntitiesCount< SuperdimensionTag::value >(); - auto& subentitiesStorage = mesh.template getSubentityStorageNetwork< SuperdimensionTag::value, DimensionTag::value >(); - using Multimap = typename std::remove_reference< decltype(subentitiesStorage) >::type; - Pointers::DevicePointer< Mesh > meshPointer( mesh ); - Pointers::DevicePointer< Multimap > subentitiesStoragePointer( subentitiesStorage ); - - auto kernel = [] __cuda_callable__ - ( IndexType i, - Mesh* mesh, - Multimap* subentitiesStorage ) - { - auto& superentity = mesh->template getEntity< SuperdimensionTag::value >( i ); - superentity.template bindSubentitiesStorageNetwork< DimensionTag::value >( subentitiesStorage->getValues( i ) ); - }; - - Algorithms::ParallelFor< DeviceType >::exec( (IndexType) 0, entitiesCount, - kernel, - &meshPointer.template modifyData< DeviceType >(), - &subentitiesStoragePointer.template modifyData< DeviceType >() ); - } - }; - - template< typename DimensionTag, - typename SuperdimensionTag > - struct SubentityWorker< DimensionTag, SuperdimensionTag, false > - { - static void bindSubentities( Mesh& mesh ) {} - }; - - - template< int Dimension, int Superdimension > - struct InnerLoop - { - static void exec( Mesh& mesh ) - { - using DimensionTag = Meshes::DimensionTag< Dimension >; - using SuperdimensionTag = Meshes::DimensionTag< Superdimension >; - SuperentityWorker< DimensionTag, SuperdimensionTag >::bindSuperentities( mesh ); - SubentityWorker< DimensionTag, SuperdimensionTag >::bindSubentities( mesh ); - } - }; - - template< int Dimension > - struct OuterLoop - { - template< int Superdimension > - using Inner = InnerLoop< Dimension, Superdimension >; - - static void exec( Mesh& mesh ) - { - Algorithms::TemplateStaticFor< int, Dimension + 1, Mesh::getMeshDimension() + 1, Inner >::execHost( mesh ); - } - }; - -public: - static void exec( Mesh& mesh ) - { - Algorithms::TemplateStaticFor< int, 0, Mesh::getMeshDimension() + 1, OuterLoop >::execHost( mesh ); - } -}; - -} // namespace Meshes -} // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h b/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h index bd9c02411283241b41a78fef4c008a71772f1ea6..71e9fab0eb2a691372af6adc0b5d93c3ef415889 100644 --- a/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h +++ b/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h @@ -12,7 +12,7 @@ #include #include -#include +#include namespace TNL { namespace Meshes { @@ -21,7 +21,7 @@ template< typename Mesh, int Dimension > struct IndexPermutationApplier { private: - using GlobalIndexVector = typename Mesh::GlobalIndexVector; + using GlobalIndexArray = typename Mesh::GlobalIndexArray; template< int Subdimension, bool Enabled = @@ -30,17 +30,17 @@ private: > struct _SubentitiesStorageWorker { - static void exec( Mesh& mesh, const GlobalIndexVector& perm ) + static void exec( Mesh& mesh, const GlobalIndexArray& perm ) { - auto& subentitiesStorage = mesh.template getSubentityStorageNetwork< Dimension, Subdimension >(); - Containers::Multimaps::permuteMultimapKeys( subentitiesStorage, perm ); + auto& subentitiesStorage = mesh.template getSubentitiesMatrix< Dimension, Subdimension >(); + Matrices::permuteMatrixRows( subentitiesStorage, perm ); } }; template< int Subdimension > struct _SubentitiesStorageWorker< Subdimension, false > { - static void exec( Mesh& mesh, const GlobalIndexVector& iperm ) {} + static void exec( Mesh& mesh, const GlobalIndexArray& iperm ) {} }; @@ -51,17 +51,18 @@ private: > struct _SuperentitiesStorageWorker { - static void exec( Mesh& mesh, const GlobalIndexVector& perm ) + static void exec( Mesh& mesh, const GlobalIndexArray& perm ) { - auto& superentitiesStorage = mesh.template getSuperentityStorageNetwork< Dimension, Superdimension >(); - Containers::Multimaps::permuteMultimapKeys( superentitiesStorage, perm ); + permuteArray( mesh.template getSuperentitiesCountsArray< Dimension, Superdimension >(), perm ); + auto& superentitiesStorage = mesh.template getSuperentitiesMatrix< Dimension, Superdimension >(); + Matrices::permuteMatrixRows( superentitiesStorage, perm ); } }; template< int Superdimension > struct _SuperentitiesStorageWorker< Superdimension, false > { - static void exec( Mesh& mesh, const GlobalIndexVector& iperm ) {} + static void exec( Mesh& mesh, const GlobalIndexArray& iperm ) {} }; @@ -72,17 +73,17 @@ private: > struct IndexPermutationApplierSubentitiesWorker { - static void exec( Mesh& mesh, const GlobalIndexVector& iperm ) + static void exec( Mesh& mesh, const GlobalIndexArray& iperm ) { - auto& superentitiesStorage = mesh.template getSuperentityStorageNetwork< Subdimension, Dimension >(); - Containers::Multimaps::permuteMultimapValues( superentitiesStorage, iperm ); + auto& superentitiesStorage = mesh.template getSuperentitiesMatrix< Subdimension, Dimension >(); + Matrices::permuteMatrixColumns( superentitiesStorage, iperm ); } }; template< int Subdimension > struct IndexPermutationApplierSubentitiesWorker< Subdimension, false > { - static void exec( Mesh& mesh, const GlobalIndexVector& iperm ) {} + static void exec( Mesh& mesh, const GlobalIndexArray& iperm ) {} }; @@ -93,17 +94,17 @@ private: > struct IndexPermutationApplierSuperentitiesWorker { - static void exec( Mesh& mesh, const GlobalIndexVector& iperm ) + static void exec( Mesh& mesh, const GlobalIndexArray& iperm ) { - auto& subentitiesStorage = mesh.template getSubentityStorageNetwork< Superdimension, Dimension >(); - Containers::Multimaps::permuteMultimapValues( subentitiesStorage, iperm ); + auto& subentitiesStorage = mesh.template getSubentitiesMatrix< Superdimension, Dimension >(); + Matrices::permuteMatrixColumns( subentitiesStorage, iperm ); } }; template< int Superdimension > struct IndexPermutationApplierSuperentitiesWorker< Superdimension, false > { - static void exec( Mesh& mesh, const GlobalIndexVector& iperm ) {} + static void exec( Mesh& mesh, const GlobalIndexArray& iperm ) {} }; @@ -120,51 +121,67 @@ private: template< int Superdimension > using SuperentitiesWorker = IndexPermutationApplierSuperentitiesWorker< Superdimension >; -public: - static void exec( Mesh& mesh, - const GlobalIndexVector& perm, - const GlobalIndexVector& iperm ) + template< typename Mesh_, std::enable_if_t< Mesh_::Config::dualGraphStorage(), bool > = true > + static void permuteDualGraph( Mesh_& mesh, const GlobalIndexArray& perm, const GlobalIndexArray& iperm ) { - using IndexType = typename Mesh::GlobalIndexType; - using DeviceType = typename Mesh::DeviceType; - using StorageArrayType = typename Mesh::template EntityTraits< Dimension >::StorageArrayType; + permuteArray( mesh.getNeighborCounts(), perm ); + auto& graph = mesh.getDualGraph(); + Matrices::permuteMatrixRows( graph, perm ); + Matrices::permuteMatrixColumns( graph, iperm ); + } - const IndexType entitiesCount = mesh.template getEntitiesCount< Dimension >(); + template< typename Mesh_, std::enable_if_t< ! Mesh_::Config::dualGraphStorage(), bool > = true > + static void permuteDualGraph( Mesh_& mesh, const GlobalIndexArray& perm, const GlobalIndexArray& iperm ) {} - StorageArrayType entities; - entities.setSize( entitiesCount ); +public: + template< typename ArrayOrView > + static void permuteArray( ArrayOrView& array, const GlobalIndexArray& perm ) + { + using ValueType = typename ArrayOrView::ValueType; + using IndexType = typename ArrayOrView::IndexType; + using DeviceType = typename ArrayOrView::DeviceType; - // kernel to copy entities to new array, applying the permutation + Containers::Array< ValueType, DeviceType, IndexType > buffer( array.getSize() ); + + // kernel to copy values to new array, applying the permutation auto kernel1 = [] __cuda_callable__ ( IndexType i, - const Mesh* mesh, - typename StorageArrayType::ValueType* entitiesArray, + const ValueType* array, + ValueType* buffer, const IndexType* perm ) { - entitiesArray[ i ] = mesh->template getEntity< Dimension >( perm[ i ] ); + buffer[ i ] = array[ perm[ i ] ]; }; - // kernel to copy permuted entities back to the mesh + // kernel to copy permuted values back to the mesh auto kernel2 = [] __cuda_callable__ ( IndexType i, - Mesh* mesh, - const typename StorageArrayType::ValueType* entitiesArray ) + ValueType* array, + const ValueType* buffer ) { - auto& entity = mesh->template getEntity< Dimension >( i ); - entity = entitiesArray[ i ]; - entity.setIndex( i ); + array[ i ] = buffer[ i ]; }; - Pointers::DevicePointer< Mesh > meshPointer( mesh ); - Algorithms::ParallelFor< DeviceType >::exec( (IndexType) 0, entitiesCount, + Algorithms::ParallelFor< DeviceType >::exec( (IndexType) 0, array.getSize(), kernel1, - &meshPointer.template getData< DeviceType >(), - entities.getData(), + array.getData(), + buffer.getData(), perm.getData() ); - Algorithms::ParallelFor< DeviceType >::exec( (IndexType) 0, entitiesCount, + Algorithms::ParallelFor< DeviceType >::exec( (IndexType) 0, array.getSize(), kernel2, - &meshPointer.template modifyData< DeviceType >(), - entities.getData() ); + array.getData(), + buffer.getData() ); + } + + static void exec( Mesh& mesh, + const GlobalIndexArray& perm, + const GlobalIndexArray& iperm ) + { + using IndexType = typename Mesh::GlobalIndexType; + using DeviceType = typename Mesh::DeviceType; + + if( Dimension == 0 ) + permuteArray( mesh.getPoints(), perm ); // permute superentities storage Algorithms::TemplateStaticFor< int, 0, Dimension, SubentitiesStorageWorker >::execHost( mesh, perm ); @@ -177,6 +194,11 @@ public: // update subentity indices from the superentities Algorithms::TemplateStaticFor< int, Dimension + 1, Mesh::getMeshDimension() + 1, SuperentitiesWorker >::execHost( mesh, iperm ); + + if( Dimension == Mesh::getMeshDimension() ) { + // permute dual graph + permuteDualGraph( mesh, perm, iperm ); + } } }; diff --git a/src/TNL/Meshes/MeshDetails/MeshEntityIndex.h b/src/TNL/Meshes/MeshDetails/MeshEntityIndex.h deleted file mode 100644 index 110fa9eefc1ca498435ce2fd11d1d84df2ad4410..0000000000000000000000000000000000000000 --- a/src/TNL/Meshes/MeshDetails/MeshEntityIndex.h +++ /dev/null @@ -1,75 +0,0 @@ -/*************************************************************************** - MeshEntityIndex.h - description - ------------------- - begin : Feb 28, 2014 - copyright : (C) 2014 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 IDType > -class MeshEntityIndex -{ -public: - // FIXME: IDType may be unsigned - MeshEntityIndex() - : id( -1 ) - {} - - __cuda_callable__ - const IDType& getIndex() const - { - TNL_ASSERT_GE( this->id, 0, "entity index is negative" ); - return this->id; - } - - __cuda_callable__ - bool operator==( const MeshEntityIndex& id ) const - { - return ( this->id == id.id ); - } - -protected: - __cuda_callable__ - void setIndex( IDType id ) - { - this->id = id; - } - - IDType id; -}; - -template<> -class MeshEntityIndex< void > -{ -public: - __cuda_callable__ - bool operator==( const MeshEntityIndex& id ) const - { - return true; - } - -protected: - template< typename Index > - __cuda_callable__ - void setIndex( Index ) - {} -}; - -} // namespace Meshes -} // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/MeshEntity_impl.h b/src/TNL/Meshes/MeshDetails/MeshEntity_impl.h deleted file mode 100644 index 5c7414b422976804cf819f266a312ad0d65cf40a..0000000000000000000000000000000000000000 --- a/src/TNL/Meshes/MeshDetails/MeshEntity_impl.h +++ /dev/null @@ -1,344 +0,0 @@ -/*************************************************************************** - MeshEntity_impl.h - description - ------------------- - begin : Sep 8, 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 Device, - typename EntityTopology > -__cuda_callable__ -MeshEntity< MeshConfig, Device, EntityTopology >:: -MeshEntity( const MeshEntity& entity ) - : SubentityAccessLayerFamily< MeshConfig, Device, EntityTopology >( entity ), - SuperentityAccessLayerFamily< MeshConfig, Device, EntityTopology >( entity ), - MeshEntityIndex< typename MeshConfig::IdType >( entity ) -{ -} - -template< typename MeshConfig, - typename Device, - typename EntityTopology > - template< typename Device_ > -MeshEntity< MeshConfig, Device, EntityTopology >:: -MeshEntity( const MeshEntity< MeshConfig, Device_, EntityTopology >& entity ) - // no cross-device copy of subentities and superentities here - Mesh constructor has to rebind pointers - : MeshEntityIndex< typename MeshConfig::IdType >( entity ) -{ - static_assert( ! std::is_same< Device, Device_ >::value, "this should never happen" ); -} - -template< typename MeshConfig, - typename Device, - typename EntityTopology > -__cuda_callable__ -MeshEntity< MeshConfig, Device, EntityTopology >& -MeshEntity< MeshConfig, Device, EntityTopology >:: -operator=( const MeshEntity& entity ) -{ - SubentityAccessLayerFamily< MeshConfig, Device, EntityTopology >::operator=( entity ); - SuperentityAccessLayerFamily< MeshConfig, Device, EntityTopology >::operator=( entity ); - MeshEntityIndex< typename MeshConfig::IdType >::operator=( entity ); - return *this; -} - -template< typename MeshConfig, - typename Device, - typename EntityTopology > - template< typename Device_ > -__cuda_callable__ -MeshEntity< MeshConfig, Device, EntityTopology >& -MeshEntity< MeshConfig, Device, EntityTopology >:: -operator=( const MeshEntity< MeshConfig, Device_, EntityTopology >& entity ) -{ - static_assert( ! std::is_same< Device, Device_ >::value, "this should never happen" ); - - // no cross-device copy of subentities and superentities here - Mesh::operator= has to rebind pointers - MeshEntityIndex< typename MeshConfig::IdType >::operator=( entity ); - return *this; -} - -template< typename MeshConfig, - typename Device, - typename EntityTopology > -String -MeshEntity< MeshConfig, Device, EntityTopology >:: -getSerializationType() -{ - return String( "MeshEntity<" ) + - TNL::getSerializationType< MeshConfig >() + ", " + - TNL::getSerializationType< EntityTopology >() + ">"; -} - -template< typename MeshConfig, - typename Device, - typename EntityTopology > -String -MeshEntity< MeshConfig, Device, EntityTopology >:: -getSerializationTypeVirtual() const -{ - return this->getSerializationType(); -} - -template< typename MeshConfig, - typename Device, - typename EntityTopology > -void -MeshEntity< MeshConfig, Device, EntityTopology >:: -save( File& file ) const -{ - // no I/O for subentities and superentities - not loaded anyway -} - -template< typename MeshConfig, - typename Device, - typename EntityTopology > -void -MeshEntity< MeshConfig, Device, EntityTopology >:: -load( File& file ) -{ - // no I/O for subentities and superentities - Mesh::load has to rebind pointers -} - -template< typename MeshConfig, - typename Device, - typename EntityTopology > -void -MeshEntity< MeshConfig, Device, EntityTopology >:: -print( std::ostream& str ) const -{ - str << "\t Mesh entity dimension: " << EntityTopology::dimension << std::endl; - SubentityAccessLayerFamily< MeshConfig, Device, EntityTopology >::print( str ); - SuperentityAccessLayerFamily< MeshConfig, Device, EntityTopology >::print( str ); -} - -template< typename MeshConfig, - typename Device, - typename EntityTopology > -__cuda_callable__ -bool -MeshEntity< MeshConfig, Device, EntityTopology >:: -operator==( const MeshEntity& entity ) const -{ - return ( SubentityAccessLayerFamily< MeshConfig, Device, EntityTopology >::operator==( entity ) && - SuperentityAccessLayerFamily< MeshConfig, Device, EntityTopology >::operator==( entity ) && - MeshEntityIndex< typename MeshConfig::IdType >::operator==( entity ) ); -} - -template< typename MeshConfig, - typename Device, - typename EntityTopology > -__cuda_callable__ -bool -MeshEntity< MeshConfig, Device, EntityTopology >:: -operator!=( const MeshEntity& entity ) const -{ - return ! ( *this == entity ); -} - -template< typename MeshConfig, - typename Device, - typename EntityTopology > -constexpr int -MeshEntity< MeshConfig, Device, EntityTopology >:: -getEntityDimension() -{ - return EntityTopology::dimension; -} - -/**** - * Subentities - */ -template< typename MeshConfig, - typename Device, - typename EntityTopology > -constexpr typename MeshEntity< MeshConfig, Device, EntityTopology >::LocalIndexType -MeshEntity< MeshConfig, Device, EntityTopology >:: -getVerticesCount() -{ - return SubentityTraits< 0 >::count; -} - -template< typename MeshConfig, - typename Device, - typename EntityTopology > -typename MeshEntity< MeshConfig, Device, EntityTopology >::GlobalIndexType -MeshEntity< MeshConfig, Device, EntityTopology >:: -getVertexIndex( const LocalIndexType localIndex ) const -{ - return this->template getSubentityIndex< 0 >( localIndex ); -} - - -/**** - * Vertex entity specialization - */ -template< typename MeshConfig, typename Device > -__cuda_callable__ -MeshEntity< MeshConfig, Device, Topologies::Vertex >:: -MeshEntity( const MeshEntity& entity ) - : SuperentityAccessLayerFamily< MeshConfig, Device, Topologies::Vertex >( entity ), - MeshEntityIndex< typename MeshConfig::IdType >( entity ) -{ - setPoint( entity.getPoint() ); -} - -template< typename MeshConfig, typename Device > - template< typename Device_ > -MeshEntity< MeshConfig, Device, Topologies::Vertex >:: -MeshEntity( const MeshEntity< MeshConfig, Device_, Topologies::Vertex >& entity ) - // no cross-device copy of superentities here - Mesh constructor has to rebind pointers - : MeshEntityIndex< typename MeshConfig::IdType >( entity ) -{ - static_assert( ! std::is_same< Device, Device_ >::value, "this should never happen" ); - - setPoint( entity.getPoint() ); -} - -template< typename MeshConfig, typename Device > -__cuda_callable__ -MeshEntity< MeshConfig, Device, Topologies::Vertex >& -MeshEntity< MeshConfig, Device, Topologies::Vertex >:: -operator=( const MeshEntity& entity ) -{ - SuperentityAccessLayerFamily< MeshConfig, Device, Topologies::Vertex >::operator=( entity ); - MeshEntityIndex< typename MeshConfig::IdType >::operator=( entity ); - setPoint( entity.getPoint() ); - return *this; -} - -template< typename MeshConfig, typename Device > - template< typename Device_ > -__cuda_callable__ -MeshEntity< MeshConfig, Device, Topologies::Vertex >& -MeshEntity< MeshConfig, Device, Topologies::Vertex >:: -operator=( const MeshEntity< MeshConfig, Device_, Topologies::Vertex >& entity ) -{ - static_assert( ! std::is_same< Device, Device_ >::value, "this should never happen" ); - - // no cross-device copy of superentities here - Mesh::operator= has to rebind pointers - MeshEntityIndex< typename MeshConfig::IdType >::operator=( entity ); - setPoint( entity.getPoint() ); - return *this; -} - -template< typename MeshConfig, typename Device > -String -MeshEntity< MeshConfig, Device, Topologies::Vertex >:: -getSerializationType() -{ - return String( "MeshEntity<" ) + - TNL::getSerializationType< MeshConfig >() + ", " + - TNL::getSerializationType< Topologies::Vertex >() + ">"; -} - -template< typename MeshConfig, typename Device > -String -MeshEntity< MeshConfig, Device, Topologies::Vertex >:: -getSerializationTypeVirtual() const -{ - return this->getSerializationType(); -} - -template< typename MeshConfig, typename Device > -void -MeshEntity< MeshConfig, Device, Topologies::Vertex >:: -save( File& file ) const -{ - // no I/O for superentities - not loaded anyway - point.save( file ); -} - -template< typename MeshConfig, typename Device > -void -MeshEntity< MeshConfig, Device, Topologies::Vertex >:: -load( File& file ) -{ - // no I/O for superentities - Mesh::load has to rebind pointers - point.load( file ); -} - -template< typename MeshConfig, typename Device > -void -MeshEntity< MeshConfig, Device, Topologies::Vertex >:: -print( std::ostream& str ) const -{ - str << "\t Mesh entity dimension: " << Topologies::Vertex::dimension << std::endl; - str << "\t Coordinates = " << point << std::endl; - SuperentityAccessLayerFamily< MeshConfig, Device, Topologies::Vertex >::print( str ); -} - -template< typename MeshConfig, typename Device > -__cuda_callable__ -bool -MeshEntity< MeshConfig, Device, Topologies::Vertex >:: -operator==( const MeshEntity& entity ) const -{ - return ( SuperentityAccessLayerFamily< MeshConfig, Device, Topologies::Vertex >::operator==( entity ) && - MeshEntityIndex< typename MeshConfig::IdType >::operator==( entity ) && - point == entity.point ); -} - -template< typename MeshConfig, typename Device > -__cuda_callable__ -bool -MeshEntity< MeshConfig, Device, Topologies::Vertex >:: -operator!=( const MeshEntity& entity ) const -{ - return ! ( *this == entity ); -} - -template< typename MeshConfig, typename Device > -constexpr int -MeshEntity< MeshConfig, Device, Topologies::Vertex >:: -getEntityDimension() -{ - return EntityTopology::dimension; -} - -template< typename MeshConfig, typename Device > -__cuda_callable__ -typename MeshEntity< MeshConfig, Device, Topologies::Vertex >::PointType -MeshEntity< MeshConfig, Device, Topologies::Vertex >:: -getPoint() const -{ - return this->point; -} - -template< typename MeshConfig, typename Device > -__cuda_callable__ -void -MeshEntity< MeshConfig, Device, Topologies::Vertex >:: -setPoint( const PointType& point ) -{ - this->point = point; -} - -template< typename MeshConfig, - typename Device, - typename EntityTopology > -std::ostream& operator<<( std::ostream& str, const MeshEntity< MeshConfig, Device, EntityTopology >& entity ) -{ - entity.print( str ); - return str; -} - -} // namespace Meshes -} // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/Layer.h b/src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/Layer.h deleted file mode 100644 index e31c76dae0a4ebd5555cd0340c4d732e49451e02..0000000000000000000000000000000000000000 --- a/src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/Layer.h +++ /dev/null @@ -1,255 +0,0 @@ -/*************************************************************************** - Layer.h - description - ------------------- - begin : Dec 25, 2016 - copyright : (C) 2016 by Tomas Oberhuber et al. - email : tomas.oberhuber@fjfi.cvut.cz - ***************************************************************************/ - -/* See Copyright Notice in tnl/Copyright */ - -#pragma once - -#include -#include -#include - -#include "Traits.h" - -namespace TNL { -namespace Meshes { -namespace BoundaryTags { - -// This is the implementation of the boundary tags layer for one specific dimension. -// It is inherited by the BoundaryTags::LayerFamily. -template< typename MeshConfig, - typename Device, - typename DimensionTag, - bool TagStorage = WeakStorageTrait< MeshConfig, Device, DimensionTag >::boundaryTagsEnabled > -class Layer -{ - using MeshTraitsType = MeshTraits< MeshConfig, Device >; - -public: - using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; - using BoundaryTagsArray = Containers::Array< bool, Device, GlobalIndexType >; - using OrderingArray = Containers::Array< GlobalIndexType, Device, GlobalIndexType >; - - Layer() = default; - - explicit Layer( const Layer& other ) - { - operator=( other ); - } - - template< typename Device_ > - Layer( const Layer< MeshConfig, Device_, DimensionTag >& other ) - { - operator=( other ); - } - - Layer& operator=( const Layer& other ) - { - boundaryTags.setLike( other.boundaryTags ); - boundaryIndices.setLike( other.boundaryIndices ); - interiorIndices.setLike( other.interiorIndices ); - boundaryTags = other.boundaryTags; - boundaryIndices = other.boundaryIndices; - interiorIndices = other.interiorIndices; - return *this; - } - - template< typename Device_ > - Layer& operator=( const Layer< MeshConfig, Device_, DimensionTag >& other ) - { - boundaryTags.setLike( other.boundaryTags ); - boundaryIndices.setLike( other.boundaryIndices ); - interiorIndices.setLike( other.interiorIndices ); - boundaryTags = other.boundaryTags; - boundaryIndices = other.boundaryIndices; - interiorIndices = other.interiorIndices; - return *this; - } - - - void setEntitiesCount( DimensionTag, const GlobalIndexType& entitiesCount ) - { - boundaryTags.setSize( entitiesCount ); - } - - void resetBoundaryTags( DimensionTag ) - { - boundaryTags.setValue( false ); - } - - __cuda_callable__ - void setIsBoundaryEntity( DimensionTag, const GlobalIndexType& entityIndex, bool isBoundary ) - { - boundaryTags[ entityIndex ] = isBoundary; - } - - __cuda_callable__ - bool isBoundaryEntity( DimensionTag, const GlobalIndexType& entityIndex ) const - { - return boundaryTags[ entityIndex ]; - } - - void updateBoundaryIndices( DimensionTag ) - { - // Array does not have sum(), Vector of bools does not fit due to arithmetics - Containers::VectorView< typename BoundaryTagsArray::ValueType, typename BoundaryTagsArray::DeviceType, typename BoundaryTagsArray::IndexType > _boundaryTagsVector; - _boundaryTagsVector.bind( boundaryTags.getData(), boundaryTags.getSize() ); - const GlobalIndexType boundaryEntities = sum(cast< GlobalIndexType >( _boundaryTagsVector )); - boundaryIndices.setSize( boundaryEntities ); - interiorIndices.setSize( boundaryTags.getSize() - boundaryEntities ); - - if( std::is_same< Device, Devices::Host >::value ) { - GlobalIndexType b = 0; - GlobalIndexType i = 0; - while( b + i < boundaryTags.getSize() ) { - const GlobalIndexType e = b + i; - if( boundaryTags[ e ] ) - boundaryIndices[ b++ ] = e; - else - interiorIndices[ i++ ] = e; - } - } - // TODO: parallelize directly on the device - else { - using BoundaryTagsHostArray = typename BoundaryTagsArray::template Self< typename BoundaryTagsArray::ValueType, Devices::Host >; - using OrderingHostArray = typename OrderingArray::template Self< typename OrderingArray::ValueType, Devices::Host >; - - BoundaryTagsHostArray hostBoundaryTags; - OrderingHostArray hostBoundaryIndices; - OrderingHostArray hostInteriorIndices; - - hostBoundaryTags.setLike( boundaryTags ); - hostBoundaryIndices.setLike( boundaryIndices ); - hostInteriorIndices.setLike( interiorIndices ); - - hostBoundaryTags = boundaryTags; - - GlobalIndexType b = 0; - GlobalIndexType i = 0; - while( b + i < boundaryTags.getSize() ) { - const GlobalIndexType e = b + i; - if( hostBoundaryTags[ e ] ) - hostBoundaryIndices[ b++ ] = e; - else - hostInteriorIndices[ i++ ] = e; - } - - boundaryIndices = hostBoundaryIndices; - interiorIndices = hostInteriorIndices; - } - } - - __cuda_callable__ - GlobalIndexType getBoundaryEntitiesCount( DimensionTag ) const - { - return boundaryIndices.getSize(); - } - - __cuda_callable__ - GlobalIndexType getBoundaryEntityIndex( DimensionTag, const GlobalIndexType& i ) const - { - return boundaryIndices[ i ]; - } - - __cuda_callable__ - GlobalIndexType getInteriorEntitiesCount( DimensionTag ) const - { - return interiorIndices.getSize(); - } - - __cuda_callable__ - GlobalIndexType getInteriorEntityIndex( DimensionTag, const GlobalIndexType& i ) const - { - return interiorIndices[ i ]; - } - - void save( File& file ) const - { - file << boundaryTags; - } - - void load( File& file ) - { - file >> boundaryTags; - updateBoundaryIndices( DimensionTag() ); - } - - void print( std::ostream& str ) const - { - str << "Boundary tags for entities of dimension " << DimensionTag::value << " are: "; - str << boundaryTags << std::endl; - str << "Indices of the boundary entities of dimension " << DimensionTag::value << " are: "; - str << boundaryIndices << std::endl; - str << "Indices of the interior entities of dimension " << DimensionTag::value << " are: "; - str << interiorIndices << std::endl; - } - - bool operator==( const Layer& layer ) const - { - TNL_ASSERT( ( boundaryTags == layer.boundaryTags && boundaryIndices == layer.boundaryIndices && interiorIndices == layer.interiorIndices ) || - ( boundaryTags != layer.boundaryTags && boundaryIndices != layer.boundaryIndices && interiorIndices != layer.interiorIndices ), - std::cerr << "The BoundaryTags layer is in inconsistent state - this is probably a bug in the boundary tags initializer." << std::endl - << "boundaryTags = " << boundaryTags << std::endl - << "layer.boundaryTags = " << layer.boundaryTags << std::endl - << "boundaryIndices = " << boundaryIndices << std::endl - << "layer.boundaryIndices = " << layer.boundaryIndices << std::endl - << "interiorIndices = " << interiorIndices << std::endl - << "layer.interiorIndices = " << layer.interiorIndices << std::endl; ); - return boundaryTags == layer.boundaryTags; - } - -private: - BoundaryTagsArray boundaryTags; - OrderingArray boundaryIndices; - OrderingArray interiorIndices; - - // friend class is needed for templated assignment operators - template< typename MeshConfig_, typename Device_, typename DimensionTag_, bool TagStorage_ > - friend class Layer; -}; - -template< typename MeshConfig, - typename Device, - typename DimensionTag > -class Layer< MeshConfig, Device, DimensionTag, false > -{ -protected: - using GlobalIndexType = typename MeshConfig::GlobalIndexType; - - Layer() = default; - explicit Layer( const Layer& other ) {} - template< typename Device_ > - Layer( const Layer< MeshConfig, Device_, DimensionTag >& other ) {} - Layer& operator=( const Layer& other ) { return *this; } - template< typename Device_ > - Layer& operator=( const Layer< MeshConfig, Device_, DimensionTag >& other ) { return *this; } - - void setEntitiesCount( DimensionTag, const GlobalIndexType& entitiesCount ) {} - void resetBoundaryTags( DimensionTag ) {} - void setIsBoundaryEntity( DimensionTag, const GlobalIndexType& entityIndex, bool isBoundary ) {} - void isBoundaryEntity( DimensionTag, const GlobalIndexType& entityIndex ) const {} - void updateBoundaryIndices( DimensionTag ) {} - void getBoundaryEntitiesCount( DimensionTag ) const {} - void getBoundaryEntityIndex( DimensionTag, const GlobalIndexType& i ) const {} - void getInteriorEntitiesCount( DimensionTag ) const {} - void getInteriorEntityIndex( DimensionTag, const GlobalIndexType& i ) const {} - - void save( File& file ) const {} - void load( File& file ) {} - - void print( std::ostream& str ) const {} - - bool operator==( const Layer& layer ) const - { - return true; - } -}; - -} // namespace BoundaryTags -} // namespace Meshes -} // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/MeshLayers/StorageLayer.h b/src/TNL/Meshes/MeshDetails/MeshLayers/StorageLayer.h deleted file mode 100644 index 86c19eeb2873aedc1d35fb1a27560ec1d3d20eff..0000000000000000000000000000000000000000 --- a/src/TNL/Meshes/MeshDetails/MeshLayers/StorageLayer.h +++ /dev/null @@ -1,273 +0,0 @@ -/*************************************************************************** - StorageLayer.h - description - ------------------- - begin : Feb 11, 2014 - copyright : (C) 2014 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 -#include -#include - -namespace TNL { -namespace Meshes { - -template< typename MeshConfig, - typename Device, - typename DimensionTag, - bool EntityStorage = WeakEntityStorageTrait< MeshConfig, Device, DimensionTag >::storageEnabled > -class StorageLayer; - - -template< typename MeshConfig, typename Device > -class StorageLayerFamily - : public StorageLayer< MeshConfig, Device, DimensionTag< 0 > > -{ - using MeshTraitsType = MeshTraits< MeshConfig, Device >; - using BaseType = StorageLayer< MeshConfig, Device, DimensionTag< 0 > >; - template< int Dimension > - using EntityTraits = typename MeshTraitsType::template EntityTraits< Dimension >; - -public: - // inherit constructors and assignment operators (including templated versions) - using BaseType::BaseType; - using BaseType::operator=; - -protected: - template< int Dimension > - void setEntitiesCount( const typename MeshTraitsType::GlobalIndexType& entitiesCount ) - { - static_assert( EntityTraits< Dimension >::storageEnabled, "You try to set number of entities which are not configured for storage." ); - BaseType::setEntitiesCount( DimensionTag< Dimension >(), entitiesCount ); - } - - template< int Dimension, int Subdimension > - typename MeshTraitsType::template SubentityTraits< typename EntityTraits< Dimension >::EntityTopology, Subdimension >::StorageNetworkType& - getSubentityStorageNetwork() - { - static_assert( EntityTraits< Dimension >::storageEnabled, "You try to get subentity storage of entities which are not configured for storage." ); - static_assert( Dimension > Subdimension, "Invalid combination of Dimension and Subdimension." ); - using BaseType = SubentityStorageLayerFamily< MeshConfig, - Device, - typename EntityTraits< Dimension >::EntityTopology >; - return BaseType::template getSubentityStorageNetwork< Subdimension >(); - } - - template< int Dimension, int Superdimension > - typename MeshTraitsType::template SuperentityTraits< typename EntityTraits< Dimension >::EntityTopology, Superdimension >::StorageNetworkType& - getSuperentityStorageNetwork() - { - static_assert( EntityTraits< Dimension >::storageEnabled, "You try to get superentity storage of entities which are not configured for storage." ); - static_assert( Dimension < Superdimension, "Invalid combination of Dimension and Superdimension." ); - using BaseType = SuperentityStorageLayerFamily< MeshConfig, - Device, - typename EntityTraits< Dimension >::EntityTopology >; - return BaseType::template getSuperentityStorageNetwork< Superdimension >(); - } -}; - - -template< typename MeshConfig, - typename Device, - typename DimensionTag > -class StorageLayer< MeshConfig, - Device, - DimensionTag, - true > - : public SubentityStorageLayerFamily< MeshConfig, - Device, - typename MeshTraits< MeshConfig, Device >::template EntityTraits< DimensionTag::value >::EntityTopology >, - public SuperentityStorageLayerFamily< MeshConfig, - Device, - typename MeshTraits< MeshConfig, Device >::template EntityTraits< DimensionTag::value >::EntityTopology >, - public StorageLayer< MeshConfig, Device, typename DimensionTag::Increment > -{ -public: - using BaseType = StorageLayer< MeshConfig, Device, typename DimensionTag::Increment >; - using MeshTraitsType = MeshTraits< MeshConfig, Device >; - using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; - using EntityTraitsType = typename MeshTraitsType::template EntityTraits< DimensionTag::value >; - using StorageArrayType = typename EntityTraitsType::StorageArrayType; - using EntityType = typename EntityTraitsType::EntityType; - using EntityTopology = typename EntityTraitsType::EntityTopology; - using SubentityStorageBaseType = SubentityStorageLayerFamily< MeshConfig, Device, EntityTopology >; - using SuperentityStorageBaseType = SuperentityStorageLayerFamily< MeshConfig, Device, EntityTopology >; - - StorageLayer() = default; - - explicit StorageLayer( const StorageLayer& other ) - { - operator=( other ); - } - - template< typename Device_ > - StorageLayer( const StorageLayer< MeshConfig, Device_, DimensionTag >& other ) - { - operator=( other ); - } - - StorageLayer& operator=( const StorageLayer& other ) - { - entities.setLike( other.entities ); - entities = other.entities; - SubentityStorageBaseType::operator=( other ); - SuperentityStorageBaseType::operator=( other ); - BaseType::operator=( other ); - return *this; - } - - template< typename Device_ > - StorageLayer& operator=( const StorageLayer< MeshConfig, Device_, DimensionTag >& other ) - { - entities.setLike( other.entities ); - entities = other.entities; - SubentityStorageBaseType::operator=( other ); - SuperentityStorageBaseType::operator=( other ); - BaseType::operator=( other ); - return *this; - } - - void save( File& file ) const - { - SubentityStorageBaseType::save( file ); - SuperentityStorageBaseType::save( file ); - file << this->entities; - BaseType::save( file ); - } - - void load( File& file ) - { - SubentityStorageBaseType::load( file ); - SuperentityStorageBaseType::load( file ); - file >> this->entities; - BaseType::load( file ); - } - - void print( std::ostream& str ) const - { - str << "The entities with dimension " << DimensionTag::value << " are: " << std::endl; - for( GlobalIndexType i = 0; i < entities.getSize(); i++ ) - str << i << " " << entities[ i ] << std::endl; - SubentityStorageBaseType::print( str ); - SuperentityStorageBaseType::print( str ); - str << std::endl; - BaseType::print( str ); - } - - bool operator==( const StorageLayer& meshLayer ) const - { - return ( entities == meshLayer.entities && - SubentityStorageBaseType::operator==( meshLayer ) && - SuperentityStorageBaseType::operator==( meshLayer ) && - BaseType::operator==( meshLayer ) ); - } - - - using BaseType::getEntitiesCount; - __cuda_callable__ - GlobalIndexType getEntitiesCount( DimensionTag ) const - { - return this->entities.getSize(); - } - - using BaseType::getEntity; - __cuda_callable__ - EntityType& getEntity( DimensionTag, - const GlobalIndexType entityIndex ) - { - return this->entities[ entityIndex ]; - } - - __cuda_callable__ - const EntityType& getEntity( DimensionTag, - const GlobalIndexType entityIndex ) const - { - return this->entities[ entityIndex ]; - } - -protected: - using BaseType::setEntitiesCount; - void setEntitiesCount( DimensionTag, const GlobalIndexType& entitiesCount ) - { - this->entities.setSize( entitiesCount ); - SubentityStorageBaseType::setEntitiesCount( entitiesCount ); - SuperentityStorageBaseType::setEntitiesCount( entitiesCount ); - } - - StorageArrayType entities; - - // friend class is needed for templated assignment operators - template< typename MeshConfig_, typename Device_, typename DimensionTag_, bool Storage_ > - friend class StorageLayer; -}; - -template< typename MeshConfig, - typename Device, - typename DimensionTag > -class StorageLayer< MeshConfig, Device, DimensionTag, false > - : public StorageLayer< MeshConfig, Device, typename DimensionTag::Increment > -{ - using BaseType = StorageLayer< MeshConfig, Device, typename DimensionTag::Increment >; -public: - // inherit constructors and assignment operators (including templated versions) - using BaseType::BaseType; - using BaseType::operator=; -}; - -// termination of recursive inheritance (everything is reduced to EntityStorage == false thanks to the WeakEntityStorageTrait) -template< typename MeshConfig, - typename Device > -class StorageLayer< MeshConfig, Device, DimensionTag< MeshConfig::meshDimension + 1 >, false > -{ -protected: - using DimensionTag = Meshes::DimensionTag< MeshConfig::meshDimension >; - using GlobalIndexType = typename MeshConfig::GlobalIndexType; - - StorageLayer() = default; - - explicit StorageLayer( const StorageLayer& other ) {} - - template< typename Device_ > - StorageLayer( const StorageLayer< MeshConfig, Device_, DimensionTag >& other ) {} - - StorageLayer& operator=( const StorageLayer& other ) - { - return *this; - } - - template< typename Device_ > - StorageLayer& operator=( const StorageLayer< MeshConfig, Device_, DimensionTag >& other ) - { - return *this; - } - - - void setEntitiesCount() {} - void getEntitiesCount() const {} - void getEntity() const {} - - void save( File& file ) const {} - void load( File& file ) {} - - void print( std::ostream& str ) const {} - - bool operator==( const StorageLayer& meshLayer ) const - { - return true; - } -}; - -} // namespace Meshes -} // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/Mesh_impl.h b/src/TNL/Meshes/MeshDetails/Mesh_impl.h deleted file mode 100644 index 4b0488b2d05f1cb9a36cd4ee157f0b355d679d63..0000000000000000000000000000000000000000 --- a/src/TNL/Meshes/MeshDetails/Mesh_impl.h +++ /dev/null @@ -1,298 +0,0 @@ -/*************************************************************************** - Mesh_impl.h - description - ------------------- - begin : Sep 5, 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 -#include -#include - -namespace TNL { -namespace Meshes { - -template< typename MeshConfig, typename Device, typename MeshType > -void -MeshInitializableBase< MeshConfig, Device, MeshType >:: -init( typename MeshTraitsType::PointArrayType& points, - typename MeshTraitsType::CellSeedArrayType& cellSeeds ) -{ - Initializer< typename MeshType::Config > initializer; - initializer.createMesh( points, cellSeeds, *static_cast(this) ); - // init boundary tags - static_cast< BoundaryTags::LayerFamily< MeshConfig, Device, MeshType >* >( static_cast< MeshType* >( this ) )->initLayer(); -} - - -template< typename MeshConfig, typename Device > -Mesh< MeshConfig, Device >:: -Mesh( const Mesh& mesh ) - : StorageBaseType( mesh ), - BoundaryTagsLayerFamily( mesh ) -{ - // update pointers from entities into the subentity and superentity storage networks - EntityStorageRebinder< Mesh< MeshConfig, Device > >::exec( *this ); -} - -template< typename MeshConfig, typename Device > - template< typename Device_ > -Mesh< MeshConfig, Device >:: -Mesh( const Mesh< MeshConfig, Device_ >& mesh ) - : StorageBaseType( mesh ), - BoundaryTagsLayerFamily( mesh ) -{ - // update pointers from entities into the subentity and superentity storage networks - EntityStorageRebinder< Mesh< MeshConfig, Device > >::exec( *this ); -} - -template< typename MeshConfig, typename Device > -Mesh< MeshConfig, Device >& -Mesh< MeshConfig, Device >:: -operator=( const Mesh& mesh ) -{ - StorageBaseType::operator=( mesh ); - BoundaryTagsLayerFamily::operator=( mesh ); - // update pointers from entities into the subentity and superentity storage networks - EntityStorageRebinder< Mesh< MeshConfig, Device > >::exec( *this ); - return *this; -} - -template< typename MeshConfig, typename Device > - template< typename Device_ > -Mesh< MeshConfig, Device >& -Mesh< MeshConfig, Device >:: -operator=( const Mesh< MeshConfig, Device_ >& mesh ) -{ - StorageBaseType::operator=( mesh ); - BoundaryTagsLayerFamily::operator=( mesh ); - // update pointers from entities into the subentity and superentity storage networks - EntityStorageRebinder< Mesh< MeshConfig, Device > >::exec( *this ); - return *this; -} - -template< typename MeshConfig, typename Device > -constexpr int -Mesh< MeshConfig, Device >:: -getMeshDimension() -{ - return MeshTraitsType::meshDimension; -} - -template< typename MeshConfig, typename Device > -String -Mesh< MeshConfig, Device >:: -getSerializationType() -{ - return String( "Meshes::Mesh< ") + TNL::getType< MeshConfig >() + " >"; -} - -template< typename MeshConfig, typename Device > -String -Mesh< MeshConfig, Device >:: -getSerializationTypeVirtual() const -{ - return this->getSerializationType(); -} - -template< typename MeshConfig, typename Device > - template< int Dimension > -constexpr bool -Mesh< MeshConfig, Device >:: -entitiesAvailable() -{ - return MeshTraitsType::template EntityTraits< Dimension >::storageEnabled; -} - -template< typename MeshConfig, typename Device > - template< int Dimension > -__cuda_callable__ -typename Mesh< MeshConfig, Device >::GlobalIndexType -Mesh< MeshConfig, Device >:: -getEntitiesCount() const -{ - static_assert( EntityTraits< Dimension >::storageEnabled, "You try to get number of entities which are not configured for storage." ); - return StorageBaseType::getEntitiesCount( DimensionTag< Dimension >() ); -} - -template< typename MeshConfig, typename Device > - template< int Dimension > -__cuda_callable__ -typename Mesh< MeshConfig, Device >::template EntityType< Dimension >& -Mesh< MeshConfig, Device >:: -getEntity( const GlobalIndexType& entityIndex ) -{ - static_assert( EntityTraits< Dimension >::storageEnabled, "You try to get entity which is not configured for storage." ); - return StorageBaseType::getEntity( DimensionTag< Dimension >(), entityIndex ); -} - -template< typename MeshConfig, typename Device > - template< int Dimension > -__cuda_callable__ -const typename Mesh< MeshConfig, Device >::template EntityType< Dimension >& -Mesh< MeshConfig, Device >:: -getEntity( const GlobalIndexType& entityIndex ) const -{ - static_assert( EntityTraits< Dimension >::storageEnabled, "You try to get entity which is not configured for storage." ); - return StorageBaseType::getEntity( DimensionTag< Dimension >(), entityIndex ); -} - - -// duplicated for compatibility with grids -template< typename MeshConfig, typename Device > - template< typename Entity > -__cuda_callable__ -typename Mesh< MeshConfig, Device >::GlobalIndexType -Mesh< MeshConfig, Device >:: -getEntitiesCount() const -{ - return getEntitiesCount< Entity::getEntityDimension() >(); -} - -template< typename MeshConfig, typename Device > - template< typename Entity > -__cuda_callable__ -Entity& -Mesh< MeshConfig, Device >:: -getEntity( const GlobalIndexType& entityIndex ) -{ - return getEntity< Entity::getEntityDimension() >( entityIndex ); -} - -template< typename MeshConfig, typename Device > - template< typename Entity > -__cuda_callable__ -const Entity& -Mesh< MeshConfig, Device >:: -getEntity( const GlobalIndexType& entityIndex ) const -{ - return getEntity< Entity::getEntityDimension() >( entityIndex ); -} - - -template< typename MeshConfig, typename Device > - template< int Dimension > -void -Mesh< MeshConfig, Device >:: -reorderEntities( const GlobalIndexVector& perm, - const GlobalIndexVector& iperm ) -{ - static_assert( entitiesAvailable< Dimension >(), "Entities which are not stored cannot be reordered." ); - - const GlobalIndexType entitiesCount = getEntitiesCount< Dimension >(); - - // basic sanity check - if( perm.getSize() != entitiesCount || iperm.getSize() != entitiesCount ) { - throw std::logic_error( "Wrong size of permutation vectors: " - "perm size = " + std::to_string( perm.getSize() ) + ", " - "iperm size = " + std::to_string( iperm.getSize() ) ); - } - TNL_ASSERT( min( perm ) == 0 && max( perm ) == entitiesCount - 1, - std::cerr << "Given array is not a permutation: min = " << min( perm ) - << ", max = " << max( perm ) - << ", number of entities = " << entitiesCount - << ", array = " << perm << std::endl; ); - TNL_ASSERT( min( iperm ) == 0 && max( iperm ) == entitiesCount - 1, - std::cerr << "Given array is not a permutation: min = " << min( iperm ) - << ", max = " << max( iperm ) - << ", number of entities = " << entitiesCount - << ", array = " << iperm << std::endl; ); - - IndexPermutationApplier< Mesh, Dimension >::exec( *this, perm, iperm ); - // update pointers from entities into the subentity and superentity storage networks - // TODO: it would be enough to rebind just the permuted entities - EntityStorageRebinder< Mesh< MeshConfig, Device > >::exec( *this ); - // update boundary tags - static_cast< BoundaryTagsLayerFamily* >( this )->initLayer(); -} - - -template< typename MeshConfig, typename Device > -void -Mesh< MeshConfig, Device >:: -save( File& file ) const -{ - Object::save( file ); - StorageBaseType::save( file ); - BoundaryTagsLayerFamily::save( file ); -} - -template< typename MeshConfig, typename Device > -void -Mesh< MeshConfig, Device >:: -load( File& file ) -{ - Object::load( file ); - StorageBaseType::load( file ); - BoundaryTagsLayerFamily::load( file ); - // update pointers from entities into the subentity and superentity storage networks - EntityStorageRebinder< Mesh< MeshConfig, Device > >::exec( *this ); -} - -template< typename MeshConfig, typename Device > -void -Mesh< MeshConfig, Device >:: -print( std::ostream& str ) const -{ - // FIXME: there is a problem with multimaps and accessors holding pointers into the device memory - if( std::is_same< Device, Devices::Cuda >::value ) { - str << "Textual representation of meshes stored on the CUDA device is not supported." << std::endl; - } - else { - StorageBaseType::print( str ); - BoundaryTagsLayerFamily::print( str ); - } -} - -template< typename MeshConfig, typename Device > -bool -Mesh< MeshConfig, Device >:: -operator==( const Mesh& mesh ) const -{ - return StorageBaseType::operator==( mesh ) && - BoundaryTagsLayerFamily::operator==( mesh ); -} - -template< typename MeshConfig, typename Device > -bool -Mesh< MeshConfig, Device >:: -operator!=( const Mesh& mesh ) const -{ - return ! operator==( mesh ); -} - -template< typename MeshConfig, typename Device > -void -Mesh< MeshConfig, Device >:: -writeProlog( Logger& logger ) const -{ - logger.writeParameter( "Dimension:", getMeshDimension() ); - logger.writeParameter( "Number of cells:", getEntitiesCount< getMeshDimension() >() ); - if( getMeshDimension() > 1 ) - logger.writeParameter( "Number of faces:", getEntitiesCount< getMeshDimension() - 1 >() ); - logger.writeParameter( "Number of vertices:", getEntitiesCount< 0 >() ); - // TODO: more parameters? -} - - -template< typename MeshConfig, typename Device > -std::ostream& operator<<( std::ostream& str, const Mesh< MeshConfig, Device >& mesh ) -{ - mesh.print( str ); - return str; -} - -} // namespace Meshes -} // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/Traverser_impl.h b/src/TNL/Meshes/MeshDetails/Traverser_impl.h deleted file mode 100644 index 2ce07addfff74fc55fd26981cc97f8704341aaf2..0000000000000000000000000000000000000000 --- a/src/TNL/Meshes/MeshDetails/Traverser_impl.h +++ /dev/null @@ -1,106 +0,0 @@ -/*************************************************************************** - Traverser_impl.h - description - ------------------- - begin : Dec 25, 2016 - copyright : (C) 2016 by Tomas Oberhuber et al. - email : tomas.oberhuber@fjfi.cvut.cz - ***************************************************************************/ - -/* See Copyright Notice in tnl/Copyright */ - -#pragma once - -#include -#include - -namespace TNL { -namespace Meshes { - -template< typename Mesh, - typename MeshEntity, - int EntitiesDimension > - template< typename EntitiesProcessor, - typename UserData > -void -Traverser< Mesh, MeshEntity, EntitiesDimension >:: -processBoundaryEntities( const MeshPointer& meshPointer, - UserData userData ) const -{ - const GlobalIndexType entitiesCount = meshPointer->template getBoundaryEntitiesCount< MeshEntity::getEntityDimension() >(); - auto kernel = [] __cuda_callable__ - ( const GlobalIndexType i, - const Mesh* mesh, - UserData userData ) - { - const GlobalIndexType entityIndex = mesh->template getBoundaryEntityIndex< MeshEntity::getEntityDimension() >( i ); - auto& entity = mesh->template getEntity< MeshEntity::getEntityDimension() >( entityIndex ); - // TODO: if the Mesh::IdType is void, then we should also pass the entityIndex - EntitiesProcessor::processEntity( *mesh, userData, entity ); - }; - Pointers::synchronizeSmartPointersOnDevice< DeviceType >(); - Algorithms::ParallelFor< DeviceType >::exec( - (GlobalIndexType) 0, entitiesCount, - kernel, - &meshPointer.template getData< DeviceType >(), - userData ); -} - -template< typename Mesh, - typename MeshEntity, - int EntitiesDimension > - template< typename EntitiesProcessor, - typename UserData > -void -Traverser< Mesh, MeshEntity, EntitiesDimension >:: -processInteriorEntities( const MeshPointer& meshPointer, - UserData userData ) const -{ - const auto entitiesCount = meshPointer->template getInteriorEntitiesCount< MeshEntity::getEntityDimension() >(); - auto kernel = [] __cuda_callable__ - ( const GlobalIndexType i, - const Mesh* mesh, - UserData userData ) - { - const GlobalIndexType entityIndex = mesh->template getInteriorEntityIndex< MeshEntity::getEntityDimension() >( i ); - auto& entity = mesh->template getEntity< MeshEntity::getEntityDimension() >( entityIndex ); - // TODO: if the Mesh::IdType is void, then we should also pass the entityIndex - EntitiesProcessor::processEntity( *mesh, userData, entity ); - }; - Pointers::synchronizeSmartPointersOnDevice< DeviceType >(); - Algorithms::ParallelFor< DeviceType >::exec( - (GlobalIndexType) 0, entitiesCount, - kernel, - &meshPointer.template getData< DeviceType >(), - userData ); -} - -template< typename Mesh, - typename MeshEntity, - int EntitiesDimension > - template< typename EntitiesProcessor, - typename UserData > -void -Traverser< Mesh, MeshEntity, EntitiesDimension >:: -processAllEntities( const MeshPointer& meshPointer, - UserData userData ) const -{ - const auto entitiesCount = meshPointer->template getEntitiesCount< MeshEntity::getEntityDimension() >(); - auto kernel = [] __cuda_callable__ - ( const GlobalIndexType entityIndex, - const Mesh* mesh, - UserData userData ) - { - auto& entity = mesh->template getEntity< MeshEntity::getEntityDimension() >( entityIndex ); - // TODO: if the Mesh::IdType is void, then we should also pass the entityIndex - EntitiesProcessor::processEntity( *mesh, userData, entity ); - }; - Pointers::synchronizeSmartPointersOnDevice< DeviceType >(); - Algorithms::ParallelFor< DeviceType >::exec( - (GlobalIndexType) 0, entitiesCount, - kernel, - &meshPointer.template getData< DeviceType >(), - userData ); -} - -} // namespace Meshes -} // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h b/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h index ada83b5fb0cf3a3af6d67e5559746d4743c5f71d..0ee9f2e3676cccb07419319d249327edcb59df46 100644 --- a/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h +++ b/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h @@ -18,7 +18,6 @@ #include #include -#include namespace TNL { namespace Meshes { @@ -46,7 +45,9 @@ template< typename MeshConfig, class EntityInitializerLayer; template< typename MeshConfig, - typename EntityTopology > + typename EntityTopology, + bool SubvertexStorage = + MeshConfig::subentityStorage( typename MeshTraits< MeshConfig >::template EntityTraits< EntityTopology::dimension >::EntityTopology(), 0 ) > class EntityInitializer : public EntityInitializerLayer< MeshConfig, DimensionTag< EntityTopology::dimension >, @@ -59,39 +60,39 @@ class EntityInitializer using MeshTraitsType = MeshTraits< MeshConfig >; using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; using LocalIndexType = typename MeshTraitsType::LocalIndexType; - using EntityTraitsType = typename MeshTraitsType::template EntityTraits< EntityTopology::dimension >; - using EntityType = typename EntityTraitsType::EntityType; using SeedType = EntitySeed< MeshConfig, EntityTopology >; using InitializerType = Initializer< MeshConfig >; public: - static void initEntity( EntityType& entity, const GlobalIndexType& entityIndex, const SeedType& entitySeed, InitializerType& initializer) + static void initSubvertexMatrix( const GlobalIndexType entitiesCount, InitializerType& initializer ) + { + initializer.template initSubentityMatrix< EntityTopology::dimension, 0 >( entitiesCount ); + } + + static void initEntity( const GlobalIndexType entityIndex, const SeedType& entitySeed, InitializerType& initializer ) { - initializer.setEntityIndex( entity, entityIndex ); // this is necessary if we want to use existing entities instead of intermediate seeds to create subentity seeds for( LocalIndexType i = 0; i < entitySeed.getCornerIds().getSize(); i++ ) - initializer.template setSubentityIndex< 0 >( entity, entityIndex, i, entitySeed.getCornerIds()[ i ] ); + initializer.template setSubentityIndex< EntityTopology::dimension, 0 >( entityIndex, i, entitySeed.getCornerIds()[ i ] ); } }; -template< typename MeshConfig > -class EntityInitializer< MeshConfig, Topologies::Vertex > +template< typename MeshConfig, + typename EntityTopology > +class EntityInitializer< MeshConfig, EntityTopology, false > : public EntityInitializerLayer< MeshConfig, - DimensionTag< 0 >, + DimensionTag< EntityTopology::dimension >, DimensionTag< MeshTraits< MeshConfig >::meshDimension > > { -public: - using VertexType = typename MeshTraits< MeshConfig >::VertexType; - using GlobalIndexType = typename MeshTraits< MeshConfig >::GlobalIndexType; - using PointType = typename MeshTraits< MeshConfig >::PointType; - using InitializerType = Initializer< MeshConfig >; + using MeshTraitsType = MeshTraits< MeshConfig >; + using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; - static void initEntity( VertexType& entity, const GlobalIndexType& entityIndex, const PointType& point, InitializerType& initializer) - { - initializer.setEntityIndex( entity, entityIndex ); - initializer.setVertexPoint( entity, point ); - } + using SeedType = EntitySeed< MeshConfig, EntityTopology >; + using InitializerType = Initializer< MeshConfig >; +public: + static void initSubvertexMatrix( const GlobalIndexType entitiesCount, InitializerType& initializer ) {} + static void initEntity( const GlobalIndexType entityIndex, const SeedType& entitySeed, InitializerType& initializer ) {} }; @@ -123,33 +124,57 @@ class EntityInitializerLayer< MeshConfig, 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 SuperentityInitializerType = SuperentityStorageInitializer< MeshConfig, SubdimensionTag, SuperdimensionTag >; + 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; - SuperentityInitializerType superentityInitializer; - for( GlobalIndexType superentityIndex = 0; - superentityIndex < mesh.template getEntitiesCount< SuperdimensionTag::value >(); - superentityIndex++ ) - { - auto& superentity = mesh.template getEntity< SuperdimensionTag::value >( superentityIndex ); - auto subentitySeeds = SubentitySeedsCreatorType::create( meshInitializer.getSubvertices( superentity, superentityIndex ) ); + 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 ) ); for( LocalIndexType i = 0; i < subentitySeeds.getSize(); i++ ) { const GlobalIndexType subentityIndex = meshInitializer.findEntitySeedIndex( subentitySeeds[ i ] ); - meshInitializer.template setSubentityIndex< SubdimensionTag::value >( superentity, superentityIndex, i, subentityIndex ); - superentityInitializer.addSuperentity( subentityIndex, superentityIndex ); + meshInitializer.template setSubentityIndex< SuperdimensionTag::value, SubdimensionTag::value >( superentityIndex, i, subentityIndex ); + superentitiesCounts[ subentityIndex ]++; } } - superentityInitializer.initSuperentities( meshInitializer ); + // 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 ); } @@ -183,37 +208,59 @@ class EntityInitializerLayer< MeshConfig, 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 SuperentityInitializerType = SuperentityStorageInitializer< MeshConfig, SubdimensionTag, SuperdimensionTag >; + 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; - SuperentityInitializerType superentityInitializer; - for( GlobalIndexType superentityIndex = 0; - superentityIndex < mesh.template getEntitiesCount< SuperdimensionTag::value >(); - superentityIndex++ ) - { - auto& superentity = mesh.template getEntity< SuperdimensionTag::value >( superentityIndex ); - auto subentitySeeds = SubentitySeedsCreatorType::create( meshInitializer.getSubvertices( superentity, superentityIndex ) ); + 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 ); - auto& subentityOrientationsArray = InitializerType::template subentityOrientationsArray< SubdimensionTag >( superentity ); + // 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< SubdimensionTag::value >( superentity, superentityIndex, i, subentityIndex ); - superentityInitializer.addSuperentity( subentityIndex, superentityIndex ); - + meshInitializer.template setSubentityIndex< SuperdimensionTag::value, SubdimensionTag::value >( superentityIndex, i, subentityIndex ); + superentitiesCounts[ subentityIndex ]++; subentityOrientationsArray[ i ] = meshInitializer.template getReferenceOrientation< SubdimensionTag >( subentityIndex ).createOrientation( subentitySeeds[ i ] ); } } - superentityInitializer.initSuperentities( meshInitializer ); + // 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 ); } @@ -255,19 +302,24 @@ 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& superentity = mesh.template getEntity< SuperdimensionTag::value >( superentityIndex ); - auto subentitySeeds = SubentitySeedsCreatorType::create( meshInitializer.getSubvertices( superentity, superentityIndex ) ); + auto subentitySeeds = SubentitySeedsCreatorType::create( meshInitializer.template getSubvertices< SuperdimensionTag::value >( superentityIndex ) ); - auto& subentityOrientationsArray = InitializerType::template subentityOrientationsArray< SubdimensionTag >( superentity ); + 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< SubdimensionTag::value >( superentity, superentityIndex, i, subentityIndex ); + meshInitializer.template setSubentityIndex< SuperdimensionTag::value, SubdimensionTag::value >( superentityIndex, i, subentityIndex ); subentityOrientationsArray[ i ] = meshInitializer.template getReferenceOrientation< SubdimensionTag >( subentityIndex ).createOrientation( subentitySeeds[ i ] ); } @@ -313,17 +365,22 @@ 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& superentity = mesh.template getEntity< SuperdimensionTag::value >( superentityIndex ); - auto subentitySeeds = SubentitySeedsCreatorType::create( meshInitializer.getSubvertices( superentity, superentityIndex ) ); + auto subentitySeeds = SubentitySeedsCreatorType::create( meshInitializer.template getSubvertices< SuperdimensionTag::value >( superentityIndex ) ); for( LocalIndexType i = 0; i < subentitySeeds.getSize(); i++ ) { const GlobalIndexType subentityIndex = meshInitializer.findEntitySeedIndex( subentitySeeds[ i ] ); - meshInitializer.template setSubentityIndex< SubdimensionTag::value >( superentity, superentityIndex, i, subentityIndex ); + meshInitializer.template setSubentityIndex< SuperdimensionTag::value, SubdimensionTag::value >( superentityIndex, i, subentityIndex ); } } @@ -359,32 +416,53 @@ class EntityInitializerLayer< MeshConfig, 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 SuperentityInitializerType = SuperentityStorageInitializer< MeshConfig, SubdimensionTag, SuperdimensionTag >; + 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; - SuperentityInitializerType superentityInitializer; - for( GlobalIndexType superentityIndex = 0; - superentityIndex < mesh.template getEntitiesCount< SuperdimensionTag::value >(); - superentityIndex++ ) - { - auto& superentity = mesh.template getEntity< SuperdimensionTag::value >( superentityIndex ); - auto subentitySeeds = SubentitySeedsCreatorType::create( meshInitializer.getSubvertices( superentity, superentityIndex ) ); + const GlobalIndexType subentitiesCount = mesh.template getEntitiesCount< SubdimensionTag::value >(); + const GlobalIndexType superentitiesCount = mesh.template getEntitiesCount< SuperdimensionTag::value >(); + + // 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 ) ); for( LocalIndexType i = 0; i < subentitySeeds.getSize(); i++ ) { const GlobalIndexType subentityIndex = meshInitializer.findEntitySeedIndex( subentitySeeds[ i ] ); - superentityInitializer.addSuperentity( subentityIndex, superentityIndex ); + superentitiesCounts[ subentityIndex ]++; } } - superentityInitializer.initSuperentities( meshInitializer ); + // 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++ ) + { + auto subentitySeeds = SubentitySeedsCreatorType::create( meshInitializer.template getSubvertices< SuperdimensionTag::value >( superentityIndex ) ); + for( LocalIndexType i = 0; i < subentitySeeds.getSize(); i++ ) + { + const GlobalIndexType subentityIndex = meshInitializer.findEntitySeedIndex( subentitySeeds[ i ] ); + auto row = matrix.getRow( subentityIndex ); + row.setElement( superentitiesCounts[ subentityIndex ]++, superentityIndex, true ); + } + } BaseType::initSuperentities( meshInitializer, mesh ); } diff --git a/src/TNL/Meshes/MeshDetails/initializer/Initializer.h b/src/TNL/Meshes/MeshDetails/initializer/Initializer.h index acaeeaff4ab66abf552dc97b86ac71f77e67e67c..754e82a2c5bfd6cec9243e7a19050962d186be8c 100644 --- a/src/TNL/Meshes/MeshDetails/initializer/Initializer.h +++ b/src/TNL/Meshes/MeshDetails/initializer/Initializer.h @@ -29,6 +29,8 @@ * not allocated at once, but by parts (by dimensions). The flow is roughly the * following: * + * - Allocate vertices and set their physical coordinates, deallocate input + * array of points. * - Allocate cells and set their subvertex indices (but not other subentity * indices), deallocate cell seeds (the cells will be used later). * - For all dimensions D from (cell dimension - 1) to 1: @@ -43,8 +45,6 @@ * - For entities with dimension D, initialize their superentity indices * with dimension S. * - Deallocate all intermediate data structures. - * - Allocate vertices and set their physical coordinates, deallocate input - * array of points. * - For all superdimensions S > 0, repeat the same steps as above. * * Optimization notes: @@ -62,12 +62,8 @@ namespace Meshes { template< typename MeshConfig, typename DimensionTag, - bool EntityStorage = - MeshTraits< MeshConfig >::template EntityTraits< DimensionTag::value >::storageEnabled, bool EntityReferenceOrientationStorage = - MeshTraits< MeshConfig >::template EntityTraits< DimensionTag::value >::orientationNeeded && - // orientationNeeded does not make sense without storageEnabled - MeshTraits< MeshConfig >::template EntityTraits< DimensionTag::value >::storageEnabled > + MeshTraits< MeshConfig >::template EntityTraits< DimensionTag::value >::orientationNeeded > class InitializerLayer; @@ -78,7 +74,7 @@ class Initializer { protected: // must be declared before its use in expression with decltype() - Mesh< MeshConfig >* mesh; + Mesh< MeshConfig >* mesh = nullptr; public: using MeshType = Mesh< MeshConfig >; @@ -88,88 +84,88 @@ class Initializer using PointArrayType = typename MeshTraitsType::PointArrayType; using CellSeedArrayType = typename MeshTraitsType::CellSeedArrayType; using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; + using LocalIndexType = typename MeshTraitsType::LocalIndexType; - Initializer() - : mesh( 0 ) - {} - // The points and cellSeeds arrays will be reset when not needed to save memory. void createMesh( PointArrayType& points, CellSeedArrayType& cellSeeds, MeshType& mesh ) { + // copy points + mesh.template setEntitiesCount< 0 >( points.getSize() ); + mesh.getPoints().swap( points ); + points.reset(); + this->mesh = &mesh; - BaseType::initEntities( *this, points, cellSeeds, mesh ); - // set pointers from entities into the subentity and superentity storage networks - EntityStorageRebinder< Mesh< MeshConfig > >::exec( mesh ); + BaseType::initEntities( *this, cellSeeds, mesh ); } - template< typename Entity, typename GlobalIndex > - void setEntityIndex( Entity& entity, const GlobalIndex& index ) + template< int Dimension, int Subdimension > + void initSubentityMatrix( const GlobalIndexType entitiesCount, GlobalIndexType subentitiesCount = 0 ) { - entity.setIndex( index ); + if( Subdimension == 0 ) + subentitiesCount = mesh->template getEntitiesCount< 0 >(); + auto& matrix = mesh->template getSubentitiesMatrix< Dimension, Subdimension >(); + matrix.setDimensions( entitiesCount, subentitiesCount ); + using EntityTraitsType = typename MeshTraitsType::template EntityTraits< Dimension >; + using EntityTopology = typename EntityTraitsType::EntityTopology; + using SubentityTraitsType = typename MeshTraitsType::template SubentityTraits< EntityTopology, Subdimension >; + constexpr int count = SubentityTraitsType::count; + typename std::decay_t::RowsCapacitiesType capacities( entitiesCount ); + capacities.setValue( count ); + matrix.setRowCapacities( capacities ); } template< int Dimension > - void setEntitiesCount( const GlobalIndexType& entitiesCount ) + void setEntitiesCount( const GlobalIndexType entitiesCount ) { - //std::cout << "Setting number of entities with " << Dimension << " dimension to " << entitiesCount << std::endl; mesh->template setEntitiesCount< Dimension >( entitiesCount ); } - template< int Subdimension, typename EntityType, typename LocalIndex, typename GlobalIndex > + template< int Dimension, int Subdimension > void - setSubentityIndex( const EntityType& entity, const GlobalIndex& entityIndex, const LocalIndex& localIndex, const GlobalIndex& globalIndex ) - { - // The mesh entities are not yet bound to the storage network at this point, - // so we operate directly on the storage. - mesh->template getSubentityStorageNetwork< EntityType::EntityTopology::dimension, Subdimension >().getValues( entityIndex )[ localIndex ] = globalIndex; - } - - template< int Subdimension, typename EntityType, typename LocalIndex, typename GlobalIndex > - GlobalIndex - getSubentityIndex( const EntityType& entity, const GlobalIndex& entityIndex, const LocalIndex& localIndex ) + setSubentityIndex( const GlobalIndexType entityIndex, const LocalIndexType localIndex, const GlobalIndexType globalIndex ) { - // The mesh entities are not yet bound to the storage network at this point, - // so we operate directly on the storage. - return mesh->template getSubentityStorageNetwork< EntityType::EntityTopology::dimension, Subdimension >().getValues( entityIndex )[ localIndex ]; + mesh->template getSubentitiesMatrix< Dimension, Subdimension >().getRow( entityIndex ).setElement( localIndex, globalIndex, true ); } - template< typename EntityType, typename GlobalIndex > + template< int Dimension > auto - getSubvertices( const EntityType& entity, const GlobalIndex& entityIndex ) - -> decltype( this->mesh->template getSubentityStorageNetwork< EntityType::EntityTopology::dimension, 0 >().getValues( 0 ) ) + getSubvertices( const GlobalIndexType entityIndex ) + -> decltype( this->mesh->template getSubentitiesMatrix< Dimension, 0 >().getRow( 0 ) ) { - // The mesh entities are not yet bound to the storage network at this point, - // so we operate directly on the storage. - return mesh->template getSubentityStorageNetwork< EntityType::EntityTopology::dimension, 0 >().getValues( entityIndex ); + return mesh->template getSubentitiesMatrix< Dimension, 0 >().getRow( entityIndex ); } - template< typename EntityTopology, int Superdimension > - typename MeshTraitsType::template SuperentityTraits< EntityTopology, Superdimension >::StorageNetworkType& - meshSuperentityStorageNetwork() + template< int Dimension, int Superdimension > + auto + getSuperentitiesCountsArray() + -> decltype( this->mesh->template getSuperentitiesCountsArray< Dimension, Superdimension >() ) { - return mesh->template getSuperentityStorageNetwork< EntityTopology::dimension, Superdimension >(); + return mesh->template getSuperentitiesCountsArray< Dimension, Superdimension >(); } - static void - setVertexPoint( typename MeshType::Vertex& vertex, const typename MeshType::PointType& point ) + template< int Dimension, int Superdimension > + auto + getSuperentitiesMatrix() + -> decltype( this->mesh->template getSuperentitiesMatrix< Dimension, Superdimension >() ) { - vertex.setPoint( point ); + return mesh->template getSuperentitiesMatrix< Dimension, Superdimension >(); } - template< typename SubDimensionTag, typename MeshEntity > - static typename MeshTraitsType::template SubentityTraits< typename MeshEntity::EntityTopology, SubDimensionTag::value >::OrientationArrayType& - subentityOrientationsArray( MeshEntity& entity ) + template< int Dimension, int Subdimension > + auto + subentityOrientationsArray( const GlobalIndexType entityIndex ) + -> decltype( this->mesh->template subentityOrientationsArray< Dimension, Subdimension >( entityIndex ) ) { - return entity.template subentityOrientationsArray< SubDimensionTag::value >(); + return mesh->template subentityOrientationsArray< Dimension, Subdimension >( entityIndex ); } template< typename DimensionTag > const MeshEntityReferenceOrientation< MeshConfig, typename MeshTraitsType::template EntityTraits< DimensionTag::value >::EntityTopology >& - getReferenceOrientation( GlobalIndexType index ) const + getReferenceOrientation( const GlobalIndexType index ) const { return BaseType::getReferenceOrientation( DimensionTag(), index ); } @@ -177,13 +173,11 @@ class Initializer /**** * Mesh initializer layer for cells - * - entities storage must turned on (cells must always be stored ) * - entities orientation does not make sense for cells => it is turned off */ template< typename MeshConfig > class InitializerLayer< MeshConfig, typename MeshTraits< MeshConfig >::DimensionTag, - true, false > : public InitializerLayer< MeshConfig, typename MeshTraits< MeshConfig >::DimensionTag::Decrement > @@ -196,24 +190,24 @@ class InitializerLayer< MeshConfig, using EntityTraitsType = typename MeshTraitsType::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 CellSeedArrayType = typename MeshTraitsType::CellSeedArrayType; - using LocalIndexType = typename MeshTraitsType::LocalIndexType; - using PointArrayType = typename MeshTraitsType::PointArrayType; public: - void initEntities( InitializerType& initializer, PointArrayType& points, CellSeedArrayType& cellSeeds, MeshType& mesh ) + void initEntities( InitializerType& initializer, CellSeedArrayType& cellSeeds, MeshType& mesh ) { //std::cout << " Initiating entities with dimension " << DimensionTag::value << " ... " << std::endl; initializer.template setEntitiesCount< DimensionTag::value >( cellSeeds.getSize() ); + initializer.template initSubentityMatrix< DimensionTag::value, 0 >( cellSeeds.getSize() ); for( GlobalIndexType i = 0; i < cellSeeds.getSize(); i++ ) - EntityInitializerType::initEntity( mesh.template getEntity< DimensionTag::value >( i ), i, cellSeeds[ i ], initializer ); + EntityInitializerType::initEntity( i, cellSeeds[ i ], initializer ); cellSeeds.reset(); - BaseType::initEntities( initializer, points, mesh ); + BaseType::initEntities( initializer, mesh ); } using BaseType::findEntitySeedIndex; @@ -221,14 +215,12 @@ class InitializerLayer< MeshConfig, /**** * Mesh initializer layer for other mesh entities than cells - * - entities storage is turned on * - entities orientation storage is turned off */ template< typename MeshConfig, typename DimensionTag > class InitializerLayer< MeshConfig, DimensionTag, - true, false > : public InitializerLayer< MeshConfig, typename DimensionTag::Decrement > @@ -240,11 +232,10 @@ class InitializerLayer< MeshConfig, using EntityTraitsType = typename MeshTraitsType::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 LocalIndexType = typename MeshTraitsType::LocalIndexType; - using PointArrayType = typename MeshTraitsType::PointArrayType; 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; @@ -258,7 +249,7 @@ class InitializerLayer< MeshConfig, for( GlobalIndexType i = 0; i < mesh.template getEntitiesCount< MeshType::getMeshDimension() >(); i++ ) { - auto subentitySeeds = SubentitySeedsCreator::create( initializer.getSubvertices( mesh.template getEntity< MeshType::getMeshDimension() >( i ), i ) ); + auto subentitySeeds = SubentitySeedsCreator::create( initializer.template getSubvertices< MeshType::getMeshDimension() >( i ) ); for( LocalIndexType j = 0; j < subentitySeeds.getSize(); j++ ) seedSet.insert( subentitySeeds[ j ] ); } @@ -272,16 +263,17 @@ class InitializerLayer< MeshConfig, return this->seedsIndexedSet.insert( seed ); } - void initEntities( InitializerType& initializer, PointArrayType& points, MeshType& mesh ) + 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 ); 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.getSubvertices( mesh.template getEntity< MeshType::getMeshDimension() >( i ), i ) ); + auto subentitySeeds = SubentitySeedsCreator::create( initializer.template getSubvertices< MeshType::getMeshDimension() >( i ) ); for( LocalIndexType j = 0; j < subentitySeeds.getSize(); j++ ) { auto& seed = subentitySeeds[ j ]; @@ -289,7 +281,7 @@ class InitializerLayer< MeshConfig, const GlobalIndexType& entityIndex = pair.first; if( pair.second ) { // insertion took place, initialize the entity - EntityInitializerType::initEntity( mesh.template getEntity< DimensionTag::value >( entityIndex ), entityIndex, seed, initializer ); + EntityInitializerType::initEntity( entityIndex, seed, initializer ); } } } @@ -297,12 +289,11 @@ class InitializerLayer< MeshConfig, EntityInitializerType::initSuperentities( initializer, mesh ); this->seedsIndexedSet.clear(); - BaseType::initEntities( initializer, points, mesh ); + BaseType::initEntities( initializer, mesh ); } using BaseType::getReferenceOrientation; - using ReferenceOrientationType = typename EntityTraitsType::ReferenceOrientationType; - const ReferenceOrientationType& getReferenceOrientation( DimensionTag, GlobalIndexType index ) const {} + void getReferenceOrientation( DimensionTag, GlobalIndexType index ) const {} private: SeedIndexedSet seedsIndexedSet; @@ -310,14 +301,12 @@ class InitializerLayer< MeshConfig, /**** * Mesh initializer layer for other mesh entities than cells - * - entities storage is turned on * - entities orientation storage is turned on */ template< typename MeshConfig, typename DimensionTag > class InitializerLayer< MeshConfig, DimensionTag, - true, true > : public InitializerLayer< MeshConfig, typename DimensionTag::Decrement > @@ -329,11 +318,10 @@ class InitializerLayer< MeshConfig, 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 LocalIndexType = typename MeshTraitsType::LocalIndexType; - using PointArrayType = typename MeshTraitsType::PointArrayType; 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; @@ -349,7 +337,7 @@ class InitializerLayer< MeshConfig, for( GlobalIndexType i = 0; i < mesh.template getEntitiesCount< MeshType::getMeshDimension() >(); i++ ) { - auto subentitySeeds = SubentitySeedsCreator::create( initializer.getSubvertices( mesh.template getEntity< MeshType::getMeshDimension() >( i ), i ) ); + auto subentitySeeds = SubentitySeedsCreator::create( initializer.template getSubvertices< MeshType::getMeshDimension() >( i ) ); for( LocalIndexType j = 0; j < subentitySeeds.getSize(); j++ ) seedSet.insert( subentitySeeds[ j ] ); } @@ -363,17 +351,18 @@ class InitializerLayer< MeshConfig, return this->seedsIndexedSet.insert( seed ); } - void initEntities( InitializerType& initializer, PointArrayType& points, MeshType& mesh ) + 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.getSubvertices( mesh.template getEntity< MeshType::getMeshDimension() >( i ), i ) ); + auto subentitySeeds = SubentitySeedsCreator::create( initializer.template getSubvertices< MeshType::getMeshDimension() >( i ) ); for( LocalIndexType j = 0; j < subentitySeeds.getSize(); j++ ) { auto& seed = subentitySeeds[ j ]; @@ -381,7 +370,7 @@ class InitializerLayer< MeshConfig, const GlobalIndexType& entityIndex = pair.first; if( pair.second ) { // insertion took place, initialize the entity - EntityInitializerType::initEntity( mesh.template getEntity< DimensionTag::value >( entityIndex ), entityIndex, seed, initializer ); + EntityInitializerType::initEntity( entityIndex, seed, initializer ); this->referenceOrientations[ entityIndex ] = ReferenceOrientationType( seed ); } } @@ -390,8 +379,9 @@ class InitializerLayer< MeshConfig, EntityInitializerType::initSuperentities( initializer, mesh ); this->seedsIndexedSet.clear(); this->referenceOrientations.clear(); + this->referenceOrientations.shrink_to_fit(); - BaseType::initEntities( initializer, points, mesh ); + BaseType::initEntities( initializer, mesh ); } using BaseType::getReferenceOrientation; @@ -405,28 +395,13 @@ class InitializerLayer< MeshConfig, ReferenceOrientationArrayType referenceOrientations; }; -/**** - * Mesh initializer layer for entities not being stored - */ -template< typename MeshConfig, - typename DimensionTag > -class InitializerLayer< MeshConfig, - DimensionTag, - false, - false > - : public InitializerLayer< MeshConfig, - typename DimensionTag::Decrement > -{}; - /**** * Mesh initializer layer for vertices - * - vertices must always be stored * - their orientation does not make sense */ template< typename MeshConfig > class InitializerLayer< MeshConfig, DimensionTag< 0 >, - true, false > { using MeshType = Mesh< MeshConfig >; @@ -435,11 +410,10 @@ class InitializerLayer< MeshConfig, using EntityTraitsType = typename MeshTraitsType::template EntityTraits< DimensionTag::value >; using EntityTopology = typename EntityTraitsType::EntityTopology; - - using InitializerType = Initializer< MeshConfig >; using GlobalIndexType = typename MeshTraits< MeshConfig >::GlobalIndexType; using LocalIndexType = typename MeshTraits< MeshConfig >::LocalIndexType; - using PointArrayType = typename MeshTraits< MeshConfig >::PointArrayType; + + using InitializerType = Initializer< MeshConfig >; using EntityInitializerType = EntityInitializer< MeshConfig, EntityTopology >; using SeedType = EntitySeed< MeshConfig, EntityTopology >; @@ -450,22 +424,14 @@ class InitializerLayer< MeshConfig, return seed.getCornerIds()[ 0 ]; } - void initEntities( InitializerType& initializer, PointArrayType& points, MeshType& mesh ) + void initEntities( InitializerType& initializer, MeshType& mesh ) { //std::cout << " Initiating entities with dimension " << DimensionTag::value << " ... " << std::endl; - initializer.template setEntitiesCount< 0 >( points.getSize() ); - for( GlobalIndexType i = 0; i < points.getSize(); i++ ) - EntityInitializerType::initEntity( mesh.template getEntity< 0 >( i ), i, points[ i ], initializer ); - points.reset(); - EntityInitializerType::initSuperentities( initializer, mesh ); } - // This method is due to 'using BaseType::findEntityIndex;' in the derived class. - void findEntitySeedIndex() {} - - using ReferenceOrientationType = typename EntityTraitsType::ReferenceOrientationType; - const ReferenceOrientationType& getReferenceOrientation( DimensionTag, GlobalIndexType index ) const {} + // 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/initializer/SubentitySeedsCreator.h b/src/TNL/Meshes/MeshDetails/initializer/SubentitySeedsCreator.h index 30cbb31e6a960b2cd00e8faafe5262b7ef17e472..1e2edb332325921132a5050d166190d394db24d5 100644 --- a/src/TNL/Meshes/MeshDetails/initializer/SubentitySeedsCreator.h +++ b/src/TNL/Meshes/MeshDetails/initializer/SubentitySeedsCreator.h @@ -28,26 +28,22 @@ template< typename MeshConfig, class SubentitySeedsCreator { using MeshTraitsType = MeshTraits< MeshConfig >; - using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; using LocalIndexType = typename MeshTraitsType::LocalIndexType; using EntityTraitsType = typename MeshTraitsType::template EntityTraits< EntityDimensionTag::value >; - using EntityType = typename EntityTraitsType::EntityType; using EntityTopology = typename EntityTraitsType::EntityTopology; - using SubvertexAccessorType = typename MeshTraitsType::template SubentityTraits< EntityTopology, 0 >::SubentityAccessorType; + using SubvertexAccessorType = typename MeshTraitsType::SubentityMatrixType::RowView; using SubentityTraits = typename MeshTraitsType::template SubentityTraits< EntityTopology, SubentityDimensionTag::value >; - using SubentityType = typename SubentityTraits::SubentityType; using SubentityTopology = typename SubentityTraits::SubentityTopology; - static constexpr LocalIndexType SUBENTITIES_COUNT = EntityType::template getSubentitiesCount< SubentityDimensionTag::value >(); - static constexpr LocalIndexType SUBENTITY_VERTICES_COUNT = SubentityType::template getSubentitiesCount< 0 >(); + static constexpr LocalIndexType SUBENTITY_VERTICES_COUNT = MeshTraitsType::template SubentityTraits< SubentityTopology, 0 >::count; public: - using SubentitySeedArray = typename SubentityTraits::SeedArrayType; + using SubentitySeedArray = Containers::StaticArray< SubentityTraits::count, EntitySeed< MeshConfig, SubentityTopology > >; static SubentitySeedArray create( const SubvertexAccessorType& subvertices ) { SubentitySeedArray subentitySeeds; - Algorithms::TemplateStaticFor< LocalIndexType, 0, SUBENTITIES_COUNT, CreateSubentitySeeds >::execHost( subentitySeeds, subvertices ); + Algorithms::TemplateStaticFor< LocalIndexType, 0, SubentitySeedArray::getSize(), CreateSubentitySeeds >::execHost( subentitySeeds, subvertices ); return subentitySeeds; } @@ -72,7 +68,7 @@ private: static void exec( SubentitySeed& subentitySeed, const SubvertexAccessorType& subvertices ) { static constexpr LocalIndexType VERTEX_INDEX = SubentityTraits::template Vertex< subentityIndex, subentityVertexIndex >::index; - subentitySeed.setCornerId( subentityVertexIndex, subvertices[ VERTEX_INDEX ] ); + subentitySeed.setCornerId( subentityVertexIndex, subvertices.getColumnIndex( VERTEX_INDEX ) ); } }; }; @@ -83,24 +79,21 @@ template< typename MeshConfig, class SubentitySeedsCreator< MeshConfig, EntityDimensionTag, DimensionTag< 0 > > { using MeshTraitsType = MeshTraits< MeshConfig >; - using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; using LocalIndexType = typename MeshTraitsType::LocalIndexType; using EntityTraitsType = typename MeshTraitsType::template EntityTraits< EntityDimensionTag::value >; - using EntityType = typename EntityTraitsType::EntityType; using EntityTopology = typename EntityTraitsType::EntityTopology; - using SubvertexAccessorType = typename MeshTraitsType::template SubentityTraits< EntityTopology, 0 >::SubentityAccessorType; + using SubvertexAccessorType = typename MeshTraitsType::SubentityMatrixType::RowView; using SubentityTraits = typename MeshTraitsType::template SubentityTraits< EntityTopology, 0 >; - - static constexpr LocalIndexType SUBENTITIES_COUNT = EntityType::template getSubentitiesCount< 0 >(); + using SubentityTopology = typename SubentityTraits::SubentityTopology; public: - using SubentitySeedArray = typename SubentityTraits::SeedArrayType; + using SubentitySeedArray = Containers::StaticArray< SubentityTraits::count, EntitySeed< MeshConfig, SubentityTopology > >; static SubentitySeedArray create( const SubvertexAccessorType& subvertices ) { SubentitySeedArray seeds; for( LocalIndexType i = 0; i < seeds.getSize(); i++ ) - seeds[ i ].setCornerId( 0, subvertices[ i ] ); + seeds[ i ].setCornerId( 0, subvertices.getColumnIndex( i ) ); return seeds; } }; diff --git a/src/TNL/Meshes/MeshDetails/initializer/SuperentityStorageInitializer.h b/src/TNL/Meshes/MeshDetails/initializer/SuperentityStorageInitializer.h deleted file mode 100644 index c0327785d64dacc22bfffb380fe3235c5f734051..0000000000000000000000000000000000000000 --- a/src/TNL/Meshes/MeshDetails/initializer/SuperentityStorageInitializer.h +++ /dev/null @@ -1,103 +0,0 @@ -/*************************************************************************** - SuperentityStorageInitializer.h - description - ------------------- - begin : Feb 27, 2014 - copyright : (C) 2014 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 - -#include -#include - -namespace TNL { -namespace Meshes { - -template< typename MeshConfig > -class Initializer; - -template< typename MeshConfig, - typename SubdimensionTag, - typename SuperdimensionTag > -class SuperentityStorageInitializer -{ - using MeshTraitsType = MeshTraits< MeshConfig >; - using InitializerType = Initializer< MeshConfig >; - using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; - using LocalIndexType = typename MeshTraitsType::LocalIndexType; - using EntityTraitsType = typename MeshTraitsType::template EntityTraits< SubdimensionTag::value >; - using EntityTopology = typename EntityTraitsType::EntityTopology; - using EntityType = typename EntityTraitsType::EntityType; - using SuperentityTraitsType = typename MeshTraitsType::template SuperentityTraits< EntityTopology, SuperdimensionTag::value >; - using SuperentityStorageNetwork = typename SuperentityTraitsType::StorageNetworkType; - -public: - void addSuperentity( GlobalIndexType entityIndex, GlobalIndexType superentityIndex) - { - //std::cout << "Adding superentity with " << SuperdimensionTag::value << " dimension of entity with " << SubdimensionTag::value << " dimension: entityIndex = " << entityIndex << ", superentityIndex = " << superentityIndex << std::endl; - auto& indexSet = this->dynamicStorageNetwork[ entityIndex ]; - TNL_ASSERT( indexSet.count( superentityIndex ) == 0, - std::cerr << "Superentity " << superentityIndex << " with dimension " << SuperdimensionTag::value - << " of entity " << entityIndex << " with dimension " << SubdimensionTag::value - << " has been already added. This is probably a bug in the mesh initializer." << std::endl; ); - indexSet.insert( superentityIndex ); - } - - void initSuperentities( InitializerType& meshInitializer ) - { - TNL_ASSERT_GT( dynamicStorageNetwork.size(), (size_t) 0, - "No superentity indices were collected. This is a bug in the mesh initializer." ); - TNL_ASSERT_EQ( (size_t) getMaxSuperentityIndex(), dynamicStorageNetwork.size() - 1, - "Superentities for some entities are missing. " - "This is probably a bug in the mesh initializer." ); - - SuperentityStorageNetwork& superentityStorageNetwork = meshInitializer.template meshSuperentityStorageNetwork< EntityTopology, SuperdimensionTag::value >(); - TNL_ASSERT_EQ( (size_t) superentityStorageNetwork.getKeysRange(), dynamicStorageNetwork.size(), - "Sizes of the static and dynamic storage networks don't match. " - "This is probably a bug in the mesh initializer." ); - - typename SuperentityStorageNetwork::ValuesAllocationVectorType storageNetworkAllocationVector; - storageNetworkAllocationVector.setSize( superentityStorageNetwork.getKeysRange() ); - for( auto it = dynamicStorageNetwork.cbegin(); it != dynamicStorageNetwork.cend(); it++ ) - storageNetworkAllocationVector[ it->first ] = it->second.size(); - superentityStorageNetwork.allocate( storageNetworkAllocationVector ); - - for( auto it = dynamicStorageNetwork.cbegin(); it != dynamicStorageNetwork.cend(); it++ ) { - auto superentitiesIndices = superentityStorageNetwork.getValues( it->first ); - LocalIndexType i = 0; - for( auto v_it = it->second.cbegin(); v_it != it->second.cend(); v_it++ ) - superentitiesIndices[ i++ ] = *v_it; - } - - dynamicStorageNetwork.clear(); - } - -private: - using DynamicIndexSet = std::set< GlobalIndexType >; - std::unordered_map< GlobalIndexType, DynamicIndexSet > dynamicStorageNetwork; - - GlobalIndexType getMaxSuperentityIndex() - { - GlobalIndexType max = 0; - for( auto it = dynamicStorageNetwork.cbegin(); it != dynamicStorageNetwork.cend(); it++ ) { - if( it->first > max ) - max = it->first; - } - return max; - } -}; - -} // namespace Meshes -} // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/layers/DualGraphLayer.h b/src/TNL/Meshes/MeshDetails/layers/DualGraphLayer.h new file mode 100644 index 0000000000000000000000000000000000000000..ae775319f4a9dbc9e5dfd81ef5446dcd54a37933 --- /dev/null +++ b/src/TNL/Meshes/MeshDetails/layers/DualGraphLayer.h @@ -0,0 +1,177 @@ +/*************************************************************************** + DualGraphLayer.h - description + ------------------- + begin : Apr 4, 2020 + copyright : (C) 2020 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +// Implemented by: Jakub Klinkovský + +#pragma once + +#include + +namespace TNL { +namespace Meshes { + +template< typename MeshConfig, + typename Device, + bool enabled = MeshConfig::dualGraphStorage() > +class DualGraphLayer +{ +public: + using MeshTraitsType = MeshTraits< MeshConfig, Device >; + using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; + using LocalIndexType = typename MeshTraitsType::LocalIndexType; + using DualGraph = typename MeshTraitsType::DualGraph; + using NeighborCountsArray = typename MeshTraitsType::NeighborCountsArray; + + DualGraphLayer() = default; + + explicit DualGraphLayer( const DualGraphLayer& ) = default; + + template< typename Device_ > + DualGraphLayer( const DualGraphLayer< MeshConfig, Device_ >& other ) + { + operator=( other ); + } + + DualGraphLayer& operator=( const DualGraphLayer& ) = default; + + template< typename Device_ > + DualGraphLayer& operator=( const DualGraphLayer< MeshConfig, Device_ >& other ) + { + neighborCounts = other.getNeighborCounts(); + graph = other.getDualGraph(); + return *this; + } + + bool operator==( const DualGraphLayer& other ) const + { + return neighborCounts == other.getNeighborCounts() && + graph == other.getDualGraph(); + } + + __cuda_callable__ + const NeighborCountsArray& getNeighborCounts() const + { + return neighborCounts; + } + + __cuda_callable__ + NeighborCountsArray& getNeighborCounts() + { + return neighborCounts; + } + + __cuda_callable__ + const DualGraph& getDualGraph() const + { + return graph; + } + + __cuda_callable__ + DualGraph& getDualGraph() + { + return graph; + } + + // algorithm inspired by the CreateGraphDual function in METIS + template< typename Mesh > + void initializeDualGraph( const Mesh& mesh, + // when this parameter is <= 0, it will be replaced with MeshConfig::dualGraphMinCommonVertices + LocalIndexType minCommon = 0 ) + { + static_assert( std::is_same< MeshConfig, typename Mesh::Config >::value, + "mismatched MeshConfig type" ); + static_assert( MeshConfig::superentityStorage( typename Mesh::Vertex::EntityTopology{}, Mesh::getMeshDimension() ), + "The dual graph cannot be initialized when links from vertices to cells are not stored in the mesh." ); + static_assert( MeshConfig::dualGraphMinCommonVertices >= 1, + "MeshConfig error: dualGraphMinCommonVertices must be at least 1." ); + if( minCommon <= 0 ) + minCommon = MeshConfig::dualGraphMinCommonVertices; + + const GlobalIndexType cellsCount = mesh.template getEntitiesCount< Mesh::getMeshDimension() >(); + + // allocate row lengths vector + neighborCounts.setSize( cellsCount ); + + // allocate working arrays + using GlobalIndexArray = Containers::Array< GlobalIndexType, Devices::Sequential, GlobalIndexType >; + using LocalIndexArray = Containers::Array< LocalIndexType, Devices::Sequential, GlobalIndexType >; + GlobalIndexArray neighbors( cellsCount ); + LocalIndexArray marker( cellsCount ); + marker.setValue( 0 ); + + auto findNeighbors = [&] ( const GlobalIndexType k ) + { + const LocalIndexType subvertices = mesh.template getSubentitiesCount< Mesh::getMeshDimension(), 0 >( k ); + + // find all elements that share at least one vertex with k + LocalIndexType counter = 0; + for( LocalIndexType v = 0; v < subvertices; v++ ) { + const GlobalIndexType gv = mesh.template getSubentityIndex< Mesh::getMeshDimension(), 0 >( k, v ); + const LocalIndexType supercells = mesh.template getSuperentitiesCount< 0, Mesh::getMeshDimension() >( gv ); + for( LocalIndexType sc = 0; sc < supercells; sc++ ) { + const GlobalIndexType nk = mesh.template getSuperentityIndex< 0, Mesh::getMeshDimension() >( gv, sc ); + if( marker[ nk ] == 0 ) + neighbors[ counter++ ] = nk; + marker[ nk ]++; + } + } + + // mark k to be removed from the neighbor list in the next step + marker[ k ] = 0; + + // compact the list to contain only those with at least minCommon vertices + LocalIndexType compacted = 0; + for( LocalIndexType i = 0; i < counter; i++ ) { + const GlobalIndexType nk = neighbors[ i ]; + const LocalIndexType neighborSubvertices = mesh.template getSubentitiesCount< Mesh::getMeshDimension(), 0 >( nk ); + const LocalIndexType overlap = marker[ nk ]; + if( overlap >= minCommon || overlap >= subvertices - 1 || overlap >= neighborSubvertices - 1 ) + neighbors[ compacted++ ] = nk; + marker[ nk ] = 0; + } + + return compacted; + }; + + // count neighbors of each cell + for( GlobalIndexType k = 0; k < cellsCount; k++ ) + neighborCounts[ k ] = findNeighbors( k ); + + // allocate adjacency matrix + graph.setDimensions( cellsCount, cellsCount ); + graph.setRowCapacities( neighborCounts ); + + // fill in neighbor indices + for( GlobalIndexType k = 0; k < cellsCount; k++) { + auto row = graph.getRow( k ); + const LocalIndexType nnbrs = findNeighbors( k ); + for( LocalIndexType j = 0; j < nnbrs; j++) + row.setElement( j, neighbors[ j ], true ); + } + } + +protected: + NeighborCountsArray neighborCounts; + DualGraph graph; +}; + +template< typename MeshConfig, + typename Device > +class DualGraphLayer< MeshConfig, Device, false > +{ +public: + template< typename Mesh > + void initializeDualGraph( const Mesh& mesh, + int minCommon = 0 ) + {} +}; + +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/ConfigValidator.h b/src/TNL/Meshes/MeshDetails/layers/EntityTags/ConfigValidator.h similarity index 80% rename from src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/ConfigValidator.h rename to src/TNL/Meshes/MeshDetails/layers/EntityTags/ConfigValidator.h index aba84ba806b3954aa3812db9f3977db8ce0d251e..3aa4963c344158ee2e1dc844c30b8b84a859685c 100644 --- a/src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/ConfigValidator.h +++ b/src/TNL/Meshes/MeshDetails/layers/EntityTags/ConfigValidator.h @@ -15,17 +15,15 @@ namespace TNL { namespace Meshes { -namespace BoundaryTags { +namespace EntityTags { template< typename MeshConfig, typename EntityTopology, - bool BoundaryTagsStorage = MeshConfig::boundaryTagsStorage( EntityTopology() ) > -class ConfigValidatorBoundaryTagsLayer + bool entityTagsStorage = MeshConfig::entityTagsStorage( EntityTopology() ) > +class ConfigValidatorEntityTagsLayer { using FaceTopology = typename Topologies::Subtopology< typename MeshConfig::CellTopology, MeshConfig::meshDimension - 1 >::Topology; - static_assert( MeshConfig::entityStorage( MeshConfig::meshDimension - 1 ), - "Faces must be stored when any entity has boundary tags." ); static_assert( MeshConfig::superentityStorage( FaceTopology(), MeshConfig::meshDimension ), "Faces must store the cell superentity indices when any entity has boundary tags." ); static_assert( EntityTopology::dimension >= MeshConfig::meshDimension - 1 || MeshConfig::subentityStorage( FaceTopology(), EntityTopology::dimension ), @@ -34,7 +32,7 @@ class ConfigValidatorBoundaryTagsLayer template< typename MeshConfig, typename EntityTopology > -class ConfigValidatorBoundaryTagsLayer< MeshConfig, EntityTopology, false > +class ConfigValidatorEntityTagsLayer< MeshConfig, EntityTopology, false > { }; @@ -42,7 +40,7 @@ class ConfigValidatorBoundaryTagsLayer< MeshConfig, EntityTopology, false > template< typename MeshConfig, int dimension = MeshConfig::meshDimension > class ConfigValidatorLayer : public ConfigValidatorLayer< MeshConfig, dimension - 1 >, - public ConfigValidatorBoundaryTagsLayer< MeshConfig, + public ConfigValidatorEntityTagsLayer< MeshConfig, typename MeshTraits< MeshConfig >::template EntityTraits< dimension >::EntityTopology > { }; @@ -58,6 +56,6 @@ class ConfigValidator { }; -} // namespace BoundaryTags +} // namespace EntityTags } // namespace Meshes } // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/Initializer.h b/src/TNL/Meshes/MeshDetails/layers/EntityTags/Initializer.h similarity index 71% rename from src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/Initializer.h rename to src/TNL/Meshes/MeshDetails/layers/EntityTags/Initializer.h index f29fec33e8473afce3b90ccc12bcfd3c37cac7d9..95e29182ea3fd3ebfb485da142784225886e4f10 100644 --- a/src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/Initializer.h +++ b/src/TNL/Meshes/MeshDetails/layers/EntityTags/Initializer.h @@ -16,9 +16,11 @@ #include #include +#include "Traits.h" + namespace TNL { namespace Meshes { -namespace BoundaryTags { +namespace EntityTags { template< typename MeshConfig, typename Device, typename Mesh > class Initializer @@ -31,18 +33,18 @@ protected: // _T is necessary to force *partial* specialization, since explicit specializations // at class scope are forbidden template< typename CurrentDimension = DimensionTag< MeshConfig::meshDimension >, typename _T = void > - struct BoundaryTagsNeedInitialization + struct EntityTagsNeedInitialization { using EntityTopology = typename MeshEntityTraits< MeshConfig, DeviceType, CurrentDimension::value >::EntityTopology; - static constexpr bool value = MeshConfig::boundaryTagsStorage( EntityTopology() ) || - BoundaryTagsNeedInitialization< typename CurrentDimension::Decrement >::value; + static constexpr bool value = MeshConfig::entityTagsStorage( EntityTopology() ) || + EntityTagsNeedInitialization< typename CurrentDimension::Decrement >::value; }; template< typename _T > - struct BoundaryTagsNeedInitialization< DimensionTag< 0 >, _T > + struct EntityTagsNeedInitialization< DimensionTag< 0 >, _T > { using EntityTopology = typename MeshEntityTraits< MeshConfig, DeviceType, 0 >::EntityTopology; - static constexpr bool value = MeshConfig::boundaryTagsStorage( EntityTopology() ); + static constexpr bool value = MeshConfig::entityTagsStorage( EntityTopology() ); }; template< int Dimension > @@ -50,16 +52,37 @@ protected: { static void exec( Mesh& mesh ) { - mesh.template boundaryTagsSetEntitiesCount< Dimension >( mesh.template getEntitiesCount< Dimension >() ); + mesh.template entityTagsSetEntitiesCount< Dimension >( mesh.template getEntitiesCount< Dimension >() ); } }; template< int Dimension > - struct ResetBoundaryTags + class ResetEntityTags { + using WeakTrait = WeakStorageTrait< MeshConfig, Device, DimensionTag< Dimension > >; + static constexpr bool enabled = WeakTrait::entityTagsEnabled; + + // _T is necessary to force *partial* specialization, since explicit specializations + // at class scope are forbidden + template< bool enabled = true, typename _T = void > + struct Worker + { + static void exec( Mesh& mesh ) + { + mesh.template getEntityTagsView< Dimension >().setValue( 0 ); + } + }; + + template< typename _T > + struct Worker< false, _T > + { + static void exec( Mesh& mesh ) {} + }; + + public: static void exec( Mesh& mesh ) { - mesh.template resetBoundaryTags< Dimension >(); + Worker< enabled >::exec( mesh ); } }; @@ -67,7 +90,7 @@ protected: class InitializeSubentities { using SubentityTopology = typename MeshEntityTraits< MeshConfig, DeviceType, Subdimension >::EntityTopology; - static constexpr bool enabled = MeshConfig::boundaryTagsStorage( SubentityTopology() ); + static constexpr bool enabled = MeshConfig::entityTagsStorage( SubentityTopology() ); // _T is necessary to force *partial* specialization, since explicit specializations // at class scope are forbidden @@ -80,7 +103,7 @@ protected: const LocalIndexType subentitiesCount = face.template getSubentitiesCount< Subdimension >(); for( LocalIndexType i = 0; i < subentitiesCount; i++ ) { const GlobalIndexType subentityIndex = face.template getSubentityIndex< Subdimension >( i ); - mesh.template setIsBoundaryEntity< Subdimension >( subentityIndex, true ); + mesh.template addEntityTag< Subdimension >( subentityIndex, EntityTags::BoundaryEntity ); } } }; @@ -101,11 +124,11 @@ protected: }; template< int Dimension > - struct UpdateBoundaryIndices + struct UpdateEntityTagsLayer { static void exec( Mesh& mesh ) { - mesh.template updateBoundaryIndices< Dimension >(); + mesh.template updateEntityTagsLayer< Dimension >(); } }; @@ -115,14 +138,14 @@ public: #endif // _T is necessary to force *partial* specialization, since explicit specializations // at class scope are forbidden - template< bool AnyBoundaryTags = BoundaryTagsNeedInitialization<>::value, typename _T = void > + template< bool AnyEntityTags = EntityTagsNeedInitialization<>::value, typename _T = void > class Worker { public: static void exec( Mesh& mesh ) { Algorithms::TemplateStaticFor< int, 0, Mesh::getMeshDimension() + 1, SetEntitiesCount >::execHost( mesh ); - Algorithms::TemplateStaticFor< int, 0, Mesh::getMeshDimension() + 1, ResetBoundaryTags >::execHost( mesh ); + Algorithms::TemplateStaticFor< int, 0, Mesh::getMeshDimension() + 1, ResetEntityTags >::execHost( mesh ); auto kernel = [] __cuda_callable__ ( GlobalIndexType faceIndex, @@ -131,10 +154,10 @@ public: const auto& face = mesh->template getEntity< Mesh::getMeshDimension() - 1 >( faceIndex ); if( face.template getSuperentitiesCount< Mesh::getMeshDimension() >() == 1 ) { // initialize the face - mesh->template setIsBoundaryEntity< Mesh::getMeshDimension() - 1 >( faceIndex, true ); + mesh->template addEntityTag< Mesh::getMeshDimension() - 1 >( faceIndex, EntityTags::BoundaryEntity ); // initialize the cell superentity const GlobalIndexType cellIndex = face.template getSuperentityIndex< Mesh::getMeshDimension() >( 0 ); - mesh->template setIsBoundaryEntity< Mesh::getMeshDimension() >( cellIndex, true ); + mesh->template addEntityTag< Mesh::getMeshDimension() >( cellIndex, EntityTags::BoundaryEntity ); // initialize all subentities Algorithms::TemplateStaticFor< int, 0, Mesh::getMeshDimension() - 1, InitializeSubentities >::exec( *mesh, faceIndex, face ); } @@ -146,7 +169,7 @@ public: kernel, &meshPointer.template modifyData< DeviceType >() ); - Algorithms::TemplateStaticFor< int, 0, Mesh::getMeshDimension() + 1, UpdateBoundaryIndices >::execHost( mesh ); + Algorithms::TemplateStaticFor< int, 0, Mesh::getMeshDimension() + 1, UpdateEntityTagsLayer >::execHost( mesh ); } }; @@ -163,6 +186,6 @@ public: } }; -} // namespace BoundaryTags +} // namespace EntityTags } // namespace Meshes } // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/layers/EntityTags/Layer.h b/src/TNL/Meshes/MeshDetails/layers/EntityTags/Layer.h new file mode 100644 index 0000000000000000000000000000000000000000..dab80fc7e3916d89bda1ab57581b6a9263d3891d --- /dev/null +++ b/src/TNL/Meshes/MeshDetails/layers/EntityTags/Layer.h @@ -0,0 +1,305 @@ +/*************************************************************************** + Layer.h - description + ------------------- + begin : Dec 25, 2016 + copyright : (C) 2016 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +#pragma once + +#include +#include +#include + +#include "Traits.h" + +namespace TNL { +namespace Meshes { +namespace EntityTags { + +// This is the implementation of the boundary tags layer for one specific dimension. +// It is inherited by the EntityTags::LayerFamily. +template< typename MeshConfig, + typename Device, + typename DimensionTag, + bool TagStorage = WeakStorageTrait< MeshConfig, Device, DimensionTag >::entityTagsEnabled > +class Layer +{ + using MeshTraitsType = MeshTraits< MeshConfig, Device >; + +public: + using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; + using EntityTagsArrayType = typename MeshTraitsType::EntityTagsArrayType; + using TagType = typename MeshTraitsType::EntityTagType; + using OrderingArray = Containers::Array< GlobalIndexType, Device, GlobalIndexType >; + + Layer() = default; + + explicit Layer( const Layer& other ) + { + operator=( other ); + } + + template< typename Device_ > + Layer( const Layer< MeshConfig, Device_, DimensionTag >& other ) + { + operator=( other ); + } + + Layer& operator=( const Layer& other ) + { + tags.setLike( other.tags ); + boundaryIndices.setLike( other.boundaryIndices ); + interiorIndices.setLike( other.interiorIndices ); + tags = other.tags; + boundaryIndices = other.boundaryIndices; + interiorIndices = other.interiorIndices; + ghostsOffset = other.ghostsOffset; + return *this; + } + + template< typename Device_ > + Layer& operator=( const Layer< MeshConfig, Device_, DimensionTag >& other ) + { + tags.setLike( other.tags ); + boundaryIndices.setLike( other.boundaryIndices ); + interiorIndices.setLike( other.interiorIndices ); + tags = other.tags; + boundaryIndices = other.boundaryIndices; + interiorIndices = other.interiorIndices; + ghostsOffset = other.ghostsOffset; + return *this; + } + + + void setEntitiesCount( DimensionTag, const GlobalIndexType& entitiesCount ) + { + tags.setSize( entitiesCount ); + ghostsOffset = entitiesCount; + } + + __cuda_callable__ + typename EntityTagsArrayType::ViewType + getEntityTagsView( DimensionTag ) + { + return tags.getView(); + } + + __cuda_callable__ + typename EntityTagsArrayType::ConstViewType + getEntityTagsView( DimensionTag ) const + { + return tags.getConstView(); + } + + __cuda_callable__ + TagType getEntityTag( DimensionTag, const GlobalIndexType& entityIndex ) const + { + return tags[ entityIndex ]; + } + + __cuda_callable__ + void addEntityTag( DimensionTag, const GlobalIndexType& entityIndex, TagType tag ) + { + tags[ entityIndex ] |= tag; + } + + __cuda_callable__ + void removeEntityTag( DimensionTag, const GlobalIndexType& entityIndex, TagType tag ) + { + tags[ entityIndex ] ^= tag; + } + + __cuda_callable__ + bool isBoundaryEntity( DimensionTag, const GlobalIndexType& entityIndex ) const + { + return tags[ entityIndex ] & EntityTags::BoundaryEntity; + } + + __cuda_callable__ + bool isGhostEntity( DimensionTag, const GlobalIndexType& entityIndex ) const + { + return tags[ entityIndex ] & EntityTags::GhostEntity; + } + + void updateEntityTagsLayer( DimensionTag ) + { + // count boundary entities - custom reduction because expression templates don't support filtering bits this way: + // const GlobalIndexType boundaryEntities = sum(cast< GlobalIndexType >( _tagsVector & EntityTags::BoundaryEntity )); + // NOTE: boundary/interior entities may overlap with ghost entities, so we count all categories separately + const auto tags_view = tags.getConstView(); + auto is_boundary = [=] __cuda_callable__ ( GlobalIndexType entityIndex ) -> GlobalIndexType + { + return bool(tags_view[ entityIndex ] & EntityTags::BoundaryEntity); + }; + auto is_ghost = [=] __cuda_callable__ ( GlobalIndexType entityIndex ) -> GlobalIndexType + { + return bool(tags_view[ entityIndex ] & EntityTags::GhostEntity); + }; + const GlobalIndexType boundaryEntities = Algorithms::Reduction< Device >::reduce( (GlobalIndexType) 0, tags.getSize(), std::plus<>{}, is_boundary, (GlobalIndexType) 0 ); + const GlobalIndexType ghostEntities = Algorithms::Reduction< Device >::reduce( (GlobalIndexType) 0, tags.getSize(), std::plus<>{}, is_ghost, (GlobalIndexType) 0 ); + + interiorIndices.setSize( tags.getSize() - boundaryEntities ); + boundaryIndices.setSize( boundaryEntities ); + ghostsOffset = tags.getSize() - ghostEntities; + + if( ! std::is_same< Device, Devices::Cuda >::value ) { + GlobalIndexType i = 0, b = 0; + for( GlobalIndexType e = 0; e < tags.getSize(); e++ ) { + if( tags[ e ] & EntityTags::BoundaryEntity ) + boundaryIndices[ b++ ] = e; + else + interiorIndices[ i++ ] = e; + if( tags[ e ] & EntityTags::GhostEntity && ghostEntities > 0 && e < ghostsOffset ) + throw std::runtime_error( "The mesh is inconsistent - ghost entities of dimension " + std::to_string(DimensionTag::value) + " are not ordered after local entities." ); + } + } + // TODO: parallelize directly on the device + else { + using EntityTagsHostArray = typename EntityTagsArrayType::template Self< typename EntityTagsArrayType::ValueType, Devices::Host >; + using OrderingHostArray = typename OrderingArray::template Self< typename OrderingArray::ValueType, Devices::Host >; + + EntityTagsHostArray hostTags; + OrderingHostArray hostBoundaryIndices, hostInteriorIndices; + + hostTags.setLike( tags ); + hostInteriorIndices.setLike( interiorIndices ); + hostBoundaryIndices.setLike( boundaryIndices ); + + hostTags = tags; + + GlobalIndexType i = 0, b = 0; + for( GlobalIndexType e = 0; e < tags.getSize(); e++ ) { + if( hostTags[ e ] & EntityTags::BoundaryEntity ) + hostBoundaryIndices[ b++ ] = e; + else + hostInteriorIndices[ i++ ] = e; + if( hostTags[ e ] & EntityTags::GhostEntity && ghostEntities > 0 && e < ghostsOffset ) + throw std::runtime_error( "The mesh is inconsistent - ghost entities of dimension " + std::to_string(DimensionTag::value) + " are not ordered after local entities." ); + } + + interiorIndices = hostInteriorIndices; + boundaryIndices = hostBoundaryIndices; + } + } + + __cuda_callable__ + auto getBoundaryIndices( DimensionTag ) const + { + return boundaryIndices.getConstView(); + } + + __cuda_callable__ + auto getInteriorIndices( DimensionTag ) const + { + return interiorIndices.getConstView(); + } + + __cuda_callable__ + GlobalIndexType getGhostEntitiesCount( DimensionTag ) const + { + return tags.getSize() - ghostsOffset; + } + + __cuda_callable__ + GlobalIndexType getGhostEntitiesOffset( DimensionTag ) const + { + return ghostsOffset; + } + + void save( File& file ) const + { + file << tags; + } + + void load( File& file ) + { + file >> tags; + updateEntityTagsLayer( DimensionTag() ); + } + + void print( std::ostream& str ) const + { + str << "Boundary tags for entities of dimension " << DimensionTag::value << " are: "; + str << tags << std::endl; + str << "Indices of the interior entities of dimension " << DimensionTag::value << " are: "; + str << interiorIndices << std::endl; + str << "Indices of the boundary entities of dimension " << DimensionTag::value << " are: "; + str << boundaryIndices << std::endl; + str << "Index of the first ghost entity of dimension " << DimensionTag::value << " is: "; + str << ghostsOffset << std::endl; + } + + bool operator==( const Layer& layer ) const + { + TNL_ASSERT( ( tags == layer.tags && interiorIndices == layer.interiorIndices && boundaryIndices == layer.boundaryIndices && ghostsOffset == layer.ghostsOffset ) || + ( tags != layer.tags && interiorIndices != layer.interiorIndices && boundaryIndices != layer.boundaryIndices && ghostsOffset != layer.ghostsOffset ), + std::cerr << "The EntityTags layer is in inconsistent state - this is probably a bug in the boundary tags initializer." << std::endl + << "tags = " << tags << std::endl + << "layer.tags = " << layer.tags << std::endl + << "interiorIndices = " << interiorIndices << std::endl + << "layer.interiorIndices = " << layer.interiorIndices << std::endl + << "boundaryIndices = " << boundaryIndices << std::endl + << "layer.boundaryIndices = " << layer.boundaryIndices << std::endl + << "ghostsOffset = " << ghostsOffset << std::endl + << "layer.ghostsOffset = " << layer.ghostsOffset << std::endl; ); + return tags == layer.tags; + } + +private: + EntityTagsArrayType tags; + OrderingArray interiorIndices, boundaryIndices; + GlobalIndexType ghostsOffset = 0; + + // friend class is needed for templated assignment operators + template< typename MeshConfig_, typename Device_, typename DimensionTag_, bool TagStorage_ > + friend class Layer; +}; + +template< typename MeshConfig, + typename Device, + typename DimensionTag > +class Layer< MeshConfig, Device, DimensionTag, false > +{ +protected: + using GlobalIndexType = typename MeshConfig::GlobalIndexType; + using TagType = typename MeshTraits< MeshConfig, Device >::EntityTagsArrayType::ValueType; + + Layer() = default; + explicit Layer( const Layer& other ) {} + template< typename Device_ > + Layer( const Layer< MeshConfig, Device_, DimensionTag >& other ) {} + Layer& operator=( const Layer& other ) { return *this; } + template< typename Device_ > + Layer& operator=( const Layer< MeshConfig, Device_, DimensionTag >& other ) { return *this; } + + void setEntitiesCount( DimensionTag, const GlobalIndexType& entitiesCount ) {} + void getEntityTagsView( DimensionTag ) {} + void getEntityTag( DimensionTag, const GlobalIndexType& ) const {} + void addEntityTag( DimensionTag, const GlobalIndexType&, TagType ) const {} + void removeEntityTag( DimensionTag, const GlobalIndexType&, TagType ) const {} + void isBoundaryEntity( DimensionTag, const GlobalIndexType& ) const {} + void isGhostEntity( DimensionTag, const GlobalIndexType& ) const {} + void updateEntityTagsLayer( DimensionTag ) {} + void getBoundaryIndices( DimensionTag ) const {} + void getInteriorIndices( DimensionTag ) const {} + void getGhostEntitiesCount() const; + void getGhostEntitiesOffset() const; + + void save( File& file ) const {} + void load( File& file ) {} + + void print( std::ostream& str ) const {} + + bool operator==( const Layer& layer ) const + { + return true; + } +}; + +} // namespace EntityTags +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/LayerFamily.h b/src/TNL/Meshes/MeshDetails/layers/EntityTags/LayerFamily.h similarity index 52% rename from src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/LayerFamily.h rename to src/TNL/Meshes/MeshDetails/layers/EntityTags/LayerFamily.h index d78ba1c523378e46e45e4792ee51759bb1f20541..2914850a7fed5253e3dfc772b79ed8354e4510ef 100644 --- a/src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/LayerFamily.h +++ b/src/TNL/Meshes/MeshDetails/layers/EntityTags/LayerFamily.h @@ -17,7 +17,7 @@ namespace TNL { namespace Meshes { -namespace BoundaryTags { +namespace EntityTags { template< typename MeshConfig, typename Device, typename Dimension = DimensionTag< 0 > > class LayerInheritor @@ -28,24 +28,30 @@ class LayerInheritor using BaseType = LayerInheritor< MeshConfig, Device, typename Dimension::Increment >; protected: using LayerType::setEntitiesCount; - using LayerType::resetBoundaryTags; - using LayerType::setIsBoundaryEntity; + using LayerType::getEntityTagsView; + using LayerType::getEntityTag; + using LayerType::addEntityTag; + using LayerType::removeEntityTag; using LayerType::isBoundaryEntity; - using LayerType::updateBoundaryIndices; - using LayerType::getBoundaryEntitiesCount; - using LayerType::getBoundaryEntityIndex; - using LayerType::getInteriorEntitiesCount; - using LayerType::getInteriorEntityIndex; + using LayerType::isGhostEntity; + using LayerType::updateEntityTagsLayer; + using LayerType::getBoundaryIndices; + using LayerType::getInteriorIndices; + using LayerType::getGhostEntitiesCount; + using LayerType::getGhostEntitiesOffset; using BaseType::setEntitiesCount; - using BaseType::resetBoundaryTags; - using BaseType::setIsBoundaryEntity; + using BaseType::getEntityTagsView; + using BaseType::getEntityTag; + using BaseType::addEntityTag; + using BaseType::removeEntityTag; using BaseType::isBoundaryEntity; - using BaseType::updateBoundaryIndices; - using BaseType::getBoundaryEntitiesCount; - using BaseType::getBoundaryEntityIndex; - using BaseType::getInteriorEntitiesCount; - using BaseType::getInteriorEntityIndex; + using BaseType::isGhostEntity; + using BaseType::updateEntityTagsLayer; + using BaseType::getBoundaryIndices; + using BaseType::getInteriorIndices; + using BaseType::getGhostEntitiesCount; + using BaseType::getGhostEntitiesOffset; LayerInheritor() = default; @@ -107,14 +113,17 @@ class LayerInheritor< MeshConfig, Device, DimensionTag< MeshConfig::meshDimensio { protected: void setEntitiesCount(); - void resetBoundaryTags(); - void setIsBoundaryEntity(); - void isBoundaryEntity(); - void updateBoundaryIndices(); - void getBoundaryEntitiesCount(); - void getBoundaryEntityIndex(); - void getInteriorEntitiesCount(); - void getInteriorEntityIndex(); + void getEntityTagsView(); + void getEntityTag() const; + void addEntityTag(); + void removeEntityTag(); + void isBoundaryEntity() const; + void isGhostEntity() const; + void updateEntityTagsLayer(); + void getBoundaryIndices() const; + void getInteriorIndices() const; + void getGhostEntitiesCount() const; + void getGhostEntitiesOffset() const; LayerInheritor() = default; explicit LayerInheritor( const LayerInheritor& other ) {} @@ -145,6 +154,8 @@ class LayerFamily { using MeshTraitsType = MeshTraits< MeshConfig, Device >; using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; + using EntityTagsArrayType = typename MeshTraitsType::EntityTagsArrayType; + using TagType = typename MeshTraitsType::EntityTagType; using BaseType = LayerInheritor< MeshConfig, Device, DimensionTag< 0 > >; template< int Dimension > using EntityTraits = typename MeshTraitsType::template EntityTraits< Dimension >; @@ -158,76 +169,110 @@ public: using BaseType::BaseType; using BaseType::operator=; - // getters for boundary tags template< int Dimension > __cuda_callable__ - bool isBoundaryEntity( const GlobalIndexType& entityIndex ) const + typename EntityTagsArrayType::ViewType + getEntityTagsView() { - static_assert( WeakTrait< Dimension >::boundaryTagsEnabled, "You try to access boundary tags which are not configured for storage." ); - return BaseType::isBoundaryEntity( DimensionTag< Dimension >(), entityIndex ); + static_assert( WeakTrait< Dimension >::entityTagsEnabled, "You try to access entity tags which are not configured for storage." ); + return BaseType::getEntityTagsView( DimensionTag< Dimension >() ); } template< int Dimension > __cuda_callable__ - GlobalIndexType getBoundaryEntitiesCount() const + typename EntityTagsArrayType::ConstViewType + getEntityTagsView() const { - static_assert( WeakTrait< Dimension >::boundaryTagsEnabled, "You try to access boundary tags which are not configured for storage." ); - return BaseType::getBoundaryEntitiesCount( DimensionTag< Dimension >() ); + static_assert( WeakTrait< Dimension >::entityTagsEnabled, "You try to access entity tags which are not configured for storage." ); + return BaseType::getEntityTagsView( DimensionTag< Dimension >() ); } template< int Dimension > __cuda_callable__ - GlobalIndexType getBoundaryEntityIndex( const GlobalIndexType& i ) const + TagType getEntityTag( const GlobalIndexType& entityIndex ) const { - static_assert( WeakTrait< Dimension >::boundaryTagsEnabled, "You try to access boundary tags which are not configured for storage." ); - return BaseType::getBoundaryEntityIndex( DimensionTag< Dimension >(), i ); + static_assert( WeakTrait< Dimension >::entityTagsEnabled, "You try to access entity tags which are not configured for storage." ); + return BaseType::getEntityTag( DimensionTag< Dimension >(), entityIndex ); } template< int Dimension > __cuda_callable__ - GlobalIndexType getInteriorEntitiesCount() const + void addEntityTag( const GlobalIndexType& entityIndex, TagType tag ) { - static_assert( WeakTrait< Dimension >::boundaryTagsEnabled, "You try to access boundary tags which are not configured for storage." ); - return BaseType::getInteriorEntitiesCount( DimensionTag< Dimension >() ); + static_assert( WeakTrait< Dimension >::entityTagsEnabled, "You try to access entity tags which are not configured for storage." ); + BaseType::addEntityTag( DimensionTag< Dimension >(), entityIndex, tag ); } template< int Dimension > __cuda_callable__ - GlobalIndexType getInteriorEntityIndex( const GlobalIndexType& i ) const + void removeEntityTag( const GlobalIndexType& entityIndex, TagType tag ) { - static_assert( WeakTrait< Dimension >::boundaryTagsEnabled, "You try to access boundary tags which are not configured for storage." ); - return BaseType::getInteriorEntityIndex( DimensionTag< Dimension >(), i ); + static_assert( WeakTrait< Dimension >::entityTagsEnabled, "You try to access entity tags which are not configured for storage." ); + BaseType::removeEntityTag( DimensionTag< Dimension >(), entityIndex, tag ); } -protected: - // setters for boundary tags template< int Dimension > - void boundaryTagsSetEntitiesCount( const GlobalIndexType& entitiesCount ) + __cuda_callable__ + bool isBoundaryEntity( const GlobalIndexType& entityIndex ) const { - BaseType::setEntitiesCount( DimensionTag< Dimension >(), entitiesCount ); + static_assert( WeakTrait< Dimension >::entityTagsEnabled, "You try to access entity tags which are not configured for storage." ); + return BaseType::isBoundaryEntity( DimensionTag< Dimension >(), entityIndex ); + } + + template< int Dimension > + __cuda_callable__ + bool isGhostEntity( const GlobalIndexType& entityIndex ) const + { + static_assert( WeakTrait< Dimension >::entityTagsEnabled, "You try to access entity tags which are not configured for storage." ); + return BaseType::isGhostEntity( DimensionTag< Dimension >(), entityIndex ); + } + + template< int Dimension > + __cuda_callable__ + auto getBoundaryIndices() const + { + static_assert( WeakTrait< Dimension >::entityTagsEnabled, "You try to access entity tags which are not configured for storage." ); + return BaseType::getBoundaryIndices( DimensionTag< Dimension >() ); } - + template< int Dimension > - void resetBoundaryTags() + __cuda_callable__ + auto getInteriorIndices() const { - BaseType::resetBoundaryTags( DimensionTag< Dimension >() ); + static_assert( WeakTrait< Dimension >::entityTagsEnabled, "You try to access entity tags which are not configured for storage." ); + return BaseType::getInteriorIndices( DimensionTag< Dimension >() ); } template< int Dimension > - void updateBoundaryIndices() + __cuda_callable__ + GlobalIndexType getGhostEntitiesCount() const { - BaseType::updateBoundaryIndices( DimensionTag< Dimension >() ); + static_assert( WeakTrait< Dimension >::entityTagsEnabled, "You try to access entity tags which are not configured for storage." ); + return BaseType::getGhostEntitiesCount( DimensionTag< Dimension >() ); } template< int Dimension > __cuda_callable__ - void setIsBoundaryEntity( const GlobalIndexType& entityIndex, bool isBoundary ) + GlobalIndexType getGhostEntitiesOffset() const { - static_assert( WeakTrait< Dimension >::boundaryTagsEnabled, "You try to access boundary tags which are not configured for storage." ); - BaseType::setIsBoundaryEntity( DimensionTag< Dimension >(), entityIndex, isBoundary ); + static_assert( WeakTrait< Dimension >::entityTagsEnabled, "You try to access entity tags which are not configured for storage." ); + return BaseType::getGhostEntitiesOffset( DimensionTag< Dimension >() ); + } + + template< int Dimension > + void updateEntityTagsLayer() + { + BaseType::updateEntityTagsLayer( DimensionTag< Dimension >() ); + } + +protected: + template< int Dimension > + void entityTagsSetEntitiesCount( const GlobalIndexType& entitiesCount ) + { + BaseType::setEntitiesCount( DimensionTag< Dimension >(), entitiesCount ); } }; -} // namespace BoundaryTags +} // namespace EntityTags } // namespace Meshes } // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/Traits.h b/src/TNL/Meshes/MeshDetails/layers/EntityTags/Traits.h similarity index 72% rename from src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/Traits.h rename to src/TNL/Meshes/MeshDetails/layers/EntityTags/Traits.h index 0d67f0db1d82e29bd893515fab155e9312820fb9..b7c28f15557d00722095fe7d71e32105bcde7ce1 100644 --- a/src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/Traits.h +++ b/src/TNL/Meshes/MeshDetails/layers/EntityTags/Traits.h @@ -14,7 +14,7 @@ namespace TNL { namespace Meshes { -namespace BoundaryTags { +namespace EntityTags { template< typename MeshConfig, typename Device, @@ -23,7 +23,7 @@ template< typename MeshConfig, struct WeakStorageTrait { using EntityTopology = typename MeshTraits< MeshConfig >::template EntityTraits< DimensionTag::value >::EntityTopology; - static constexpr bool boundaryTagsEnabled = MeshConfig::boundaryTagsStorage( EntityTopology() ); + static constexpr bool entityTagsEnabled = MeshConfig::entityTagsStorage( EntityTopology() ); }; template< typename MeshConfig, @@ -31,9 +31,17 @@ template< typename MeshConfig, typename DimensionTag > struct WeakStorageTrait< MeshConfig, Device, DimensionTag, false > { - static constexpr bool boundaryTagsEnabled = false; + static constexpr bool entityTagsEnabled = false; }; -} // namespace BoundaryTags +// Entity tags are used in a bitset fashion. Unused bits are available for +// user needs, but these bits should not be changed by users. +enum EntityTags : std::uint8_t +{ + BoundaryEntity = 1, + GhostEntity = 2, +}; + +} // namespace EntityTags } // namespace Meshes } // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/layers/StorageLayer.h b/src/TNL/Meshes/MeshDetails/layers/StorageLayer.h new file mode 100644 index 0000000000000000000000000000000000000000..f92729532ca01c960dc20ffb8cd1b18b99b976e4 --- /dev/null +++ b/src/TNL/Meshes/MeshDetails/layers/StorageLayer.h @@ -0,0 +1,412 @@ +/*************************************************************************** + StorageLayer.h - description + ------------------- + begin : Feb 11, 2014 + copyright : (C) 2014 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 +#include +#include +#include +#include + +namespace TNL { +namespace Meshes { + +template< typename MeshConfig, + typename Device, + typename DimensionTag, + bool EntityStorage = (DimensionTag::value <= MeshConfig::meshDimension) > +class StorageLayer; + + +template< typename MeshConfig, typename Device > +class StorageLayerFamily + : public StorageLayer< MeshConfig, Device, DimensionTag< 0 > >, + public DualGraphLayer< MeshConfig, Device > +{ + using MeshTraitsType = MeshTraits< MeshConfig, Device >; + using BaseType = StorageLayer< MeshConfig, Device, DimensionTag< 0 > >; + template< int Dimension > + using EntityTraits = typename MeshTraitsType::template EntityTraits< Dimension >; + + template< int Dimension, int Subdimension > + using SubentityTraits = typename MeshTraitsType::template SubentityTraits< typename EntityTraits< Dimension >::EntityTopology, Subdimension >; + + template< int Dimension, int Superdimension > + using SuperentityTraits = typename MeshTraitsType::template SuperentityTraits< typename EntityTraits< Dimension >::EntityTopology, Superdimension >; + +protected: + typename MeshTraitsType::PointArrayType points; + +public: + StorageLayerFamily() = default; + + explicit StorageLayerFamily( const StorageLayerFamily& other ) + { + operator=( other ); + } + + template< typename Device_ > + StorageLayerFamily( const StorageLayerFamily< MeshConfig, Device_ >& other ) + { + operator=( other ); + } + + StorageLayerFamily& operator=( const StorageLayerFamily& layer ) + { + points = layer.getPoints(); + BaseType::operator=( layer ); + DualGraphLayer< MeshConfig, Device >::operator=( layer ); + return *this; + } + + template< typename Device_ > + StorageLayerFamily& operator=( const StorageLayerFamily< MeshConfig, Device_ >& layer ) + { + points = layer.getPoints(); + BaseType::operator=( layer ); + DualGraphLayer< MeshConfig, Device >::operator=( layer ); + return *this; + } + + bool operator==( const StorageLayerFamily& layer ) const + { + return ( points == layer.points && + BaseType::operator==( layer ) && + DualGraphLayer< MeshConfig, Device >::operator==( layer ) ); + } + + void save( File& file ) const + { + file << points; + BaseType::save( file ); + } + + void load( File& file ) + { + file >> points; + BaseType::load( file ); + } + + void print( std::ostream& str ) const + { + str << "Vertex coordinates are: " << points << std::endl; + BaseType::print( str ); + } + + const typename MeshTraitsType::PointArrayType& getPoints() const + { + return points; + } + + typename MeshTraitsType::PointArrayType& getPoints() + { + return points; + } + + template< int Dimension > + void setEntitiesCount( const typename MeshTraitsType::GlobalIndexType& entitiesCount ) + { + BaseType::setEntitiesCount( DimensionTag< Dimension >(), entitiesCount ); + if( Dimension == 0 ) + points.setSize( entitiesCount ); + } + + template< int Dimension, int Subdimension > + __cuda_callable__ + typename MeshTraitsType::LocalIndexType + getSubentitiesCount() const + { + static_assert( Dimension > Subdimension, "Invalid combination of Dimension and Subdimension." ); + static_assert( SubentityTraits< Dimension, Subdimension >::storageEnabled, + "You try to get subentities count for subentities which are disabled in the mesh configuration." ); + using BaseType = SubentityStorageLayerFamily< MeshConfig, + Device, + typename EntityTraits< Dimension >::EntityTopology >; + return BaseType::template getSubentitiesCount< Subdimension >(); + } + + template< int Dimension, int Subdimension > + __cuda_callable__ + typename MeshTraitsType::SubentityMatrixType& + getSubentitiesMatrix() + { + static_assert( Dimension > Subdimension, "Invalid combination of Dimension and Subdimension." ); + static_assert( SubentityTraits< Dimension, Subdimension >::storageEnabled, + "You try to get subentities matrix which is disabled in the mesh configuration." ); + using BaseType = SubentityStorageLayerFamily< MeshConfig, + Device, + typename EntityTraits< Dimension >::EntityTopology >; + return BaseType::template getSubentitiesMatrix< Subdimension >(); + } + + template< int Dimension, int Subdimension > + __cuda_callable__ + const typename MeshTraitsType::SubentityMatrixType& + getSubentitiesMatrix() const + { + static_assert( Dimension > Subdimension, "Invalid combination of Dimension and Subdimension." ); + static_assert( SubentityTraits< Dimension, Subdimension >::storageEnabled, + "You try to get subentities matrix which is disabled in the mesh configuration." ); + using BaseType = SubentityStorageLayerFamily< MeshConfig, + Device, + typename EntityTraits< Dimension >::EntityTopology >; + return BaseType::template getSubentitiesMatrix< Subdimension >(); + } + + template< int Dimension, int Superdimension > + __cuda_callable__ + typename MeshTraitsType::NeighborCountsArray& + getSuperentitiesCountsArray() + { + static_assert( Dimension < Superdimension, "Invalid combination of Dimension and Superdimension." ); + static_assert( SuperentityTraits< Dimension, Superdimension >::storageEnabled, + "You try to get superentities counts array which is disabled in the mesh configuration." ); + using BaseType = SuperentityStorageLayerFamily< MeshConfig, + Device, + typename EntityTraits< Dimension >::EntityTopology >; + return BaseType::template getSuperentitiesCountsArray< Superdimension >(); + } + + template< int Dimension, int Superdimension > + __cuda_callable__ + const typename MeshTraitsType::NeighborCountsArray& + getSuperentitiesCountsArray() const + { + static_assert( Dimension < Superdimension, "Invalid combination of Dimension and Superdimension." ); + static_assert( SuperentityTraits< Dimension, Superdimension >::storageEnabled, + "You try to get superentities counts array which is disabled in the mesh configuration." ); + using BaseType = SuperentityStorageLayerFamily< MeshConfig, + Device, + typename EntityTraits< Dimension >::EntityTopology >; + return BaseType::template getSuperentitiesCountsArray< Superdimension >(); + } + + template< int Dimension, int Superdimension > + __cuda_callable__ + typename MeshTraitsType::SuperentityMatrixType& + getSuperentitiesMatrix() + { + static_assert( Dimension < Superdimension, "Invalid combination of Dimension and Superdimension." ); + static_assert( SuperentityTraits< Dimension, Superdimension >::storageEnabled, + "You try to get superentities matrix which is disabled in the mesh configuration." ); + using BaseType = SuperentityStorageLayerFamily< MeshConfig, + Device, + typename EntityTraits< Dimension >::EntityTopology >; + return BaseType::template getSuperentitiesMatrix< Superdimension >(); + } + + template< int Dimension, int Superdimension > + __cuda_callable__ + const typename MeshTraitsType::SuperentityMatrixType& + getSuperentitiesMatrix() const + { + static_assert( Dimension < Superdimension, "Invalid combination of Dimension and Superdimension." ); + static_assert( SuperentityTraits< Dimension, Superdimension >::storageEnabled, + "You try to get superentities matrix which is disabled in the mesh configuration." ); + using BaseType = SuperentityStorageLayerFamily< MeshConfig, + Device, + 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 ); + } +}; + + +template< typename MeshConfig, + typename Device, + typename DimensionTag > +class StorageLayer< MeshConfig, + Device, + DimensionTag, + true > + : 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 >, + public StorageLayer< MeshConfig, Device, typename DimensionTag::Increment > +{ +public: + using BaseType = StorageLayer< MeshConfig, Device, typename DimensionTag::Increment >; + using MeshTraitsType = MeshTraits< MeshConfig, Device >; + using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; + using EntityTraitsType = typename MeshTraitsType::template EntityTraits< DimensionTag::value >; + 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 ) + { + operator=( other ); + } + + template< typename Device_ > + StorageLayer( const StorageLayer< MeshConfig, Device_, DimensionTag >& other ) + { + operator=( other ); + } + + StorageLayer& operator=( const StorageLayer& other ) + { + entitiesCount = other.entitiesCount; + SubentityStorageBaseType::operator=( other ); + SuperentityStorageBaseType::operator=( other ); + BaseType::operator=( other ); + return *this; + } + + template< typename Device_ > + StorageLayer& operator=( const StorageLayer< MeshConfig, Device_, DimensionTag >& other ) + { + entitiesCount = other.getEntitiesCount( DimensionTag() ); + SubentityStorageBaseType::operator=( other ); + SuperentityStorageBaseType::operator=( other ); + BaseType::operator=( other ); + return *this; + } + + void save( File& file ) const + { + SubentityStorageBaseType::save( file ); + SuperentityStorageBaseType::save( file ); + file.save( &entitiesCount, 1 ); + BaseType::save( file ); + } + + void load( File& file ) + { + SubentityStorageBaseType::load( file ); + SuperentityStorageBaseType::load( file ); + file.load( &entitiesCount, 1 ); + BaseType::load( file ); + } + + void print( std::ostream& str ) const + { + str << "Number of entities with dimension " << DimensionTag::value << ": " << entitiesCount << std::endl; + SubentityStorageBaseType::print( str ); + SuperentityStorageBaseType::print( str ); + str << std::endl; + BaseType::print( str ); + } + + bool operator==( const StorageLayer& meshLayer ) const + { + return ( entitiesCount == meshLayer.entitiesCount && + SubentityStorageBaseType::operator==( meshLayer ) && + SuperentityStorageBaseType::operator==( meshLayer ) && + BaseType::operator==( meshLayer ) ); + } + + + using BaseType::getEntitiesCount; + __cuda_callable__ + GlobalIndexType getEntitiesCount( DimensionTag ) const + { + return this->entitiesCount; + } + +protected: + using BaseType::setEntitiesCount; + void setEntitiesCount( DimensionTag, const GlobalIndexType& entitiesCount ) + { + this->entitiesCount = entitiesCount; + SubentityOrientationsBaseType::setEntitiesCount( entitiesCount ); + } + + GlobalIndexType entitiesCount = 0; + + // friend class is needed for templated assignment operators + template< typename MeshConfig_, typename Device_, typename DimensionTag_, bool Storage_ > + friend class StorageLayer; +}; + +template< typename MeshConfig, + typename Device > +class StorageLayer< MeshConfig, Device, DimensionTag< MeshConfig::meshDimension + 1 >, false > +{ +protected: + using DimensionTag = Meshes::DimensionTag< MeshConfig::meshDimension >; + using GlobalIndexType = typename MeshConfig::GlobalIndexType; + + StorageLayer() = default; + + explicit StorageLayer( const StorageLayer& other ) {} + + template< typename Device_ > + StorageLayer( const StorageLayer< MeshConfig, Device_, DimensionTag >& other ) {} + + StorageLayer& operator=( const StorageLayer& other ) + { + return *this; + } + + template< typename Device_ > + StorageLayer& operator=( const StorageLayer< MeshConfig, Device_, DimensionTag >& other ) + { + return *this; + } + + + void subentityOrientationsArray() {} + void setEntitiesCount() {} + void getEntitiesCount() const {} + + void save( File& file ) const {} + void load( File& file ) {} + + void print( std::ostream& str ) const {} + + bool operator==( const StorageLayer& meshLayer ) const + { + return true; + } +}; + +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/layers/SubentityOrientationsLayer.h b/src/TNL/Meshes/MeshDetails/layers/SubentityOrientationsLayer.h new file mode 100644 index 0000000000000000000000000000000000000000..18a1375474c09e1d087d1f580dbe63fd6e1ec952 --- /dev/null +++ b/src/TNL/Meshes/MeshDetails/layers/SubentityOrientationsLayer.h @@ -0,0 +1,172 @@ +/*************************************************************************** + 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/MeshLayers/SubentityStorageLayer.h b/src/TNL/Meshes/MeshDetails/layers/SubentityStorageLayer.h similarity index 73% rename from src/TNL/Meshes/MeshDetails/MeshLayers/SubentityStorageLayer.h rename to src/TNL/Meshes/MeshDetails/layers/SubentityStorageLayer.h index a598289e5dd1457917734470192c6a3bf2cf9ae7..ed84e2b8257ddcc3319498edf0d7ffbd9382e249 100644 --- a/src/TNL/Meshes/MeshDetails/MeshLayers/SubentityStorageLayer.h +++ b/src/TNL/Meshes/MeshDetails/layers/SubentityStorageLayer.h @@ -47,11 +47,30 @@ public: protected: template< int Subdimension > - typename MeshTraitsType::template SubentityTraits< EntityTopology, Subdimension >::StorageNetworkType& - getSubentityStorageNetwork() + __cuda_callable__ + typename MeshTraitsType::LocalIndexType + getSubentitiesCount() const { static_assert( EntityTopology::dimension > Subdimension, "Invalid combination of Dimension and Subdimension." ); - return BaseType::getSubentityStorageNetwork( DimensionTag< Subdimension >() ); + return BaseType::getSubentitiesCount( DimensionTag< Subdimension >() ); + } + + template< int Subdimension > + __cuda_callable__ + typename MeshTraitsType::SubentityMatrixType& + getSubentitiesMatrix() + { + static_assert( EntityTopology::dimension > Subdimension, "Invalid combination of Dimension and Subdimension." ); + return BaseType::getSubentitiesMatrix( DimensionTag< Subdimension >() ); + } + + template< int Subdimension > + __cuda_callable__ + const typename MeshTraitsType::SubentityMatrixType& + getSubentitiesMatrix() const + { + static_assert( EntityTopology::dimension > Subdimension, "Invalid combination of Dimension and Subdimension." ); + return BaseType::getSubentitiesMatrix( DimensionTag< Subdimension >() ); } }; @@ -68,11 +87,11 @@ class SubentityStorageLayer< MeshConfig, { using BaseType = SubentityStorageLayer< MeshConfig, Device, EntityTopology, typename SubdimensionTag::Increment >; using MeshTraitsType = MeshTraits< MeshConfig, Device >; - using SubentityTraitsType = typename MeshTraitsType::template SubentityTraits< EntityTopology, SubdimensionTag::value >; protected: using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; - using StorageNetworkType = typename SubentityTraitsType::StorageNetworkType; + using LocalIndexType = typename MeshTraitsType::LocalIndexType; + using SubentityMatrixType = typename MeshTraitsType::SubentityMatrixType; SubentityStorageLayer() = default; @@ -90,8 +109,7 @@ protected: SubentityStorageLayer& operator=( const SubentityStorageLayer& other ) { BaseType::operator=( other ); - storageNetwork.setLike( other.storageNetwork ); - storageNetwork = other.storageNetwork; + matrix = other.matrix; return *this; } @@ -99,8 +117,7 @@ protected: SubentityStorageLayer& operator=( const SubentityStorageLayer< MeshConfig, Device_, EntityTopology, SubdimensionTag >& other ) { BaseType::operator=( other ); - storageNetwork.setLike( other.storageNetwork ); - storageNetwork = other.storageNetwork; + matrix = other.matrix; return *this; } @@ -108,44 +125,52 @@ protected: void save( File& file ) const { BaseType::save( file ); - this->storageNetwork.save( file ); + matrix.save( file ); } void load( File& file ) { BaseType::load( file ); - this->storageNetwork.load( file ); + matrix.load( file ); } void print( std::ostream& str ) const { BaseType::print( str ); - str << "Storage network for subentities with dimension " << SubdimensionTag::value << " of entities with dimension " << EntityTopology::dimension << " is: " << std::endl; - str << this->storageNetwork << std::endl; + str << "Adjacency matrix for subentities with dimension " << SubdimensionTag::value << " of entities with dimension " << EntityTopology::dimension << " is: " << std::endl; + str << matrix << std::endl; } bool operator==( const SubentityStorageLayer& layer ) const { return ( BaseType::operator==( layer ) && - storageNetwork == layer.storageNetwork ); + matrix == layer.matrix ); } protected: - void setEntitiesCount( const GlobalIndexType& entitiesCount ) + using BaseType::getSubentitiesCount; + __cuda_callable__ + LocalIndexType getSubentitiesCount( SubdimensionTag ) const { - BaseType::setEntitiesCount( entitiesCount ); - this->storageNetwork.setKeysRange( entitiesCount ); - this->storageNetwork.allocate(); + using SubentityTraitsType = typename MeshTraitsType::template SubentityTraits< EntityTopology, SubdimensionTag::value >; + return SubentityTraitsType::count; } - using BaseType::getSubentityStorageNetwork; - StorageNetworkType& getSubentityStorageNetwork( SubdimensionTag ) + using BaseType::getSubentitiesMatrix; + __cuda_callable__ + SubentityMatrixType& getSubentitiesMatrix( SubdimensionTag ) { - return this->storageNetwork; + return matrix; + } + + __cuda_callable__ + const SubentityMatrixType& getSubentitiesMatrix( SubdimensionTag ) const + { + return matrix; } private: - StorageNetworkType storageNetwork; + SubentityMatrixType matrix; // friend class is needed for templated assignment operators template< typename MeshConfig_, typename Device_, typename EntityTopology_, typename SubdimensionTag_, bool Storage_ > @@ -192,8 +217,6 @@ protected: template< typename Device_ > SubentityStorageLayer& operator=( const SubentityStorageLayer< MeshConfig, Device_, EntityTopology, SubdimensionTag >& other ) { return *this; } - void setEntitiesCount( GlobalIndexType entitiesCount ) {} - void print( std::ostream& str ) const {} bool operator==( const SubentityStorageLayer& layer ) const @@ -204,7 +227,8 @@ protected: void save( File& file ) const {} void load( File& file ) {} - void getSubentityStorageNetwork( SubdimensionTag ) {} + void getSubentitiesCount( SubdimensionTag ) {} + void getSubentitiesMatrix( SubdimensionTag ) {} }; } // namespace Meshes diff --git a/src/TNL/Meshes/MeshDetails/MeshLayers/SuperentityStorageLayer.h b/src/TNL/Meshes/MeshDetails/layers/SuperentityStorageLayer.h similarity index 66% rename from src/TNL/Meshes/MeshDetails/MeshLayers/SuperentityStorageLayer.h rename to src/TNL/Meshes/MeshDetails/layers/SuperentityStorageLayer.h index 9cc1c5535b5d7aaedc89208532794b111b7a75fb..339090de556b1c6c5f171643ae6a3c28b528be30 100644 --- a/src/TNL/Meshes/MeshDetails/MeshLayers/SuperentityStorageLayer.h +++ b/src/TNL/Meshes/MeshDetails/layers/SuperentityStorageLayer.h @@ -53,11 +53,39 @@ public: protected: template< int Superdimension > - typename MeshTraitsType::template SuperentityTraits< EntityTopology, Superdimension >::StorageNetworkType& - getSuperentityStorageNetwork() + __cuda_callable__ + typename MeshTraitsType::NeighborCountsArray& + getSuperentitiesCountsArray() { static_assert( EntityTopology::dimension < Superdimension, "Invalid combination of Dimension and Superdimension." ); - return BaseType::getSuperentityStorageNetwork( DimensionTag< Superdimension >() ); + return BaseType::getSuperentitiesCountsArray( DimensionTag< Superdimension >() ); + } + + template< int Superdimension > + __cuda_callable__ + const typename MeshTraitsType::NeighborCountsArray& + getSuperentitiesCountsArray() const + { + static_assert( EntityTopology::dimension < Superdimension, "Invalid combination of Dimension and Superdimension." ); + return BaseType::getSuperentitiesCountsArray( DimensionTag< Superdimension >() ); + } + + template< int Superdimension > + __cuda_callable__ + typename MeshTraitsType::SuperentityMatrixType& + getSuperentitiesMatrix() + { + static_assert( EntityTopology::dimension < Superdimension, "Invalid combination of Dimension and Superdimension." ); + return BaseType::getSuperentitiesMatrix( DimensionTag< Superdimension >() ); + } + + template< int Superdimension > + __cuda_callable__ + const typename MeshTraitsType::SuperentityMatrixType& + getSuperentitiesMatrix() const + { + static_assert( EntityTopology::dimension < Superdimension, "Invalid combination of Dimension and Superdimension." ); + return BaseType::getSuperentitiesMatrix( DimensionTag< Superdimension >() ); } }; @@ -69,13 +97,12 @@ class SuperentityStorageLayer< MeshConfig, Device, EntityTopology, Superdimensio : public SuperentityStorageLayer< MeshConfig, Device, EntityTopology, typename SuperdimensionTag::Decrement > { using BaseType = SuperentityStorageLayer< MeshConfig, Device, EntityTopology, typename SuperdimensionTag::Decrement >; - using MeshTraitsType = MeshTraits< MeshConfig, Device >; - using SuperentityTraitsType = typename MeshTraitsType::template SuperentityTraits< EntityTopology, SuperdimensionTag::value >; + using MeshTraitsType = MeshTraits< MeshConfig, Device >; protected: - using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; - using StorageNetworkType = typename SuperentityTraitsType::StorageNetworkType; - + using NeighborCountsArray = typename MeshTraitsType::NeighborCountsArray; + using SuperentityMatrixType = typename MeshTraitsType::SuperentityMatrixType; + SuperentityStorageLayer() = default; explicit SuperentityStorageLayer( const SuperentityStorageLayer& other ) @@ -92,8 +119,8 @@ protected: SuperentityStorageLayer& operator=( const SuperentityStorageLayer& other ) { BaseType::operator=( other ); - storageNetwork.setLike( other.storageNetwork ); - storageNetwork = other.storageNetwork; + superentitiesCounts = other.superentitiesCounts; + matrix = other.matrix; return *this; } @@ -101,8 +128,8 @@ protected: SuperentityStorageLayer& operator=( const SuperentityStorageLayer< MeshConfig, Device_, EntityTopology, SuperdimensionTag >& other ) { BaseType::operator=( other ); - storageNetwork.setLike( other.storageNetwork ); - storageNetwork = other.storageNetwork; + superentitiesCounts = other.superentitiesCounts; + matrix = other.matrix; return *this; } @@ -110,43 +137,60 @@ protected: void save( File& file ) const { BaseType::save( file ); - this->storageNetwork.save( file ); + matrix.save( file ); } void load( File& file ) { BaseType::load( file ); - this->storageNetwork.load( file ); + matrix.load( file ); + matrix.getCompressedRowLengths( superentitiesCounts ); } void print( std::ostream& str ) const { BaseType::print( str ); - str << "Storage network for superentities with dimension " << SuperdimensionTag::value << " of entities with dimension " << EntityTopology::dimension << " is: " << std::endl; - str << this->storageNetwork << std::endl; + str << "Adjacency matrix for superentities with dimension " << SuperdimensionTag::value << " of entities with dimension " << EntityTopology::dimension << " is: " << std::endl; + str << matrix << std::endl; } bool operator==( const SuperentityStorageLayer& layer ) const { return ( BaseType::operator==( layer ) && - storageNetwork == layer.storageNetwork ); + superentitiesCounts == layer.superentitiesCounts && + matrix == layer.matrix ); } protected: - void setEntitiesCount( const GlobalIndexType& entitiesCount ) + using BaseType::getSuperentitiesCountsArray; + __cuda_callable__ + NeighborCountsArray& getSuperentitiesCountsArray( SuperdimensionTag ) + { + return superentitiesCounts; + } + + __cuda_callable__ + const NeighborCountsArray& getSuperentitiesCountsArray( SuperdimensionTag ) const + { + return superentitiesCounts; + } + + using BaseType::getSuperentitiesMatrix; + __cuda_callable__ + SuperentityMatrixType& getSuperentitiesMatrix( SuperdimensionTag ) { - BaseType::setEntitiesCount( entitiesCount ); - this->storageNetwork.setKeysRange( entitiesCount ); + return matrix; } - using BaseType::getSuperentityStorageNetwork; - StorageNetworkType& getSuperentityStorageNetwork( SuperdimensionTag ) + __cuda_callable__ + const SuperentityMatrixType& getSuperentitiesMatrix( SuperdimensionTag ) const { - return this->storageNetwork; + return matrix; } private: - StorageNetworkType storageNetwork; + NeighborCountsArray superentitiesCounts; + SuperentityMatrixType matrix; // friend class is needed for templated assignment operators template< typename MeshConfig_, typename Device_, typename EntityTopology_, typename SuperdimensionTag_, bool Storage_ > @@ -176,8 +220,6 @@ class SuperentityStorageLayer< MeshConfig, Device, EntityTopology, DimensionTag< using SuperdimensionTag = DimensionTag< EntityTopology::dimension >; protected: - using GlobalIndexType = typename MeshConfig::GlobalIndexType; - SuperentityStorageLayer() = default; explicit SuperentityStorageLayer( const SuperentityStorageLayer& other ) {} template< typename Device_ > @@ -185,7 +227,7 @@ protected: template< typename Device_ > SuperentityStorageLayer& operator=( const SuperentityStorageLayer< MeshConfig, Device_, EntityTopology, SuperdimensionTag >& other ) { return *this; } - void setEntitiesCount( GlobalIndexType entitiesCount ) {} + void getSuperentitiesCountsArray() {} void print( std::ostream& str ) const {} @@ -197,7 +239,7 @@ protected: void save( File& file ) const {} void load( File& file ) {} - void getSuperentityStorageNetwork( SuperdimensionTag ) {} + void getSuperentitiesMatrix( SuperdimensionTag ) {} }; } // namespace Meshes diff --git a/src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h b/src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h index 796cc1b1e9fd039c58916828d8b9aaac2c098208..4e1115dafc93e7b672f3c6541a672442a8964cc7 100644 --- a/src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h +++ b/src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h @@ -52,6 +52,16 @@ 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" ); @@ -60,13 +70,11 @@ public: using SeedType = EntitySeed< MeshConfig, EntityTopology >; using ReferenceOrientationType = MeshEntityReferenceOrientation< MeshConfig, EntityTopology >; - using StorageArrayType = Containers::Array< EntityType, Device, GlobalIndexType >; 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 storageEnabled = MeshConfig::entityStorage( Dimension ); - static constexpr bool orientationNeeded = 0 < Dimension && Dimension < MeshConfig::meshDimension; + 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 c10b569582f776ef73796bf8000abfe2e4bbd820..5706719310b3782871c2d2ddac6727ac298d9c85 100644 --- a/src/TNL/Meshes/MeshDetails/traits/MeshSubentityTraits.h +++ b/src/TNL/Meshes/MeshDetails/traits/MeshSubentityTraits.h @@ -17,18 +17,14 @@ #pragma once #include -#include #include #include -#include namespace TNL { namespace Meshes { template< typename MeshConfig, typename EntityTopology > class MeshEntityOrientation; -template< typename MeshConfig, typename EntityTopology > -class EntitySeed; template< typename MeshConfig, typename Device, @@ -49,22 +45,12 @@ public: using SubentityTopology = typename MeshEntityTraits< MeshConfig, Device, Dimension >::EntityTopology; using SubentityType = typename MeshEntityTraits< MeshConfig, Device, Dimension >::EntityType; - using Seed = EntitySeed< MeshConfig, SubentityTopology >; using Orientation = MeshEntityOrientation< MeshConfig, SubentityTopology >; - /**** - * Type of container for storing of the subentities indices. - */ - // TODO: write general operator= for different SliceSize and remove the '32' here - using StorageNetworkType = Containers::Multimaps::StaticEllpackIndexMultimap< count, GlobalIndexType, Device, LocalIndexType, 32 >; - using SubentityAccessorType = typename StorageNetworkType::ValuesAccessorType; - - // static array used in MeshSubentitySeedCreator - using SeedArrayType = Containers::StaticArray< count, Seed >; - // 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/MeshDetails/traits/MeshSuperentityTraits.h b/src/TNL/Meshes/MeshDetails/traits/MeshSuperentityTraits.h index 5e535b4a7fcdeaa2d093a035db3a6af0f6f1dafa..4b5e388195724582250d175d212dfdc0370030b2 100644 --- a/src/TNL/Meshes/MeshDetails/traits/MeshSuperentityTraits.h +++ b/src/TNL/Meshes/MeshDetails/traits/MeshSuperentityTraits.h @@ -16,9 +16,7 @@ #pragma once -#include #include -#include namespace TNL { namespace Meshes { @@ -40,13 +38,6 @@ public: using SuperentityTopology = typename MeshEntityTraits< MeshConfig, Device, Dimension >::EntityTopology; using SuperentityType = typename MeshEntityTraits< MeshConfig, Device, Dimension >::EntityType; - - /**** - * Type of container for storing of the superentities indices. - */ - // TODO: write general operator= for different SliceSize and remove the '32' here - using StorageNetworkType = Containers::Multimaps::EllpackIndexMultimap< GlobalIndexType, Device, LocalIndexType, 32 >; - using SuperentityAccessorType = typename StorageNetworkType::ValuesAccessorType; }; } // namespace Meshes diff --git a/src/TNL/Meshes/MeshDetails/traits/MeshTraits.h b/src/TNL/Meshes/MeshDetails/traits/MeshTraits.h index 545edbb23a2a0c8bdbb68f962adbd635ce24cec6..1bad55de4c11347632ece834e3b82b71b1b3832a 100644 --- a/src/TNL/Meshes/MeshDetails/traits/MeshTraits.h +++ b/src/TNL/Meshes/MeshDetails/traits/MeshTraits.h @@ -18,6 +18,9 @@ #include #include +#include +#include +#include #include #include @@ -30,6 +33,12 @@ template< typename MeshConfig, typename Device, int Dimension > class MeshEntity template< typename MeshConfig, typename Device, typename MeshEntity, int Subdimension > class MeshSubentityTraits; template< typename MeshConfig, typename Device, typename MeshEntity, int Superdimension > class MeshSuperentityTraits; +// helper templates (must be public because nvcc sucks, and outside of MeshTraits to avoid duplicate code generation) +template< typename Device, typename Index, typename IndexAlocator > +using EllpackSegments = Algorithms::Segments::Ellpack< Device, Index, IndexAlocator >; +template< typename Device, typename Index, typename IndexAlocator > +using SlicedEllpackSegments = Algorithms::Segments::SlicedEllpack< Device, Index, IndexAlocator >; + template< typename MeshConfig, typename Device = Devices::Host > class MeshTraits @@ -38,18 +47,21 @@ public: static constexpr int meshDimension = MeshConfig::CellTopology::dimension; static constexpr int worldDimension = MeshConfig::worldDimension; - using DeviceType = Device; - using GlobalIndexType = typename MeshConfig::GlobalIndexType; - using LocalIndexType = typename MeshConfig::LocalIndexType; + using DeviceType = Device; + using GlobalIndexType = typename MeshConfig::GlobalIndexType; + using LocalIndexType = typename MeshConfig::LocalIndexType; - using CellTopology = typename MeshConfig::CellTopology; - using CellType = MeshEntity< MeshConfig, Device, CellTopology >; - using VertexType = MeshEntity< MeshConfig, Device, Topologies::Vertex >; - using PointType = Containers::StaticVector< worldDimension, typename MeshConfig::RealType >; - using CellSeedType = EntitySeed< MeshConfig, CellTopology >; + using CellTopology = typename MeshConfig::CellTopology; + using CellType = MeshEntity< MeshConfig, Device, CellTopology >; + using VertexType = MeshEntity< MeshConfig, Device, Topologies::Vertex >; + using PointType = Containers::StaticVector< worldDimension, typename MeshConfig::RealType >; + using CellSeedType = EntitySeed< MeshConfig, CellTopology >; + using EntityTagType = std::uint8_t; - using PointArrayType = Containers::Array< PointType, DeviceType, GlobalIndexType >; - using CellSeedArrayType = Containers::Array< CellSeedType, DeviceType, GlobalIndexType >; + using NeighborCountsArray = Containers::Vector< LocalIndexType, DeviceType, GlobalIndexType >; + using PointArrayType = Containers::Array< PointType, DeviceType, GlobalIndexType >; + using CellSeedArrayType = Containers::Array< CellSeedType, DeviceType, GlobalIndexType >; + using EntityTagsArrayType = Containers::Array< EntityTagType, DeviceType, GlobalIndexType >; template< int Dimension > using EntityTraits = MeshEntityTraits< MeshConfig, DeviceType, Dimension >; @@ -61,6 +73,15 @@ public: using SuperentityTraits = MeshSuperentityTraits< MeshConfig, DeviceType, EntityTopology, Superdimension >; using DimensionTag = Meshes::DimensionTag< meshDimension >; + + // container for storing the subentity indices + using SubentityMatrixType = Matrices::SparseMatrix< bool, Device, GlobalIndexType, Matrices::GeneralMatrix, EllpackSegments >; + + // container for storing the superentity indices + using SuperentityMatrixType = Matrices::SparseMatrix< bool, Device, GlobalIndexType, Matrices::GeneralMatrix, SlicedEllpackSegments >; + + // container for storing the dual graph adjacency matrix + using DualGraph = Matrices::SparseMatrix< bool, Device, GlobalIndexType, Matrices::GeneralMatrix, SlicedEllpackSegments >; }; } // namespace Meshes diff --git a/src/TNL/Meshes/MeshDetails/traits/WeakStorageTraits.h b/src/TNL/Meshes/MeshDetails/traits/WeakStorageTraits.h index 154681fdd52d29376e26d940a7723e56ba99eddc..1b8f481bbdd70b3c182b74a7858b89149624e66b 100644 --- a/src/TNL/Meshes/MeshDetails/traits/WeakStorageTraits.h +++ b/src/TNL/Meshes/MeshDetails/traits/WeakStorageTraits.h @@ -22,24 +22,6 @@ namespace TNL { namespace Meshes { -template< typename MeshConfig, - typename Device, - typename DimensionTag, - bool sensible = (DimensionTag::value <= MeshConfig::meshDimension) > -struct WeakEntityStorageTrait -{ - static constexpr bool storageEnabled = MeshTraits< MeshConfig, Device >::template EntityTraits< DimensionTag::value >::storageEnabled; -}; - -template< typename MeshConfig, - typename Device, - typename DimensionTag > -struct WeakEntityStorageTrait< MeshConfig, Device, DimensionTag, false > -{ - static constexpr bool storageEnabled = false; -}; - - template< typename MeshConfig, typename Device, typename EntityTopology, diff --git a/src/TNL/Meshes/MeshEntity.h b/src/TNL/Meshes/MeshEntity.h index b1c8afea57430ae06a7b59c6d9aba6c495b26017..744febe5f0a0e4960969dada45b8d574ea5b4faa 100644 --- a/src/TNL/Meshes/MeshEntity.h +++ b/src/TNL/Meshes/MeshEntity.h @@ -16,71 +16,46 @@ #pragma once -#include -#include +#include #include -#include -#include -#include namespace TNL { namespace Meshes { -template< typename MeshConfig, typename Device > class Mesh; -template< typename MeshConfig > class Initializer; -template< typename Mesh > class EntityStorageRebinder; -template< typename Mesh, int Dimension > struct IndexPermutationApplier; - template< typename MeshConfig, typename Device, typename EntityTopology_ > class MeshEntity - : protected SubentityAccessLayerFamily< MeshConfig, Device, EntityTopology_ >, - protected SuperentityAccessLayerFamily< MeshConfig, Device, EntityTopology_ >, - public MeshEntityIndex< typename MeshConfig::IdType > { - static_assert( std::is_same< EntityTopology_, typename MeshTraits< MeshConfig, Device >::template EntityTraits< EntityTopology_::dimension >::EntityTopology >::value, + static_assert( std::is_same< EntityTopology_, typename Mesh< MeshConfig, Device >::template EntityTraits< EntityTopology_::dimension >::EntityTopology >::value, "Specified entity topology is not compatible with the MeshConfig." ); public: - using MeshTraitsType = MeshTraits< MeshConfig, Device >; + using MeshType = Mesh< MeshConfig, Device >; using DeviceType = Device; using EntityTopology = EntityTopology_; - using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; - using LocalIndexType = typename MeshTraitsType::LocalIndexType; + using GlobalIndexType = typename MeshType::GlobalIndexType; + using LocalIndexType = typename MeshType::LocalIndexType; + using PointType = typename MeshType::PointType; + using TagType = typename MeshType::MeshTraitsType::EntityTagType; template< int Subdimension > - using SubentityTraits = typename MeshTraitsType::template SubentityTraits< EntityTopology, Subdimension >; + using SubentityTraits = typename MeshType::MeshTraitsType::template SubentityTraits< EntityTopology, Subdimension >; template< int Superdimension > - using SuperentityTraits = typename MeshTraitsType::template SuperentityTraits< EntityTopology, Superdimension >; + using SuperentityTraits = typename MeshType::MeshTraitsType::template SuperentityTraits< EntityTopology, Superdimension >; // constructors - MeshEntity() = default; + MeshEntity() = delete; __cuda_callable__ - MeshEntity( const MeshEntity& entity ); - - template< typename Device_ > - MeshEntity( const MeshEntity< MeshConfig, Device_, EntityTopology >& entity ); + MeshEntity( const MeshType& mesh, const GlobalIndexType index ); __cuda_callable__ - MeshEntity& operator=( const MeshEntity& entity ); + MeshEntity( const MeshEntity& entity ) = default; - template< typename Device_ > __cuda_callable__ - MeshEntity& operator=( const MeshEntity< MeshConfig, Device_, EntityTopology >& entity ); - - - static String getSerializationType(); - - String getSerializationTypeVirtual() const; - - void save( File& file ) const; - - void load( File& file ); - - void print( std::ostream& str ) const; + MeshEntity& operator=( const MeshEntity& entity ) = default; __cuda_callable__ bool operator==( const MeshEntity& entity ) const; @@ -90,128 +65,54 @@ class MeshEntity static constexpr int getEntityDimension(); - /**** - * Subentities - */ - using SubentityAccessLayerFamily< MeshConfig, Device, EntityTopology_ >::getSubentitiesCount; - using SubentityAccessLayerFamily< MeshConfig, Device, EntityTopology_ >::getSubentityIndex; - using SubentityAccessLayerFamily< MeshConfig, Device, EntityTopology_ >::getSubentityOrientation; + __cuda_callable__ + const MeshType& getMesh() const; - /**** - * Superentities - */ - using SuperentityAccessLayerFamily< MeshConfig, Device, EntityTopology_ >::getSuperentitiesCount; - using SuperentityAccessLayerFamily< MeshConfig, Device, EntityTopology_ >::getSuperentityIndex; + __cuda_callable__ + GlobalIndexType getIndex() const; /**** - * Vertices + * Points */ - static constexpr LocalIndexType getVerticesCount(); - - GlobalIndexType getVertexIndex( const LocalIndexType localIndex ) const; + __cuda_callable__ + PointType getPoint() const; - protected: /**** - * Methods for the mesh initialization + * Subentities */ - using SubentityAccessLayerFamily< MeshConfig, Device, EntityTopology_ >::bindSubentitiesStorageNetwork; - using SubentityAccessLayerFamily< MeshConfig, Device, EntityTopology_ >::setSubentityIndex; - using SubentityAccessLayerFamily< MeshConfig, Device, EntityTopology_ >::subentityOrientationsArray; - - using SuperentityAccessLayerFamily< MeshConfig, Device, EntityTopology_ >::bindSuperentitiesStorageNetwork; - using SuperentityAccessLayerFamily< MeshConfig, Device, EntityTopology_ >::setNumberOfSuperentities; - using SuperentityAccessLayerFamily< MeshConfig, Device, EntityTopology_ >::setSuperentityIndex; - - friend Initializer< MeshConfig >; - - friend EntityStorageRebinder< Mesh< MeshConfig, DeviceType > >; - - template< typename Mesh, int Dimension > - friend struct IndexPermutationApplier; -}; - -/**** - * Vertex entity specialization - */ -template< typename MeshConfig, typename Device > -class MeshEntity< MeshConfig, Device, Topologies::Vertex > - : protected SuperentityAccessLayerFamily< MeshConfig, Device, Topologies::Vertex >, - public MeshEntityIndex< typename MeshConfig::IdType > -{ - public: - using MeshTraitsType = MeshTraits< MeshConfig, Device >; - using DeviceType = Device; - using EntityTopology = Topologies::Vertex; - using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; - using LocalIndexType = typename MeshTraitsType::LocalIndexType; - using PointType = typename MeshTraitsType::PointType; - - template< int Superdimension > - using SuperentityTraits = typename MeshTraitsType::template SuperentityTraits< EntityTopology, Superdimension >; - - // constructors - MeshEntity() = default; - - __cuda_callable__ - MeshEntity( const MeshEntity& entity ); - - template< typename Device_ > - MeshEntity( const MeshEntity< MeshConfig, Device_, EntityTopology >& entity ); - - __cuda_callable__ - MeshEntity& operator=( const MeshEntity& entity ); - - template< typename Device_ > + template< int Subdimension > __cuda_callable__ - MeshEntity& operator=( const MeshEntity< MeshConfig, Device_, EntityTopology >& entity ); - - - static String getSerializationType(); - - String getSerializationTypeVirtual() const; - - void save( File& file ) const; - - void load( File& file ); - - void print( std::ostream& str ) const; + LocalIndexType getSubentitiesCount() const; + template< int Subdimension > __cuda_callable__ - bool operator==( const MeshEntity& entity ) const; + GlobalIndexType getSubentityIndex( const LocalIndexType localIndex ) const; + template< int Subdimension > __cuda_callable__ - bool operator!=( const MeshEntity& entity ) const; - - static constexpr int getEntityDimension(); + const typename SubentityTraits< Subdimension >::OrientationArrayType& + getSubentityOrientation( const LocalIndexType localIndex ) const; /**** * Superentities */ - using SuperentityAccessLayerFamily< MeshConfig, Device, Topologies::Vertex >::getSuperentitiesCount; - using SuperentityAccessLayerFamily< MeshConfig, Device, Topologies::Vertex >::getSuperentityIndex; + template< int Superdimension > + __cuda_callable__ + LocalIndexType getSuperentitiesCount() const; - /**** - * Points - */ + template< int Superdimension > __cuda_callable__ - PointType getPoint() const; + GlobalIndexType getSuperentityIndex( const LocalIndexType localIndex ) const; + /**** + * Tags + */ __cuda_callable__ - void setPoint( const PointType& point ); + TagType getTag() const; protected: - using SuperentityAccessLayerFamily< MeshConfig, Device, Topologies::Vertex >::bindSuperentitiesStorageNetwork; - using SuperentityAccessLayerFamily< MeshConfig, Device, Topologies::Vertex >::setNumberOfSuperentities; - using SuperentityAccessLayerFamily< MeshConfig, Device, Topologies::Vertex >::setSuperentityIndex; - - PointType point; - - friend Initializer< MeshConfig >; - - friend EntityStorageRebinder< Mesh< MeshConfig, DeviceType > >; - - template< typename Mesh, int Dimension > - friend struct IndexPermutationApplier; + const MeshType* meshPointer = nullptr; + GlobalIndexType index = 0; }; template< typename MeshConfig, @@ -222,4 +123,4 @@ std::ostream& operator<<( std::ostream& str, const MeshEntity< MeshConfig, Devic } // namespace Meshes } // namespace TNL -#include +#include diff --git a/src/TNL/Meshes/MeshEntity.hpp b/src/TNL/Meshes/MeshEntity.hpp new file mode 100644 index 0000000000000000000000000000000000000000..36ced6134e18445413e0b484b9204f94754c5263 --- /dev/null +++ b/src/TNL/Meshes/MeshEntity.hpp @@ -0,0 +1,187 @@ +/*************************************************************************** + MeshEntity.hpp - description + ------------------- + begin : Sep 8, 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 Device, + typename EntityTopology > +__cuda_callable__ +MeshEntity< MeshConfig, Device, EntityTopology >:: +MeshEntity( const MeshType& mesh, const GlobalIndexType index ) +: meshPointer( &mesh ), + index( index ) +{ +} + +template< typename MeshConfig, + typename Device, + typename EntityTopology > +__cuda_callable__ +bool +MeshEntity< MeshConfig, Device, EntityTopology >:: +operator==( const MeshEntity& entity ) const +{ + return getIndex() == entity.getIndex(); +} + +template< typename MeshConfig, + typename Device, + typename EntityTopology > +__cuda_callable__ +bool +MeshEntity< MeshConfig, Device, EntityTopology >:: +operator!=( const MeshEntity& entity ) const +{ + return ! ( *this == entity ); +} + +template< typename MeshConfig, + typename Device, + typename EntityTopology > +constexpr int +MeshEntity< MeshConfig, Device, EntityTopology >:: +getEntityDimension() +{ + return EntityTopology::dimension; +} + +template< typename MeshConfig, + typename Device, + typename EntityTopology > +__cuda_callable__ +const Mesh< MeshConfig, Device >& +MeshEntity< MeshConfig, Device, EntityTopology >:: +getMesh() const +{ + return *meshPointer; +} + +template< typename MeshConfig, + typename Device, + typename EntityTopology > +__cuda_callable__ +typename Mesh< MeshConfig, Device >::GlobalIndexType +MeshEntity< MeshConfig, Device, EntityTopology >:: +getIndex() const +{ + return index; +} + +template< typename MeshConfig, + typename Device, + typename EntityTopology > +__cuda_callable__ +typename MeshEntity< MeshConfig, Device, EntityTopology >::PointType +MeshEntity< MeshConfig, Device, EntityTopology >:: +getPoint() const +{ + static_assert( getEntityDimension() == 0, "getPoint() can be used only on vertices" ); + return meshPointer->getPoint( getIndex() ); +} + +template< typename MeshConfig, + typename Device, + typename EntityTopology > + template< int Subdimension > +__cuda_callable__ +typename MeshEntity< MeshConfig, Device, EntityTopology >::LocalIndexType +MeshEntity< MeshConfig, Device, EntityTopology >:: +getSubentitiesCount() const +{ + TNL_ASSERT_TRUE( meshPointer, "meshPointer was not set" ); + return meshPointer->template getSubentitiesCount< getEntityDimension(), Subdimension >( this->getIndex() ); +} + +template< typename MeshConfig, + typename Device, + typename EntityTopology > + template< int Subdimension > +__cuda_callable__ +typename MeshEntity< MeshConfig, Device, EntityTopology >::GlobalIndexType +MeshEntity< MeshConfig, Device, EntityTopology >:: +getSubentityIndex( const LocalIndexType localIndex ) const +{ + TNL_ASSERT_TRUE( meshPointer, "meshPointer was not set" ); + return meshPointer->template getSubentityIndex< getEntityDimension(), Subdimension >( this->getIndex(), localIndex ); +} + +template< typename MeshConfig, + typename Device, + typename EntityTopology > + template< int Subdimension > +__cuda_callable__ +const typename MeshEntity< MeshConfig, Device, EntityTopology >::template SubentityTraits< Subdimension >::OrientationArrayType& +MeshEntity< MeshConfig, Device, EntityTopology >:: +getSubentityOrientation( const LocalIndexType localIndex ) const +{ + TNL_ASSERT_TRUE( meshPointer, "meshPointer was not set" ); + return meshPointer->template getSubentityOrientation< getEntityDimension(), Subdimension >( this->getIndex(), localIndex ); +} + +template< typename MeshConfig, + typename Device, + typename EntityTopology > + template< int Superdimension > +__cuda_callable__ +typename MeshEntity< MeshConfig, Device, EntityTopology >::LocalIndexType +MeshEntity< MeshConfig, Device, EntityTopology >:: +getSuperentitiesCount() const +{ + TNL_ASSERT_TRUE( meshPointer, "meshPointer was not set" ); + return meshPointer->template getSuperentitiesCount< getEntityDimension(), Superdimension >( this->getIndex() ); +} + +template< typename MeshConfig, + typename Device, + typename EntityTopology > + template< int Superdimension > +__cuda_callable__ +typename MeshEntity< MeshConfig, Device, EntityTopology >::GlobalIndexType +MeshEntity< MeshConfig, Device, EntityTopology >:: +getSuperentityIndex( const LocalIndexType localIndex ) const +{ + TNL_ASSERT_TRUE( meshPointer, "meshPointer was not set" ); + return meshPointer->template getSuperentityIndex< getEntityDimension(), Superdimension >( this->getIndex(), localIndex ); +} + +template< typename MeshConfig, + typename Device, + typename EntityTopology > +__cuda_callable__ +typename MeshEntity< MeshConfig, Device, EntityTopology >::TagType +MeshEntity< MeshConfig, Device, EntityTopology >:: +getTag() const +{ + TNL_ASSERT_TRUE( meshPointer, "meshPointer was not set" ); + return meshPointer->template getEntityTag< getEntityDimension() >( this->getIndex() ); +} + +template< typename MeshConfig, + typename Device, + typename EntityTopology > +std::ostream& operator<<( std::ostream& str, const MeshEntity< MeshConfig, Device, EntityTopology >& entity ) +{ + return str << getType< decltype(entity) >() << "( , " << entity.getIndex() << " )"; +} + +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/Readers/EntityShape.h b/src/TNL/Meshes/Readers/EntityShape.h deleted file mode 100644 index c2c39eae53352568bd3f6d5208f5446eb6e85e57..0000000000000000000000000000000000000000 --- a/src/TNL/Meshes/Readers/EntityShape.h +++ /dev/null @@ -1,133 +0,0 @@ -/*************************************************************************** - EntityShape.h - description - ------------------- - begin : Nov 22, 2016 - copyright : (C) 2016 by Tomas Oberhuber et al. - email : tomas.oberhuber@fjfi.cvut.cz - ***************************************************************************/ - -/* See Copyright Notice in tnl/Copyright */ - -#pragma once - -#include - -#include -#include -#include -#include -#include - -namespace TNL { -namespace Meshes { -namespace Readers { - -/* - * Enumeration of entity shapes, inspired by the VTK library. - */ -enum class EntityShape -{ - Vertex = 1, - PolyVertex = 2, - Line = 3, - PolyLine = 4, - Triangle = 5, - TriangleStrip = 6, - Polygon = 7, - Pixel = 8, - Quad = 9, - Tetra = 10, - Voxel = 11, - Hexahedron = 12, - Wedge = 13, - Pyramid = 14 -}; - -inline std::ostream& operator<<( std::ostream& str, EntityShape shape ) -{ - switch( shape ) - { - case EntityShape::Vertex: - str << "Entity::Vertex"; - break; - case EntityShape::PolyVertex: - str << "Entity::PolyVertex"; - break; - case EntityShape::Line: - str << "Entity::Line"; - break; - case EntityShape::PolyLine: - str << "Entity::PolyLine"; - break; - case EntityShape::Triangle: - str << "Entity::Triangle"; - break; - case EntityShape::TriangleStrip: - str << "Entity::TriangleStrip"; - break; - case EntityShape::Polygon: - str << "Entity::Polygon"; - break; - case EntityShape::Pixel: - str << "Entity::Pixel"; - break; - case EntityShape::Quad: - str << "Entity::Quad"; - break; - case EntityShape::Tetra: - str << "Entity::Tetra"; - break; - case EntityShape::Voxel: - str << "Entity::Voxel"; - break; - case EntityShape::Hexahedron: - str << "Entity::Hexahedron"; - break; - case EntityShape::Wedge: - str << "Entity::Wedge"; - break; - case EntityShape::Pyramid: - str << "Entity::Pyramid"; - break; - default: - str << ""; - } - return str; -} - -inline int getEntityDimension( EntityShape shape ) -{ - switch( shape ) - { - case EntityShape::Vertex: return 0; - case EntityShape::PolyVertex: return 0; - case EntityShape::Line: return 1; - case EntityShape::PolyLine: return 1; - case EntityShape::Triangle: return 2; - case EntityShape::TriangleStrip: return 2; - case EntityShape::Polygon: return 2; - case EntityShape::Pixel: return 2; - case EntityShape::Quad: return 2; - case EntityShape::Tetra: return 3; - case EntityShape::Voxel: return 3; - case EntityShape::Hexahedron: return 3; - case EntityShape::Wedge: return 3; - case EntityShape::Pyramid: return 3; - } - // this just avoids a compiler warning in GCC and nvcc (clang actually knows if the - // switch above covers all cases, and print a warning only when it does not) - throw 1; -} - -// static mapping of TNL entity topologies to EntityShape -template< typename Topology > struct TopologyToEntityShape {}; -template<> struct TopologyToEntityShape< Topologies::Vertex > { static constexpr EntityShape shape = EntityShape::Vertex; }; -template<> struct TopologyToEntityShape< Topologies::Edge > { static constexpr EntityShape shape = EntityShape::Line; }; -template<> struct TopologyToEntityShape< Topologies::Triangle > { static constexpr EntityShape shape = EntityShape::Triangle; }; -template<> struct TopologyToEntityShape< Topologies::Quadrilateral > { static constexpr EntityShape shape = EntityShape::Quad; }; -template<> struct TopologyToEntityShape< Topologies::Tetrahedron > { static constexpr EntityShape shape = EntityShape::Tetra; }; -template<> struct TopologyToEntityShape< Topologies::Hexahedron > { static constexpr EntityShape shape = EntityShape::Hexahedron; }; - -} // namespace Readers -} // namespace Meshes -} // namespace TNL diff --git a/src/TNL/Meshes/Readers/MeshReader.h b/src/TNL/Meshes/Readers/MeshReader.h new file mode 100644 index 0000000000000000000000000000000000000000..abf550510646e22cf268ac93faa9da087f3e9240 --- /dev/null +++ b/src/TNL/Meshes/Readers/MeshReader.h @@ -0,0 +1,227 @@ +/*************************************************************************** + MeshReader.h - description + ------------------- + begin : Apr 10, 2020 + copyright : (C) 2020 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +// Implemented by: Jakub Klinkovský + +#pragma once + +#include +#include +#include + +#include +#include + +namespace TNL { +namespace Meshes { +namespace Readers { + +struct MeshReaderError +: public std::runtime_error +{ + MeshReaderError( std::string readerName, std::string msg ) + : std::runtime_error( readerName + " error: " + msg ) + {} +}; + +class MeshReader +{ +public: + using VariantVector = mpark::variant< std::vector< std::int8_t >, + std::vector< std::uint8_t >, + std::vector< std::int16_t >, + std::vector< std::uint16_t >, + std::vector< std::int32_t >, + std::vector< std::uint32_t >, + std::vector< std::int64_t >, + std::vector< std::uint64_t >, + std::vector< float >, + std::vector< double > >; + + MeshReader() = default; + + MeshReader( const std::string& fileName ) + : fileName( fileName ) + {} + + virtual ~MeshReader() {} + + void setFileName( const std::string& fileName ) + { + reset(); + this->fileName = fileName; + } + + /** + * \brief This method resets the reader to an empty state. + * + * In particular, implementations should call the \ref resetBase method to + * reset the arrays holding the intermediate mesh representation. + */ + virtual void reset() + { + resetBase(); + } + + /** + * \brief Main method responsible for reading the mesh file. + * + * The implementation has to set all protected attributes of this class such + * that the mesh representation can be loaded into the mesh object by the + * \ref loadMesh method. + */ + virtual void detectMesh() = 0; + + /** + * \brief Method which loads the intermediate mesh representation into a + * mesh object. + * + * When the method exits, the intermediate mesh representation is destroyed + * to save memory. However, depending on the specific file format, the mesh + * file may remain open so that the user can load additional data. + */ + template< typename MeshType > + void loadMesh( MeshType& mesh ) + { + // check that detectMesh has been called + if( meshType == "" ) + detectMesh(); + + // check that the cell shape mathes + const VTK::EntityShape meshCellShape = VTK::TopologyToEntityShape< typename MeshType::template EntityTraits< MeshType::getMeshDimension() >::EntityTopology >::shape; + if( meshCellShape != cellShape ) + throw MeshReaderError( "MeshReader", "the mesh cell shape " + VTK::getShapeName(meshCellShape) + " does not match the shape " + + "of cells used in the file (" + VTK::getShapeName(cellShape) + ")" ); + + using MeshBuilder = MeshBuilder< MeshType >; + using IndexType = typename MeshType::GlobalIndexType; + using PointType = typename MeshType::PointType; + using CellSeedType = typename MeshBuilder::CellSeedType; + + MeshBuilder meshBuilder; + meshBuilder.setPointsCount( NumberOfPoints ); + meshBuilder.setCellsCount( NumberOfCells ); + + // assign points + visit( [&meshBuilder](auto&& array) { + PointType p; + std::size_t i = 0; + for( auto c : array ) { + int dim = i++ % 3; + if( dim >= PointType::getSize() ) + continue; + p[dim] = c; + if( dim == PointType::getSize() - 1 ) + meshBuilder.setPoint( (i - 1) / 3, p ); + } + }, + pointsArray + ); + + // assign cells + visit( [this, &meshBuilder](auto&& connectivity) { + // let's just assume that the connectivity and offsets arrays have the same type... + using mpark::get; + const auto& offsets = get< std::decay_t >( offsetsArray ); + std::size_t offsetStart = 0; + for( std::size_t i = 0; i < NumberOfCells; i++ ) { + CellSeedType& seed = meshBuilder.getCellSeed( i ); + const std::size_t offsetEnd = offsets[ i ]; + for( std::size_t o = offsetStart; o < offsetEnd; o++ ) + seed.setCornerId( o - offsetStart, connectivity[ o ] ); + offsetStart = offsetEnd; + } + }, + connectivityArray + ); + + // reset arrays since they are not needed anymore + pointsArray = connectivityArray = offsetsArray = typesArray = {}; + + if( ! meshBuilder.build( mesh ) ) + throw MeshReaderError( "VTKReader", "MeshBuilder failed" ); + } + + std::string + getMeshType() const + { + return meshType; + } + + int + getMeshDimension() const + { + return meshDimension; + } + + int + getWorldDimension() const + { + return worldDimension; + } + + VTK::EntityShape + getCellShape() const + { + return cellShape; + } + + std::string + getRealType() const + { + return pointsType; + } + + std::string + getGlobalIndexType() const + { + return connectivityType; + } + + std::string + getLocalIndexType() const + { + // not stored in any file format + return "short int"; + } + +protected: + // input file name + std::string fileName; + + // type of the mesh (either Meshes::Grid or Meshes::Mesh or Meshes::DistributedMesh) + // (it is also an indicator that detectMesh has been successfully called) + std::string meshType; + + // attributes of the mesh + std::size_t NumberOfPoints, NumberOfCells; + int meshDimension, worldDimension; + VTK::EntityShape cellShape = VTK::EntityShape::Vertex; + + // intermediate representation of the unstructured mesh (matches the VTU + // file format, other formats have to be converted) + VariantVector pointsArray, connectivityArray, offsetsArray, typesArray; + // string representation of each array's value type + std::string pointsType, connectivityType, offsetsType, typesType; + + void resetBase() + { + meshType = ""; + NumberOfPoints = NumberOfCells = 0; + meshDimension = worldDimension = 0; + cellShape = VTK::EntityShape::Vertex; + pointsArray = connectivityArray = offsetsArray = typesArray = {}; + pointsType = connectivityType = offsetsType = typesType = ""; + } +}; + +} // namespace Readers +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/Readers/NetgenReader.h b/src/TNL/Meshes/Readers/NetgenReader.h index f781d3da4067a799b1ddcfbff7cd47f210c70b94..3cd69a1aba36e8991566f8d473bf4f295e5b9c94 100644 --- a/src/TNL/Meshes/Readers/NetgenReader.h +++ b/src/TNL/Meshes/Readers/NetgenReader.h @@ -10,291 +10,151 @@ /*** * Authors: - * Oberhuber Tomas, tomas.oberhuber@fjfi.cvut.cz - * Zabka Vitezslav, zabkav@gmail.com + * Tomas Oberhuber + * Vitezslav Zabka + * Jakub Klinkovsky */ #pragma once #include -#include #include -#include -#include +#include namespace TNL { namespace Meshes { namespace Readers { class NetgenReader +: public MeshReader { public: - bool detectMesh( const String& fileName ) - { - this->reset(); - this->fileName = fileName; - - std::ifstream inputFile( fileName.getString() ); - if( ! inputFile ) - { - std::cerr << "I am not able to open the file " << fileName << "." << std::endl; - return false; - } - - std::string line; - std::istringstream iss; - - /**** - * Skip whitespaces - */ - inputFile >> std::ws; - - /**** - * Skip number of vertices - */ - if( ! inputFile ) - return false; - getline( inputFile, line ); - iss.str( line ); - long int numberOfVertices; - iss >> numberOfVertices; - - //cout << "There are " << numberOfVertices << " vertices." << std::endl; - - /**** - * Read the first vertex and compute number of components - */ - if( ! inputFile ) - return false; - getline( inputFile, line ); - iss.clear(); - iss.str( line ); - meshDimension = worldDimension = -1; - while( iss ) - { - double aux; - iss >> aux; - meshDimension = ++worldDimension; - } - - /**** - * Skip vertices - */ - long int verticesRead( 1 ); - while( verticesRead < numberOfVertices ) - { - getline( inputFile, line ); - if( ! inputFile ) - { - std::cerr << "The mesh file " << fileName << " is probably corrupted, some vertices are missing." << std::endl; - return false; - } - verticesRead++; - } - - /**** - * Skip whitespaces - */ - inputFile >> std::ws; - - /**** - * Get number of cells - */ - long int numberOfCells; - getline( inputFile, line ); - iss.clear(); - iss.str( line ); - iss >> numberOfCells; - //cout << "There are " << numberOfCells << " cells." << std::endl; - - /**** - * Get number of vertices in a cell - */ - getline( inputFile, line ); - iss.clear(); - iss.str( line ); - int verticesInCell = -2; - while( iss ) - { - int aux; - iss >> aux; - verticesInCell++; - } - //cout << "There are " << verticesInCell << " vertices in cell ..." << std::endl; - - if( meshDimension == 1 && verticesInCell == 2 ) - cellShape = EntityShape::Line; - else if( meshDimension == 2 ) { - if( verticesInCell == 3 ) - cellShape = EntityShape::Triangle; - else if( verticesInCell == 4 ) - cellShape = EntityShape::Quad; - } - else if( meshDimension == 3 ) { - if( verticesInCell == 4 ) - cellShape = EntityShape::Tetra; - else if( verticesInCell == 8 ) - cellShape = EntityShape::Hexahedron; - } - if( cellShape == EntityShape::Vertex ) { - std::cerr << "Unknown cell topology: mesh dimension is " << meshDimension << ", number of vertices in cells is " << verticesInCell << "." << std::endl; - return false; - } + NetgenReader() = default; - return true; - } + NetgenReader( const std::string& fileName ) + : MeshReader( fileName ) + {} - template< typename MeshType > - static bool readMesh( const String& fileName, MeshType& mesh ) + virtual void detectMesh() override { - typedef typename MeshType::PointType PointType; - typedef MeshBuilder< MeshType > MeshBuilder; - - const int dimension = PointType::getSize(); + reset(); - std::ifstream inputFile( fileName.getString() ); + std::ifstream inputFile( fileName ); if( ! inputFile ) - { - std::cerr << "I am not able to open the file " << fileName << "." << std::endl; - return false; - } + throw MeshReaderError( "NetgenReader", "failed to open the file '" + fileName + "'." ); - MeshBuilder meshBuilder; std::string line; std::istringstream iss; - /**** - * Skip white spaces - */ + // skip whitespace inputFile >> std::ws; - - /**** - * Read the number of vertices - */ if( ! inputFile ) - return false; + throw MeshReaderError( "NetgenReader", "unexpected error when reading the file '" + fileName + "'." ); + + // read number of points getline( inputFile, line ); iss.str( line ); - typedef typename MeshType::Config::GlobalIndexType VertexIndexType; - VertexIndexType pointsCount; - iss >> pointsCount; - meshBuilder.setPointsCount( pointsCount ); - - for( VertexIndexType i = 0; i < pointsCount; i++ ) - { + iss >> NumberOfPoints; + + // real type is not stored in Netgen files + pointsType = "double"; + // global index type is not stored in Netgen files + connectivityType = offsetsType = "std::int32_t"; + // only std::uint8_t makes sense for entity types + typesType = "std::uint8_t"; + + // arrays holding the data from the file + std::vector< double > pointsArray; + std::vector< std::int32_t > connectivityArray, offsetsArray; + std::vector< std::uint8_t > typesArray; + + // read points + worldDimension = 0; + for( std::size_t pointIndex = 0; pointIndex < NumberOfPoints; pointIndex++ ) { + if( ! inputFile ) { + reset(); + throw MeshReaderError( "NetgenReader", "unable to read enough vertices, the file may be invalid or corrupted." ); + } getline( inputFile, line ); + + // read the coordinates and compute the world dimension iss.clear(); iss.str( line ); - PointType p; - for( int d = 0; d < dimension; d++ ) - iss >> p[ d ]; - //cout << "Setting point number " << i << " of " << pointsCount << std::endl; - meshBuilder.setPoint( i, p ); - //const PointType& point = mesh.getVertex( i ).getPoint(); + for( int i = 0; i < 3; i++ ) { + double aux; + iss >> aux; + if( ! iss ) { + // the intermediate mesh representation uses the VTK convention - all points must have 3 coordinates + aux = 0; + } + if( aux != 0.0 ) + worldDimension = std::max( worldDimension, i + 1 ); + pointsArray.push_back( aux ); + } } - /**** - * Skip white spaces - */ + // netgen supports only triangular and tetrahedral meshes + meshDimension = worldDimension; + if( meshDimension == 1 ) + cellShape = VTK::EntityShape::Line; + else if( meshDimension == 2 ) + cellShape = VTK::EntityShape::Triangle; + else if( meshDimension == 3 ) + cellShape = VTK::EntityShape::Tetra; + else + throw MeshReaderError( "NetgenReader", "unsupported mesh dimension: " + std::to_string(meshDimension) ); + + // skip whitespace inputFile >> std::ws; - - /**** - * Read number of cells - */ - typedef typename MeshType::Config::GlobalIndexType CellIndexType; if( ! inputFile ) - { - std::cerr << "I cannot read the mesh cells." << std::endl; - return false; - } + throw MeshReaderError( "NetgenReader", "unexpected error when reading the file '" + fileName + "'." ); + + // read number of cells getline( inputFile, line ); iss.clear(); iss.str( line ); - CellIndexType numberOfCells = atoi( line.data() ); - //iss >> numberOfCells; // TODO: I do not know why this does not work - meshBuilder.setCellsCount( numberOfCells ); - for( CellIndexType i = 0; i < numberOfCells; i++ ) - { + iss >> NumberOfCells; + + // read cells + for( std::size_t cellIndex = 0; cellIndex < NumberOfCells; cellIndex++ ) { + if( ! inputFile ) { + reset(); + throw MeshReaderError( "NetgenReader", "unable to read enough cells, the file may be invalid or corrupted." + " (cellIndex = " + std::to_string(cellIndex) + ")" ); + } getline( inputFile, line ); + iss.clear(); iss.str( line ); - int subdomainIndex; - iss >> subdomainIndex; - //cout << "Setting cell number " << i << " of " << numberOfCells << std::endl; - typedef typename MeshBuilder::CellSeedType CellSeedType; - for( int cellVertex = 0; cellVertex < CellSeedType::getCornersCount(); cellVertex++ ) - { - VertexIndexType vertexIdx; - iss >> vertexIdx; - meshBuilder.getCellSeed( i ).setCornerId( cellVertex, vertexIdx - 1 ); + // skip subdomain number + int subdomain; + iss >> subdomain; + for( int v = 0; v <= meshDimension; v++ ) { + std::size_t vid; + iss >> vid; + if( ! iss ) { + reset(); + throw MeshReaderError( "NetgenReader", "unable to read enough cells, the file may be invalid or corrupted." + " (cellIndex = " + std::to_string(cellIndex) + ", subvertex = " + std::to_string(v) + ")" ); + } + // convert point index from 1-based to 0-based + connectivityArray.push_back( vid - 1 ); } + offsetsArray.push_back( connectivityArray.size() ); } - meshBuilder.build( mesh ); - return true; - } - String - getMeshType() const - { - return "Meshes::Mesh"; - } + // set cell types + typesArray.resize( NumberOfCells, (std::uint8_t) cellShape ); - int getMeshDimension() const - { - return this->meshDimension; - } - - int - getWorldDimension() const - { - return worldDimension; - } - - EntityShape - getCellShape() const - { - return cellShape; - } - - String - getRealType() const - { - // not stored in the Netgen file - return "float"; - } + // set the arrays to the base class + this->pointsArray = std::move(pointsArray); + this->connectivityArray = std::move(connectivityArray); + this->offsetsArray = std::move(offsetsArray); + this->typesArray = std::move(typesArray); - String - getGlobalIndexType() const - { - // not stored in the Netgen file - return "int"; - } - - String - getLocalIndexType() const - { - // not stored in the Netgen file - return "short int"; - } - - String - getIdType() const - { - // not stored in the Netgen file - return "int"; - } - -protected: - String fileName; - int meshDimension, worldDimension; - EntityShape cellShape = EntityShape::Vertex; - - void reset() - { - fileName = ""; - meshDimension = worldDimension = 0; - cellShape = EntityShape::Vertex; + // indicate success by setting the mesh type + meshType = "Meshes::Mesh"; } }; diff --git a/src/TNL/Meshes/Readers/PVTUReader.h b/src/TNL/Meshes/Readers/PVTUReader.h new file mode 100644 index 0000000000000000000000000000000000000000..666aa4f453478c260240fb45a1dc78d8698ce39e --- /dev/null +++ b/src/TNL/Meshes/Readers/PVTUReader.h @@ -0,0 +1,238 @@ +/*************************************************************************** + PVTUReader.h - description + ------------------- + begin : Apr 10, 2020 + copyright : (C) 2020 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +// Implemented by: Jakub Klinkovský + +#pragma once + +#include + +#include +#include +#include + +namespace TNL { +namespace Meshes { +namespace Readers { + +class PVTUReader +: public XMLVTK +{ + std::string + getSourcePath( std::string source ) + { + namespace fs = std::experimental::filesystem; + return fs::path(fileName).parent_path() / source; + } + +#ifdef HAVE_TINYXML2 + void readParallelUnstructuredGrid() + { + using namespace tinyxml2; + + // read GhostLevel attribute + ghostLevels = getAttributeInteger( datasetElement, "GhostLevel" ); + // read MinCommonVertices attribute (TNL-specific, optional) + minCommonVertices = getAttributeInteger( datasetElement, "MinCommonVertices", 0 ); + + // read points info + const XMLElement* points = getChildSafe( datasetElement, "PPoints" ); + const XMLElement* pointsData = verifyHasOnlyOneChild( points, "PDataArray" ); + verifyDataArray( pointsData, "PDataArray" ); + const std::string pointsDataName = getAttributeString( pointsData, "Name" ); + if( pointsDataName != "Points" ) + throw MeshReaderError( "PVTUReader", "the tag does not contain a with Name=\"Points\" attribute" ); + pointsType = VTKDataTypes.at( getAttributeString( pointsData, "type" ) ); + + // read pieces info + const XMLElement* piece = getChildSafe( datasetElement, "Piece" ); + while( piece ) { + const std::string source = getAttributeString( piece, "Source" ); + if( source != "" ) { + pieceSources.push_back( getSourcePath( source ) ); + } + else + throw MeshReaderError( "PVTUReader", "the Source attribute of a element was found empty." ); + // find next + piece = piece->NextSiblingElement( "Piece" ); + } + if( pieceSources.size() == 0 ) + throw MeshReaderError( "PVTUReader", "the file does not contain any element." ); + + // check that the number of pieces matches the number of MPI ranks + const int nproc = CommunicatorType::GetSize( group ); + if( (int) pieceSources.size() != nproc ) + throw MeshReaderError( "PVTUReader", "the number of subdomains does not match the number of MPI ranks (" + + std::to_string(pieceSources.size()) + " vs " + std::to_string(nproc) + ")." ); + + // read the local piece source + const int rank = CommunicatorType::GetRank( group ); + localReader.setFileName( pieceSources[ rank ] ); + localReader.detectMesh(); + + // copy attributes from the local reader + worldDimension = localReader.getWorldDimension(); + meshDimension = localReader.getMeshDimension(); + cellShape = localReader.getCellShape(); + pointsType = localReader.getRealType(); + connectivityType = offsetsType = localReader.getGlobalIndexType(); + typesType = "std::uint8_t"; + + // TODO: assert that all MPI ranks have the same attributes + + if( ghostLevels > 0 ) { + // load the vtkGhostType arrays from PointData and CellData + pointTags = localReader.readPointData( VTK::ghostArrayName() ); + cellTags = localReader.readCellData( VTK::ghostArrayName() ); + + // load the GlobalIndex arrays from PointData and CellData + pointGlobalIndices = localReader.readPointData( "GlobalIndex" ); + cellGlobalIndices = localReader.readCellData( "GlobalIndex" ); + } + } +#endif + +public: + using CommunicatorType = Communicators::MpiCommunicator; + using CommunicationGroup = typename CommunicatorType::CommunicationGroup; + + PVTUReader() = default; + + PVTUReader( const std::string& fileName, CommunicationGroup group = CommunicatorType::AllGroup ) + : XMLVTK( fileName ), group( group ) + {} + + virtual void detectMesh() override + { +#ifdef HAVE_TINYXML2 + reset(); + try { + openVTKFile(); + } + catch( const MeshReaderError& ) { + reset(); + throw; + } + + // verify file type + if( fileType == "PUnstructuredGrid" ) + readParallelUnstructuredGrid(); + else + throw MeshReaderError( "PVTUReader", "the reader cannot read data of the type " + fileType + ". Use a different reader if possible." ); + + // indicate success by setting the mesh type + meshType = "Meshes::DistributedMesh"; +#else + throw_no_tinyxml(); +#endif + } + + template< typename MeshType > + void loadMesh( MeshType& mesh ) + { + // check that detectMesh has been called + if( meshType == "" ) + detectMesh(); + + // load the local mesh + auto& localMesh = mesh.getLocalMesh(); + localReader.loadMesh( localMesh ); + + using Index = typename MeshType::GlobalIndexType; + const Index pointsCount = mesh.getLocalMesh().template getEntitiesCount< 0 >(); + const Index cellsCount = mesh.getLocalMesh().template getEntitiesCount< MeshType::getMeshDimension() >(); + + // set ghost levels + mesh.setGhostLevels( ghostLevels ); + // check MinCommonVertices + if( minCommonVertices > 0 && minCommonVertices != MeshType::Config::dualGraphMinCommonVertices ) + std::cerr << "WARNING: the mesh was decomposed with different MinCommonVertices value than the value set in the mesh configuration " + "(" << minCommonVertices << " vs " << MeshType::Config::dualGraphMinCommonVertices << ")." << std::endl; + + if( ghostLevels > 0 ) { + // assign point ghost tags + using mpark::get; + const std::vector pointTags = get< std::vector >( this->pointTags ); + if( (Index) pointTags.size() != pointsCount ) + throw MeshReaderError( "PVTUReader", "the vtkGhostType array in PointData has wrong size: " + std::to_string(pointTags.size()) ); + mesh.vtkPointGhostTypes() = pointTags; + for( Index i = 0; i < pointsCount; i++ ) + if( pointTags[ i ] & (std::uint8_t) VTK::PointGhostTypes::DUPLICATEPOINT ) + localMesh.template addEntityTag< 0 >( i, EntityTags::GhostEntity ); + + // assign cell ghost tags + using mpark::get; + const std::vector cellTags = get< std::vector >( this->cellTags ); + if( (Index) cellTags.size() != cellsCount ) + throw MeshReaderError( "PVTUReader", "the vtkGhostType array in CellData has wrong size: " + std::to_string(cellTags.size()) ); + mesh.vtkCellGhostTypes() = cellTags; + for( Index i = 0; i < cellsCount; i++ ) { + if( cellTags[ i ] & (std::uint8_t) VTK::CellGhostTypes::DUPLICATECELL ) + localMesh.template addEntityTag< MeshType::getMeshDimension() >( i, EntityTags::GhostEntity ); + } + + // update the entity tags layers after setting ghost indices + mesh.getLocalMesh().template updateEntityTagsLayer< 0 >(); + mesh.getLocalMesh().template updateEntityTagsLayer< MeshType::getMeshDimension() >(); + + // assign global indices + auto& points_indices = mesh.template getGlobalIndices< 0 >(); + auto& cells_indices = mesh.template getGlobalIndices< MeshType::getMeshDimension() >(); + auto assign_variant_vector = [] ( auto& array, const VariantVector& variant_vector, Index expectedSize ) + { + using mpark::visit; + visit( [&array, expectedSize](auto&& vector) { + if( (Index) vector.size() != expectedSize ) + throw MeshReaderError( "PVTUReader", "the GlobalIndex array has wrong size: " + std::to_string(vector.size()) + + " (expected " + std::to_string(expectedSize) + ")." ); + array.setSize( vector.size() ); + std::size_t idx = 0; + for( auto v : vector ) + array[ idx++ ] = v; + }, + variant_vector + ); + }; + assign_variant_vector( points_indices, pointGlobalIndices, pointsCount ); + assign_variant_vector( cells_indices, cellGlobalIndices, cellsCount ); + } + + // reset arrays since they are not needed anymore + this->pointTags = this->cellTags = pointGlobalIndices = cellGlobalIndices = {}; + + // set the communication group + mesh.setCommunicationGroup( group ); + } + + virtual void reset() override + { + resetBase(); + ghostLevels = 0; + pieceSources = {}; + localReader.reset(); + pointTags = cellTags = pointGlobalIndices = cellGlobalIndices = {}; + } + +protected: + CommunicationGroup group; + + int ghostLevels = 0; + int minCommonVertices = 0; + std::vector pieceSources; + + VTUReader localReader; + + // additinal arrays we need to read from the localReader + VariantVector pointTags, cellTags, pointGlobalIndices, cellGlobalIndices; +}; + +} // namespace Readers +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/Readers/TNLReader.h b/src/TNL/Meshes/Readers/TNLReader.h index 88869ffc1281d5231943180b9e3a8b610ff65326..1258a051b53778386c3fa573c3cbab1acda10482 100644 --- a/src/TNL/Meshes/Readers/TNLReader.h +++ b/src/TNL/Meshes/Readers/TNLReader.h @@ -12,7 +12,7 @@ #include #include -#include +#include namespace TNL { namespace Meshes { @@ -21,11 +21,16 @@ namespace Readers { class TNLReader { public: + TNLReader() = delete; + + TNLReader( const String& fileName ) + : fileName( fileName ) + {} + bool - detectMesh( const String& fileName ) + detectMesh() { this->reset(); - this->fileName = fileName; const String objectType = getObjectType( fileName ); const std::vector< String > parsedMeshType = parseObjectType( objectType ); @@ -39,14 +44,14 @@ public: // save parts necessary to determine the mesh type meshDimension = worldDimension = std::atoi( parsedMeshType[ 1 ].getString() ); realType = parsedMeshType[ 2 ]; - globalIndexType = localIndexType = idType = parsedMeshType[ 4 ]; + globalIndexType = localIndexType = parsedMeshType[ 4 ]; // populate entity types (not necessary for GridTypeResolver, but while we're at it...) if( meshDimension == 1 ) - cellShape = EntityShape::Line; + cellShape = VTK::EntityShape::Line; else if( meshDimension == 2 ) - cellShape = EntityShape::Quad; + cellShape = VTK::EntityShape::Quad; else if( meshDimension == 3 ) - cellShape = EntityShape::Hexahedron; + cellShape = VTK::EntityShape::Hexahedron; } else if( meshType == "Meshes::Mesh" ) { const std::vector< String > parsedMeshConfig = parseObjectType( parsedMeshType[ 1 ] ); @@ -54,8 +59,8 @@ public: std::cerr << "Unable to parse the mesh config type " << parsedMeshType[ 1 ] << "." << std::endl; return false; } - if( parsedMeshConfig.size() != 7 ) { - std::cerr << "The parsed mesh config type has wrong size (expected 7 elements):" << std::endl; + if( parsedMeshConfig.size() != 6 ) { + std::cerr << "The parsed mesh config type has wrong size (expected 6 elements):" << std::endl; std::cerr << "[ "; for( std::size_t i = 0; i < parsedMeshConfig.size() - 1; i++ ) std::cerr << parsedMeshConfig[ i ] << ", "; @@ -69,18 +74,17 @@ public: realType = parsedMeshConfig[ 3 ]; globalIndexType = parsedMeshConfig[ 4 ]; localIndexType = parsedMeshConfig[ 5 ]; - idType = parsedMeshConfig[ 6 ]; - - if( topology == "MeshEdgeTopology" ) - cellShape = EntityShape::Line; - else if( topology == "MeshTriangleTopology" ) - cellShape = EntityShape::Triangle; - else if( topology == "MeshQuadrilateralTopology" ) - cellShape = EntityShape::Quad; - else if( topology == "MeshTetrahedronTopology" ) - cellShape = EntityShape::Tetra; - else if( topology == "MeshHexahedronTopology" ) - cellShape = EntityShape::Hexahedron; + + if( topology == "TNL::Meshes::Topologies::Edge" ) + cellShape = VTK::EntityShape::Line; + else if( topology == "TNL::Meshes::Topologies::Triangle" ) + cellShape = VTK::EntityShape::Triangle; + else if( topology == "TNL::Meshes::Topologies::Quadrilateral" ) + cellShape = VTK::EntityShape::Quad; + else if( topology == "TNL::Meshes::Topologies::Tetrahedron" ) + cellShape = VTK::EntityShape::Tetra; + else if( topology == "TNL::Meshes::Topologies::Hexahedron" ) + cellShape = VTK::EntityShape::Hexahedron; else { std::cerr << "Detected topology '" << topology << "' is not supported." << std::endl; return false; @@ -95,11 +99,10 @@ public: } template< typename MeshType > - static bool - readMesh( const String& fileName, MeshType& mesh ) + void + loadMesh( MeshType& mesh ) { mesh.load( fileName ); - return true; } String @@ -120,12 +123,12 @@ public: return worldDimension; } - EntityShape + VTK::EntityShape getCellShape() const { return cellShape; } - + String getRealType() const { @@ -137,37 +140,29 @@ public: { return globalIndexType; } - + String getLocalIndexType() const { return localIndexType; } - - String - getIdType() const - { - return idType; - } - + protected: String fileName; String meshType; int meshDimension = 0; int worldDimension = 0; - EntityShape cellShape = EntityShape::Vertex; + VTK::EntityShape cellShape = VTK::EntityShape::Vertex; String realType; String globalIndexType; String localIndexType; - String idType; void reset() { - fileName = ""; meshType = ""; meshDimension = worldDimension = 0; - cellShape = EntityShape::Vertex; - realType = localIndexType = globalIndexType = idType = ""; + cellShape = VTK::EntityShape::Vertex; + realType = localIndexType = globalIndexType = ""; } }; diff --git a/src/TNL/Meshes/Readers/VTKReader.h b/src/TNL/Meshes/Readers/VTKReader.h index d78796ba2cc6b98267b11fc640544924122b4700..58a196a852d1b506593b3a35c1babd12d2da16bd 100644 --- a/src/TNL/Meshes/Readers/VTKReader.h +++ b/src/TNL/Meshes/Readers/VTKReader.h @@ -8,15 +8,15 @@ /* See Copyright Notice in tnl/Copyright */ +// Implemented by: Jakub Klinkovský + #pragma once #include #include -#include #include -#include -#include +#include #include namespace TNL { @@ -24,55 +24,63 @@ namespace Meshes { namespace Readers { class VTKReader +: public MeshReader { public: - bool detectMesh( const String& fileName ) + VTKReader() = default; + + VTKReader( const std::string& fileName ) + : MeshReader( fileName ) + {} + + virtual void detectMesh() override { - this->reset(); - this->fileName = fileName; + reset(); - std::ifstream inputFile( fileName.getString() ); - if( ! inputFile ) { - std::cerr << "Failed to open the file " << fileName << "." << std::endl; - return false; - } + std::ifstream inputFile( fileName ); + if( ! inputFile ) + throw MeshReaderError( "VTKReader", "failed to open the file '" + fileName + "'." ); if( ! parseHeader( inputFile ) ) - return false; + throw MeshReaderError( "VTKReader", "failed to parse the VTK file header." ); + const auto positionAfterHeading = inputFile.tellg(); - if( dataset != "UNSTRUCTURED_GRID" ) { - std::cerr << "VTKReader: the dataset '" << dataset << "' is not supported." << std::endl; - return false; - } + if( dataset != "UNSTRUCTURED_GRID" ) + throw MeshReaderError( "VTKReader", "the dataset '" + dataset + "' is not supported." ); // TODO: implement binary parsing - if( dataType == "BINARY" ) { + if( dataType == "BINARY" ) throw Exceptions::NotImplementedError("VTKReader: parsing of BINARY data is not implemented yet."); - } std::string line, aux; std::istringstream iss; // parse points section - if( ! findSection( inputFile, "POINTS" ) ) { - std::cerr << "VTKReader: unable to find the POINTS section, the file may be invalid or corrupted." << std::endl; - return false; - } + if( ! findSection( inputFile, "POINTS" ) ) + throw MeshReaderError( "VTKReader", "unable to find the POINTS section, the file may be invalid or corrupted." ); getline( inputFile, line ); iss.clear(); iss.str( line ); iss >> aux; - long int numberOfVertices; - iss >> numberOfVertices; - iss >> realType; + iss >> NumberOfPoints; + iss >> pointsType; + + // global index type is not stored in legacy VTK files + connectivityType = offsetsType = "std::int32_t"; + // only std::uint8_t makes sense for entity types + typesType = "std::uint8_t"; + + // arrays holding the data from the VTK file + std::vector< double > pointsArray; + std::vector< std::int32_t > connectivityArray, offsetsArray; + std::vector< std::uint8_t > typesArray; // read points - long int verticesRead = 0; worldDimension = 0; - while( verticesRead < numberOfVertices ) { + for( std::size_t pointIndex = 0; pointIndex < NumberOfPoints; pointIndex++ ) { if( ! inputFile ) { - std::cerr << "VTKReader: unable to read enough vertices, the file may be invalid or corrupted." << std::endl; - return false; + reset(); + throw MeshReaderError( "VTKReader", "unable to read enough vertices, the file may be invalid or corrupted." ); } getline( inputFile, line ); @@ -83,35 +91,32 @@ public: double aux; iss >> aux; if( ! iss ) { - std::cerr << "VTKReader: unable to read " << i << "th component of the vertex number " << verticesRead << "." << std::endl; - return false; + reset(); + throw MeshReaderError( "VTKReader", "unable to read " + std::to_string(i) + "th component of the vertex number " + std::to_string(pointIndex) + "." ); } if( aux != 0.0 ) worldDimension = std::max( worldDimension, i + 1 ); + pointsArray.push_back( aux ); } - - verticesRead++; } // skip to the CELL_TYPES section if( ! findSection( inputFile, "CELL_TYPES" ) ) { - std::cerr << "VTKReader: unable to find the CELL_TYPES section, the file may be invalid or corrupted." << std::endl; - return false; + reset(); + throw MeshReaderError( "VTKReader", "unable to find the CELL_TYPES section, the file may be invalid or corrupted." ); } getline( inputFile, line ); iss.clear(); iss.str( line ); iss >> aux; - long int numberOfEntities; - iss >> numberOfEntities; + std::size_t NumberOfEntities; + iss >> NumberOfEntities; // read entity types - long int entitiesRead = 0; - std::map< int, EntityShape > entityTypes; - while( entitiesRead < numberOfEntities ) { + for( std::size_t entityIndex = 0; entityIndex < NumberOfEntities; entityIndex++ ) { if( ! inputFile ) { - std::cerr << "VTKReader: unable to read enough entity types, the file may be invalid or corrupted." << std::endl; - return false; + reset(); + throw MeshReaderError( "VTKReader", "unable to read enough cell types, the file may be invalid or corrupted." ); } getline( inputFile, line ); @@ -120,227 +125,114 @@ public: iss.clear(); iss.str( line ); iss >> typeId; - const EntityShape type = (EntityShape) typeId; - const int dimension = getEntityDimension( type ); - - // check entity type - if( entityTypes.find( dimension ) == entityTypes.cend() ) - entityTypes.emplace( std::make_pair( dimension, type ) ); - else if( entityTypes[ dimension ] != type ) { - std::cerr << "Mixed unstructured meshes are not supported. There are elements of dimension " << dimension - << " with type " << entityTypes[ dimension ] << " and " << type << ". " - << "The type of all entities with the same dimension must be the same." << std::endl; - this->reset(); - return false; - } - - entitiesRead++; + typesArray.push_back( typeId ); } - // set meshDimension and cellShape - meshDimension = 0; - for( auto it : entityTypes ) - if( it.first > meshDimension ) { - meshDimension = it.first; - cellShape = it.second; - } - - return true; - } - - template< typename MeshType > - bool readMesh( const String& fileName, MeshType& mesh ) - { - using MeshBuilder = MeshBuilder< MeshType >; - using IndexType = typename MeshType::GlobalIndexType; - using PointType = typename MeshType::PointType; - using CellSeedType = typename MeshBuilder::CellSeedType; - - const EntityShape cellType = TopologyToEntityShape< typename MeshType::template EntityTraits< MeshType::getMeshDimension() >::EntityTopology >::shape; - MeshBuilder meshBuilder; - - std::ifstream inputFile( fileName.getString() ); - if( ! inputFile ) { - std::cerr << "Failed to open the file " << fileName << "." << std::endl; - return false; + // count entities for each dimension + std::size_t entitiesCounts[4] = {0, 0, 0, 0}; + for( auto c : typesArray ) { + const int dimension = getEntityDimension( (VTK::EntityShape) c ); + ++entitiesCounts[dimension]; } - if( ! parseHeader( inputFile ) ) - return false; - const auto positionAfterHeading = inputFile.tellg(); - - if( dataset != "UNSTRUCTURED_GRID" ) { - std::cerr << "VTKReader: the dataset '" << dataset << "' is not supported." << std::endl; - return false; + // set meshDimension + meshDimension = 3; + if( entitiesCounts[3] == 0 ) { + meshDimension--; + if( entitiesCounts[2] == 0 ) { + meshDimension--; + if( entitiesCounts[1] == 0 ) + meshDimension--; + } } - // TODO: implement binary parsing - if( dataType == "BINARY" ) { - throw Exceptions::NotImplementedError("VTKReader: parsing of BINARY data is not implemented yet."); + if( meshDimension == 0 ) { + reset(); + throw MeshReaderError( "VTKReader", "Mesh dimension cannot be 0. Are there any entities at all?" ); } - std::string line, aux; - std::istringstream iss; - - // parse the points section - if( ! findSection( inputFile, "POINTS" ) ) { - std::cerr << "VTKReader: unable to find the POINTS section, the file may be invalid or corrupted." << std::endl; - return false; + // filter out cell shapes + std::vector< std::uint8_t > cellTypes; + for( auto c : typesArray ) { + const int dimension = getEntityDimension( (VTK::EntityShape) c ); + if( dimension == meshDimension ) + cellTypes.push_back( c ); } - getline( inputFile, line ); - iss.clear(); - iss.str( line ); - iss >> aux; - IndexType numberOfVertices; - iss >> numberOfVertices; - - // allocate vertices - meshBuilder.setPointsCount( numberOfVertices ); - - // read points - for( IndexType vertexIndex = 0; vertexIndex < numberOfVertices; vertexIndex++ ) { - if( ! inputFile ) { - std::cerr << "VTKReader: unable to read enough vertices, the file may be invalid or corrupted." << std::endl; - return false; - } - getline( inputFile, line ); - // read the coordinates - iss.clear(); - iss.str( line ); - PointType p; - for( int i = 0; i < p.getSize(); i++ ) { - iss >> p[ i ]; - if( ! iss ) { - std::cerr << "VTKReader: unable to read " << i << "th component of the vertex number " << vertexIndex << "." << std::endl; - return false; - } - } - meshBuilder.setPoint( vertexIndex, p ); + // set number of cells + NumberOfCells = cellTypes.size(); + if( NumberOfCells == 0 || NumberOfCells != entitiesCounts[meshDimension] ) { + const std::string msg = "invalid number of cells (" + std::to_string(NumberOfCells) + "). Counts of entities for each dimension (0,1,2,3) are: " + + std::to_string(entitiesCounts[0]) + ", " + std::to_string(entitiesCounts[1]) + ", " + + std::to_string(entitiesCounts[2]) + ", " + std::to_string(entitiesCounts[3]); + reset(); + throw MeshReaderError( "VTKReader", msg ); } - // find to the CELL_TYPES section - if( ! findSection( inputFile, "CELL_TYPES", positionAfterHeading ) ) { - std::cerr << "VTKReader: unable to find the CELL_TYPES section, the file may be invalid or corrupted." << std::endl; - return false; - } - getline( inputFile, line ); - iss.clear(); - iss.str( line ); - iss >> aux; - IndexType numberOfEntities; - iss >> numberOfEntities; - - // read entity types, count cells - std::vector< EntityShape > entityTypes; - entityTypes.resize( numberOfEntities ); - IndexType numberOfCells = 0; - for( IndexType entityIndex = 0; entityIndex < numberOfEntities; entityIndex++ ) { - if( ! inputFile ) { - std::cerr << "VTKReader: unable to read enough entity types, the file may be invalid or corrupted." << std::endl; - return false; + // validate cell types + cellShape = (VTK::EntityShape) cellTypes[0]; + for( auto c : cellTypes ) + if( (VTK::EntityShape) c != cellShape ) { + const std::string msg = "Mixed unstructured meshes are not supported. There are cells with type " + + VTK::getShapeName(cellShape) + " and " + VTK::getShapeName((VTK::EntityShape) c) + "."; + reset(); + throw MeshReaderError( "VTKReader", msg ); } - getline( inputFile, line ); - - // get entity type - int typeId; - iss.clear(); - iss.str( line ); - iss >> typeId; - entityTypes[ entityIndex ] = (EntityShape) typeId; - const int dimension = getEntityDimension( entityTypes[ entityIndex ] ); - if( dimension == MeshType::getMeshDimension() ) - numberOfCells++; - } - - meshBuilder.setCellsCount( numberOfCells ); // find to the CELLS section if( ! findSection( inputFile, "CELLS", positionAfterHeading ) ) { - std::cerr << "VTKReader: unable to find the CELLS section, the file may be invalid or corrupted." << std::endl; - return false; + reset(); + throw MeshReaderError( "VTKReader", "unable to find the CELLS section, the file may be invalid or corrupted." ); } getline( inputFile, line ); - // read cells - IndexType cellIndex = 0; - for( IndexType entityIndex = 0; entityIndex < numberOfEntities; entityIndex++ ) { + // read entities + for( std::size_t entityIndex = 0; entityIndex < NumberOfEntities; entityIndex++ ) { if( ! inputFile ) { - std::cerr << "VTKReader: unable to read enough entities, the file may be invalid or corrupted." << std::endl; - return false; + reset(); + throw MeshReaderError( "VTKReader", "unable to read enough cells, the file may be invalid or corrupted." + " (entityIndex = " + std::to_string(entityIndex) + ")" ); } getline( inputFile, line ); - if( entityTypes[ entityIndex ] == cellType ) { + if( (VTK::EntityShape) typesArray[ entityIndex ] == cellShape ) { iss.clear(); iss.str( line ); - int vid; - iss >> vid; // ignore number of subvertices - CellSeedType& seed = meshBuilder.getCellSeed( cellIndex++ ); - for( int v = 0; v < CellSeedType::getCornersCount(); v++ ) { + // read number of subvertices + int subvertices = 0; + iss >> subvertices; + for( int v = 0; v < subvertices; v++ ) { + std::size_t vid; iss >> vid; - if( ! iss ) - return false; - seed.setCornerId( v, vid ); + if( ! iss ) { + reset(); + throw MeshReaderError( "VTKReader", "unable to read enough cells, the file may be invalid or corrupted." + " (entityIndex = " + std::to_string(entityIndex) + ", subvertex = " + std::to_string(v) + ")" ); + } + connectivityArray.push_back( vid ); } + offsetsArray.push_back( connectivityArray.size() ); } } - // no cells found - if( cellIndex == 0 ) - return false; - - return meshBuilder.build( mesh ); - } - - String - getMeshType() const - { - return "Meshes::Mesh"; - } - - int getMeshDimension() const - { - return this->meshDimension; - } - - int - getWorldDimension() const - { - return worldDimension; - } - - EntityShape - getCellShape() const - { - return cellShape; - } - - String - getRealType() const - { - return realType.c_str(); - } + // set cell types + std::swap( cellTypes, typesArray ); - String - getGlobalIndexType() const - { - // not stored in the VTK file - return "int"; - } + // set the arrays to the base class + this->pointsArray = std::move(pointsArray); + this->connectivityArray = std::move(connectivityArray); + this->offsetsArray = std::move(offsetsArray); + this->typesArray = std::move(typesArray); - String - getLocalIndexType() const - { - // not stored in the VTK file - return "short int"; + // indicate success by setting the mesh type + meshType = "Meshes::Mesh"; } - String - getIdType() const + virtual void reset() override { - // not stored in the VTK file - return "int"; + resetBase(); + dataType = ""; + dataset = ""; } protected: @@ -348,19 +240,6 @@ protected: std::string dataType; std::string dataset; - String fileName; - int meshDimension, worldDimension; - EntityShape cellShape = EntityShape::Vertex; - std::string realType; - - void reset() - { - fileName = ""; - meshDimension = worldDimension = 0; - cellShape = EntityShape::Vertex; - realType = ""; - } - bool parseHeader( std::istream& str ) { std::string line; diff --git a/src/TNL/Meshes/Readers/VTKReader_libvtk.h b/src/TNL/Meshes/Readers/VTKReader_libvtk.h deleted file mode 100644 index 0e2b404214adef06bc4bebc50d513def250c6f9d..0000000000000000000000000000000000000000 --- a/src/TNL/Meshes/Readers/VTKReader_libvtk.h +++ /dev/null @@ -1,330 +0,0 @@ -/*************************************************************************** - VTKReader_libvtk.h - description - ------------------- - begin : Nov 6, 2016 - copyright : (C) 2016 by Tomas Oberhuber et al. - email : tomas.oberhuber@fjfi.cvut.cz - ***************************************************************************/ - -/* See Copyright Notice in tnl/Copyright */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#ifdef HAVE_VTK -#include -#include -#include -#include -#endif - -namespace TNL { -namespace Meshes { -namespace Readers { - -// types used in VTK -using VTKRealType = double; -#ifdef HAVE_VTK -using VTKIndexType = vtkIdType; -#else -using VTKIndexType = int; -#endif -// wrapper type for physical coordinates to preserve my sanity -using VTKPointType = Containers::StaticVector< 3, VTKRealType >; - -template< typename Index = int > -class VTKReader_libvtk -{ -public: - using IndexType = Index; - - bool - detectMesh( const String& fileName ) - { - resetAll(); - if( ! loadVTKFile( fileName ) ) { - resetAll(); - return false; - } - reset(); - return true; - } - - template< typename MeshType > - bool - readMesh( const String& fileName, MeshType& mesh ) - { - static_assert( std::is_same< IndexType, typename MeshType::GlobalIndexType >::value, "VTKReader_libvtk::IndexType and MeshType::GlobalIndexType must be the same type." ); - - resetAll(); - if( ! loadVTKFile( fileName ) ) { - resetAll(); - return false; - } - - // GOTCHA: The unary "+" is a workaround due to odr-use of undefined static data member. See: - // https://stackoverflow.com/questions/39646958/constexpr-static-member-before-after-c17 - // https://stackoverflow.com/questions/272900/undefined-reference-to-static-class-member/272996#272996 - TNL_ASSERT_EQ( this->worldDimension, + MeshType::Config::worldDimension, "world dimensions do not match" ); - TNL_ASSERT_EQ( this->meshDimension, + MeshType::Config::meshDimension, "mesh dimensions do not match" ); - const int subvertices = Topologies::Subtopology< typename MeshType::Config::CellTopology, 0 >::count; - TNL_ASSERT_EQ( this->verticesInEntities.at( this->meshDimension ), subvertices, "numbers of cell subvertices do not match" ); - - using MeshBuilder = MeshBuilder< MeshType >; - using GlobalIndexType = typename MeshType::GlobalIndexType; - - const GlobalIndexType numberOfPoints = this->pointsData.size(); - const GlobalIndexType numberOfCells = this->entityIdMappings.at( this->meshDimension ).size(); - - MeshBuilder meshBuilder; - meshBuilder.setPointsCount( numberOfPoints ); - meshBuilder.setCellsCount( numberOfCells ); - - for( GlobalIndexType i = 0; i < numberOfPoints; i++ ) { - typename MeshType::PointType p; - for( int j = 0; j < p.getSize(); j++ ) - p[ j ] = this->pointsData.at( i )[ j ]; - meshBuilder.setPoint( i, p ); - } - - const auto& cellIdMap = this->entityIdMappings.at( this->meshDimension ); - for( GlobalIndexType i = 0; i < numberOfCells; i++ ) { - const VTKIndexType vtkCellIndex = cellIdMap.at( i ); - const auto& vtkCellSeeds = this->entitySeeds.at( vtkCellIndex ); - using CellSeedType = typename MeshBuilder::CellSeedType; - TNL_ASSERT_EQ( CellSeedType::getCornersCount(), vtkCellSeeds.size(), "wrong number of subvertices" ); - CellSeedType& seed = meshBuilder.getCellSeed( i ); - for( int v = 0; v < CellSeedType::getCornersCount(); v++ ) { - seed.setCornerId( v, vtkCellSeeds[ v ] ); - } - } - - // drop all data from the VTK file since it's not needed anymore - this->resetAll(); - - return meshBuilder.build( mesh ); - } - - String - getMeshType() const - { - // we can read only the UNSTRUCTURED_GRID dataset - return "Meshes::Mesh"; - } - - int - getWorldDimension() const - { - return this->worldDimension; - } - - int - getMeshDimension() const - { - return this->meshDimension; - } - - EntityShape - getCellShape() const - { - return this->entityTypes.at( this->meshDimension ); - } - -// int -// getVerticesInCell() const -// { -// return this->verticesInEntities.at( this->getMeshDimension() ); -// } - -// EntityShape -// getEntityType( int entityDimension ) const -// { -// return this->entityTypes.at( entityDimension ); -// } - - String - getRealType() const - { - // TODO: how to extract it from the VTK object? -// return "float"; - return "double"; - } - - String - getGlobalIndexType() const - { - // not stored in the VTK file - return "int"; - } - - String - getLocalIndexType() const - { - // not stored in the VTK file - return "short int"; - } - - String - getIdType() const - { - // not stored in the VTK file - return "int"; - } - -protected: - int worldDimension = 0; - int meshDimension = 0; - - // maps vertex indices to physical coordinates - std::unordered_map< VTKIndexType, VTKPointType > pointsData; - - // maps entity dimension to the number of vertices in the entity - std::map< int, int > verticesInEntities; - - // type for mapping TNL entity indices to VTK cell indices - using TNL2VTKindexmap = std::unordered_map< IndexType, VTKIndexType >; - // maps dimension to maps of TNL entity IDs to corresponding VTK cell IDs - std::unordered_map< int, TNL2VTKindexmap > entityIdMappings; - - // maps VTK cell indices to entity seeds (set of indices of subvertices) - std::unordered_map< VTKIndexType, std::vector< VTKIndexType > > entitySeeds; - - // maps dimension to VTK type of the entity with given dimension - std::unordered_map< int, EntityShape > entityTypes; - - void reset() - { - pointsData.clear(); - verticesInEntities.clear(); - entityIdMappings.clear(); - entitySeeds.clear(); - } - - void resetAll() - { - reset(); - worldDimension = 0; - meshDimension = 0; - entityTypes.clear(); - } - - bool loadVTKFile( const String& fileName ) - { -#ifdef HAVE_VTK - vtkSmartPointer< vtkUnstructuredGridReader > vtkReader - = vtkSmartPointer::New(); - vtkReader->SetFileName( fileName.getString() ); - vtkReader->Update(); - - if( ! vtkReader->IsFileUnstructuredGrid() ) { - std::cerr << "The file '" << fileName << "' is not a VTK Legacy file with an UNSTRUCTURED_GRID dataset type." << std::endl; - return false; - } - - auto& vtkMesh = *vtkReader->GetOutput(); - - /* To determine the mesh type, we need: - * - world dimension (default 1D, if some 2nd coordinate is non-zero -> 2D, some 3rd coordinate non-zero -> 3D) - * - mesh dimension (highest dimension of the entities present in mesh) - * - check that it is homogeneous - all entities of the same dimension should have the same number of vertices and type - * - types of entities for each dimension - * - * To initialize the mesh, we need: - * - points data (vertex indices and world coordinates) - * - cell seeds (indices of vertices composing each cell) - */ - const VTKIndexType numberOfPoints = vtkMesh.GetNumberOfPoints(); - const VTKIndexType numberOfCells = vtkMesh.GetNumberOfCells(); - - if( numberOfPoints == 0 ) { - std::cerr << "There are no points data in the file '" << fileName << "'." << std::endl; - return false; - } - - if( numberOfCells == 0 ) { - std::cerr << "There are no cells data in the file '" << fileName << "'." << std::endl; - return false; - } - - for( VTKIndexType i = 0; i < numberOfPoints; i++ ) { - VTKRealType* p = vtkMesh.GetPoint( i ); - - // get world dimension - for( int j = 0; j < 3; j++ ) - if( p[ j ] != 0.0 ) - this->worldDimension = std::max( this->worldDimension, j + 1 ); - - // copy points data - this->pointsData[ i ] = VTKPointType( p[ 0 ], p[ 1 ], p[ 2 ] ); - } - - for( VTKIndexType i = 0; i < numberOfCells; i++ ) { - vtkCell* cell = vtkMesh.GetCell( i ); - const int dimension = cell->GetCellDimension(); - const int points = cell->GetNumberOfPoints(); - const EntityShape type = (EntityShape) cell->GetCellType(); - - // number of vertices in entities - if( this->verticesInEntities.find( dimension ) == this->verticesInEntities.cend() ) - this->verticesInEntities[ dimension ] = points; - else if( this->verticesInEntities[ dimension ] != points ) { - std::cerr << "Mixed unstructured meshes are not supported. There are elements of dimension " << dimension - << " with " << this->verticesInEntities[ dimension ] << " vertices and with " << points - << " vertices. The number of vertices per entity must be constant." << std::endl; - this->reset(); - return false; - } - - // entity types - if( this->entityTypes.find( dimension ) == this->entityTypes.cend() ) - this->entityTypes.emplace( std::make_pair( dimension, type ) ); - else if( this->entityTypes[ dimension ] != type ) { - std::cerr << "Mixed unstructured meshes are not supported. There are elements of dimension " << dimension - << " with type " << this->entityTypes[ dimension ] << " and " << type - << ". The type of all entities with the same dimension must be the same." << std::endl; - this->reset(); - return false; - } - - // mapping between TNL and VTK indices - auto& map = this->entityIdMappings[ dimension ]; - map[ map.size() ] = i; - - // copy seed - auto& seed = this->entitySeeds[ i ]; - for( int j = 0; j < points; j++ ) - seed.push_back( cell->GetPointId( j ) ); - } - - // std::map is sorted, so the last key is the maximum - this->meshDimension = this->verticesInEntities.rbegin()->first; - - if( this->meshDimension > this->worldDimension ) { - std::cerr << "Invalid mesh: world dimension is " << this->worldDimension - << ", but mesh dimension is " << this->meshDimension << "." << std::endl; - this->reset(); - return false; - } - - return true; -#else - std::cerr << "The VTKReader_libvtk needs to be compiled with the VTK library." << std::endl; - return false; -#endif - } -}; - -} // namespace Readers -} // namespace Meshes -} // namespace TNL diff --git a/src/TNL/Meshes/Readers/VTUReader.h b/src/TNL/Meshes/Readers/VTUReader.h new file mode 100644 index 0000000000000000000000000000000000000000..f9c3902d06fb6c8efb0dec7982b94537a743b45d --- /dev/null +++ b/src/TNL/Meshes/Readers/VTUReader.h @@ -0,0 +1,164 @@ +/*************************************************************************** + VTUReader.h - description + ------------------- + begin : Mar 21, 2020 + copyright : (C) 2020 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +// Implemented by: Jakub Klinkovský + +#pragma once + +#include + +namespace TNL { +namespace Meshes { +namespace Readers { + +class VTUReader +: public XMLVTK +{ +#ifdef HAVE_TINYXML2 + void readUnstructuredGrid() + { + using namespace tinyxml2; + const XMLElement* piece = getChildSafe( datasetElement, "Piece" ); + if( piece->NextSiblingElement( "Piece" ) ) + // ambiguity - throw error, we don't know which piece to parse (or all of them?) + throw MeshReaderError( "VTUReader", "the serial UnstructuredGrid file contains more than one element" ); + NumberOfPoints = getAttributeInteger( piece, "NumberOfPoints" ); + NumberOfCells = getAttributeInteger( piece, "NumberOfCells" ); + + // verify points + const XMLElement* points = getChildSafe( piece, "Points" ); + const XMLElement* pointsData = verifyHasOnlyOneChild( points, "DataArray" ); + verifyDataArray( pointsData ); + const std::string pointsDataName = getAttributeString( pointsData, "Name" ); + if( pointsDataName != "Points" ) + throw MeshReaderError( "VTUReader", "the tag does not contain a with Name=\"Points\" attribute" ); + + // verify cells + const XMLElement* cells = getChildSafe( piece, "Cells" ); + const XMLElement* connectivity = getDataArrayByName( cells, "connectivity" ); + const XMLElement* offsets = getDataArrayByName( cells, "offsets" ); + const XMLElement* types = getDataArrayByName( cells, "types" ); + + // read the points, connectivity, offsets and types into intermediate arrays + pointsArray = readDataArray( pointsData, "Points" ); + pointsType = VTKDataTypes.at( getAttributeString( pointsData, "type" ) ); + connectivityArray = readDataArray( connectivity, "connectivity" ); + connectivityType = VTKDataTypes.at( getAttributeString( connectivity, "type" ) ); + offsetsArray = readDataArray( offsets, "offsets" ); + offsetsType = VTKDataTypes.at( getAttributeString( offsets, "type" ) ); + typesArray = readDataArray( types, "types" ); + typesType = VTKDataTypes.at( getAttributeString( types, "type" ) ); + + // connectivity and offsets must have the same type + if( connectivityType != offsetsType ) + throw MeshReaderError( "VTUReader", "the \"connectivity\" and \"offsets\" array do not have the same type (" + + connectivityType + " vs " + offsetsType + ")" ); + // cell types can be only uint8_t + if( typesType != "std::uint8_t" ) + throw MeshReaderError( "VTUReader", "unsupported data type for the Name=\"types\" array" ); + + using mpark::visit; + // validate points + visit( [this](auto&& array) { + // check array size + if( array.size() != 3 * NumberOfPoints ) + throw MeshReaderError( "VTUReader", "invalid size of the Points data array (" + std::to_string(array.size()) + + " vs " + std::to_string(NumberOfPoints) + ")" ); + // set worldDimension + worldDimension = 1; + std::size_t i = 0; + for( auto c : array ) { + if( c != 0 ) { + int dim = i % 3 + 1; + worldDimension = std::max( worldDimension, dim ); + } + ++i; + } + }, + pointsArray + ); + // validate types + visit( [this](auto&& array) { + // check array size + if( array.size() != NumberOfCells ) + throw MeshReaderError( "VTUReader", "size of the types data array does not match the NumberOfCells attribute" ); + cellShape = (VTK::EntityShape) array[0]; + meshDimension = getEntityDimension( cellShape ); + // TODO: check only entities of the same dimension (edges, faces and cells separately) + for( auto c : array ) + if( (VTK::EntityShape) c != cellShape ) + throw MeshReaderError( "VTUReader", "Mixed unstructured meshes are not supported. There are cells with type " + + VTK::getShapeName(cellShape) + " and " + VTK::getShapeName((VTK::EntityShape) c) + "." ); + }, + typesArray + ); + // validate offsets + std::size_t max_offset = 0; + visit( [this, &max_offset](auto&& array) mutable { + if( array.size() != NumberOfCells ) + throw MeshReaderError( "VTUReader", "size of the offsets data array does not match the NumberOfCells attribute" ); + for( auto c : array ) { + if( c <= (decltype(c)) max_offset ) + throw MeshReaderError( "VTUReader", "the offsets array is not monotonically increasing" ); + max_offset = c; + } + }, + offsetsArray + ); + // validate connectivity + visit( [this, max_offset](auto&& array) { + if( array.size() != max_offset ) + throw MeshReaderError( "VTUReader", "size of the connectivity data array does not match the offsets array" ); + for( auto c : array ) { + if( c < 0 || (std::size_t) c >= NumberOfPoints ) + throw MeshReaderError( "VTUReader", "connectivity index " + std::to_string(c) + " is out of range" ); + } + }, + connectivityArray + ); + } +#endif + +public: + VTUReader() = default; + + VTUReader( const std::string& fileName ) + : XMLVTK( fileName ) + {} + + virtual void detectMesh() override + { +#ifdef HAVE_TINYXML2 + reset(); + try { + openVTKFile(); + } + catch( const MeshReaderError& ) { + reset(); + throw; + } + + // verify file type + if( fileType == "UnstructuredGrid" ) + readUnstructuredGrid(); + else + throw MeshReaderError( "VTUReader", "the reader cannot read data of the type " + fileType + ". Use a different reader if possible." ); + + // indicate success by setting the mesh type + meshType = "Meshes::Mesh"; +#else + throw_no_tinyxml(); +#endif + } +}; + +} // namespace Readers +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/Readers/XMLVTK.h b/src/TNL/Meshes/Readers/XMLVTK.h new file mode 100644 index 0000000000000000000000000000000000000000..fb8e1eb40df1919a7a57b86db8978fde042b2262 --- /dev/null +++ b/src/TNL/Meshes/Readers/XMLVTK.h @@ -0,0 +1,377 @@ +/*************************************************************************** + XMLVTK.h - description + ------------------- + begin : Mar 21, 2020 + copyright : (C) 2020 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +// Implemented by: Jakub Klinkovský + +#pragma once + +#include +#include + +#include +#include +#include + +#ifdef HAVE_ZLIB + #include +#endif + +#ifdef HAVE_TINYXML2 + #include +#endif + +namespace TNL { +namespace Meshes { +namespace Readers { + +static const std::map< std::string, std::string > VTKDataTypes { + {"Int8", "std::int8_t"}, + {"UInt8", "std::uint8_t"}, + {"Int16", "std::int16_t"}, + {"UInt16", "std::uint16_t"}, + {"Int32", "std::int32_t"}, + {"UInt32", "std::uint32_t"}, + {"Int64", "std::int64_t"}, + {"UInt64", "std::uint64_t"}, + {"Float32", "float"}, + {"Float64", "double"} +}; + +class XMLVTK +: public MeshReader +{ +#ifdef HAVE_TINYXML2 +protected: + static void verifyElement( const tinyxml2::XMLElement* elem, const std::string name ) + { + if( ! elem ) + throw MeshReaderError( "XMLVTK", "tag <" + name + "> not found" ); + if( elem->Name() != name ) + throw MeshReaderError( "XMLVTK", "invalid XML format - expected a <" + name + "> element, got <" + elem->Name() + ">" ); + } + + static const tinyxml2::XMLElement* + verifyHasOnlyOneChild( const tinyxml2::XMLElement* parent, const std::string childName = "" ) + { + const std::string parentName = parent->Name(); + const tinyxml2::XMLElement* elem = parent->FirstChildElement(); + if( ! childName.empty() ) + verifyElement( elem, childName ); + else if( ! elem ) + throw MeshReaderError( "XMLVTK", "element " + parentName + " does not contain any child" ); + if( elem->NextSibling() ) + throw MeshReaderError( "XMLVTK", "<" + childName + "> is not the only element in <" + parentName + ">" ); + return elem; + } + + static std::string + getAttributeString( const tinyxml2::XMLElement* elem, std::string name, std::string defaultValue = "" ) + { + const char* attribute = nullptr; + attribute = elem->Attribute( name.c_str() ); + if( attribute ) + return attribute; + if( ! defaultValue.empty() ) + return defaultValue; + throw MeshReaderError( "XMLVTK", "element <" + std::string(elem->Name()) + "> does not have the attribute '" + name + "'" ); + } + + static std::int64_t + getAttributeInteger( const tinyxml2::XMLElement* elem, std::string name ) + { + std::int64_t value; + tinyxml2::XMLError status = elem->QueryInt64Attribute( name.c_str(), &value ); + if( status != tinyxml2::XML_SUCCESS ) + throw MeshReaderError( "XMLVTK", "element <" + std::string(elem->Name()) + "> does not have the attribute '" + name + "' or it could not be converted to int64_t" ); + return value; + } + + static std::int64_t + getAttributeInteger( const tinyxml2::XMLElement* elem, std::string name, int64_t defaultValue ) + { + std::int64_t value; + tinyxml2::XMLError status = elem->QueryInt64Attribute( name.c_str(), &value ); + if( status != tinyxml2::XML_SUCCESS ) + return defaultValue; + return value; + } + + static const tinyxml2::XMLElement* + getChildSafe( const tinyxml2::XMLElement* parent, std::string name ) + { + const tinyxml2::XMLElement* child = parent->FirstChildElement( name.c_str() ); + verifyElement( child, name ); + return child; + } + + static void + verifyDataArray( const tinyxml2::XMLElement* elem, std::string elemName = "DataArray" ) + { + // the elemName parameter is necessary due to parallel formats using "PDataArray" + verifyElement( elem, elemName.c_str() ); + // verify Name + getAttributeString( elem, "Name" ); + // verify type + const std::string type = getAttributeString( elem, "type" ); + if( VTKDataTypes.count( type ) == 0 ) + throw MeshReaderError( "XMLVTK", "unsupported " + elemName + " type: " + type ); + // verify format (attribute not used on PDataArray) + if( elemName == "DataArray" ) { + const std::string format = getAttributeString( elem, "format" ); + if( format != "ascii" && format != "binary" ) + throw MeshReaderError( "XMLVTK", "unsupported " + elemName + " format: " + format ); + } + // verify NumberOfComponents (optional) + const std::string NumberOfComponents = getAttributeString( elem, "NumberOfComponents", "0" ); + static const std::set< std::string > validNumbersOfComponents = {"0", "1", "2", "3"}; + if( validNumbersOfComponents.count( NumberOfComponents ) == 0 ) + throw MeshReaderError( "XMLVTK", "unsupported NumberOfComponents in " + elemName + ": " + NumberOfComponents ); + } + + static const tinyxml2::XMLElement* + getDataArrayByName( const tinyxml2::XMLElement* parent, std::string name ) + { + const tinyxml2::XMLElement* found = nullptr; + const tinyxml2::XMLElement* child = parent->FirstChildElement( "DataArray" ); + while( child != nullptr ) { + verifyElement( child, "DataArray" ); + std::string arrayName; + try { + arrayName = getAttributeString( child, "Name" ); + } + catch( const MeshReaderError& ) {} + if( arrayName == name ) { + if( found == nullptr ) + found = child; + else + throw MeshReaderError( "XMLVTK", "the <" + std::string(parent->Name()) + "> tag contains multiple tags with the Name=\"" + name + "\" attribute" ); + } + child = child->NextSiblingElement( "DataArray" ); + } + if( found == nullptr ) + throw MeshReaderError( "XMLVTK", "the <" + std::string(parent->Name()) + "> tag does not contain any tag with the Name=\"" + name + "\" attribute" ); + verifyDataArray( found ); + return found; + } + + template< typename HeaderType > + static std::size_t + readBlockSize( const char* block ) + { + std::pair> decoded_data = decode_block( block, get_encoded_length(sizeof(HeaderType)) ); + if( decoded_data.first != sizeof(HeaderType) ) + throw MeshReaderError( "XMLVTK", "base64-decoding failed - mismatched data size in the binary header (read " + + std::to_string(decoded_data.first) + " bytes, expected " + std::to_string(sizeof(HeaderType)) + " bytes)" ); + const HeaderType* blockSize = reinterpret_cast(decoded_data.second.get()); + return *blockSize; + } + + template< typename HeaderType, typename T > + VariantVector + readBinaryBlock( const char* block ) const + { + // skip whitespace at the beginning + while( *block != '\0' && std::isspace( *block ) ) + ++block; + + if( compressor == "" ) { + const std::size_t blockSize = readBlockSize< HeaderType >( block ); + block += get_encoded_length(sizeof(HeaderType)); + std::pair> decoded_data = decode_block( block, get_encoded_length(blockSize) ); + std::vector vector( decoded_data.first / sizeof(T) ); + for( std::size_t i = 0; i < vector.size(); i++ ) + vector[i] = reinterpret_cast(decoded_data.second.get())[i]; + return vector; + } + else if( compressor == "vtkZLibDataCompressor" ) { +#ifdef HAVE_ZLIB + std::pair> decoded_data = decompress_block< HeaderType, T >(block); + std::vector vector( decoded_data.first ); + for( std::size_t i = 0; i < vector.size(); i++ ) + vector[i] = decoded_data.second.get()[i]; + return vector; +#else + throw MeshReaderError( "XMLVTK", "The ZLIB compression is not available in this build. Make sure that ZLIB is " + "installed and recompile the program with -DHAVE_ZLIB." ); +#endif + } + else + throw MeshReaderError( "XMLVTK", "unsupported compressor type: " + compressor + " (only vtkZLibDataCompressor is supported)" ); + } + + template< typename T > + VariantVector + readBinaryBlock( const char* block ) const + { + if( headerType == "std::int8_t" ) return readBinaryBlock< std::int8_t, T >( block ); + else if( headerType == "std::uint8_t" ) return readBinaryBlock< std::uint8_t, T >( block ); + else if( headerType == "std::int16_t" ) return readBinaryBlock< std::int16_t, T >( block ); + else if( headerType == "std::uint16_t" ) return readBinaryBlock< std::uint16_t, T >( block ); + else if( headerType == "std::int32_t" ) return readBinaryBlock< std::int32_t, T >( block ); + else if( headerType == "std::uint32_t" ) return readBinaryBlock< std::uint32_t, T >( block ); + else if( headerType == "std::int64_t" ) return readBinaryBlock< std::int64_t, T >( block ); + else if( headerType == "std::uint64_t" ) return readBinaryBlock< std::uint64_t, T >( block ); + else throw MeshReaderError( "XMLVTK", "unsupported header type: " + headerType ); + } + + VariantVector + readDataArray( const tinyxml2::XMLElement* elem, std::string arrayName ) const + { + verifyElement( elem, "DataArray" ); + const char* block = elem->GetText(); + if( ! block ) + throw MeshReaderError( "XMLVTK", "the DataArray with Name=\"" + arrayName + "\" does not contain any data" ); + const std::string type = getAttributeString( elem, "type" ); + const std::string format = getAttributeString( elem, "format" ); + if( format == "ascii" ) { + // TODO + throw MeshReaderError( "XMLVTK", "reading ASCII arrays is not implemented yet" ); + } + else if( format == "binary" ) { + if( type == "Int8" ) return readBinaryBlock< std::int8_t >( block ); + else if( type == "UInt8" ) return readBinaryBlock< std::uint8_t >( block ); + else if( type == "Int16" ) return readBinaryBlock< std::int16_t >( block ); + else if( type == "UInt16" ) return readBinaryBlock< std::uint16_t >( block ); + else if( type == "Int32" ) return readBinaryBlock< std::int32_t >( block ); + else if( type == "UInt32" ) return readBinaryBlock< std::uint32_t >( block ); + else if( type == "Int64" ) return readBinaryBlock< std::int64_t >( block ); + else if( type == "UInt64" ) return readBinaryBlock< std::uint64_t >( block ); + else if( type == "Float32" ) return readBinaryBlock< float >( block ); + else if( type == "Float64" ) return readBinaryBlock< double >( block ); + else throw MeshReaderError( "XMLVTK", "unsupported DataArray type: " + type ); + } + else + throw MeshReaderError( "XMLVTK", "unsupported DataArray format: " + format ); + } + + VariantVector + readPointOrCellData( std::string sectionName, std::string arrayName ) + { + const tinyxml2::XMLElement* piece = getChildSafe( datasetElement, "Piece" ); + if( piece->NextSiblingElement( "Piece" ) ) + // ambiguity - throw error, we don't know which piece to parse + throw MeshReaderError( "XMLVTK", "the dataset element <" + fileType + "> contains more than one element" ); + const tinyxml2::XMLElement* pointData = getChildSafe( piece, sectionName.c_str() ); + if( pointData->NextSiblingElement( sectionName.c_str() ) ) + throw MeshReaderError( "XMLVTK", "the element contains more than one <" + sectionName + "> element" ); + const tinyxml2::XMLElement* dataArray = getDataArrayByName( pointData, arrayName.c_str() ); + return readDataArray( dataArray, arrayName.c_str() ); + } +#endif + +protected: + [[noreturn]] static void + throw_no_tinyxml() + { + throw std::runtime_error("The program was compiled without XML parsing. Make sure that TinyXML-2 is " + "installed and recompile the program with -DHAVE_TINYXML2."); + } + +public: + XMLVTK() = default; + + XMLVTK( const std::string& fileName ) + : MeshReader( fileName ) + {} + + void openVTKFile() + { +#ifdef HAVE_TINYXML2 + using namespace tinyxml2; + + // load and verify XML + tinyxml2::XMLError status = dom.LoadFile( fileName.c_str() ); + if( status != XML_SUCCESS ) + throw MeshReaderError( "XMLVTK", "failed to parse the file as an XML document." ); + + // verify root element + const XMLElement* elem = dom.FirstChildElement(); + verifyElement( elem, "VTKFile" ); + if( elem->NextSibling() ) + throw MeshReaderError( "XMLVTK", " is not the only element in the file" ); + + // verify byte order + const std::string systemByteOrder = (isLittleEndian()) ? "LittleEndian" : "BigEndian"; + byteOrder = getAttributeString( elem, "byte_order" ); + if( byteOrder != systemByteOrder ) + throw MeshReaderError( "XMLVTK", "incompatible byte_order: " + byteOrder + " (the system is " + systemByteOrder + " and the conversion " + "from BigEndian to LittleEndian or vice versa is not implemented yet)" ); + + // verify header type + headerType = getAttributeString( elem, "header_type", "UInt32" ); + if( VTKDataTypes.count( headerType ) == 0 ) + throw MeshReaderError( "XMLVTK", "invalid header_type: " + headerType ); + headerType = VTKDataTypes.at( headerType ); + + // verify compressor + compressor = getAttributeString( elem, "compressor", "" ); + if( compressor == "" ) + compressor = ""; + if( compressor != "" && compressor != "vtkZLibDataCompressor" ) + throw MeshReaderError( "XMLVTK", "unsupported compressor type: " + compressor + " (only vtkZLibDataCompressor is supported)" ); + + // get file type and the corresponding XML element + fileType = getAttributeString( elem, "type" ); + datasetElement = verifyHasOnlyOneChild( elem, fileType ); +#else + throw_no_tinyxml(); +#endif + } + + VariantVector + readPointData( std::string arrayName ) + { +#ifdef HAVE_TINYXML2 + return readPointOrCellData( "PointData", arrayName ); +#else + throw_no_tinyxml(); +#endif + } + + VariantVector + readCellData( std::string arrayName ) + { +#ifdef HAVE_TINYXML2 + return readPointOrCellData( "CellData", arrayName ); +#else + throw_no_tinyxml(); +#endif + } + + virtual void reset() override + { + resetBase(); + fileType = ""; + byteOrder = compressor = headerType = ""; +#ifdef HAVE_TINYXML2 + dom.Clear(); + datasetElement = nullptr; +#endif + } + +protected: + // parsed VTK file type (e.g. "UnstructuredGrid") + std::string fileType; + + // other attributes parsed from the element + std::string byteOrder, compressor, headerType; + +#ifdef HAVE_TINYXML2 + // internal attribute representing the whole XML file + tinyxml2::XMLDocument dom; + + // pointer to the main XML element (e.g. ) + const tinyxml2::XMLElement* datasetElement = nullptr; +#endif +}; + +} // namespace Readers +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/Traverser.h b/src/TNL/Meshes/Traverser.h index f157e3afcd56fb6ffebf95865d7546490fb00668..c0c2103c120b4129dcbcc6e43be20a880a7d370b 100644 --- a/src/TNL/Meshes/Traverser.h +++ b/src/TNL/Meshes/Traverser.h @@ -22,32 +22,42 @@ template< typename Mesh, int EntitiesDimension = MeshEntity::getEntityDimension() > class Traverser { - public: - using MeshType = Mesh; - using MeshPointer = Pointers::SharedPointer< MeshType >; - using DeviceType = typename MeshType::DeviceType; - using GlobalIndexType = typename MeshType::GlobalIndexType; - - template< typename EntitiesProcessor, - typename UserData > - void processBoundaryEntities( const MeshPointer& meshPointer, - UserData userData ) const; - - template< typename EntitiesProcessor, - typename UserData > - void processInteriorEntities( const MeshPointer& meshPointer, - UserData userData ) const; - - template< typename EntitiesProcessor, - typename UserData > - void processAllEntities( const MeshPointer& meshPointer, - UserData userData ) const; +public: + using MeshType = Mesh; + using MeshPointer = Pointers::SharedPointer< MeshType >; + using DeviceType = typename MeshType::DeviceType; + using GlobalIndexType = typename MeshType::GlobalIndexType; + + template< typename EntitiesProcessor, + typename UserData > + void processBoundaryEntities( const MeshPointer& meshPointer, + UserData userData ) const; + + template< typename EntitiesProcessor, + typename UserData > + void processInteriorEntities( const MeshPointer& meshPointer, + UserData userData ) const; + + template< typename EntitiesProcessor, + typename UserData > + void processAllEntities( const MeshPointer& meshPointer, + UserData userData ) const; + + template< typename EntitiesProcessor, + typename UserData > + void processGhostEntities( const MeshPointer& meshPointer, + UserData userData ) const; + + template< typename EntitiesProcessor, + typename UserData > + void processLocalEntities( const MeshPointer& meshPointer, + UserData userData ) const; }; } // namespace Meshes } // namespace TNL -#include +#include #include #include #include diff --git a/src/TNL/Meshes/Traverser.hpp b/src/TNL/Meshes/Traverser.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9f99b1e74e46b41b425c7078e3025e0e04b1e8cf --- /dev/null +++ b/src/TNL/Meshes/Traverser.hpp @@ -0,0 +1,160 @@ +/*************************************************************************** + Traverser.hpp - description + ------------------- + begin : Dec 25, 2016 + copyright : (C) 2016 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +#pragma once + +#include +#include + +namespace TNL { +namespace Meshes { + +template< typename Mesh, + typename MeshEntity, + int EntitiesDimension > + template< typename EntitiesProcessor, + typename UserData > +void +Traverser< Mesh, MeshEntity, EntitiesDimension >:: +processBoundaryEntities( const MeshPointer& meshPointer, + UserData userData ) const +{ + const auto boundaryIndices = meshPointer->template getBoundaryIndices< MeshEntity::getEntityDimension() >(); + const GlobalIndexType entitiesCount = boundaryIndices.getSize(); + auto kernel = [boundaryIndices] __cuda_callable__ + ( const GlobalIndexType i, + const Mesh* mesh, + UserData userData ) + { + const GlobalIndexType entityIndex = boundaryIndices[ i ]; + const auto entity = mesh->template getEntity< MeshEntity::getEntityDimension() >( entityIndex ); + EntitiesProcessor::processEntity( *mesh, userData, entity ); + }; + Pointers::synchronizeSmartPointersOnDevice< DeviceType >(); + Algorithms::ParallelFor< DeviceType >::exec( + (GlobalIndexType) 0, entitiesCount, + kernel, + &meshPointer.template getData< DeviceType >(), + userData ); +} + +template< typename Mesh, + typename MeshEntity, + int EntitiesDimension > + template< typename EntitiesProcessor, + typename UserData > +void +Traverser< Mesh, MeshEntity, EntitiesDimension >:: +processInteriorEntities( const MeshPointer& meshPointer, + UserData userData ) const +{ + const auto interiorIndices = meshPointer->template getInteriorIndices< MeshEntity::getEntityDimension() >(); + const GlobalIndexType entitiesCount = interiorIndices.getSize(); + auto kernel = [interiorIndices] __cuda_callable__ + ( const GlobalIndexType i, + const Mesh* mesh, + UserData userData ) + { + const GlobalIndexType entityIndex = interiorIndices[ i ]; + const auto entity = mesh->template getEntity< MeshEntity::getEntityDimension() >( entityIndex ); + EntitiesProcessor::processEntity( *mesh, userData, entity ); + }; + Pointers::synchronizeSmartPointersOnDevice< DeviceType >(); + Algorithms::ParallelFor< DeviceType >::exec( + (GlobalIndexType) 0, entitiesCount, + kernel, + &meshPointer.template getData< DeviceType >(), + userData ); +} + +template< typename Mesh, + typename MeshEntity, + int EntitiesDimension > + template< typename EntitiesProcessor, + typename UserData > +void +Traverser< Mesh, MeshEntity, EntitiesDimension >:: +processAllEntities( const MeshPointer& meshPointer, + UserData userData ) const +{ + const GlobalIndexType entitiesCount = meshPointer->template getEntitiesCount< MeshEntity::getEntityDimension() >(); + auto kernel = [] __cuda_callable__ + ( const GlobalIndexType entityIndex, + const Mesh* mesh, + UserData userData ) + { + const auto entity = mesh->template getEntity< MeshEntity::getEntityDimension() >( entityIndex ); + EntitiesProcessor::processEntity( *mesh, userData, entity ); + }; + Pointers::synchronizeSmartPointersOnDevice< DeviceType >(); + Algorithms::ParallelFor< DeviceType >::exec( + (GlobalIndexType) 0, entitiesCount, + kernel, + &meshPointer.template getData< DeviceType >(), + userData ); +} + +template< typename Mesh, + typename MeshEntity, + int EntitiesDimension > + template< typename EntitiesProcessor, + typename UserData > +void +Traverser< Mesh, MeshEntity, EntitiesDimension >:: +processGhostEntities( const MeshPointer& meshPointer, + UserData userData ) const +{ + const GlobalIndexType ghostsOffset = meshPointer->template getGhostEntitiesOffset< MeshEntity::getEntityDimension() >(); + const GlobalIndexType entitiesCount = meshPointer->template getEntitiesCount< MeshEntity::getEntityDimension() >(); + auto kernel = [] __cuda_callable__ + ( const GlobalIndexType entityIndex, + const Mesh* mesh, + UserData userData ) + { + const auto entity = mesh->template getEntity< MeshEntity::getEntityDimension() >( entityIndex ); + EntitiesProcessor::processEntity( *mesh, userData, entity ); + }; + Pointers::synchronizeSmartPointersOnDevice< DeviceType >(); + Algorithms::ParallelFor< DeviceType >::exec( + ghostsOffset, entitiesCount, + kernel, + &meshPointer.template getData< DeviceType >(), + userData ); +} + +template< typename Mesh, + typename MeshEntity, + int EntitiesDimension > + template< typename EntitiesProcessor, + typename UserData > +void +Traverser< Mesh, MeshEntity, EntitiesDimension >:: +processLocalEntities( const MeshPointer& meshPointer, + UserData userData ) const +{ + const GlobalIndexType ghostsOffset = meshPointer->template getGhostEntitiesOffset< MeshEntity::getEntityDimension() >(); + auto kernel = [] __cuda_callable__ + ( const GlobalIndexType entityIndex, + const Mesh* mesh, + UserData userData ) + { + const auto entity = mesh->template getEntity< MeshEntity::getEntityDimension() >( entityIndex ); + EntitiesProcessor::processEntity( *mesh, userData, entity ); + }; + Pointers::synchronizeSmartPointersOnDevice< DeviceType >(); + Algorithms::ParallelFor< DeviceType >::exec( + (GlobalIndexType) 0, ghostsOffset, + kernel, + &meshPointer.template getData< DeviceType >(), + userData ); +} + +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/TypeResolver/GridTypeResolver.h b/src/TNL/Meshes/TypeResolver/GridTypeResolver.h index 498ae0f589cb28a40c31b0f382f0e620f1a96a4b..3a23a80288a96940b01f0955d27c893d5e0e71ac 100644 --- a/src/TNL/Meshes/TypeResolver/GridTypeResolver.h +++ b/src/TNL/Meshes/TypeResolver/GridTypeResolver.h @@ -17,84 +17,75 @@ namespace TNL { namespace Meshes { -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > +template< typename ConfigTag, + typename Device > class GridTypeResolver { public: - static bool run( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); + template< typename Reader, typename Functor > + static bool run( Reader& reader, Functor&& functor ); protected: - - static bool resolveGridDimension( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); - - // NOTE: We could disable the grids only by the GridTag, but doing the - // resolution for all subtypes is more flexible and also pretty - // good optimization of compilation times. - - // Overload for disabled grid dimensions - template< int MeshDimension, - typename = typename std::enable_if< ! BuildConfigTags::GridDimensionTag< ConfigTag, MeshDimension >::enabled >::type, - typename = void > - static bool resolveReal( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); - - // Overload for enabled grid dimensions - template< int MeshDimension, - typename = typename std::enable_if< BuildConfigTags::GridDimensionTag< ConfigTag, MeshDimension >::enabled >::type > - static bool resolveReal( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); - - // Overload for disabled real types - template< int MeshDimension, - typename Real, - typename = typename std::enable_if< ! BuildConfigTags::GridRealTag< ConfigTag, Real >::enabled >::type, - typename = void > - static bool resolveIndex( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); - - // Overload for enabled real types - template< int MeshDimension, - typename Real, - typename = typename std::enable_if< BuildConfigTags::GridRealTag< ConfigTag, Real >::enabled >::type > - static bool resolveIndex( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); - - // Overload for disabled index types - template< int MeshDimension, - typename Real, - typename Index, - typename = typename std::enable_if< ! BuildConfigTags::GridIndexTag< ConfigTag, Index >::enabled >::type, - typename = void > - static bool resolveGridType( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); - - // Overload for enabled index types - template< int MeshDimension, - typename Real, - typename Index, - typename = typename std::enable_if< BuildConfigTags::GridIndexTag< ConfigTag, Index >::enabled >::type > - static bool resolveGridType( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); - - // Overload for disabled grid types - template< typename GridType, - typename = typename std::enable_if< ! BuildConfigTags::GridTag< ConfigTag, GridType >::enabled >::type, - typename = void > - static bool resolveTerminate( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); - - // Overload for enabled grid types - template< typename GridType, - typename = typename std::enable_if< BuildConfigTags::GridTag< ConfigTag, GridType >::enabled >::type > - static bool resolveTerminate( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); + template< typename Reader, typename Functor > + struct detail + { + static bool resolveGridDimension( Reader& reader, Functor&& functor ); + + // NOTE: We could disable the grids only by the GridTag, but doing the + // resolution for all subtypes is more flexible and also pretty + // good optimization of compilation times. + + // Overload for disabled grid dimensions + template< int MeshDimension, + typename = typename std::enable_if< ! BuildConfigTags::GridDimensionTag< ConfigTag, MeshDimension >::enabled >::type, + typename = void > + static bool resolveReal( Reader& reader, Functor&& functor ); + + // Overload for enabled grid dimensions + template< int MeshDimension, + typename = typename std::enable_if< BuildConfigTags::GridDimensionTag< ConfigTag, MeshDimension >::enabled >::type > + static bool resolveReal( Reader& reader, Functor&& functor ); + + // Overload for disabled real types + template< int MeshDimension, + typename Real, + typename = typename std::enable_if< ! BuildConfigTags::GridRealTag< ConfigTag, Real >::enabled >::type, + typename = void > + static bool resolveIndex( Reader& reader, Functor&& functor ); + + // Overload for enabled real types + template< int MeshDimension, + typename Real, + typename = typename std::enable_if< BuildConfigTags::GridRealTag< ConfigTag, Real >::enabled >::type > + static bool resolveIndex( Reader& reader, Functor&& functor ); + + // Overload for disabled index types + template< int MeshDimension, + typename Real, + typename Index, + typename = typename std::enable_if< ! BuildConfigTags::GridIndexTag< ConfigTag, Index >::enabled >::type, + typename = void > + static bool resolveGridType( Reader& reader, Functor&& functor ); + + // Overload for enabled index types + template< int MeshDimension, + typename Real, + typename Index, + typename = typename std::enable_if< BuildConfigTags::GridIndexTag< ConfigTag, Index >::enabled >::type > + static bool resolveGridType( Reader& reader, Functor&& functor ); + + // Overload for disabled grid types + template< typename GridType, + typename = typename std::enable_if< ! BuildConfigTags::GridTag< ConfigTag, GridType >::enabled >::type, + typename = void > + static bool resolveTerminate( Reader& reader, Functor&& functor ); + + // Overload for enabled grid types + template< typename GridType, + typename = typename std::enable_if< BuildConfigTags::GridTag< ConfigTag, GridType >::enabled >::type > + static bool resolveTerminate( Reader& reader, Functor&& functor ); + }; }; } // namespace Meshes diff --git a/src/TNL/Meshes/TypeResolver/GridTypeResolver_impl.h b/src/TNL/Meshes/TypeResolver/GridTypeResolver_impl.h index 20f40c26820e611b3055879ec96445e13656f802..817d2b23fc85baccbed3e708b1ca82636f441423 100644 --- a/src/TNL/Meshes/TypeResolver/GridTypeResolver_impl.h +++ b/src/TNL/Meshes/TypeResolver/GridTypeResolver_impl.h @@ -17,184 +17,172 @@ #include namespace TNL { -namespace Meshes { +namespace Meshes { -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > bool -GridTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -run( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +GridTypeResolver< ConfigTag, Device >:: +run( Reader& reader, Functor&& functor ) { - return resolveGridDimension( reader, std::forward(problemSetterArgs)... ); + return detail< Reader, Functor >::resolveGridDimension( reader, std::forward(functor) ); } -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > bool -GridTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveGridDimension( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +GridTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: +resolveGridDimension( Reader& reader, Functor&& functor ) { if( reader.getMeshDimension() == 1 ) - return resolveReal< 1 >( reader, std::forward(problemSetterArgs)... ); + return resolveReal< 1 >( reader, std::forward(functor) ); if( reader.getMeshDimension() == 2 ) - return resolveReal< 2 >( reader, std::forward(problemSetterArgs)... ); + return resolveReal< 2 >( reader, std::forward(functor) ); if( reader.getMeshDimension() == 3 ) - return resolveReal< 3 >( reader, std::forward(problemSetterArgs)... ); + return resolveReal< 3 >( reader, std::forward(functor) ); std::cerr << "Unsupported mesh dimension: " << reader.getMeshDimension() << std::endl; return false; } -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > - template< int MeshDimension, - typename, typename > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > + template< int MeshDimension, + typename, typename > bool -GridTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveReal( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +GridTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: +resolveReal( Reader& reader, Functor&& functor ) { std::cerr << "The grid dimension " << MeshDimension << " is disabled in the build configuration." << std::endl; return false; } -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > - template< int MeshDimension, - typename > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > + template< int MeshDimension, + typename > bool -GridTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveReal( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +GridTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: +resolveReal( Reader& reader, Functor&& functor ) { if( reader.getRealType() == "float" ) - return resolveIndex< MeshDimension, float >( reader, std::forward(problemSetterArgs)... ); + return resolveIndex< MeshDimension, float >( reader, std::forward(functor) ); if( reader.getRealType() == "double" ) - return resolveIndex< MeshDimension, double >( reader, std::forward(problemSetterArgs)... ); - if( reader.getRealType() == "long-double" ) - return resolveIndex< MeshDimension, long double >( reader, std::forward(problemSetterArgs)... ); + return resolveIndex< MeshDimension, double >( reader, std::forward(functor) ); + if( reader.getRealType() == "long double" ) + return resolveIndex< MeshDimension, long double >( reader, std::forward(functor) ); std::cerr << "Unsupported real type: " << reader.getRealType() << std::endl; return false; } -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > - template< int MeshDimension, - typename Real, - typename, typename > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > + template< int MeshDimension, + typename Real, + typename, typename > bool -GridTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveIndex( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +GridTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: +resolveIndex( Reader& reader, Functor&& functor ) { std::cerr << "The grid real type " << getType< Real >() << " is disabled in the build configuration." << std::endl; return false; } -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > - template< int MeshDimension, - typename Real, - typename > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > + template< int MeshDimension, + typename Real, + typename > bool -GridTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveIndex( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +GridTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: +resolveIndex( Reader& reader, Functor&& functor ) { - if( reader.getGlobalIndexType() == "short int" ) - return resolveGridType< MeshDimension, Real, short int >( reader, std::forward(problemSetterArgs)... ); - if( reader.getGlobalIndexType() == "int" ) - return resolveGridType< MeshDimension, Real, int >( reader, std::forward(problemSetterArgs)... ); - if( reader.getGlobalIndexType() == "long int" ) - return resolveGridType< MeshDimension, Real, long int >( reader, std::forward(problemSetterArgs)... ); + if( reader.getGlobalIndexType() == "short" || + reader.getGlobalIndexType() == "short int" || + reader.getGlobalIndexType() == "std::int16_t" || + reader.getGlobalIndexType() == "std::uint16_t" ) + return resolveGridType< MeshDimension, Real, short int >( reader, std::forward(functor) ); + if( reader.getGlobalIndexType() == "int" || + reader.getGlobalIndexType() == "std::int32_t" || + reader.getGlobalIndexType() == "std::uint32_t" ) + return resolveGridType< MeshDimension, Real, int >( reader, std::forward(functor) ); + if( reader.getGlobalIndexType() == "long" || + reader.getGlobalIndexType() == "long int" || + reader.getGlobalIndexType() == "std::int64_t" || + reader.getGlobalIndexType() == "std::uint64_t" ) + return resolveGridType< MeshDimension, Real, long int >( reader, std::forward(functor) ); std::cerr << "Unsupported index type: " << reader.getRealType() << std::endl; return false; } -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > - template< int MeshDimension, - typename Real, - typename Index, - typename, typename > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > + template< int MeshDimension, + typename Real, + typename Index, + typename, typename > bool -GridTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveGridType( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +GridTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: +resolveGridType( Reader& reader, Functor&& functor ) { std::cerr << "The grid index type " << getType< Index >() << " is disabled in the build configuration." << std::endl; return false; } -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > - template< int MeshDimension, - typename Real, - typename Index, - typename > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > + template< int MeshDimension, + typename Real, + typename Index, + typename > bool -GridTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveGridType( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +GridTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: +resolveGridType( Reader& reader, Functor&& functor ) { using GridType = Meshes::Grid< MeshDimension, Real, Device, Index >; - return resolveTerminate< GridType >( reader, std::forward(problemSetterArgs)... ); + return resolveTerminate< GridType >( reader, std::forward(functor) ); } -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > - template< typename GridType, - typename, typename > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > + template< typename GridType, + typename, typename > bool -GridTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveTerminate( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +GridTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: +resolveTerminate( Reader& reader, Functor&& functor ) { std::cerr << "The mesh type " << TNL::getType< GridType >() << " is disabled in the build configuration." << std::endl; return false; }; -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > - template< typename GridType, - typename > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > + template< typename GridType, + typename > bool -GridTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveTerminate( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +GridTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: +resolveTerminate( Reader& reader, Functor&& functor ) { - return ProblemSetter< GridType >::run( std::forward(problemSetterArgs)... ); + return std::forward(functor)( reader, GridType{} ); }; } // namespace Meshes diff --git a/src/TNL/Meshes/TypeResolver/MeshTypeResolver.h b/src/TNL/Meshes/TypeResolver/MeshTypeResolver.h index dfdb0ad1ed312e8a4841361644f5bdb97ee37dd2..a8b1ecb4ac707ab99bb0f8b9a290eb5b2c75c5cd 100644 --- a/src/TNL/Meshes/TypeResolver/MeshTypeResolver.h +++ b/src/TNL/Meshes/TypeResolver/MeshTypeResolver.h @@ -17,165 +17,127 @@ namespace TNL { namespace Meshes { -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > +template< typename ConfigTag, + typename Device > class MeshTypeResolver { public: - static bool run( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); + template< typename Reader, typename Functor > + static bool run( Reader& reader, Functor&& functor ); protected: - - static bool resolveCellTopology( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); - - // NOTE: We could disable the meshes only by the MeshTag, but doing the - // resolution for all subtypes is more flexible and also pretty - // good optimization of compilation times. - - // Overload for disabled cell topologies - template< typename CellTopology, - typename = typename std::enable_if< ! BuildConfigTags::MeshCellTopologyTag< ConfigTag, CellTopology >::enabled >::type, - typename = void > - static bool resolveWorldDimension( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); - - // Overload for enabled cell topologies - template< typename CellTopology, - typename = typename std::enable_if< BuildConfigTags::MeshCellTopologyTag< ConfigTag, CellTopology >::enabled >::type > - static bool resolveWorldDimension( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); - - // Overload for disabled world dimensions - template< typename CellTopology, - int WorldDimension, - typename = typename std::enable_if< ! BuildConfigTags::MeshWorldDimensionTag< ConfigTag, CellTopology, WorldDimension >::enabled >::type, - typename = void > - static bool resolveReal( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); - - // Overload for enabled world dimensions - template< typename CellTopology, - int WorldDimension, - typename = typename std::enable_if< BuildConfigTags::MeshWorldDimensionTag< ConfigTag, CellTopology, WorldDimension >::enabled >::type > - static bool resolveReal( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); - - // Overload for disabled real types - template< typename CellTopology, - int WorldDimension, - typename Real, - typename = typename std::enable_if< ! BuildConfigTags::MeshRealTag< ConfigTag, Real >::enabled >::type, - typename = void > - static bool resolveGlobalIndex( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); - - // Overload for enabled real types - template< typename CellTopology, - int WorldDimension, - typename Real, - typename = typename std::enable_if< BuildConfigTags::MeshRealTag< ConfigTag, Real >::enabled >::type > - static bool resolveGlobalIndex( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); - - // Overload for disabled global index types - template< typename CellTopology, - int WorldDimension, - typename Real, - typename GlobalIndex, - typename = typename std::enable_if< ! BuildConfigTags::MeshGlobalIndexTag< ConfigTag, GlobalIndex >::enabled >::type, - typename = void > - static bool resolveLocalIndex( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); - - // Overload for enabled global index types - template< typename CellTopology, - int WorldDimension, - typename Real, - typename GlobalIndex, - typename = typename std::enable_if< BuildConfigTags::MeshGlobalIndexTag< ConfigTag, GlobalIndex >::enabled >::type > - static bool resolveLocalIndex( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); - - // Overload for disabled local index types - template< typename CellTopology, - int WorldDimension, - typename Real, - typename GlobalIndex, - typename LocalIndex, - typename = typename std::enable_if< ! BuildConfigTags::MeshLocalIndexTag< ConfigTag, LocalIndex >::enabled >::type, - typename = void > - static bool resolveId( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); - - // Overload for enabled local index types - template< typename CellTopology, - int WorldDimension, - typename Real, - typename GlobalIndex, - typename LocalIndex, - typename = typename std::enable_if< BuildConfigTags::MeshLocalIndexTag< ConfigTag, LocalIndex >::enabled >::type > - static bool resolveId( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); - - // Overload for disabled id types - template< typename CellTopology, - int WorldDimension, - typename Real, - typename GlobalIndex, - typename LocalIndex, - typename Id, - typename = typename std::enable_if< ! BuildConfigTags::MeshIdTag< ConfigTag, GlobalIndex, Id >::enabled >::type, - typename = void > - static bool resolveMeshType( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); - - // Overload for enabled id types - template< typename CellTopology, - int WorldDimension, - typename Real, - typename GlobalIndex, - typename LocalIndex, - typename Id, - typename = typename std::enable_if< BuildConfigTags::MeshIdTag< ConfigTag, GlobalIndex, Id >::enabled >::type > - static bool resolveMeshType( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); - - // Overload for disabled mesh types - template< typename MeshConfig, - typename = typename std::enable_if< ! BuildConfigTags::MeshDeviceTag< ConfigTag, Device >::enabled || - ! BuildConfigTags::MeshTag< ConfigTag, - Device, - typename MeshConfig::CellTopology, - MeshConfig::worldDimension, - typename MeshConfig::RealType, - typename MeshConfig::GlobalIndexType, - typename MeshConfig::LocalIndexType, - typename MeshConfig::IdType - >::enabled >::type, - typename = void > - static bool resolveTerminate( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); - - // Overload for enabled mesh types - template< typename MeshConfig, - typename = typename std::enable_if< BuildConfigTags::MeshDeviceTag< ConfigTag, Device >::enabled && - BuildConfigTags::MeshTag< ConfigTag, - Device, - typename MeshConfig::CellTopology, - MeshConfig::worldDimension, - typename MeshConfig::RealType, - typename MeshConfig::GlobalIndexType, - typename MeshConfig::LocalIndexType, - typename MeshConfig::IdType - >::enabled >::type > - static bool resolveTerminate( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ); + template< typename Reader, typename Functor > + struct detail + { + static bool resolveCellTopology( Reader& reader, Functor&& functor ); + + // NOTE: We could disable the meshes only by the MeshTag, but doing the + // resolution for all subtypes is more flexible and also pretty + // good optimization of compilation times. + + // Overload for disabled cell topologies + template< typename CellTopology, + typename = typename std::enable_if< ! BuildConfigTags::MeshCellTopologyTag< ConfigTag, CellTopology >::enabled >::type, + typename = void > + static bool resolveWorldDimension( Reader& reader, Functor&& functor ); + + // Overload for enabled cell topologies + template< typename CellTopology, + typename = typename std::enable_if< BuildConfigTags::MeshCellTopologyTag< ConfigTag, CellTopology >::enabled >::type > + static bool resolveWorldDimension( Reader& reader, Functor&& functor ); + + // Overload for disabled world dimensions + template< typename CellTopology, + int WorldDimension, + typename = typename std::enable_if< ! BuildConfigTags::MeshWorldDimensionTag< ConfigTag, CellTopology, WorldDimension >::enabled >::type, + typename = void > + static bool resolveReal( Reader& reader, Functor&& functor ); + + // Overload for enabled world dimensions + template< typename CellTopology, + int WorldDimension, + typename = typename std::enable_if< BuildConfigTags::MeshWorldDimensionTag< ConfigTag, CellTopology, WorldDimension >::enabled >::type > + static bool resolveReal( Reader& reader, Functor&& functor ); + + // Overload for disabled real types + template< typename CellTopology, + int WorldDimension, + typename Real, + typename = typename std::enable_if< ! BuildConfigTags::MeshRealTag< ConfigTag, Real >::enabled >::type, + typename = void > + static bool resolveGlobalIndex( Reader& reader, Functor&& functor ); + + // Overload for enabled real types + template< typename CellTopology, + int WorldDimension, + typename Real, + typename = typename std::enable_if< BuildConfigTags::MeshRealTag< ConfigTag, Real >::enabled >::type > + static bool resolveGlobalIndex( Reader& reader, Functor&& functor ); + + // Overload for disabled global index types + template< typename CellTopology, + int WorldDimension, + typename Real, + typename GlobalIndex, + typename = typename std::enable_if< ! BuildConfigTags::MeshGlobalIndexTag< ConfigTag, GlobalIndex >::enabled >::type, + typename = void > + static bool resolveLocalIndex( Reader& reader, Functor&& functor ); + + // Overload for enabled global index types + template< typename CellTopology, + int WorldDimension, + typename Real, + typename GlobalIndex, + typename = typename std::enable_if< BuildConfigTags::MeshGlobalIndexTag< ConfigTag, GlobalIndex >::enabled >::type > + static bool resolveLocalIndex( Reader& reader, Functor&& functor ); + + // Overload for disabled local index types + template< typename CellTopology, + int WorldDimension, + typename Real, + typename GlobalIndex, + typename LocalIndex, + typename = typename std::enable_if< ! BuildConfigTags::MeshLocalIndexTag< ConfigTag, LocalIndex >::enabled >::type, + typename = void > + static bool resolveMeshType( Reader& reader, Functor&& functor ); + + // Overload for enabled local index types + template< typename CellTopology, + int WorldDimension, + typename Real, + typename GlobalIndex, + typename LocalIndex, + typename = typename std::enable_if< BuildConfigTags::MeshLocalIndexTag< ConfigTag, LocalIndex >::enabled >::type > + static bool resolveMeshType( Reader& reader, Functor&& functor ); + + // Overload for disabled mesh types + template< typename MeshConfig, + typename = typename std::enable_if< ! BuildConfigTags::MeshDeviceTag< ConfigTag, Device >::enabled || + ! BuildConfigTags::MeshTag< ConfigTag, + Device, + typename MeshConfig::CellTopology, + MeshConfig::worldDimension, + typename MeshConfig::RealType, + typename MeshConfig::GlobalIndexType, + typename MeshConfig::LocalIndexType + >::enabled >::type, + typename = void > + static bool resolveTerminate( Reader& reader, Functor&& functor ); + + // Overload for enabled mesh types + template< typename MeshConfig, + typename = typename std::enable_if< BuildConfigTags::MeshDeviceTag< ConfigTag, Device >::enabled && + BuildConfigTags::MeshTag< ConfigTag, + Device, + typename MeshConfig::CellTopology, + MeshConfig::worldDimension, + typename MeshConfig::RealType, + typename MeshConfig::GlobalIndexType, + typename MeshConfig::LocalIndexType + >::enabled >::type > + static bool resolveTerminate( Reader& reader, Functor&& functor ); + }; }; } // namespace Meshes diff --git a/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h b/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h index b92148fa90ddf5c0d8a46e90aa817d9b8531ffd2..c4a29fadc34bccb9cbbbe0bd138322b8a5cff99d 100644 --- a/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h +++ b/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h @@ -15,107 +15,96 @@ #include #include #include -#include +#include namespace TNL { namespace Meshes { -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > bool -MeshTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -run( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +MeshTypeResolver< ConfigTag, Device >:: +run( Reader& reader, Functor&& functor ) { - return resolveCellTopology( reader, std::forward(problemSetterArgs)... ); + return detail< Reader, Functor >::resolveCellTopology( reader, std::forward(functor) ); } -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > bool -MeshTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveCellTopology( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +MeshTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: +resolveCellTopology( Reader& reader, Functor&& functor ) { - using Readers::EntityShape; switch( reader.getCellShape() ) { - case EntityShape::Line: - return resolveWorldDimension< Topologies::Edge >( reader, std::forward(problemSetterArgs)... ); - case EntityShape::Triangle: - return resolveWorldDimension< Topologies::Triangle >( reader, std::forward(problemSetterArgs)... ); - case EntityShape::Quad: - return resolveWorldDimension< Topologies::Quadrilateral >( reader, std::forward(problemSetterArgs)... ); - case EntityShape::Tetra: - return resolveWorldDimension< Topologies::Tetrahedron >( reader, std::forward(problemSetterArgs)... ); - case EntityShape::Hexahedron: - return resolveWorldDimension< Topologies::Hexahedron >( reader, std::forward(problemSetterArgs)... ); + case VTK::EntityShape::Line: + return resolveWorldDimension< Topologies::Edge >( reader, std::forward(functor) ); + case VTK::EntityShape::Triangle: + return resolveWorldDimension< Topologies::Triangle >( reader, std::forward(functor) ); + case VTK::EntityShape::Quad: + return resolveWorldDimension< Topologies::Quadrilateral >( reader, std::forward(functor) ); + case VTK::EntityShape::Tetra: + return resolveWorldDimension< Topologies::Tetrahedron >( reader, std::forward(functor) ); + case VTK::EntityShape::Hexahedron: + return resolveWorldDimension< Topologies::Hexahedron >( reader, std::forward(functor) ); default: - std::cerr << "unsupported cell topology: " << reader.getCellShape() << std::endl; + std::cerr << "unsupported cell topology: " << VTK::getShapeName( reader.getCellShape() ) << std::endl; return false; } } -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > - template< typename CellTopology, - typename, typename > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > + template< typename CellTopology, + typename, typename > bool -MeshTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveWorldDimension( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +MeshTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: +resolveWorldDimension( Reader& reader, Functor&& functor ) { std::cerr << "The cell topology " << getType< CellTopology >() << " is disabled in the build configuration." << std::endl; return false; } -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > - template< typename CellTopology, - typename > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > + template< typename CellTopology, + typename > bool -MeshTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveWorldDimension( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +MeshTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: +resolveWorldDimension( Reader& reader, Functor&& functor ) { switch( reader.getWorldDimension() ) { case 1: - return resolveReal< CellTopology, 1 >( reader, std::forward(problemSetterArgs)... ); + return resolveReal< CellTopology, 1 >( reader, std::forward(functor) ); case 2: - return resolveReal< CellTopology, 2 >( reader, std::forward(problemSetterArgs)... ); + return resolveReal< CellTopology, 2 >( reader, std::forward(functor) ); case 3: - return resolveReal< CellTopology, 3 >( reader, std::forward(problemSetterArgs)... ); + return resolveReal< CellTopology, 3 >( reader, std::forward(functor) ); default: std::cerr << "unsupported world dimension: " << reader.getWorldDimension() << std::endl; return false; } } -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > - template< typename CellTopology, - int WorldDimension, - typename, typename > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > + template< typename CellTopology, + int WorldDimension, + typename, typename > bool -MeshTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveReal( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +MeshTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: +resolveReal( Reader& reader, Functor&& functor ) { std::cerr << "The combination of world dimension (" << WorldDimension << ") and mesh dimension (" << CellTopology::dimension @@ -123,235 +112,183 @@ resolveReal( const Reader& reader, return false; } -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > - template< typename CellTopology, - int WorldDimension, - typename > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > + template< typename CellTopology, + int WorldDimension, + typename > bool -MeshTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveReal( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +MeshTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: +resolveReal( Reader& reader, Functor&& functor ) { if( reader.getRealType() == "float" ) - return resolveGlobalIndex< CellTopology, WorldDimension, float >( reader, std::forward(problemSetterArgs)... ); + return resolveGlobalIndex< CellTopology, WorldDimension, float >( reader, std::forward(functor) ); if( reader.getRealType() == "double" ) - return resolveGlobalIndex< CellTopology, WorldDimension, double >( reader, std::forward(problemSetterArgs)... ); - if( reader.getRealType() == "long-double" ) - return resolveGlobalIndex< CellTopology, WorldDimension, long double >( reader, std::forward(problemSetterArgs)... ); + return resolveGlobalIndex< CellTopology, WorldDimension, double >( reader, std::forward(functor) ); + if( reader.getRealType() == "long double" ) + return resolveGlobalIndex< CellTopology, WorldDimension, long double >( reader, std::forward(functor) ); std::cerr << "Unsupported real type: " << reader.getRealType() << std::endl; return false; } -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > - template< typename CellTopology, - int WorldDimension, - typename Real, - typename, typename > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > + template< typename CellTopology, + int WorldDimension, + typename Real, + typename, typename > bool -MeshTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveGlobalIndex( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +MeshTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: +resolveGlobalIndex( Reader& reader, Functor&& functor ) { std::cerr << "The mesh real type " << getType< Real >() << " is disabled in the build configuration." << std::endl; return false; } -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > - template< typename CellTopology, - int WorldDimension, - typename Real, - typename > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > + template< typename CellTopology, + int WorldDimension, + typename Real, + typename > bool -MeshTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveGlobalIndex( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +MeshTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: +resolveGlobalIndex( Reader& reader, Functor&& functor ) { - if( reader.getGlobalIndexType() == "short int" ) - return resolveLocalIndex< CellTopology, WorldDimension, Real, short int >( reader, std::forward(problemSetterArgs)... ); - if( reader.getGlobalIndexType() == "int" ) - return resolveLocalIndex< CellTopology, WorldDimension, Real, int >( reader, std::forward(problemSetterArgs)... ); - if( reader.getGlobalIndexType() == "long int" ) - return resolveLocalIndex< CellTopology, WorldDimension, Real, long int >( reader, std::forward(problemSetterArgs)... ); + if( reader.getGlobalIndexType() == "short" || + reader.getGlobalIndexType() == "short int" || + reader.getGlobalIndexType() == "std::int16_t" || + reader.getGlobalIndexType() == "std::uint16_t" ) + return resolveLocalIndex< CellTopology, WorldDimension, Real, short int >( reader, std::forward(functor) ); + if( reader.getGlobalIndexType() == "int" || + reader.getGlobalIndexType() == "std::int32_t" || + reader.getGlobalIndexType() == "std::uint32_t" ) + return resolveLocalIndex< CellTopology, WorldDimension, Real, int >( reader, std::forward(functor) ); + if( reader.getGlobalIndexType() == "long" || + reader.getGlobalIndexType() == "long int" || + reader.getGlobalIndexType() == "std::int64_t" || + reader.getGlobalIndexType() == "std::uint64_t" ) + return resolveLocalIndex< CellTopology, WorldDimension, Real, long int >( reader, std::forward(functor) ); std::cerr << "Unsupported global index type: " << reader.getGlobalIndexType() << std::endl; return false; } -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > - template< typename CellTopology, - int WorldDimension, - typename Real, - typename GlobalIndex, - typename, typename > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > + template< typename CellTopology, + int WorldDimension, + typename Real, + typename GlobalIndex, + typename, typename > bool -MeshTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveLocalIndex( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +MeshTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: +resolveLocalIndex( Reader& reader, Functor&& functor ) { std::cerr << "The mesh global index type " << getType< GlobalIndex >() << " is disabled in the build configuration." << std::endl; return false; } -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > - template< typename CellTopology, - int WorldDimension, - typename Real, - typename GlobalIndex, - typename > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > + template< typename CellTopology, + int WorldDimension, + typename Real, + typename GlobalIndex, + typename > bool -MeshTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveLocalIndex( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +MeshTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: +resolveLocalIndex( Reader& reader, Functor&& functor ) { - if( reader.getLocalIndexType() == "short int" ) - return resolveId< CellTopology, WorldDimension, Real, GlobalIndex, short int >( reader, std::forward(problemSetterArgs)... ); - if( reader.getLocalIndexType() == "int" ) - return resolveId< CellTopology, WorldDimension, Real, GlobalIndex, int >( reader, std::forward(problemSetterArgs)... ); - if( reader.getLocalIndexType() == "long int" ) - return resolveId< CellTopology, WorldDimension, Real, GlobalIndex, long int >( reader, std::forward(problemSetterArgs)... ); + if( reader.getLocalIndexType() == "short" || + reader.getLocalIndexType() == "short int" || + reader.getLocalIndexType() == "std::int16_t" || + reader.getLocalIndexType() == "std::uint16_t" ) + return resolveMeshType< CellTopology, WorldDimension, Real, GlobalIndex, short int >( reader, std::forward(functor) ); + if( reader.getLocalIndexType() == "int" || + reader.getLocalIndexType() == "std::int32_t" || + reader.getLocalIndexType() == "std::uint32_t" ) + return resolveMeshType< CellTopology, WorldDimension, Real, GlobalIndex, int >( reader, std::forward(functor) ); + if( reader.getLocalIndexType() == "long" || + reader.getLocalIndexType() == "long int" || + reader.getLocalIndexType() == "std::int64_t" || + reader.getLocalIndexType() == "std::uint64_t" ) + return resolveMeshType< CellTopology, WorldDimension, Real, GlobalIndex, long int >( reader, std::forward(functor) ); std::cerr << "Unsupported local index type: " << reader.getLocalIndexType() << std::endl; return false; } -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > - template< typename CellTopology, - int WorldDimension, - typename Real, - typename GlobalIndex, - typename LocalIndex, - typename, typename > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > + template< typename CellTopology, + int WorldDimension, + typename Real, + typename GlobalIndex, + typename LocalIndex, + typename, typename > bool -MeshTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveId( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +MeshTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: +resolveMeshType( Reader& reader, Functor&& functor ) { std::cerr << "The mesh local index type " << getType< LocalIndex >() << " is disabled in the build configuration." << std::endl; return false; } -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > - template< typename CellTopology, - int WorldDimension, - typename Real, - typename GlobalIndex, - typename LocalIndex, - typename > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > + template< typename CellTopology, + int WorldDimension, + typename Real, + typename GlobalIndex, + typename LocalIndex, + typename > bool -MeshTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveId( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +MeshTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: +resolveMeshType( Reader& reader, Functor&& functor ) { - if( reader.getIdType() == "short int" ) - return resolveMeshType< CellTopology, WorldDimension, Real, GlobalIndex, LocalIndex, short int >( reader, std::forward(problemSetterArgs)... ); - if( reader.getIdType() == "int" ) - return resolveMeshType< CellTopology, WorldDimension, Real, GlobalIndex, LocalIndex, int >( reader, std::forward(problemSetterArgs)... ); - if( reader.getIdType() == "long int" ) - return resolveMeshType< CellTopology, WorldDimension, Real, GlobalIndex, LocalIndex, long int >( reader, std::forward(problemSetterArgs)... ); - if( reader.getIdType() == "void" ) - return resolveMeshType< CellTopology, WorldDimension, Real, GlobalIndex, LocalIndex, void >( reader, std::forward(problemSetterArgs)... ); - std::cerr << "Unsupported id type: " << reader.getIdType() << std::endl; - return false; -} - -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > - template< typename CellTopology, - int WorldDimension, - typename Real, - typename GlobalIndex, - typename LocalIndex, - typename Id, - typename, typename > -bool -MeshTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveMeshType( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) -{ - std::cerr << "The mesh id type " << getType< Id >() << " is disabled in the build configuration." << std::endl; - return false; -} - -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > - template< typename CellTopology, - int WorldDimension, - typename Real, - typename GlobalIndex, - typename LocalIndex, - typename Id, - typename > -bool -MeshTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveMeshType( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) -{ - using MeshConfig = typename BuildConfigTags::MeshConfigTemplateTag< ConfigTag >::template MeshConfig< CellTopology, WorldDimension, Real, GlobalIndex, LocalIndex, Id >; - return resolveTerminate< MeshConfig >( reader, std::forward(problemSetterArgs)... ); + using MeshConfig = typename BuildConfigTags::MeshConfigTemplateTag< ConfigTag >::template MeshConfig< CellTopology, WorldDimension, Real, GlobalIndex, LocalIndex >; + return resolveTerminate< MeshConfig >( reader, std::forward(functor) ); } -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > - template< typename MeshConfig, - typename, typename > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > + template< typename MeshConfig, + typename, typename > bool -MeshTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveTerminate( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +MeshTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: +resolveTerminate( Reader& reader, Functor&& functor ) { std::cerr << "The mesh config type " << getType< MeshConfig >() << " is disabled in the build configuration for device " << getType< Device >() << "." << std::endl; return false; } -template< typename Reader, - typename ConfigTag, - typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > - template< typename MeshConfig, - typename > +template< typename ConfigTag, + typename Device > + template< typename Reader, + typename Functor > + template< typename MeshConfig, + typename > bool -MeshTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveTerminate( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +MeshTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: +resolveTerminate( Reader& reader, Functor&& functor ) { using MeshType = Meshes::Mesh< MeshConfig, Device >; - return ProblemSetter< MeshType >::run( std::forward(problemSetterArgs)... ); + return std::forward(functor)( reader, MeshType{} ); } } // namespace Meshes diff --git a/src/TNL/Meshes/TypeResolver/TypeResolver.h b/src/TNL/Meshes/TypeResolver/TypeResolver.h index a04391af61acc23c1bdc43a12ac3243dbe4b9c25..a4002eaaeb083acd2897a9ce270ea4e47a4fb613 100644 --- a/src/TNL/Meshes/TypeResolver/TypeResolver.h +++ b/src/TNL/Meshes/TypeResolver/TypeResolver.h @@ -10,7 +10,8 @@ #pragma once -#include +#include +#include namespace TNL { namespace Meshes { @@ -18,15 +19,75 @@ namespace Meshes { /* * This function does the following (in pseudo-code): * + * using Reader = [based on file type detection] + * Reader reader; * using MeshType = [black magic] - * return ProblemSetter< MeshType >::run( problemSetterArgs... ); + * MeshType mesh; + * return functor( reader, mesh ); + * + * The functor should be a generic lambda expression with the following + * signature (or an equivalent functor): + * + * auto functor = [] ( auto& reader, auto&& mesh ) -> bool {}; + */ +template< typename ConfigTag, + typename Device, + typename Functor > +bool +resolveMeshType( Functor&& functor, + const std::string& fileName, + const std::string& fileFormat = "auto" ); + +/* + * This function dues the same as `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 ); */ template< typename ConfigTag, typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > -bool resolveMeshType( const String& fileName, - ProblemSetterArgs&&... problemSetterArgs ); + typename Functor > +bool +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`. + */ +template< typename MeshConfig, + typename Device > +bool +loadMesh( Mesh< MeshConfig, Device >& mesh, + const std::string& fileName, + const std::string& fileFormat = "auto" ); + +template< typename MeshConfig > +bool +loadMesh( Mesh< MeshConfig, Devices::Cuda >& mesh, + const std::string& fileName, + const std::string& fileFormat = "auto" ); + +template< int Dimension, + typename Real, + typename Device, + typename Index > +bool +loadMesh( Grid< Dimension, Real, Device, Index >& grid, + const std::string& fileName, + const std::string& fileFormat = "auto" ); } // namespace Meshes } // namespace TNL diff --git a/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h b/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h index f18188b9c5ebc40b3d40c70d22445529962fcd7c..45ba4ca1864ba36d63b8c62132ef3ce38826e150 100644 --- a/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h +++ b/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h @@ -10,287 +10,207 @@ #pragma once -#include -#include +#include #include +#include +#include #include #include #include -#include -#include -#include - -// TODO: implement this in TNL::String -inline bool ends_with( const std::string& value, const std::string& ending ) -{ - if (ending.size() > value.size()) - return false; - return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); -} +#include namespace TNL { namespace Meshes { -/* - * TODO: - * The variadic template parameter pack ProblemSetterArgs will not be necessary - * in C++14 as it will be possible to use generic lambda functions to pass - * parameters to the ProblemSetter: - * - * // wrapper for MeshTypeResolver - * template< typename MeshType > - * using ProblemSetterWrapper = ProblemSetter< Real, Device, Index, MeshType, ConfigTag, SolverStarter< ConfigTag > >; - * - * bool run( const Config::ParameterContainer& parameters ) - * { - * const String& meshFileName = parameters.getParameter< String >( "mesh" ); - * auto wrapper = []( auto&& mesh ) { - * return ProblemSetterWrapper< decltype(mesh) >::run( parameters ); - * }; - * return MeshTypeResolver< ConfigTag, Device, wrapper >::run( meshFileName ); - * } - */ template< typename ConfigTag, typename Device, - template< typename MeshType > class ProblemSetter, - typename... ProblemSetterArgs > -bool resolveMeshType( const String& fileName_, - ProblemSetterArgs&&... problemSetterArgs ) + typename Functor > +bool +resolveMeshType( Functor&& functor, + const std::string& fileName, + const std::string& fileFormat ) { - std::cout << "Detecting mesh from file " << fileName_ << " ..." << std::endl; - std::string fileName( fileName_.getString() ); - if( ends_with( fileName, ".tnl" ) ) { - Readers::TNLReader reader; - if( ! reader.detectMesh( fileName_ ) ) + std::cout << "Detecting mesh from file " << fileName << " ..." << std::endl; + + namespace fs = std::experimental::filesystem; + std::string format = fileFormat; + if( format == "auto" ) { + format = fs::path(fileName).extension(); + if( format.length() > 0 ) + // remove dot from the extension + format = format.substr(1); + } + + // TODO: when TNLReader is gone, use the MeshReader type instead of a template parameter in the mesh type resolver (and remove static_casts in this function) + if( format == "tnl" ) { + Readers::TNLReader reader( fileName ); + if( ! reader.detectMesh() ) return false; if( reader.getMeshType() == "Meshes::Grid" ) - return GridTypeResolver< decltype(reader), ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: - run( reader, std::forward(problemSetterArgs)... ); + return GridTypeResolver< ConfigTag, Device >::run( reader, functor ); else if( reader.getMeshType() == "Meshes::Mesh" ) - return MeshTypeResolver< decltype(reader), ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: - run( reader, std::forward(problemSetterArgs)... ); + return MeshTypeResolver< ConfigTag, Device >::run( reader, functor ); else { std::cerr << "The mesh type " << reader.getMeshType() << " is not supported in the TNL reader." << std::endl; return false; } } - else if( ends_with( fileName, ".ng" ) ) { - // FIXME: The Netgen files don't store the real, global index, local index and id types. + else if( format == "ng" ) { + // FIXME: The Netgen files don't store the real, global index and local index types. // The reader has some defaults, but they might be disabled by the BuildConfigTags - in // this case we should use the first enabled type. - Readers::NetgenReader reader; - if( ! reader.detectMesh( fileName_ ) ) - return false; + Readers::NetgenReader reader( fileName ); + reader.detectMesh(); if( reader.getMeshType() == "Meshes::Mesh" ) - return MeshTypeResolver< decltype(reader), ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: - run( reader, std::forward(problemSetterArgs)... ); + return MeshTypeResolver< ConfigTag, Device >::run( static_cast(reader), functor ); else { std::cerr << "The mesh type " << reader.getMeshType() << " is not supported in the Netgen reader." << std::endl; return false; } } - else if( ends_with( fileName, ".vtk" ) ) { - // FIXME: The VTK files don't store the global index, local index and id types. + else if( format == "vtk" ) { + // FIXME: The VTK files don't store the global and local index types. // The reader has some defaults, but they might be disabled by the BuildConfigTags - in // this case we should use the first enabled type. - Readers::VTKReader reader; - if( ! reader.detectMesh( fileName_ ) ) + Readers::VTKReader reader( fileName ); + reader.detectMesh(); + if( reader.getMeshType() == "Meshes::Mesh" ) + return MeshTypeResolver< ConfigTag, Device >::run( static_cast(reader), functor ); + else { + std::cerr << "The mesh type " << reader.getMeshType() << " is not supported in the VTK reader." << std::endl; return false; + } + } + else if( format == "vtu" ) { + // FIXME: The XML VTK files don't store the local index type. + // The reader has some defaults, but they might be disabled by the BuildConfigTags - in + // this case we should use the first enabled type. + Readers::VTUReader reader( fileName ); + reader.detectMesh(); if( reader.getMeshType() == "Meshes::Mesh" ) - return MeshTypeResolver< decltype(reader), ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: - run( reader, std::forward(problemSetterArgs)... ); + return MeshTypeResolver< ConfigTag, Device >::run( static_cast(reader), functor ); else { std::cerr << "The mesh type " << reader.getMeshType() << " is not supported in the VTK reader." << std::endl; return false; } } else { - std::cerr << "File '" << fileName << "' has unknown extension. Supported extensions are '.tnl', '.vtk' and '.ng'." << std::endl; + if( fileFormat == "auto" ) + std::cerr << "File '" << fileName << "' has unsupported format (based on the file extension): " << format << "."; + else + std::cerr << "Unsupported fileFormat parameter: " << fileFormat << "."; + std::cerr << " Supported formats are 'tnl', 'vtk', 'vtu' and 'ng'." << std::endl; return false; } } -// TODO: reorganize -template< typename CommunicatorType, - typename MeshConfig, - typename Device > +template< typename ConfigTag, + typename Device, + typename Functor > bool -loadMesh( const String& fileName, - Mesh< MeshConfig, Device >& mesh, - DistributedMeshes::DistributedMesh< Mesh< MeshConfig, Device > >& distributedMesh ) +resolveAndLoadMesh( Functor&& functor, + const std::string& fileName, + const std::string& fileFormat ) { - if( CommunicatorType::isDistributed() ) - { - std::cerr << "Distributed Mesh is not supported yet, only Distributed Grid is supported."; - return false; - } - - std::cout << "Loading mesh from file " << fileName << " ..." << std::endl; - std::string fileName_( fileName.getString() ); - bool status = true; - - if( ends_with( fileName_, ".tnl" ) ) - mesh.load( fileName ); - else if( ends_with( fileName_, ".ng" ) ) { - Readers::NetgenReader reader; - status = reader.readMesh( fileName, mesh ); - } - else if( ends_with( fileName_, ".vtk" ) ) { - Readers::VTKReader reader; - status = reader.readMesh( fileName, mesh ); - } - else { - std::cerr << "File '" << fileName << "' has unknown extension. Supported extensions are '.tnl', '.vtk' and '.ng'." << std::endl; - return false; - } - - if( ! status ) + auto wrapper = [&]( auto& reader, auto&& mesh ) -> bool { - std::cerr << "I am not able to load the mesh from the file " << fileName << ". " - "Perhaps the mesh stored in the file is not supported by the mesh " - "passed to the loadMesh function? The mesh type is " - << getType< decltype(mesh) >() << std::endl; - return false; - } - return true; + using MeshType = std::decay_t< decltype(mesh) >; + std::cout << "Loading a mesh from the file " << fileName << " ..." << std::endl; + try { + reader.loadMesh( mesh ); + } + catch( const Meshes::Readers::MeshReaderError& e ) { + std::cerr << "Failed to load the mesh from the file " << fileName << ". The error is:\n" << e.what() << std::endl; + return false; + } + return functor( reader, std::forward(mesh) ); + }; + return resolveMeshType< ConfigTag, Device >( wrapper, fileName, fileFormat ); } -template< typename Problem, - typename MeshConfig, +template< typename MeshConfig, typename Device > bool -decomposeMesh( const Config::ParameterContainer& parameters, - const String& prefix, - Mesh< MeshConfig, Device >& mesh, - DistributedMeshes::DistributedMesh< Mesh< MeshConfig, Device > >& distributedMesh, - Problem& problem ) +loadMesh( Mesh< MeshConfig, Device >& mesh, + const std::string& fileName, + const std::string& fileFormat ) { - using CommunicatorType = typename Problem::CommunicatorType; - if( CommunicatorType::isDistributed() ) - { - std::cerr << "Distributed Mesh is not supported yet, only Distributed Grid is supported."; - return false; + std::cout << "Loading a mesh from the file " << fileName << " ..." << std::endl; + + namespace fs = std::experimental::filesystem; + std::string format = fileFormat; + if( format == "auto" ) { + format = fs::path(fileName).extension(); + if( format.length() > 0 ) + // remove dot from the extension + format = format.substr(1); + } + + try { + if( format == "tnl" ) + mesh.load( fileName ); + else if( format == "ng" ) { + Readers::NetgenReader reader( fileName ); + reader.loadMesh( mesh ); + } + else if( format == "vtk" ) { + Readers::VTKReader reader( fileName ); + reader.loadMesh( mesh ); + } + else if( format == "vtu" ) { + Readers::VTUReader reader( fileName ); + reader.loadMesh( mesh ); + } + else { + if( fileFormat == "auto" ) + std::cerr << "File '" << fileName << "' has unsupported format (based on the file extension): " << format << "."; + else + std::cerr << "Unsupported fileFormat parameter: " << fileFormat << "."; + std::cerr << " Supported formats are 'tnl', 'vtk', 'vtu' and 'ng'." << std::endl; + return false; + } } + catch( const Meshes::Readers::MeshReaderError& e ) { + std::cerr << "Failed to load the mesh from the file " << fileName << ". The error is:\n" << e.what() << std::endl; + return false; + } + return true; } -template< typename CommunicatorType, - typename MeshConfig > +template< typename MeshConfig > bool -loadMesh( const String& fileName, - Mesh< MeshConfig, Devices::Cuda >& mesh, - DistributedMeshes::DistributedMesh< Mesh< MeshConfig, Devices::Cuda > >& distributedMesh ) +loadMesh( Mesh< MeshConfig, Devices::Cuda >& mesh, + const std::string& fileName, + const std::string& fileFormat ) { - if(CommunicatorType::isDistributed()) - { - std::cerr << "Distributed Mesh is not supported yet, only Distributed Grid is supported."; - return false; - } - Mesh< MeshConfig, Devices::Host > hostMesh; - DistributedMeshes::DistributedMesh< Mesh< MeshConfig, Devices::Host > > hostDistributedMesh; - if( ! loadMesh< CommunicatorType >( fileName, hostMesh, hostDistributedMesh ) ) + if( ! loadMesh( hostMesh, fileName, fileFormat ) ) return false; mesh = hostMesh; - // TODO -// distributedMesh = hostDistributedMesh; return true; } -// Specializations for grids -template< typename CommunicatorType, - int Dimension, +// overload for grids +template< int Dimension, typename Real, typename Device, typename Index > bool -loadMesh( const String& fileName, - Grid< Dimension, Real, Device, Index >& mesh, - DistributedMeshes::DistributedMesh< Grid< Dimension, Real, Device, Index > > &distributedMesh ) +loadMesh( Grid< Dimension, Real, Device, Index >& grid, + const std::string& fileName, + const std::string& fileFormat ) { - - if( CommunicatorType::isDistributed() ) - { - std::cout << "Loading a global mesh from the file " << fileName << "..."; - Grid< Dimension, Real, Device, Index > globalGrid; - try - { - globalGrid.load( fileName ); - } - catch(...) - { - std::cerr << std::endl; - std::cerr << "I am not able to load the global mesh from the file " << fileName << "." << std::endl; - return false; - } - std::cout << " [ OK ] " << std::endl; - - typename Meshes::DistributedMeshes::DistributedMesh>::SubdomainOverlapsType overlap; - distributedMesh.template setGlobalGrid< CommunicatorType >( globalGrid ); - distributedMesh.setupGrid(mesh); + std::cout << "Loading a grid from the file " << fileName << "..." << std::endl; + try { + grid.load( fileName ); return true; } - else - { - std::cout << "Loading a mesh from the file " << fileName << "..."; - try - { - mesh.load( fileName ); - } - catch(...) - { - std::cerr << std::endl; - std::cerr << "I am not able to load the mesh from the file " << fileName << "." << std::endl; - std::cerr << " You may create it with tools like tnl-grid-setup or tnl-mesh-convert." << std::endl; - return false; - } - std::cout << " [ OK ] " << std::endl; - return true; - } -} - -template< typename Problem, - int Dimension, - typename Real, - typename Device, - typename Index > -bool -decomposeMesh( const Config::ParameterContainer& parameters, - const String& prefix, - Grid< Dimension, Real, Device, Index >& mesh, - DistributedMeshes::DistributedMesh< Grid< Dimension, Real, Device, Index > > &distributedMesh, - Problem& problem ) -{ - using GridType = Grid< Dimension, Real, Device, Index >; - using DistributedGridType = DistributedMeshes::DistributedMesh< GridType >; - using SubdomainOverlapsType = typename DistributedGridType::SubdomainOverlapsType; - using CommunicatorType = typename Problem::CommunicatorType; - - if( CommunicatorType::isDistributed() ) - { - SubdomainOverlapsType lower( 0 ), upper( 0 ); - distributedMesh.setOverlaps( lower, upper ); - distributedMesh.setupGrid( mesh ); - - problem.getSubdomainOverlaps( parameters, prefix, mesh, lower, upper ); - distributedMesh.setOverlaps( lower, upper ); - distributedMesh.setupGrid( mesh ); - return true; + catch(...) { + std::cerr << "I am not able to load the grid from the file " << fileName << "." << std::endl; + return false; } - else - return true; -} - -// convenient overload for non-distributed meshes -template< typename Mesh > -bool -loadMesh( const String& fileName, Mesh& mesh ) -{ - using Communicator = TNL::Communicators::NoDistrCommunicator; - using DistributedMesh = DistributedMeshes::DistributedMesh< Mesh>; - DistributedMesh distributedMesh; - return loadMesh< Communicator >( fileName, mesh, distributedMesh ); } } // namespace Meshes diff --git a/src/TNL/Meshes/VTKTraits.h b/src/TNL/Meshes/VTKTraits.h new file mode 100644 index 0000000000000000000000000000000000000000..0db339acf672b1d0595d41772c2a0b3b608a7e82 --- /dev/null +++ b/src/TNL/Meshes/VTKTraits.h @@ -0,0 +1,200 @@ +/*************************************************************************** + VTKTraits.h - description + ------------------- + begin : Nov 22, 2016 + copyright : (C) 2016 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +namespace TNL { +namespace Meshes { +namespace VTK { + +// VTK file formats +enum class FileFormat +: std::uint8_t +{ + ascii, + binary, + zlib_compressed +}; + +// VTK data types +enum class DataType +: std::uint8_t +{ + CellData, + PointData +}; + +// VTK entity shapes +enum class EntityShape +: std::uint8_t +{ + Vertex = 1, + PolyVertex = 2, + Line = 3, + PolyLine = 4, + Triangle = 5, + TriangleStrip = 6, + Polygon = 7, + Pixel = 8, + Quad = 9, + Tetra = 10, + Voxel = 11, + Hexahedron = 12, + Wedge = 13, + Pyramid = 14 +}; + +inline std::string getShapeName( EntityShape shape ) +{ + switch( shape ) + { + case EntityShape::Vertex: + return "Vertex"; + case EntityShape::PolyVertex: + return "PolyVertex"; + case EntityShape::Line: + return "Line"; + case EntityShape::PolyLine: + return "PolyLine"; + case EntityShape::Triangle: + return "Triangle"; + case EntityShape::TriangleStrip: + return "TriangleStrip"; + case EntityShape::Polygon: + return "Polygon"; + case EntityShape::Pixel: + return "Pixel"; + case EntityShape::Quad: + return "Quad"; + case EntityShape::Tetra: + return "Tetra"; + case EntityShape::Voxel: + return "Voxel"; + case EntityShape::Hexahedron: + return "Hexahedron"; + case EntityShape::Wedge: + return "Wedge"; + case EntityShape::Pyramid: + return "Pyramid"; + } + return ""; +} + +inline int getEntityDimension( EntityShape shape ) +{ + switch( shape ) + { + case EntityShape::Vertex: return 0; + case EntityShape::PolyVertex: return 0; + case EntityShape::Line: return 1; + case EntityShape::PolyLine: return 1; + case EntityShape::Triangle: return 2; + case EntityShape::TriangleStrip: return 2; + case EntityShape::Polygon: return 2; + case EntityShape::Pixel: return 2; + case EntityShape::Quad: return 2; + case EntityShape::Tetra: return 3; + case EntityShape::Voxel: return 3; + case EntityShape::Hexahedron: return 3; + case EntityShape::Wedge: return 3; + case EntityShape::Pyramid: return 3; + } + // this just avoids a compiler warning in GCC and nvcc (clang actually knows if the + // switch above covers all cases, and print a warning only when it does not) + throw 1; +} + +// static mapping of TNL entity topologies to EntityShape +template< typename Topology > struct TopologyToEntityShape {}; +template<> struct TopologyToEntityShape< Topologies::Vertex > { static constexpr EntityShape shape = EntityShape::Vertex; }; +template<> struct TopologyToEntityShape< Topologies::Edge > { static constexpr EntityShape shape = EntityShape::Line; }; +template<> struct TopologyToEntityShape< Topologies::Triangle > { static constexpr EntityShape shape = EntityShape::Triangle; }; +template<> struct TopologyToEntityShape< Topologies::Quadrilateral > { static constexpr EntityShape shape = EntityShape::Quad; }; +template<> struct TopologyToEntityShape< Topologies::Tetrahedron > { static constexpr EntityShape shape = EntityShape::Tetra; }; +template<> struct TopologyToEntityShape< Topologies::Hexahedron > { static constexpr EntityShape shape = EntityShape::Hexahedron; }; + +// mapping used in VTKWriter +template< typename GridEntity > +struct GridEntityShape +{ +private: + static constexpr int dim = GridEntity::getEntityDimension(); + static_assert( dim >= 0 && dim <= 3, "unexpected dimension of the grid entity" ); + +public: + static constexpr EntityShape shape = + (dim == 0) ? EntityShape::Vertex : + (dim == 1) ? EntityShape::Line : + (dim == 2) ? EntityShape::Pixel : + EntityShape::Voxel; +}; + +// type names used in the VTK library (for the XML formats) +inline std::string getTypeName( std::int8_t ) { return "Int8"; } +inline std::string getTypeName( std::uint8_t ) { return "UInt8"; } +inline std::string getTypeName( std::int16_t ) { return "Int16"; } +inline std::string getTypeName( std::uint16_t ) { return "UInt16"; } +inline std::string getTypeName( std::int32_t ) { return "Int32"; } +inline std::string getTypeName( std::uint32_t ) { return "UInt32"; } +inline std::string getTypeName( std::int64_t ) { return "Int64"; } +inline std::string getTypeName( std::uint64_t ) { return "UInt64"; } +inline std::string getTypeName( float ) { return "Float32"; } +inline std::string getTypeName( double ) { return "Float64"; } + +/** + * Ghost points and ghost cells + * + * The following bit fields are consistent with the corresponding VTK enums [1], which in turn + * are consistent with VisIt ghost zones specification [2]. + * + * - [1] https://github.com/Kitware/VTK/blob/060f626b8df0b8144ec8f10c41f936b712c0330b/Common/DataModel/vtkDataSetAttributes.h#L118-L138 + * - [2] http://www.visitusers.org/index.php?title=Representing_ghost_data + */ +enum class CellGhostTypes +: std::uint8_t +{ + DUPLICATECELL = 1, // the cell is present on multiple processors + HIGHCONNECTIVITYCELL = 2, // the cell has more neighbors than in a regular mesh + LOWCONNECTIVITYCELL = 4, // the cell has less neighbors than in a regular mesh + REFINEDCELL = 8, // other cells are present that refines it. + EXTERIORCELL = 16, // the cell is on the exterior of the data set + HIDDENCELL = 32 // the cell is needed to maintain connectivity, but the data values should be ignored. +}; + +enum class PointGhostTypes +: std::uint8_t +{ + DUPLICATEPOINT = 1, // the cell is present on multiple processors + HIDDENPOINT = 2 // the point is needed to maintain connectivity, but the data values should be ignored. +}; + +/** + * A DataArray with this name is used in the parallel VTK files to indicate ghost regions. + * Each value must be assigned according to the bit fields PointGhostTypes or CellGhostType. + * + * For details, see https://blog.kitware.com/ghost-and-blanking-visibility-changes/ + */ +inline const char* ghostArrayName() +{ + return "vtkGhostType"; +} + +} // namespace VTK +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/Writers/AsymptoteWriter.h b/src/TNL/Meshes/Writers/AsymptoteWriter.h index 62a9f073dbd8880072e0d92f4c073286a0e423cf..5f0663782599d04d38e5ca2edba587ab6ef9c87e 100644 --- a/src/TNL/Meshes/Writers/AsymptoteWriter.h +++ b/src/TNL/Meshes/Writers/AsymptoteWriter.h @@ -27,12 +27,6 @@ public: { throw Exceptions::NotImplementedError(); } - - template< int EntityDimension = Mesh::getMeshDimension() > - static void writeEntities( const Mesh& mesh, std::ostream& str ) - { - throw Exceptions::NotImplementedError(); - } }; template< typename Real, typename Device, typename Index > @@ -151,12 +145,6 @@ public: */ } } - - template< int EntityDimension = Mesh::getMeshDimension() > - static void writeEntities( const Mesh& mesh, std::ostream& str ) - { - throw Exceptions::NotImplementedError(); - } }; } // namespace Writers diff --git a/src/TNL/Meshes/Writers/NetgenWriter.h b/src/TNL/Meshes/Writers/NetgenWriter.h index 7a3a86f9230ba317505c71d850ed8c86cef87267..860034dcda28b1f56565a2f6908b57563526e354 100644 --- a/src/TNL/Meshes/Writers/NetgenWriter.h +++ b/src/TNL/Meshes/Writers/NetgenWriter.h @@ -59,7 +59,7 @@ public: for( int cellVertexIdx = 0; cellVertexIdx < meshDimension + 1; cellVertexIdx++ ) - str << " " << cell.getVertexIndex( cellVertexIdx ); + str << " " << cell.template getSubentityIndex< 0 >( cellVertexIdx ); str << "\n"; } } diff --git a/src/TNL/Meshes/Writers/PVTUWriter.h b/src/TNL/Meshes/Writers/PVTUWriter.h new file mode 100644 index 0000000000000000000000000000000000000000..8ef4d2b7bc1c6f84090cf2d8f0dbd929bb4c9bda --- /dev/null +++ b/src/TNL/Meshes/Writers/PVTUWriter.h @@ -0,0 +1,107 @@ +/*************************************************************************** + PVTUWriter.h - description + ------------------- + begin : Apr 17, 2020 + copyright : (C) 2020 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +// Implemented by: Jakub Klinkovský + +#pragma once + +#include +#include + +namespace TNL { +namespace Meshes { +namespace Writers { + +// NOTE: Mesh should be the local mesh type, not DistributedMesh +template< typename Mesh > +class PVTUWriter +{ + using HeaderType = std::uint64_t; +public: + using MeshRealType = typename Mesh::RealType; + using IndexType = typename Mesh::GlobalIndexType; + + PVTUWriter() = delete; + + PVTUWriter( std::ostream& str, VTK::FileFormat format = VTK::FileFormat::zlib_compressed ) + : str(str), format(format) + {} + + // If desired, cycle and time of the simulation can put into the file. This follows the instructions at + // http://www.visitusers.org/index.php?title=Time_and_Cycle_in_VTK_files + void writeMetadata( std::int32_t cycle = -1, double time = -1 ); + + template< int EntityDimension = Mesh::getMeshDimension() > + void writeEntities( const DistributedMeshes::DistributedMesh< Mesh >& distributedMesh ); + + template< int EntityDimension = Mesh::getMeshDimension() > + void writeEntities( const Mesh& mesh, + const unsigned GhostLevel = 0, + const unsigned MinCommonVertices = 0 ); + + template< typename ValueType > + void writePPointData( const String& name, + const int numberOfComponents = 1 ); + + template< typename ValueType > + void writePCellData( const String& name, + const int numberOfComponents = 1 ); + + template< typename ValueType > + void writePDataArray( const String& name, + const int numberOfComponents = 1 ); + + // add a single piece and return its source path + // (useful for sequential writing, e.g. from tnl-decompose-mesh) + std::string addPiece( const String& mainFileName, + const unsigned subdomainIndex ); + + // add all pieces and return the source path for the current rank + // (useful for parallel writing) + template< typename Communicator > + std::string addPiece( const String& mainFileName, + const typename Communicator::CommunicationGroup group ); + + ~PVTUWriter(); + +protected: + void writeHeader( const unsigned GhostLevel = 0, + const unsigned MinCommonVertices = 0 ); + + void writePoints( const Mesh& mesh ); + + void writeFooter(); + + std::ostream& str; + + VTK::FileFormat format; + + // indicator if the tag is open + bool vtkfileOpen = false; + + // indicators if a tag is open or closed + bool pCellDataOpen = false; + bool pCellDataClosed = false; + + // indicators if a tag is open or closed + bool pPointDataOpen = false; + bool pPointDataClosed = false; + + void openPCellData(); + void closePCellData(); + void openPPointData(); + void closePPointData(); +}; + +} // namespace Writers +} // namespace Meshes +} // namespace TNL + +#include diff --git a/src/TNL/Meshes/Writers/PVTUWriter.hpp b/src/TNL/Meshes/Writers/PVTUWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..71e19da1de2bcf4e77f22f31473eacd0d57340a9 --- /dev/null +++ b/src/TNL/Meshes/Writers/PVTUWriter.hpp @@ -0,0 +1,255 @@ +/*************************************************************************** + PVTUWriter.hpp - description + ------------------- + begin : Apr 17, 2020 + copyright : (C) 2020 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +// Implemented by: Jakub Klinkovský + +#pragma once + +#include + +#include + +namespace TNL { +namespace Meshes { +namespace Writers { + +template< typename Mesh > +void +PVTUWriter< Mesh >::writeMetadata( int cycle, double time ) +{ + if( ! vtkfileOpen ) + throw std::logic_error("writeMetadata has to be called after writeEntities in case of the PVTU format, otherwise header attributes would be left unset." ); + + if( cycle >= 0 || time >= 0 ) + str << "\n"; + + if( cycle >= 0 ) { + str << "" + << cycle << "\n"; + } + if( time >= 0 ) { + str.precision( std::numeric_limits< double >::digits10 ); + str << "" + << time << "\n"; + } + + if( cycle >= 0 || time >= 0 ) + str << "\n"; +} + +template< typename Mesh > + template< int EntityDimension > +void +PVTUWriter< Mesh >::writeEntities( const DistributedMeshes::DistributedMesh< Mesh >& distributedMesh ) +{ + writeEntities< EntityDimension >( distributedMesh.getLocalMesh(), distributedMesh.getGhostLevels(), Mesh::Config::dualGraphMinCommonVertices ); +} + +template< typename Mesh > + template< int EntityDimension > +void +PVTUWriter< Mesh >::writeEntities( const Mesh& mesh, + const unsigned GhostLevel, + const unsigned MinCommonVertices ) +{ + if( ! vtkfileOpen ) + writeHeader( GhostLevel, MinCommonVertices ); + + // write points + writePoints( mesh ); +} + +template< typename Mesh > + template< typename ValueType > +void +PVTUWriter< Mesh >::writePPointData( const String& name, + const int numberOfComponents ) +{ + if( ! vtkfileOpen ) + throw std::logic_error("The VTKFile has not been opened yet - call writeEntities first."); + openPPointData(); + writePDataArray< ValueType >( name, numberOfComponents ); +} + +template< typename Mesh > + template< typename ValueType > +void +PVTUWriter< Mesh >::writePCellData( const String& name, + const int numberOfComponents ) +{ + if( ! vtkfileOpen ) + throw std::logic_error("The VTKFile has not been opened yet - call writeEntities first."); + openPCellData(); + writePDataArray< ValueType >( name, numberOfComponents ); +} + +template< typename Mesh > + template< typename ValueType > +void +PVTUWriter< Mesh >::writePDataArray( const String& name, + const int numberOfComponents ) +{ + if( numberOfComponents != 0 && numberOfComponents != 1 && numberOfComponents != 3 ) + throw std::logic_error("Unsupported numberOfComponents parameter: " + std::to_string(numberOfComponents)); + + str << "\n"; +} + +template< typename Mesh > +std::string +PVTUWriter< Mesh >::addPiece( const String& mainFileName, + const unsigned subdomainIndex ) +{ + if( ! mainFileName.endsWith( ".pvtu" ) ) + throw std::logic_error("The mainFileName parameter must be the name of the " + ".pvtu file (i.e., it must have the .pvtu suffix)."); + + // close PCellData and PPointData sections + closePCellData(); + closePPointData(); + + namespace fs = std::experimental::filesystem; + + // get the basename of the main file (filename without extension) + const fs::path mainPath = mainFileName.getString(); + const fs::path basename = mainPath.stem(); + + // create subdirectory for subdomains + const fs::path subdirectory = mainPath.parent_path() / basename; + fs::create_directory( subdirectory ); + + // write tag + const std::string subfile = "subdomain." + std::to_string(subdomainIndex) + ".vtu"; + const std::string source = basename / subfile; + str << "\n"; + + // return subfile path + return subdirectory / subfile; +} + +template< typename Mesh > + template< typename Communicator > +std::string +PVTUWriter< Mesh >::addPiece( const String& mainFileName, + const typename Communicator::CommunicationGroup group ) +{ + std::string source; + for( int i = 0; i < Communicator::GetSize( group ); i++ ) { + const std::string s = addPiece( mainFileName, i ); + if( i == Communicator::GetRank( group ) ) + source = s; + } + return source; +} + +template< typename Mesh > +void +PVTUWriter< Mesh >::writeHeader( const unsigned GhostLevel, + const unsigned MinCommonVertices ) +{ + str << "\n"; + str << "\n"; + str << " 0 ) + str << " MinCommonVertices=\"" << MinCommonVertices << "\""; + str << ">\n"; + + vtkfileOpen = true; +} + +template< typename Mesh > +void +PVTUWriter< Mesh >::writePoints( const Mesh& mesh ) +{ + str << "\n"; + writePDataArray< typename Mesh::RealType >( "Points", 3 ); + str << "\n"; +} + +template< typename Mesh > +void +PVTUWriter< Mesh >::writeFooter() +{ + closePCellData(); + closePPointData(); + str << "\n"; + str << "\n"; +} + +template< typename Mesh > +PVTUWriter< Mesh >::~PVTUWriter() +{ + if( vtkfileOpen ) + writeFooter(); +} + +template< typename Mesh > +void +PVTUWriter< Mesh >::openPCellData() +{ + if( pCellDataClosed ) + throw std::logic_error("The tag has already been closed."); + closePPointData(); + if( ! pCellDataOpen ) { + str << "\n"; + pCellDataOpen = true; + } +} + +template< typename Mesh > +void +PVTUWriter< Mesh >::closePCellData() +{ + if( pCellDataOpen ) { + str << "\n"; + pCellDataClosed = true; + pCellDataOpen = false; + } +} + +template< typename Mesh > +void +PVTUWriter< Mesh >::openPPointData() +{ + if( pPointDataClosed ) + throw std::logic_error("The tag has already been closed."); + closePCellData(); + if( ! pPointDataOpen ) { + str << "\n"; + pPointDataOpen = true; + } +} + +template< typename Mesh > +void +PVTUWriter< Mesh >::closePPointData() +{ + if( pPointDataOpen ) { + str << "\n"; + pPointDataClosed = true; + pPointDataOpen = false; + } +} + +} // namespace Writers +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/Writers/VTKWriter.h b/src/TNL/Meshes/Writers/VTKWriter.h index 4d5ad00e82b259c68849847dd9219d1e2d637588..8010317a375becd2054e810776df21e217c42a09 100644 --- a/src/TNL/Meshes/Writers/VTKWriter.h +++ b/src/TNL/Meshes/Writers/VTKWriter.h @@ -10,21 +10,20 @@ #pragma once -#include - #include #include +#include namespace TNL { namespace Meshes { namespace Writers { -namespace __impl { +namespace details { template< typename Mesh, int EntityDimension > struct MeshEntitiesVTKWriter; template< typename Mesh, int EntityDimension > struct MeshEntityTypesVTKWriter; -} // namespace __impl +} // namespace details template< typename Mesh > class VTKWriter @@ -34,27 +33,73 @@ class VTKWriter // static_assert( Mesh::getWorldDimension() <= 3, "The VTK format supports only 1D, 2D and 3D meshes." ); template< int EntityDimension > - using EntitiesWriter = __impl::MeshEntitiesVTKWriter< Mesh, EntityDimension >; + using EntitiesWriter = details::MeshEntitiesVTKWriter< Mesh, EntityDimension >; template< int EntityDimension > - using EntityTypesWriter = __impl::MeshEntityTypesVTKWriter< Mesh, EntityDimension >; + using EntityTypesWriter = details::MeshEntityTypesVTKWriter< Mesh, EntityDimension >; public: - using Index = typename Mesh::GlobalIndexType; + using IndexType = typename Mesh::GlobalIndexType; + + VTKWriter() = delete; - static void writeAllEntities( const Mesh& mesh, std::ostream& str ); + VTKWriter( std::ostream& str, VTK::FileFormat format = VTK::FileFormat::binary ) + : str(str), format(format) + { + if( format != VTK::FileFormat::ascii && format != VTK::FileFormat::binary ) + throw std::domain_error("The Legacy VTK file formats support only ASCII and BINARY formats."); + } + + // If desired, cycle and time of the simulation can put into the file. This follows the instructions at + // http://www.visitusers.org/index.php?title=Time_and_Cycle_in_VTK_files + void writeMetadata( std::int32_t cycle = -1, double time = -1 ); template< int EntityDimension = Mesh::getMeshDimension() > - static void writeEntities( const Mesh& mesh, std::ostream& str ); + void writeEntities( const Mesh& mesh ); + + template< typename Array > + void writePointData( const Array& array, + const String& name, + const int numberOfComponents = 1 ); + + template< typename Array > + void writeCellData( const Array& array, + const String& name, + const int numberOfComponents = 1 ); + + template< typename Array > + void writeDataArray( const Array& array, + const String& name, + const int numberOfComponents = 1 ); protected: - static void writeHeader( const Mesh& mesh, std::ostream& str ); + void writePoints( const Mesh& mesh ); + + void writeHeader(); + + std::ostream& str; + + VTK::FileFormat format; + + // number of cells (in the VTK sense) written to the file + IndexType cellsCount = 0; + + // number of points written to the file + IndexType pointsCount = 0; + + // indicator if the header has been written + bool headerWritten = false; + + // number of data arrays written in each section + int cellDataArrays = 0; + int pointDataArrays = 0; - static void writePoints( const Mesh& mesh, std::ostream& str ); + // indicator of the current section + VTK::DataType currentSection = VTK::DataType::CellData; }; } // namespace Writers } // namespace Meshes } // namespace TNL -#include +#include diff --git a/src/TNL/Meshes/Writers/VTKWriter.hpp b/src/TNL/Meshes/Writers/VTKWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..125366d0334507ff9f50c765417077cc1f253978 --- /dev/null +++ b/src/TNL/Meshes/Writers/VTKWriter.hpp @@ -0,0 +1,572 @@ +/*************************************************************************** + VTKWriter.hpp - description + ------------------- + begin : Mar 04, 2017 + copyright : (C) 2017 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 { +namespace Writers { + +namespace details { + +// TODO: 64-bit integers are most likely not supported in the BINARY format +inline void +writeInt( VTK::FileFormat format, std::ostream& str, int value ) +{ + if( format == VTK::FileFormat::binary ) { + value = forceBigEndian( value ); + str.write( reinterpret_cast(&value), sizeof(int) ); + } + else { + str << value << " "; + } +} + +template< typename Real > +void +writeReal( VTK::FileFormat format, std::ostream& str, Real value ) +{ + if( format == VTK::FileFormat::binary ) { + value = forceBigEndian( value ); + str.write( reinterpret_cast(&value), sizeof(Real) ); + } + else { + str.precision( std::numeric_limits< Real >::digits10 ); + str << value << " "; + } +} + + +// TODO: specialization for disabled entities +// Unstructured meshes, entities +template< typename Mesh, int EntityDimension > +struct MeshEntitiesVTKWriter +{ + static void exec( const Mesh& mesh, std::ostream& str, VTK::FileFormat format ) + { + using EntityType = typename Mesh::template EntityType< EntityDimension >; + using Index = typename Mesh::GlobalIndexType; + + const Index entitiesCount = mesh.template getEntitiesCount< EntityType >(); + const Index verticesPerEntity = VerticesPerEntity< EntityType >::count;; + for( Index i = 0; i < entitiesCount; i++ ) { + const auto& entity = mesh.template getEntity< EntityType >( i ); + writeInt( format, str, verticesPerEntity ); + for( Index j = 0; j < verticesPerEntity; j++ ) + writeInt( format, str, entity.template getSubentityIndex< 0 >( j ) ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +// Unstructured meshes, vertices +template< typename Mesh > +struct MeshEntitiesVTKWriter< Mesh, 0 > +{ + static void exec( const Mesh& mesh, std::ostream& str, VTK::FileFormat format ) + { + using EntityType = typename Mesh::template EntityType< 0 >; + using Index = typename Mesh::GlobalIndexType; + + const Index entitiesCount = mesh.template getEntitiesCount< EntityType >(); + const Index verticesPerEntity = 1; + for( Index i = 0; i < entitiesCount; i++ ) + { + writeInt( format, str, verticesPerEntity ); + writeInt( format, str, i ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +// 1D grids, cells +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct MeshEntitiesVTKWriter< Meshes::Grid< 1, MeshReal, Device, MeshIndex >, 1 > +{ + using MeshType = Meshes::Grid< 1, MeshReal, Device, MeshIndex >; + + static void exec( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) + { + writeInt( format, str, 2 ); + writeInt( format, str, i ); + writeInt( format, str, i+1 ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +// 1D grids, vertices +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct MeshEntitiesVTKWriter< Meshes::Grid< 1, MeshReal, Device, MeshIndex >, 0 > +{ + using MeshType = Meshes::Grid< 1, MeshReal, Device, MeshIndex >; + + static void exec( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + for( MeshIndex i = 0; i < mesh.getDimensions().x() + 1; i++ ) + { + writeInt( format, str, 1 ); + writeInt( format, str, i ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +// 2D grids, cells +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct MeshEntitiesVTKWriter< Meshes::Grid< 2, MeshReal, Device, MeshIndex >, 2 > +{ + using MeshType = Meshes::Grid< 2, MeshReal, Device, MeshIndex >; + + static void exec( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) + { + writeInt( format, str, 4 ); + writeInt( format, str, j * ( mesh.getDimensions().x() + 1 ) + i ); + writeInt( format, str, j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + writeInt( format, str, (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + writeInt( format, str, (j+1) * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +// 2D grids, faces +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct MeshEntitiesVTKWriter< Meshes::Grid< 2, MeshReal, Device, MeshIndex >, 1 > +{ + using MeshType = Meshes::Grid< 2, MeshReal, Device, MeshIndex >; + + static void exec( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i < ( mesh.getDimensions().x() + 1 ); i++ ) + { + writeInt( format, str, 2 ); + writeInt( format, str, j * ( mesh.getDimensions().x() + 1 ) + i ); + writeInt( format, str, (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + + for( MeshIndex j = 0; j < (mesh.getDimensions().y()+1); j++ ) + for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) + { + writeInt( format, str, 2 ); + writeInt( format, str, j * ( mesh.getDimensions().x() + 1 ) + i ); + writeInt( format, str, j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +// 2D grids, vertices +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct MeshEntitiesVTKWriter< Meshes::Grid< 2, MeshReal, Device, MeshIndex >, 0 > +{ + using MeshType = Meshes::Grid< 2, MeshReal, Device, MeshIndex >; + + static void exec( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + for( MeshIndex j = 0; j < ( mesh.getDimensions().y() + 1 ); j++ ) + for( MeshIndex i = 0; i < ( mesh.getDimensions().x() + 1 ); i++ ) + { + writeInt( format, str, 1 ); + writeInt( format, str, j * mesh.getDimensions().x() + i ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +// 3D grids, cells +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct MeshEntitiesVTKWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 3 > +{ + using MeshType = Meshes::Grid< 3, MeshReal, Device, MeshIndex >; + + static void exec( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + for( MeshIndex k = 0; k < mesh.getDimensions().z(); k++ ) + for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) + { + writeInt( format, str, 8 ); + writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + writeInt( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + writeInt( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + writeInt( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + writeInt( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +// 3D grids, faces +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct MeshEntitiesVTKWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 2 > +{ + using MeshType = Meshes::Grid< 3, MeshReal, Device, MeshIndex >; + + static void exec( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + for( MeshIndex k = 0; k < mesh.getDimensions().z(); k++ ) + for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i <= mesh.getDimensions().x(); i++ ) + { + writeInt( format, str, 4 ); + writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + writeInt( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + writeInt( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + + for( MeshIndex k = 0; k < mesh.getDimensions().z(); k++ ) + for( MeshIndex j = 0; j <= mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) + { + writeInt( format, str, 4 ); + writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + writeInt( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + writeInt( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + + for( MeshIndex k = 0; k <= mesh.getDimensions().z(); k++ ) + for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) + { + writeInt( format, str, 4 ); + writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +// 3D grids, edges +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct MeshEntitiesVTKWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 1 > +{ + using MeshType = Meshes::Grid< 3, MeshReal, Device, MeshIndex >; + + static void exec( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + for( MeshIndex k = 0; k <= mesh.getDimensions().z(); k++ ) + for( MeshIndex j = 0; j <= mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) + { + writeInt( format, str, 2 ); + writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + + for( MeshIndex k = 0; k <= mesh.getDimensions().z(); k++ ) + for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i <= mesh.getDimensions().x(); i++ ) + { + writeInt( format, str, 2 ); + writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + + for( MeshIndex k = 0; k < mesh.getDimensions().z(); k++ ) + for( MeshIndex j = 0; j <= mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i <= mesh.getDimensions().x(); i++ ) + { + writeInt( format, str, 2 ); + writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + writeInt( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +// 3D grids, vertices +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct MeshEntitiesVTKWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 0 > +{ + using MeshType = Meshes::Grid< 3, MeshReal, Device, MeshIndex >; + + static void exec( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + for( MeshIndex k = 0; k < ( mesh.getDimensions().z() + 1 ); k++ ) + for( MeshIndex j = 0; j < ( mesh.getDimensions().y() + 1 ); j++ ) + for( MeshIndex i = 0; i < ( mesh.getDimensions().x() + 1 ); i++ ) + { + writeInt( format, str, 1 ); + writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + + +// TODO: specialization for disabled entities +template< typename Mesh, int EntityDimension > +struct MeshEntityTypesVTKWriter +{ + static void exec( const Mesh& mesh, std::ostream& str, VTK::FileFormat format ) + { + using EntityType = typename Mesh::template EntityType< EntityDimension >; + using Index = typename Mesh::GlobalIndexType; + + const Index entitiesCount = mesh.template getEntitiesCount< EntityType >(); + for( Index i = 0; i < entitiesCount; i++ ) { + const int type = (int) VTK::TopologyToEntityShape< typename EntityType::EntityTopology >::shape; + writeInt( format, str, type ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +template< int Dimension, + typename MeshReal, + typename Device, + typename MeshIndex, + int EntityDimension > +struct MeshEntityTypesVTKWriter< Grid< Dimension, MeshReal, Device, MeshIndex >, EntityDimension > +{ + using MeshType = Grid< Dimension, MeshReal, Device, MeshIndex >; + + static void exec( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + using EntityType = typename MeshType::template EntityType< EntityDimension >; + + const MeshIndex entitiesCount = mesh.template getEntitiesCount< EntityType >(); + for( MeshIndex i = 0; i < entitiesCount; i++ ) { + const int type = (int) VTK::GridEntityShape< EntityType >::shape; + writeInt( format, str, type ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +} // namespace details + +template< typename Mesh > +void +VTKWriter< Mesh >::writeMetadata( int cycle, double time ) +{ + if( ! headerWritten ) + writeHeader(); + + int n_metadata = 0; + if( cycle >= 0 ) + ++n_metadata; + if( time >= 0 ) + ++n_metadata; + if( n_metadata > 0 ) + str << "FIELD FieldData " << n_metadata << "\n"; + if( cycle >= 0 ) { + str << "CYCLE 1 1 int\n"; + details::writeInt( format, str, cycle ); + str << "\n"; + } + if( time >= 0 ) { + str << "TIME 1 1 double\n"; + details::writeReal( format, str, time ); + str << "\n"; + } +} + +template< typename Mesh > + template< int EntityDimension > +void +VTKWriter< Mesh >::writeEntities( const Mesh& mesh ) +{ + if( ! headerWritten ) + writeHeader(); + writePoints( mesh ); + + using EntityType = typename Mesh::template EntityType< EntityDimension >; + cellsCount = mesh.template getEntitiesCount< EntityType >(); + const IndexType verticesPerEntity = VerticesPerEntity< EntityType >::count; + const IndexType cellsListSize = cellsCount * ( verticesPerEntity + 1 ); + + str << std::endl << "CELLS " << cellsCount << " " << cellsListSize << std::endl; + EntitiesWriter< EntityDimension >::exec( mesh, str, format ); + + str << std::endl << "CELL_TYPES " << cellsCount << std::endl; + EntityTypesWriter< EntityDimension >::exec( mesh, str, format ); +} + +template< typename Mesh > + template< typename Array > +void +VTKWriter< Mesh >::writePointData( const Array& array, + const String& name, + const int numberOfComponents ) +{ + if( array.getSize() / numberOfComponents != pointsCount ) + throw std::length_error("Mismatched array size for POINT_DATA section: " + std::to_string(array.getSize()) + + " (there are " + std::to_string(pointsCount) + " points in the file)"); + + // check that we won't start the section second time + if( currentSection != VTK::DataType::PointData && cellDataArrays * pointDataArrays != 0 ) + throw std::logic_error("The requested data section is not the current section and it has already been written."); + currentSection = VTK::DataType::PointData; + + // start the appropriate section if necessary + if( pointDataArrays == 0 ) + str << std::endl << "POINT_DATA " << pointsCount << std::endl; + ++pointDataArrays; + + writeDataArray( array, name, numberOfComponents ); +} + +template< typename Mesh > + template< typename Array > +void +VTKWriter< Mesh >::writeCellData( const Array& array, + const String& name, + const int numberOfComponents ) +{ + if( array.getSize() / numberOfComponents != cellsCount ) + throw std::length_error("Mismatched array size for CELL_DATA section: " + std::to_string(array.getSize()) + + " (there are " + std::to_string(cellsCount) + " cells in the file)"); + + // check that we won't start the section second time + if( currentSection != VTK::DataType::CellData && cellDataArrays * pointDataArrays != 0 ) + throw std::logic_error("The requested data section is not the current section and it has already been written."); + currentSection = VTK::DataType::CellData; + + // start the appropriate section if necessary + if( cellDataArrays == 0 ) + str << std::endl << "CELL_DATA " << cellsCount << std::endl; + ++cellDataArrays; + + writeDataArray( array, name, numberOfComponents ); +} + +template< typename Mesh > + template< typename Array > +void +VTKWriter< Mesh >::writeDataArray( const Array& array, + const String& name, + const int numberOfComponents ) +{ + // use a host buffer if direct access to the array elements is not possible + if( std::is_same< typename Array::DeviceType, Devices::Cuda >::value ) + { + using HostArray = typename Array::template Self< typename Array::ValueType, Devices::Host >; + HostArray hostBuffer; + hostBuffer = array; + writeDataArray( hostBuffer, name, numberOfComponents ); + return; + } + + if( numberOfComponents != 1 && numberOfComponents != 3 ) + throw std::logic_error("Unsupported numberOfComponents parameter: " + std::to_string(numberOfComponents)); + + // write DataArray header + if( numberOfComponents == 1 ) { + str << "SCALARS " << name << " " << getType< typename Array::ValueType >() << " 1" << std::endl; + str << "LOOKUP_TABLE default" << std::endl; + } + else { + str << "VECTORS " << name << " " << getType< typename Array::ValueType >() << " 1" << std::endl; + } + + using Meshes::Writers::details::writeReal; + for( IndexType i = 0; i < array.getSize(); i++ ) { + writeReal( format, str, array[i] ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } +} + +template< typename Mesh > +void +VTKWriter< Mesh >::writePoints( const Mesh& mesh ) +{ + using details::writeReal; + pointsCount = mesh.template getEntitiesCount< typename Mesh::Vertex >(); + str << "POINTS " << pointsCount << " " << getType< typename Mesh::RealType >() << std::endl; + for( IndexType i = 0; i < pointsCount; i++ ) { + const auto& vertex = mesh.template getEntity< typename Mesh::Vertex >( i ); + const auto& point = vertex.getPoint(); + for( IndexType j = 0; j < point.getSize(); j++ ) + writeReal( format, str, point[ j ] ); + // VTK needs zeros for unused dimensions + for( IndexType j = 0; j < 3 - point.getSize(); j++ ) + writeReal( format, str, (typename Mesh::PointType::RealType) 0 ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } +} + +template< typename Mesh > +void +VTKWriter< Mesh >::writeHeader() +{ + str << "# vtk DataFile Version 2.0\n" + << "TNL DATA\n" + << ((format == VTK::FileFormat::ascii) ? "ASCII\n" : "BINARY\n") + << "DATASET UNSTRUCTURED_GRID\n"; + headerWritten = true; +} + +} // namespace Writers +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/Writers/VTKWriter_impl.h b/src/TNL/Meshes/Writers/VTKWriter_impl.h deleted file mode 100644 index 83cf95ec4ca18f9c1b1375b9e89197884cdd8a5e..0000000000000000000000000000000000000000 --- a/src/TNL/Meshes/Writers/VTKWriter_impl.h +++ /dev/null @@ -1,472 +0,0 @@ -/*************************************************************************** - VTKWriter.h - description - ------------------- - begin : Mar 04, 2017 - copyright : (C) 2017 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 { -namespace Writers { - -namespace __impl { - -template< typename T, typename R = void > -struct enable_if_type -{ - using type = R; -}; - -template< typename T, typename Enable = void > -struct has_entity_topology : std::false_type {}; - -template< typename T > -struct has_entity_topology< T, typename enable_if_type< typename T::EntityTopology >::type > -: std::true_type -{}; - - -template< typename Entity, - bool _is_mesh_entity = has_entity_topology< Entity >::value > -struct VerticesPerEntity -{ - static constexpr int count = Entity::getVerticesCount(); -}; - -template< typename MeshConfig, typename Device > -struct VerticesPerEntity< MeshEntity< MeshConfig, Device, Topologies::Vertex >, true > -{ - static constexpr int count = 1; -}; - -template< typename GridEntity > -struct VerticesPerEntity< GridEntity, false > -{ -private: - static constexpr int dim = GridEntity::getEntityDimension(); - static_assert( dim >= 0 && dim <= 3, "unexpected dimension of the grid entity" ); - -public: - static constexpr int count = - (dim == 0) ? 1 : - (dim == 1) ? 2 : - (dim == 2) ? 4 : - 8; -}; - - -template< typename GridEntity > -struct GridEntityShape -{ -private: - static constexpr int dim = GridEntity::getEntityDimension(); - static_assert( dim >= 0 && dim <= 3, "unexpected dimension of the grid entity" ); - -public: - static constexpr Readers::EntityShape shape = - (dim == 0) ? Readers::EntityShape::Vertex : - (dim == 1) ? Readers::EntityShape::Line : - (dim == 2) ? Readers::EntityShape::Pixel : - Readers::EntityShape::Voxel; -}; - - -template< typename Mesh > -typename Mesh::GlobalIndexType -getAllMeshEntitiesCount( const Mesh& mesh, DimensionTag< 0 > ) -{ - using EntityType = typename Mesh::template EntityType< 0 >; - return mesh.template getEntitiesCount< EntityType >(); -} - -// TODO: specialization for disabled entities -template< typename Mesh, - typename DimensionTag = Meshes::DimensionTag< Mesh::getMeshDimension() > > -typename Mesh::GlobalIndexType -getAllMeshEntitiesCount( const Mesh& mesh, DimensionTag = DimensionTag() ) -{ - using EntityType = typename Mesh::template EntityType< DimensionTag::value >; - return mesh.template getEntitiesCount< EntityType >() + - getAllMeshEntitiesCount( mesh, typename DimensionTag::Decrement() ); -} - - -template< typename Mesh > -typename Mesh::GlobalIndexType -getCellsListSize( const Mesh& mesh, DimensionTag< 0 > ) -{ - using EntityType = typename Mesh::template EntityType< 0 >; - return mesh.template getEntitiesCount< EntityType >() * 2; -} - -// TODO: specialization for disabled entities -template< typename Mesh, - typename DimensionTag = Meshes::DimensionTag< Mesh::getMeshDimension() > > -typename Mesh::GlobalIndexType -getCellsListSize( const Mesh& mesh, DimensionTag = DimensionTag() ) -{ - using EntityType = typename Mesh::template EntityType< DimensionTag::value >; - const auto verticesPerEntity = VerticesPerEntity< EntityType >::count; - return ( mesh.template getEntitiesCount< EntityType >() * ( verticesPerEntity + 1 ) ) + - getCellsListSize( mesh, typename DimensionTag::Decrement() ); -} - - -// TODO: specialization for disabled entities -// Unstructured meshes, entities -template< typename Mesh, int EntityDimension > -struct MeshEntitiesVTKWriter -{ - static void exec( const Mesh& mesh, std::ostream& str ) - { - using EntityType = typename Mesh::template EntityType< EntityDimension >; - using Index = typename Mesh::GlobalIndexType; - - const Index entitiesCount = mesh.template getEntitiesCount< EntityType >(); - const Index verticesPerEntity = VerticesPerEntity< EntityType >::count;; - for( Index i = 0; i < entitiesCount; i++ ) { - const auto& entity = mesh.template getEntity< EntityType >( i ); - str << verticesPerEntity; - for( Index j = 0; j < verticesPerEntity; j++ ) - str << " " << entity.template getSubentityIndex< 0 >( j ); - str << "\n"; - } - } -}; - -// Unstructured meshes, vertices -template< typename Mesh > -struct MeshEntitiesVTKWriter< Mesh, 0 > -{ - static void exec( const Mesh& mesh, std::ostream& str ) - { - using EntityType = typename Mesh::template EntityType< 0 >; - using Index = typename Mesh::GlobalIndexType; - - const Index entitiesCount = mesh.template getEntitiesCount< EntityType >(); - const Index verticesPerEntity = 1; - for( Index i = 0; i < entitiesCount; i++ ) { - str << verticesPerEntity << " " << i << "\n"; - } - } -}; - -// 1D grids, cells -template< typename MeshReal, - typename Device, - typename MeshIndex > -struct MeshEntitiesVTKWriter< Meshes::Grid< 1, MeshReal, Device, MeshIndex >, 1 > -{ - using MeshType = Meshes::Grid< 1, MeshReal, Device, MeshIndex >; - - static void exec( const MeshType& mesh, std::ostream& str ) - { - for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) - str << "2 " << i << " " << i+1 << "\n"; - } -}; - -// 1D grids, vertices -template< typename MeshReal, - typename Device, - typename MeshIndex > -struct MeshEntitiesVTKWriter< Meshes::Grid< 1, MeshReal, Device, MeshIndex >, 0 > -{ - using MeshType = Meshes::Grid< 1, MeshReal, Device, MeshIndex >; - - static void exec( const MeshType& mesh, std::ostream& str ) - { - for( MeshIndex i = 0; i < mesh.getDimensions().x() + 1; i++ ) - str << "1 " << i << "\n"; - } -}; - -// 2D grids, cells -template< typename MeshReal, - typename Device, - typename MeshIndex > -struct MeshEntitiesVTKWriter< Meshes::Grid< 2, MeshReal, Device, MeshIndex >, 2 > -{ - using MeshType = Meshes::Grid< 2, MeshReal, Device, MeshIndex >; - - static void exec( const MeshType& mesh, std::ostream& str ) - { - for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) - for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) - str << "4 " << j * ( mesh.getDimensions().x() + 1 ) + i << " " - << j * ( mesh.getDimensions().x() + 1 ) + i + 1 << " " - << (j+1) * ( mesh.getDimensions().x() + 1 ) + i << " " - << (j+1) * ( mesh.getDimensions().x() + 1 ) + i + 1 << "\n"; - } -}; - -// 2D grids, faces -template< typename MeshReal, - typename Device, - typename MeshIndex > -struct MeshEntitiesVTKWriter< Meshes::Grid< 2, MeshReal, Device, MeshIndex >, 1 > -{ - using MeshType = Meshes::Grid< 2, MeshReal, Device, MeshIndex >; - - static void exec( const MeshType& mesh, std::ostream& str ) - { - for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) - for( MeshIndex i = 0; i < ( mesh.getDimensions().x() + 1 ); i++ ) - str << "2 " << j * ( mesh.getDimensions().x() + 1 ) + i << " " - << (j+1) * ( mesh.getDimensions().x() + 1 ) + i << "\n"; - - for( MeshIndex j = 0; j < (mesh.getDimensions().y()+1); j++ ) - for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) - str << "2 " << j * ( mesh.getDimensions().x() + 1 ) + i << " " - << j * ( mesh.getDimensions().x() + 1 ) + i + 1 << "\n"; - } -}; - -// 2D grids, vertices -template< typename MeshReal, - typename Device, - typename MeshIndex > -struct MeshEntitiesVTKWriter< Meshes::Grid< 2, MeshReal, Device, MeshIndex >, 0 > -{ - using MeshType = Meshes::Grid< 2, MeshReal, Device, MeshIndex >; - - static void exec( const MeshType& mesh, std::ostream& str ) - { - for( MeshIndex j = 0; j < ( mesh.getDimensions().y() + 1 ); j++ ) - for( MeshIndex i = 0; i < ( mesh.getDimensions().x() + 1 ); i++ ) - str << "1 " << j * mesh.getDimensions().x() + i << "\n"; - } -}; - -// 3D grids, cells -template< typename MeshReal, - typename Device, - typename MeshIndex > -struct MeshEntitiesVTKWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 3 > -{ - using MeshType = Meshes::Grid< 3, MeshReal, Device, MeshIndex >; - - static void exec( const MeshType& mesh, std::ostream& str ) - { - for( MeshIndex k = 0; k < mesh.getDimensions().z(); k++ ) - for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) - for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) - str << "8 " << k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i << " " - << k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 << " " - << k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i << " " - << k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i + 1 << " " - << (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i << " " - << (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 << " " - << (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i << " " - << (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i + 1 << "\n"; - } -}; - -// 3D grids, faces -template< typename MeshReal, - typename Device, - typename MeshIndex > -struct MeshEntitiesVTKWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 2 > -{ - using MeshType = Meshes::Grid< 3, MeshReal, Device, MeshIndex >; - - static void exec( const MeshType& mesh, std::ostream& str ) - { - for( MeshIndex k = 0; k < mesh.getDimensions().z(); k++ ) - for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) - for( MeshIndex i = 0; i <= mesh.getDimensions().x(); i++ ) - str << "4 " << k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i << " " - << k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i << " " - << (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i << " " - << (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i << "\n"; - - for( MeshIndex k = 0; k < mesh.getDimensions().z(); k++ ) - for( MeshIndex j = 0; j <= mesh.getDimensions().y(); j++ ) - for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) - str << "4 " << k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i << " " - << k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 << " " - << (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i << " " - << (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 << "\n"; - - for( MeshIndex k = 0; k <= mesh.getDimensions().z(); k++ ) - for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) - for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) - str << "4 " << k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i << " " - << k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 << " " - << k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i << " " - << k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i + 1 << "\n"; - } -}; - -// 3D grids, edges -template< typename MeshReal, - typename Device, - typename MeshIndex > -struct MeshEntitiesVTKWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 1 > -{ - using MeshType = Meshes::Grid< 3, MeshReal, Device, MeshIndex >; - - static void exec( const MeshType& mesh, std::ostream& str ) - { - for( MeshIndex k = 0; k <= mesh.getDimensions().z(); k++ ) - for( MeshIndex j = 0; j <= mesh.getDimensions().y(); j++ ) - for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) - str << "2 " << k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i << " " - << k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 << "\n"; - - for( MeshIndex k = 0; k <= mesh.getDimensions().z(); k++ ) - for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) - for( MeshIndex i = 0; i <= mesh.getDimensions().x(); i++ ) - str << "2 " << k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i << " " - << k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i << "\n"; - - for( MeshIndex k = 0; k < mesh.getDimensions().z(); k++ ) - for( MeshIndex j = 0; j <= mesh.getDimensions().y(); j++ ) - for( MeshIndex i = 0; i <= mesh.getDimensions().x(); i++ ) - str << "2 " << k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i << " " - << (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i << "\n"; - } -}; - -// 3D grids, vertices -template< typename MeshReal, - typename Device, - typename MeshIndex > -struct MeshEntitiesVTKWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 0 > -{ - using MeshType = Meshes::Grid< 3, MeshReal, Device, MeshIndex >; - - static void exec( const MeshType& mesh, std::ostream& str ) - { - for( MeshIndex k = 0; k < ( mesh.getDimensions().z() + 1 ); k++ ) - for( MeshIndex j = 0; j < ( mesh.getDimensions().y() + 1 ); j++ ) - for( MeshIndex i = 0; i < ( mesh.getDimensions().x() + 1 ); i++ ) - str << "1 " << k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i << "\n"; - } -}; - - -// TODO: specialization for disabled entities -template< typename Mesh, int EntityDimension > -struct MeshEntityTypesVTKWriter -{ - static void exec( const Mesh& mesh, std::ostream& str ) - { - using EntityType = typename Mesh::template EntityType< EntityDimension >; - using Index = typename Mesh::GlobalIndexType; - - const Index entitiesCount = mesh.template getEntitiesCount< EntityType >(); - for( Index i = 0; i < entitiesCount; i++ ) { - const int type = (int) Meshes::Readers::TopologyToEntityShape< typename EntityType::EntityTopology >::shape; - str << type << "\n"; - } - } -}; - -template< int Dimension, - typename MeshReal, - typename Device, - typename MeshIndex, - int EntityDimension > -struct MeshEntityTypesVTKWriter< Grid< Dimension, MeshReal, Device, MeshIndex >, EntityDimension > -{ - using MeshType = Grid< Dimension, MeshReal, Device, MeshIndex >; - - static void exec( const MeshType& mesh, std::ostream& str ) - { - using EntityType = typename MeshType::template EntityType< EntityDimension >; - - const MeshIndex entitiesCount = mesh.template getEntitiesCount< EntityType >(); - for( MeshIndex i = 0; i < entitiesCount; i++ ) { - const int type = (int) __impl::GridEntityShape< EntityType >::shape; - str << type << "\n"; - } - } -}; - -} // namespace __impl - -template< typename Mesh > -void -VTKWriter< Mesh >::writeAllEntities( const Mesh& mesh, std::ostream& str ) -{ - writeHeader( mesh, str ); - writePoints( mesh, str ); - - const Index allEntitiesCount = __impl::getAllMeshEntitiesCount( mesh ); - const Index cellsListSize = __impl::getCellsListSize( mesh ); - - str << std::endl << "CELLS " << allEntitiesCount << " " << cellsListSize << std::endl; - Algorithms::TemplateStaticFor< int, 0, Mesh::getMeshDimension() + 1, EntitiesWriter >::exec( mesh, str ); - - str << std::endl << "CELL_TYPES " << allEntitiesCount << std::endl; - Algorithms::TemplateStaticFor< int, 0, Mesh::getMeshDimension() + 1, EntityTypesWriter >::exec( mesh, str ); -} - -template< typename Mesh > - template< int EntityDimension > -void -VTKWriter< Mesh >::writeEntities( const Mesh& mesh, std::ostream& str ) -{ - writeHeader( mesh, str ); - writePoints( mesh, str ); - - using EntityType = typename Mesh::template EntityType< EntityDimension >; - const Index entitiesCount = mesh.template getEntitiesCount< EntityType >(); - const Index verticesPerEntity = __impl::VerticesPerEntity< EntityType >::count; - const Index cellsListSize = entitiesCount * ( verticesPerEntity + 1 ); - - str << std::endl << "CELLS " << entitiesCount << " " << cellsListSize << std::endl; - EntitiesWriter< EntityDimension >::exec( mesh, str ); - - str << std::endl << "CELL_TYPES " << entitiesCount << std::endl; - EntityTypesWriter< EntityDimension >::exec( mesh, str ); -} - -template< typename Mesh > -void -VTKWriter< Mesh >::writeHeader( const Mesh& mesh, std::ostream& str ) -{ - str << "# vtk DataFile Version 2.0\n" - << "TNL DATA\n" - << "ASCII\n" - << "DATASET UNSTRUCTURED_GRID\n"; -} - -template< typename Mesh > -void -VTKWriter< Mesh >::writePoints( const Mesh& mesh, std::ostream& str ) -{ - const Index verticesCount = mesh.template getEntitiesCount< typename Mesh::Vertex >(); - - str << "POINTS " << verticesCount << " " << getType< typename Mesh::RealType >() << std::endl; - str.precision( std::numeric_limits< typename Mesh::RealType >::digits10 ); - - for( Index i = 0; i < verticesCount; i++ ) { - const auto& vertex = mesh.template getEntity< typename Mesh::Vertex >( i ); - const auto& point = vertex.getPoint(); - for( Index j = 0; j < point.getSize(); j++ ) { - str << point[ j ]; - if( j < point.getSize() - 1 ) - str << " "; - } - // VTK needs zeros for unused dimensions - for( Index j = 0; j < 3 - point.getSize(); j++ ) - str << " 0"; - str << "\n"; - } -} - -} // namespace Writers -} // namespace Meshes -} // namespace TNL diff --git a/src/TNL/Meshes/Writers/VTUWriter.h b/src/TNL/Meshes/Writers/VTUWriter.h new file mode 100644 index 0000000000000000000000000000000000000000..9f715dce65af314acfa5eca21467c5d7eabcef84 --- /dev/null +++ b/src/TNL/Meshes/Writers/VTUWriter.h @@ -0,0 +1,117 @@ +/*************************************************************************** + VTUWriter.h - description + ------------------- + begin : Mar 18, 2020 + copyright : (C) 2020 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +// Implemented by: Jakub Klinkovský + +#pragma once + +#include +#include +#include + +namespace TNL { +namespace Meshes { +namespace Writers { + +namespace details { + +template< typename Mesh, int EntityDimension > struct MeshEntitiesVTUCollector; + +} // namespace details + +template< typename Mesh > +class VTUWriter +{ + static_assert( Mesh::getMeshDimension() <= 3, "The VTK format supports only 1D, 2D and 3D meshes." ); + // TODO: check also world dimension when grids allow it +// static_assert( Mesh::getWorldDimension() <= 3, "The VTK format supports only 1D, 2D and 3D meshes." ); + + template< int EntityDimension > + using EntitiesCollector = details::MeshEntitiesVTUCollector< Mesh, EntityDimension >; + + using HeaderType = std::uint64_t; +public: + using MeshRealType = typename Mesh::RealType; + using IndexType = typename Mesh::GlobalIndexType; + + VTUWriter() = delete; + + VTUWriter( std::ostream& str, VTK::FileFormat format = VTK::FileFormat::zlib_compressed ) + : str(str), format(format) + {} + + // If desired, cycle and time of the simulation can put into the file. This follows the instructions at + // http://www.visitusers.org/index.php?title=Time_and_Cycle_in_VTK_files + void writeMetadata( std::int32_t cycle = -1, double time = -1 ); + + template< int EntityDimension = Mesh::getMeshDimension() > + void writeEntities( const Mesh& mesh ); + + template< typename Array > + void writePointData( const Array& array, + const String& name, + const int numberOfComponents = 1 ); + + template< typename Array > + void writeCellData( const Array& array, + const String& name, + const int numberOfComponents = 1 ); + + template< typename Array > + void writeDataArray( const Array& array, + const String& name, + const int numberOfComponents = 1 ); + + ~VTUWriter(); + +protected: + void writePoints( const Mesh& mesh ); + + void writeHeader(); + + void writeFooter(); + + std::ostream& str; + + VTK::FileFormat format; + + // number of points written to the file + IndexType pointsCount = 0; + + // number of cells (in the VTK sense) written to the file + IndexType cellsCount = 0; + + // indicator if the tag is open + bool vtkfileOpen = false; + + // indicator if a tag is open + bool pieceOpen = false; + + // indicators if a tag is open or closed + bool cellDataOpen = false; + bool cellDataClosed = false; + + // indicators if a tag is open or closed + bool pointDataOpen = false; + bool pointDataClosed = false; + + void openCellData(); + void closeCellData(); + void openPointData(); + void closePointData(); + + void closePiece(); +}; + +} // namespace Writers +} // namespace Meshes +} // namespace TNL + +#include diff --git a/src/TNL/Meshes/Writers/VTUWriter.hpp b/src/TNL/Meshes/Writers/VTUWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8d609f0a78a9327885751065f015b607c39f45e0 --- /dev/null +++ b/src/TNL/Meshes/Writers/VTUWriter.hpp @@ -0,0 +1,634 @@ +/*************************************************************************** + VTUWriter.hpp - description + ------------------- + begin : Mar 18, 2020 + copyright : (C) 2020 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +// Implemented by: Jakub Klinkovský + +#pragma once + +#include + +#include +#include +#include +#include +#ifdef HAVE_ZLIB + #include +#endif + +namespace TNL { +namespace Meshes { +namespace Writers { + +namespace details { + +// TODO: specialization for disabled entities +// Unstructured meshes, entities +template< typename Mesh, int EntityDimension > +struct MeshEntitiesVTUCollector +{ + static void exec( const Mesh& mesh, + std::vector< typename Mesh::GlobalIndexType > & connectivity, + std::vector< typename Mesh::GlobalIndexType > & offsets, + std::vector< std::uint8_t > & types ) + { + using EntityType = typename Mesh::template EntityType< EntityDimension >; + using Index = typename Mesh::GlobalIndexType; + + const Index entitiesCount = mesh.template getEntitiesCount< EntityType >(); + const Index verticesPerEntity = VerticesPerEntity< EntityType >::count;; + for( Index i = 0; i < entitiesCount; i++ ) { + const auto& entity = mesh.template getEntity< EntityType >( i ); + for( Index j = 0; j < verticesPerEntity; j++ ) + connectivity.push_back( entity.template getSubentityIndex< 0 >( j ) ); + offsets.push_back( connectivity.size() ); + const std::uint8_t type = (std::uint8_t) VTK::TopologyToEntityShape< typename EntityType::EntityTopology >::shape; + types.push_back( type ); + } + } +}; + +// Unstructured meshes, vertices +template< typename Mesh > +struct MeshEntitiesVTUCollector< Mesh, 0 > +{ + static void exec( const Mesh& mesh, + std::vector< typename Mesh::GlobalIndexType > & connectivity, + std::vector< typename Mesh::GlobalIndexType > & offsets, + std::vector< std::uint8_t > & types ) + { + using EntityType = typename Mesh::template EntityType< 0 >; + using Index = typename Mesh::GlobalIndexType; + + const Index entitiesCount = mesh.template getEntitiesCount< EntityType >(); + for( Index i = 0; i < entitiesCount; i++ ) { + connectivity.push_back( i ); + offsets.push_back( connectivity.size() ); + const std::uint8_t type = (std::uint8_t) VTK::TopologyToEntityShape< typename EntityType::EntityTopology >::shape; + types.push_back( type ); + } + } +}; + +// 1D grids, cells +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct MeshEntitiesVTUCollector< Meshes::Grid< 1, MeshReal, Device, MeshIndex >, 1 > +{ + using Mesh = Meshes::Grid< 1, MeshReal, Device, MeshIndex >; + + static void exec( const Mesh& mesh, + std::vector< typename Mesh::GlobalIndexType > & connectivity, + std::vector< typename Mesh::GlobalIndexType > & offsets, + std::vector< std::uint8_t > & types ) + { + for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) + { + connectivity.push_back( i ); + connectivity.push_back( i+1 ); + offsets.push_back( connectivity.size() ); + types.push_back( (std::uint8_t) VTK::EntityShape::Line ); + } + } +}; + +// 1D grids, vertices +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct MeshEntitiesVTUCollector< Meshes::Grid< 1, MeshReal, Device, MeshIndex >, 0 > +{ + using Mesh = Meshes::Grid< 1, MeshReal, Device, MeshIndex >; + + static void exec( const Mesh& mesh, + std::vector< typename Mesh::GlobalIndexType > & connectivity, + std::vector< typename Mesh::GlobalIndexType > & offsets, + std::vector< std::uint8_t > & types ) + { + for( MeshIndex i = 0; i < mesh.getDimensions().x() + 1; i++ ) + { + connectivity.push_back( i ); + offsets.push_back( connectivity.size() ); + types.push_back( (std::uint8_t) VTK::EntityShape::Vertex ); + } + } +}; + +// 2D grids, cells +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct MeshEntitiesVTUCollector< Meshes::Grid< 2, MeshReal, Device, MeshIndex >, 2 > +{ + using Mesh = Meshes::Grid< 2, MeshReal, Device, MeshIndex >; + + static void exec( const Mesh& mesh, + std::vector< typename Mesh::GlobalIndexType > & connectivity, + std::vector< typename Mesh::GlobalIndexType > & offsets, + std::vector< std::uint8_t > & types ) + { + for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) + { + connectivity.push_back( j * ( mesh.getDimensions().x() + 1 ) + i ); + connectivity.push_back( j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + connectivity.push_back( (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + connectivity.push_back( (j+1) * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + offsets.push_back( connectivity.size() ); + types.push_back( (std::uint8_t) VTK::EntityShape::Pixel ); + } + } +}; + +// 2D grids, faces +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct MeshEntitiesVTUCollector< Meshes::Grid< 2, MeshReal, Device, MeshIndex >, 1 > +{ + using Mesh = Meshes::Grid< 2, MeshReal, Device, MeshIndex >; + + static void exec( const Mesh& mesh, + std::vector< typename Mesh::GlobalIndexType > & connectivity, + std::vector< typename Mesh::GlobalIndexType > & offsets, + std::vector< std::uint8_t > & types ) + { + for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i < ( mesh.getDimensions().x() + 1 ); i++ ) + { + connectivity.push_back( j * ( mesh.getDimensions().x() + 1 ) + i ); + connectivity.push_back( (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + offsets.push_back( connectivity.size() ); + types.push_back( (std::uint8_t) VTK::EntityShape::Line ); + } + + for( MeshIndex j = 0; j < (mesh.getDimensions().y()+1); j++ ) + for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) + { + connectivity.push_back( j * ( mesh.getDimensions().x() + 1 ) + i ); + connectivity.push_back( j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + offsets.push_back( connectivity.size() ); + types.push_back( (std::uint8_t) VTK::EntityShape::Line ); + } + } +}; + +// 2D grids, vertices +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct MeshEntitiesVTUCollector< Meshes::Grid< 2, MeshReal, Device, MeshIndex >, 0 > +{ + using Mesh = Meshes::Grid< 2, MeshReal, Device, MeshIndex >; + + static void exec( const Mesh& mesh, + std::vector< typename Mesh::GlobalIndexType > & connectivity, + std::vector< typename Mesh::GlobalIndexType > & offsets, + std::vector< std::uint8_t > & types ) + { + for( MeshIndex j = 0; j < ( mesh.getDimensions().y() + 1 ); j++ ) + for( MeshIndex i = 0; i < ( mesh.getDimensions().x() + 1 ); i++ ) + { + connectivity.push_back( j * mesh.getDimensions().x() + i ); + offsets.push_back( connectivity.size() ); + types.push_back( (std::uint8_t) VTK::EntityShape::Vertex ); + } + } +}; + +// 3D grids, cells +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct MeshEntitiesVTUCollector< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 3 > +{ + using Mesh = Meshes::Grid< 3, MeshReal, Device, MeshIndex >; + + static void exec( const Mesh& mesh, + std::vector< typename Mesh::GlobalIndexType > & connectivity, + std::vector< typename Mesh::GlobalIndexType > & offsets, + std::vector< std::uint8_t > & types ) + { + for( MeshIndex k = 0; k < mesh.getDimensions().z(); k++ ) + for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) + { + connectivity.push_back( k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + connectivity.push_back( k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + connectivity.push_back( k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + connectivity.push_back( k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + connectivity.push_back( (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + connectivity.push_back( (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + connectivity.push_back( (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + connectivity.push_back( (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + offsets.push_back( connectivity.size() ); + types.push_back( (std::uint8_t) VTK::EntityShape::Voxel ); + } + } +}; + +// 3D grids, faces +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct MeshEntitiesVTUCollector< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 2 > +{ + using Mesh = Meshes::Grid< 3, MeshReal, Device, MeshIndex >; + + static void exec( const Mesh& mesh, + std::vector< typename Mesh::GlobalIndexType > & connectivity, + std::vector< typename Mesh::GlobalIndexType > & offsets, + std::vector< std::uint8_t > & types ) + { + for( MeshIndex k = 0; k < mesh.getDimensions().z(); k++ ) + for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i <= mesh.getDimensions().x(); i++ ) + { + connectivity.push_back( k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + connectivity.push_back( k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + connectivity.push_back( (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + connectivity.push_back( (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + offsets.push_back( connectivity.size() ); + types.push_back( (std::uint8_t) VTK::EntityShape::Pixel ); + } + + for( MeshIndex k = 0; k < mesh.getDimensions().z(); k++ ) + for( MeshIndex j = 0; j <= mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) + { + connectivity.push_back( k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + connectivity.push_back( k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + connectivity.push_back( (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + connectivity.push_back( (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + offsets.push_back( connectivity.size() ); + types.push_back( (std::uint8_t) VTK::EntityShape::Pixel ); + } + + for( MeshIndex k = 0; k <= mesh.getDimensions().z(); k++ ) + for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) + { + connectivity.push_back( k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + connectivity.push_back( k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + connectivity.push_back( k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + connectivity.push_back( k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + offsets.push_back( connectivity.size() ); + types.push_back( (std::uint8_t) VTK::EntityShape::Pixel ); + } + } +}; + +// 3D grids, edges +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct MeshEntitiesVTUCollector< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 1 > +{ + using Mesh = Meshes::Grid< 3, MeshReal, Device, MeshIndex >; + + static void exec( const Mesh& mesh, + std::vector< typename Mesh::GlobalIndexType > & connectivity, + std::vector< typename Mesh::GlobalIndexType > & offsets, + std::vector< std::uint8_t > & types ) + { + for( MeshIndex k = 0; k <= mesh.getDimensions().z(); k++ ) + for( MeshIndex j = 0; j <= mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) + { + connectivity.push_back( k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + connectivity.push_back( k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + offsets.push_back( connectivity.size() ); + types.push_back( (std::uint8_t) VTK::EntityShape::Line ); + } + + for( MeshIndex k = 0; k <= mesh.getDimensions().z(); k++ ) + for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i <= mesh.getDimensions().x(); i++ ) + { + connectivity.push_back( k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + connectivity.push_back( k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + offsets.push_back( connectivity.size() ); + types.push_back( (std::uint8_t) VTK::EntityShape::Line ); + } + + for( MeshIndex k = 0; k < mesh.getDimensions().z(); k++ ) + for( MeshIndex j = 0; j <= mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i <= mesh.getDimensions().x(); i++ ) + { + connectivity.push_back( k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + connectivity.push_back( (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + offsets.push_back( connectivity.size() ); + types.push_back( (std::uint8_t) VTK::EntityShape::Line ); + } + } +}; + +// 3D grids, vertices +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct MeshEntitiesVTUCollector< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 0 > +{ + using Mesh = Meshes::Grid< 3, MeshReal, Device, MeshIndex >; + + static void exec( const Mesh& mesh, + std::vector< typename Mesh::GlobalIndexType > & connectivity, + std::vector< typename Mesh::GlobalIndexType > & offsets, + std::vector< std::uint8_t > & types ) + { + for( MeshIndex k = 0; k < ( mesh.getDimensions().z() + 1 ); k++ ) + for( MeshIndex j = 0; j < ( mesh.getDimensions().y() + 1 ); j++ ) + for( MeshIndex i = 0; i < ( mesh.getDimensions().x() + 1 ); i++ ) + { + connectivity.push_back( k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + offsets.push_back( connectivity.size() ); + types.push_back( (std::uint8_t) VTK::EntityShape::Vertex ); + } + } +}; + +} // namespace details + +template< typename Mesh > +void +VTUWriter< Mesh >::writeMetadata( int cycle, double time ) +{ + if( ! vtkfileOpen ) + writeHeader(); + + if( cycle >= 0 || time >= 0 ) + str << "\n"; + + if( cycle >= 0 ) { + str << "" + << cycle << "\n"; + } + if( time >= 0 ) { + str.precision( std::numeric_limits< double >::digits10 ); + str << "" + << time << "\n"; + } + + if( cycle >= 0 || time >= 0 ) + str << "\n"; +} + +template< typename Mesh > + template< int EntityDimension > +void +VTUWriter< Mesh >::writeEntities( const Mesh& mesh ) +{ + // count points and cells before any writing + pointsCount = mesh.template getEntitiesCount< typename Mesh::Vertex >(); + using EntityType = typename Mesh::template EntityType< EntityDimension >; + cellsCount = mesh.template getEntitiesCount< EntityType >(); + + if( ! vtkfileOpen ) + writeHeader(); + closePiece(); + str << "\n"; + pieceOpen = true; + + // write points + writePoints( mesh ); + + // collect all data before writing + std::vector< IndexType > connectivity, offsets; + std::vector< std::uint8_t > types; + EntitiesCollector< EntityDimension >::exec( mesh, connectivity, offsets, types ); + + // create array views that can be passed to writeDataArray + Containers::ArrayView< IndexType > connectivity_v( connectivity.data(), connectivity.size() ); + Containers::ArrayView< IndexType > offsets_v( offsets.data(), offsets.size() ); + Containers::ArrayView< std::uint8_t > types_v( types.data(), types.size() ); + + // write cells + str << "\n"; + writeDataArray( connectivity_v, "connectivity", 0 ); + writeDataArray( offsets_v, "offsets", 0 ); + writeDataArray( types_v, "types", 0 ); + str << "\n"; +} + +template< typename Mesh > + template< typename Array > +void +VTUWriter< Mesh >::writePointData( const Array& array, + const String& name, + const int numberOfComponents ) +{ + if( ! pieceOpen ) + throw std::logic_error("The tag has not been opened yet - call writeEntities first."); + if( array.getSize() / numberOfComponents != pointsCount ) + throw std::length_error("Mismatched array size for section: " + std::to_string(array.getSize()) + + " (there are " + std::to_string(pointsCount) + " points in the file)"); + openPointData(); + writeDataArray( array, name, numberOfComponents ); +} + +template< typename Mesh > + template< typename Array > +void +VTUWriter< Mesh >::writeCellData( const Array& array, + const String& name, + const int numberOfComponents ) +{ + if( ! pieceOpen ) + throw std::logic_error("The tag has not been opened yet - call writeEntities first."); + if( array.getSize() / numberOfComponents != cellsCount ) + throw std::length_error("Mismatched array size for section: " + std::to_string(array.getSize()) + + " (there are " + std::to_string(cellsCount) + " cells in the file)"); + openCellData(); + writeDataArray( array, name, numberOfComponents ); +} + +template< typename Mesh > + template< typename Array > +void +VTUWriter< Mesh >::writeDataArray( const Array& array, + const String& name, + const int numberOfComponents ) +{ + // use a host buffer if direct access to the array elements is not possible + if( std::is_same< typename Array::DeviceType, Devices::Cuda >::value ) + { + using HostArray = typename Array::template Self< typename Array::ValueType, Devices::Host >; + HostArray hostBuffer; + hostBuffer = array; + writeDataArray( hostBuffer, name, numberOfComponents ); + return; + } + + if( numberOfComponents != 0 && numberOfComponents != 1 && numberOfComponents != 3 ) + throw std::logic_error("Unsupported numberOfComponents parameter: " + std::to_string(numberOfComponents)); + + // write DataArray header + str << " 0 ) + str << " NumberOfComponents=\"" << numberOfComponents << "\""; + str << " format=\"" << ((format == VTK::FileFormat::ascii) ? "ascii" : "binary") << "\">\n"; + + switch( format ) + { + case VTK::FileFormat::ascii: + str.precision( std::numeric_limits< typename Array::ValueType >::digits10 ); + for( IndexType i = 0; i < array.getSize(); i++ ) + // If Array::ValueType is uint8_t, it might be a typedef for unsigned char, which + // would be normally printed as char rather than a number. Hence, we use the trick + // with unary operator+, see https://stackoverflow.com/a/28414758 + str << +array[i] << " "; + str << "\n"; + break; + case VTK::FileFormat::zlib_compressed: +#ifdef HAVE_ZLIB + write_compressed_block< HeaderType >( array.getData(), array.getSize(), str ); + str << "\n"; + break; +#endif + // fall through to binary if HAVE_ZLIB is not defined + case VTK::FileFormat::binary: + write_encoded_block< HeaderType >( array.getData(), array.getSize(), str ); + str << "\n"; + break; + } + + // write DataArray footer + str << "\n"; +} + +template< typename Mesh > +void +VTUWriter< Mesh >::writePoints( const Mesh& mesh ) +{ + // copy all coordinates into a contiguous array + using BufferType = Containers::Array< MeshRealType, Devices::Host, IndexType >; + BufferType buffer( 3 * pointsCount ); + IndexType k = 0; + for( IndexType i = 0; i < pointsCount; i++ ) { + const auto& vertex = mesh.template getEntity< typename Mesh::Vertex >( i ); + const auto& point = vertex.getPoint(); + for( IndexType j = 0; j < point.getSize(); j++ ) + buffer[ k++ ] = point[ j ]; + // VTK needs zeros for unused dimensions + for( IndexType j = point.getSize(); j < 3; j++ ) + buffer[ k++ ] = 0; + } + + // write the buffer + str << "\n"; + writeDataArray( buffer, "Points", 3 ); + str << "\n"; +} + +template< typename Mesh > +void +VTUWriter< Mesh >::writeHeader() +{ + str << "\n"; + str << "\n"; + str << "\n"; + + vtkfileOpen = true; +} + +template< typename Mesh > +void +VTUWriter< Mesh >::writeFooter() +{ + closePiece(); + str << "\n"; + str << "\n"; +} + +template< typename Mesh > +VTUWriter< Mesh >::~VTUWriter() +{ + if( vtkfileOpen ) + writeFooter(); +} + +template< typename Mesh > +void +VTUWriter< Mesh >::openCellData() +{ + if( cellDataClosed ) + throw std::logic_error("The tag has already been closed in the current section."); + closePointData(); + if( ! cellDataOpen ) { + str << "\n"; + cellDataOpen = true; + } +} + +template< typename Mesh > +void +VTUWriter< Mesh >::closeCellData() +{ + if( cellDataOpen ) { + str << "\n"; + cellDataClosed = true; + cellDataOpen = false; + } +} + +template< typename Mesh > +void +VTUWriter< Mesh >::openPointData() +{ + if( pointDataClosed ) + throw std::logic_error("The tag has already been closed in the current section."); + closeCellData(); + if( ! pointDataOpen ) { + str << "\n"; + pointDataOpen = true; + } +} + +template< typename Mesh > +void +VTUWriter< Mesh >::closePointData() +{ + if( pointDataOpen ) { + str << "\n"; + pointDataClosed = true; + pointDataOpen = false; + } +} + +template< typename Mesh > +void +VTUWriter< Mesh >::closePiece() +{ + if( pieceOpen ) { + closeCellData(); + closePointData(); + str << "\n"; + + // reset indicators - new can be started + pieceOpen = false; + cellDataOpen = cellDataClosed = false; + pointDataOpen = pointDataClosed = false; + } +} + +} // namespace Writers +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/Writers/VerticesPerEntity.h b/src/TNL/Meshes/Writers/VerticesPerEntity.h new file mode 100644 index 0000000000000000000000000000000000000000..5cefb37c72f27ea7e52b53ac6f8a534023f45d4d --- /dev/null +++ b/src/TNL/Meshes/Writers/VerticesPerEntity.h @@ -0,0 +1,71 @@ +/*************************************************************************** + VerticesPerEntity.h - description + ------------------- + begin : Mar 18, 2020 + copyright : (C) 2020 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +// Implemented by: Jakub Klinkovský + +#pragma once + +#include + +#include + +namespace TNL { +namespace Meshes { +namespace Writers { + +namespace details { + +template< typename T, typename R = void > +struct enable_if_type +{ + using type = R; +}; + +template< typename T, typename Enable = void > +struct has_entity_topology : std::false_type {}; + +template< typename T > +struct has_entity_topology< T, typename enable_if_type< typename T::EntityTopology >::type > +: std::true_type +{}; + +} // namespace details + +template< typename Entity, + bool _is_mesh_entity = details::has_entity_topology< Entity >::value > +struct VerticesPerEntity +{ + static constexpr int count = Topologies::Subtopology< typename Entity::EntityTopology, 0 >::count; +}; + +template< typename MeshConfig, typename Device > +struct VerticesPerEntity< MeshEntity< MeshConfig, Device, Topologies::Vertex >, true > +{ + static constexpr int count = 1; +}; + +template< typename GridEntity > +struct VerticesPerEntity< GridEntity, false > +{ +private: + static constexpr int dim = GridEntity::getEntityDimension(); + static_assert( dim >= 0 && dim <= 3, "unexpected dimension of the grid entity" ); + +public: + static constexpr int count = + (dim == 0) ? 1 : + (dim == 1) ? 2 : + (dim == 2) ? 4 : + 8; +}; + +} // namespace Writers +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Problems/HeatEquationProblem.h b/src/TNL/Problems/HeatEquationProblem.h index da261f039b140036bbab164a99d32aae6d301dee..342abe1ba2f2ac3ab5d5ccf4be2e18a49462994a 100644 --- a/src/TNL/Problems/HeatEquationProblem.h +++ b/src/TNL/Problems/HeatEquationProblem.h @@ -104,7 +104,7 @@ class HeatEquationProblem : public PDEProblem< Mesh, protected: - using DistributedMeshSynchronizerType = Meshes::DistributedMeshes::DistributedMeshSynchronizer< MeshFunctionType >; + using DistributedMeshSynchronizerType = Meshes::DistributedMeshes::DistributedMeshSynchronizer< Meshes::DistributedMeshes::DistributedMesh< typename MeshFunctionType::MeshType > >; DistributedMeshSynchronizerType synchronizer; MeshFunctionPointer uPointer; diff --git a/src/TNL/Solvers/FastBuildConfigTag.h b/src/TNL/Solvers/FastBuildConfigTag.h index 18e0d80adadff0256668c72d115a9ae707144a13..e0f1cc174f93b995a37dd1fb66260c1a647b3be5 100644 --- a/src/TNL/Solvers/FastBuildConfigTag.h +++ b/src/TNL/Solvers/FastBuildConfigTag.h @@ -14,14 +14,9 @@ #include namespace TNL { -namespace Solvers { +namespace Solvers { -class FastBuildConfigTag -{ - public: - - static void print() { std::cerr << "FastBuildConfigTag" << std::endl; } -}; +class FastBuildConfigTag {}; /**** * Turn off support for float and long double. diff --git a/src/TNL/Solvers/PDE/TimeDependentPDESolver_impl.h b/src/TNL/Solvers/PDE/TimeDependentPDESolver_impl.h index 89369c8865401d1be28a8e397cb52f251da18e8d..46ffa6fea83ac96ab8e987da149516ac0f6f7213 100644 --- a/src/TNL/Solvers/PDE/TimeDependentPDESolver_impl.h +++ b/src/TNL/Solvers/PDE/TimeDependentPDESolver_impl.h @@ -12,6 +12,7 @@ #include "TimeDependentPDESolver.h" #include +#include namespace TNL { namespace Solvers { @@ -59,11 +60,18 @@ setup( const Config::ParameterContainer& parameters, // Load the mesh from the mesh file // const String& meshFile = parameters.getParameter< String >( "mesh" ); + const String& meshFileFormat = parameters.getParameter< String >( "mesh-format" ); this->distributedMesh.setup( parameters, prefix ); - if( ! Meshes::loadMesh< typename Problem::CommunicatorType >( meshFile, *this->meshPointer, distributedMesh ) ) - return false; - if( ! Meshes::decomposeMesh< Problem >( parameters, prefix, *this->meshPointer, distributedMesh, *problem ) ) - return false; + if( Problem::CommunicatorType::isDistributed() ) { + if( ! Meshes::loadDistributedMesh< typename Problem::CommunicatorType >( *this->meshPointer, distributedMesh, meshFile, meshFileFormat ) ) + return false; + if( ! Meshes::decomposeMesh< Problem >( parameters, prefix, *this->meshPointer, distributedMesh, *problem ) ) + return false; + } + else { + if( ! Meshes::loadMesh( *this->meshPointer, meshFile, meshFileFormat ) ) + return false; + } problem->setMesh( this->meshPointer ); diff --git a/src/TNL/Solvers/PDE/TimeIndependentPDESolver.h b/src/TNL/Solvers/PDE/TimeIndependentPDESolver.h index 8b5b8f52ed826a15be8ef03289c0e050de5a7cd3..189584e79a8ed639ea07ecd3539bc7f69b440e46 100644 --- a/src/TNL/Solvers/PDE/TimeIndependentPDESolver.h +++ b/src/TNL/Solvers/PDE/TimeIndependentPDESolver.h @@ -24,6 +24,8 @@ #include #include +#include + namespace TNL { namespace Solvers { namespace PDE { diff --git a/src/TNL/Solvers/PDE/TimeIndependentPDESolver_impl.h b/src/TNL/Solvers/PDE/TimeIndependentPDESolver_impl.h index 81ada508d4d32c39992d3962ced3aca470180cad..455682e2b6498f13061f8a86c005c2402c44c7da 100644 --- a/src/TNL/Solvers/PDE/TimeIndependentPDESolver_impl.h +++ b/src/TNL/Solvers/PDE/TimeIndependentPDESolver_impl.h @@ -18,6 +18,8 @@ #pragma once #include +#include +#include namespace TNL { namespace Solvers { @@ -49,12 +51,19 @@ setup( const Config::ParameterContainer& parameters, // Load the mesh from the mesh file // const String& meshFile = parameters.getParameter< String >( "mesh" ); + const String& meshFileFormat = parameters.getParameter< String >( "mesh-format" ); this->distributedMesh.setup( parameters, prefix ); - if( ! Meshes::loadMesh< typename Problem::CommunicatorType >( meshFile, *this->meshPointer, distributedMesh ) ) - return false; - if( ! Meshes::decomposeMesh< Problem >( parameters, prefix, *this->meshPointer, distributedMesh, *problem ) ) - return false; - + if( Problem::CommunicatorType::isDistributed() ) { + if( ! Meshes::loadDistributedMesh< typename Problem::CommunicatorType >( *this->meshPointer, distributedMesh, meshFile, meshFileFormat ) ) + return false; + if( ! Meshes::decomposeMesh< Problem >( parameters, prefix, *this->meshPointer, distributedMesh, *problem ) ) + return false; + } + else { + if( ! Meshes::loadMesh( *this->meshPointer, meshFile, meshFileFormat ) ) + return false; + } + problem->setMesh( this->meshPointer ); /**** diff --git a/src/TNL/Solvers/SolverConfig_impl.h b/src/TNL/Solvers/SolverConfig_impl.h index 3c21a7b239efb3d9c5bfdd5b19ce02e5e19d6a00..16d106a908b9e378e1013dc0da8920d433be501d 100644 --- a/src/TNL/Solvers/SolverConfig_impl.h +++ b/src/TNL/Solvers/SolverConfig_impl.h @@ -78,7 +78,8 @@ bool SolverConfig< ConfigTag, ProblemConfig >::configSetup( Config::ConfigDescri */ config.addDelimiter( " === Space discretisation parameters ==== " ); config.addEntry< String >( "mesh", "A file which contains the numerical mesh. You may create it with tools like tnl-grid-setup or tnl-mesh-convert.", "mesh.tnl" ); - + config.addEntry< String >( "mesh-format", "Mesh file format.", "auto" ); + /**** * Domain decomposition */ diff --git a/src/TNL/Solvers/SolverInitiator_impl.h b/src/TNL/Solvers/SolverInitiator_impl.h index e54a8fe308c4478a7242a24f8032473be8431d1e..16e0fd2227830a5687e8ee9a04a878d723efd8d3 100644 --- a/src/TNL/Solvers/SolverInitiator_impl.h +++ b/src/TNL/Solvers/SolverInitiator_impl.h @@ -225,7 +225,12 @@ class SolverInitiatorMeshResolver< ProblemSetter, Real, Device, Index, ConfigTag static bool run( const Config::ParameterContainer& parameters ) { const String& meshFileName = parameters.getParameter< String >( "mesh" ); - return Meshes::resolveMeshType< ConfigTag, Device, ProblemSetterWrapper >( meshFileName, parameters ); + const String& meshFileFormat = parameters.getParameter< String >( "mesh-format" ); + auto wrapper = [&]( const auto& reader, auto&& mesh ) { + using MeshType = std::decay_t< decltype(mesh) >; + return ProblemSetterWrapper< MeshType >::run( parameters ); + }; + return Meshes::resolveMeshType< ConfigTag, Device >( wrapper, meshFileName, meshFileFormat ); } }; diff --git a/src/TNL/base64.h b/src/TNL/base64.h new file mode 100644 index 0000000000000000000000000000000000000000..cab004bb2038cb95712a61b40c0e537803e097c3 --- /dev/null +++ b/src/TNL/base64.h @@ -0,0 +1,316 @@ +/*************************************************************************** + base64.h - description + ------------------- + begin : Mar 20, 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 // std::ceil + +namespace TNL { + +// The functions in the base64 namespace are taken from the libb64 project, see +// http://sourceforge.net/projects/libb64 +// +// libb64 has been placed in the public domain +namespace base64 { + +// encoding + +typedef enum +{ + step_A, + step_B, + step_C +} base64_encodestep; + +typedef struct +{ + base64_encodestep step; + char result; +} base64_encodestate; + +inline void +base64_init_encodestate(base64_encodestate *state_in) +{ + state_in->step = step_A; + state_in->result = 0; +} + +inline char +base64_encode_value(char value_in) +{ + static const char *encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + if (value_in > 63) + return '='; + return encoding[(int)value_in]; +} + +inline std::ptrdiff_t +base64_encode_block(const char * plaintext_in, + std::size_t length_in, + char * code_out, + base64_encodestate *state_in) +{ + const char * plainchar = plaintext_in; + const char *const plaintextend = plaintext_in + length_in; + char * codechar = code_out; + char result; + + result = state_in->result; + + switch (state_in->step) + { + while (true) + { + case step_A: + { + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_A; + return codechar - code_out; + } + const char fragment = *plainchar++; + result = (fragment & 0x0fc) >> 2; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x003) << 4; + // intended fallthrough + } + case step_B: + { + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_B; + return codechar - code_out; + } + const char fragment = *plainchar++; + result |= (fragment & 0x0f0) >> 4; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x00f) << 2; + // intended fallthrough + } + case step_C: + { + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_C; + return codechar - code_out; + } + const char fragment = *plainchar++; + result |= (fragment & 0x0c0) >> 6; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x03f) >> 0; + *codechar++ = base64_encode_value(result); + } + } + } + /* control should not reach here */ + return codechar - code_out; +} + +inline std::ptrdiff_t +base64_encode_blockend(char *code_out, base64_encodestate *state_in) +{ + char *codechar = code_out; + + switch (state_in->step) + { + case step_B: + *codechar++ = base64_encode_value(state_in->result); + *codechar++ = '='; + *codechar++ = '='; + break; + case step_C: + *codechar++ = base64_encode_value(state_in->result); + *codechar++ = '='; + break; + case step_A: + break; + } + *codechar++ = '\0'; + + return codechar - code_out; +} + + +// decoding + +typedef enum +{ + step_a, step_b, step_c, step_d +} base64_decodestep; + +typedef struct +{ + base64_decodestep step; + char plainchar; +} base64_decodestate; + +inline void +base64_init_decodestate(base64_decodestate* state_in) +{ + state_in->step = step_a; + state_in->plainchar = 0; +} + +inline int +base64_decode_value(char value_in) +{ + static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51}; + static const char decoding_size = sizeof(decoding); + value_in -= 43; + if (value_in < 0 || value_in >= decoding_size) return -1; + return decoding[(int)value_in]; +} + +inline std::ptrdiff_t +base64_decode_block(const char* code_in, const std::size_t length_in, char* plaintext_out, base64_decodestate* state_in) +{ + const char* codechar = code_in; + char* plainchar = plaintext_out; + char fragment; + + *plainchar = state_in->plainchar; + + switch (state_in->step) + { + while (1) + { + case step_a: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_a; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar = (fragment & 0x03f) << 2; + case step_b: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_b; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x030) >> 4; + *plainchar = (fragment & 0x00f) << 4; + case step_c: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_c; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x03c) >> 2; + *plainchar = (fragment & 0x003) << 6; + case step_d: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_d; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x03f); + } + } + /* control should not reach here */ + return plainchar - plaintext_out; +} + +} // namespace base64 + + +/** + * Do a base64 encoding of the given data. + * + * The function returns a unique_ptr to the encoded data. + */ +inline std::unique_ptr +encode_block(const char* data, const std::size_t data_size) +{ + base64::base64_encodestate state; + base64::base64_init_encodestate(&state); + + std::unique_ptr encoded_data{new char[2 * data_size + 1]}; + + const std::size_t encoded_length_data = base64::base64_encode_block(data, data_size, encoded_data.get(), &state); + base64::base64_encode_blockend(encoded_data.get() + encoded_length_data, &state); + + return encoded_data; +} + + +/** + * Do a base64 decoding of the given data. + * + * The function returns a pair of the decoded data length and a unique_ptr to + * the decoded data. + */ +inline std::pair> +decode_block(const char* data, const std::size_t data_size) +{ + base64::base64_decodestate state; + base64::base64_init_decodestate(&state); + + std::unique_ptr decoded_data{new char[data_size + 1]}; + + const std::size_t decoded_length_data = base64::base64_decode_block(data, data_size, decoded_data.get(), &state); + decoded_data[decoded_length_data] = '\0'; + + return {decoded_length_data, std::move(decoded_data)}; +} + + +/** + * Write a base64-encoded block of data into the given stream. + * + * The encoded data is prepended with a short header, which is the base64-encoded + * byte length of the data. The type of the byte length value is `HeaderType`. + */ +template +void write_encoded_block(const T* data, const std::size_t data_length, std::ostream& output_stream) +{ + const HeaderType size = data_length * sizeof(T); + std::unique_ptr encoded_size = encode_block(reinterpret_cast(&size), sizeof(HeaderType)); + output_stream << encoded_size.get(); + std::unique_ptr encoded_data = encode_block(reinterpret_cast(data), size); + output_stream << encoded_data.get(); +} + + +/** + * Get the length of base64-encoded block for given data byte length. + */ +inline std::size_t +get_encoded_length(const std::size_t byte_length) +{ + int encoded = std::ceil(byte_length * (4.0 / 3.0)); + // base64 uses padding to a multiple of 4 + if (encoded % 4 == 0) + return encoded; + return encoded + 4 - (encoded % 4); +} + +} // namespace TNL diff --git a/src/TNL/zlib_compression.h b/src/TNL/zlib_compression.h new file mode 100644 index 0000000000000000000000000000000000000000..a9c4234753206fc1f8cbfa85efe7720588078451 --- /dev/null +++ b/src/TNL/zlib_compression.h @@ -0,0 +1,185 @@ +/*************************************************************************** + zlib_compression.h - description + ------------------- + begin : Mar 20, 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 // std::uint64_t + +#include + +#include + +namespace TNL { + +/** + * Use zlib to compress the data, then encode it with base64 and write the + * result to the given stream. + * + * Options for compression_level: + * - Z_NO_COMPRESSION + * - Z_BEST_SPEED + * - Z_BEST_COMPRESSION + * - Z_DEFAULT_COMPRESSION + */ +template +void +write_compressed_block(const T* data, + const std::size_t data_size, + std::ostream& output_stream, + const int compression_level = Z_DEFAULT_COMPRESSION) +{ + if (data_size == 0) + return; + + // allocate a buffer for compressing data and do so + uLongf compressed_data_length = compressBound(data_size * sizeof(T)); + std::unique_ptr compressed_data{new char[compressed_data_length]}; + + // compress the data + const int status = compress2( + reinterpret_cast(compressed_data.get()), + &compressed_data_length, + reinterpret_cast(data), + data_size * sizeof(T), + compression_level + ); + if (status != Z_OK) + throw std::runtime_error("zlib compression failed"); + + // create compression header + const HeaderType compression_header[4] = { + (HeaderType) 1, // number of blocks + (HeaderType) (data_size * sizeof(T)), // size of block + (HeaderType) (data_size * sizeof(T)), // size of last block + (HeaderType) compressed_data_length // list of compressed sizes of blocks + }; + + // base64-encode the compression header + std::unique_ptr encoded_header( + encode_block(reinterpret_cast(&compression_header[0]), + 4 * sizeof(HeaderType)) + ); + output_stream << encoded_header.get(); + + // base64-encode the compressed data + std::unique_ptr encoded_data( + encode_block(compressed_data.get(), compressed_data_length) + ); + output_stream << encoded_data.get(); +} + +/** + * Decompress data in given byte array and return an array of elements of + * type T and length data_size. + */ +template +std::unique_ptr +decompress_data(const char* decoded_data, const std::size_t decoded_data_length, const std::size_t data_size) +{ + // decompress the data + std::unique_ptr data{new T[data_size]}; + uLongf uncompressed_length_data = data_size * sizeof(T); + const int status = uncompress( + reinterpret_cast(data.get()), + &uncompressed_length_data, + reinterpret_cast(decoded_data), + decoded_data_length + ); + if (status != Z_OK) + throw std::runtime_error("zlib decompression failed"); + + if (uncompressed_length_data != data_size * sizeof(T)) + throw std::length_error("uncompressed data length does not match the size stored in the compression header: " + + std::to_string(uncompressed_length_data) + " vs " + + std::to_string(data_size)); + + return data; +} + +/** + * Decode and decompress data from a stream. The data must be in the format + * as produced by the write_compressed_block function. + * + * The function returns a pair of the resulting data size and a unique_ptr to + * the data. + */ +template +std::pair> +decompress_block(const char* data) +{ + // decode the header + const int encoded_header_length = get_encoded_length(4 * sizeof(HeaderType)); + std::pair> decoded_header = decode_block(data, encoded_header_length); + const HeaderType* compression_header = reinterpret_cast(decoded_header.second.get()); + + if (compression_header[0] != 1) + throw std::length_error("unexpected number of compressed blocks: " + + std::to_string(compression_header[0])); + if (compression_header[1] != compression_header[2]) + throw std::logic_error("inconsistent block sizes in the compression header: " + + std::to_string(compression_header[1]) + " vs " + + std::to_string(compression_header[2])); + const HeaderType data_size = compression_header[1] / sizeof(T); + const HeaderType compressed_data_length = get_encoded_length(compression_header[3]); + + // decode the data + std::pair> decoded_data = decode_block(data + encoded_header_length, compressed_data_length); + + // decompress the data and return + return {data_size, decompress_data(decoded_data.second.get(), decoded_data.first, data_size)}; +} + +/** + * Decode and decompress data from a stream. The data must be in the format + * as produced by the write_compressed_block function. + * + * The function returns a pair of the resulting data size and a unique_ptr to + * the data. + */ +template +std::pair> +decompress_block(std::istream& input_stream) +{ + // read the header + const int encoded_header_length = get_encoded_length(4 * sizeof(HeaderType)); + std::unique_ptr encoded_header{new char[encoded_header_length]}; + input_stream.read(encoded_header.get(), encoded_header_length); + if (!input_stream.good()) + throw std::length_error("input is not long enough to contain a compression header"); + + // decode the header + std::pair> decoded_header = decode_block(encoded_header.get(), encoded_header_length); + const HeaderType* compression_header = reinterpret_cast(decoded_header.second.get()); + + if (compression_header[0] != 1) + throw std::length_error("unexpected number of compressed blocks: " + + std::to_string(compression_header[0])); + if (compression_header[1] != compression_header[2]) + throw std::logic_error("inconsistent block sizes in the compression header: " + + std::to_string(compression_header[1]) + " vs " + + std::to_string(compression_header[2])); + const HeaderType data_size = compression_header[1] / sizeof(T); + const HeaderType compressed_data_length = get_encoded_length(compression_header[3]); + + // read the compressed data + std::unique_ptr encoded_data{new char[compressed_data_length]}; + input_stream.read(encoded_data.get(), compressed_data_length); + if (!input_stream.good()) + throw std::length_error("failed to read the compressed data"); + + // decode the data + std::pair> decoded_data = decode_block(encoded_data.get(), compressed_data_length); + + // decompress the data and return + return {data_size, decompress_data(decoded_data.second.get(), decoded_data.first, data_size)}; +} + +} // namespace TNL diff --git a/src/Tools/CMakeLists.txt b/src/Tools/CMakeLists.txt index 787bf40bf96f7d74cfcfad26f370323e6607fac0..34dfc5e3e28706dc13c601d59dd14b59d65cf736 100644 --- a/src/Tools/CMakeLists.txt +++ b/src/Tools/CMakeLists.txt @@ -6,6 +6,7 @@ CONFIGURE_FILE( "tnl-bindir.in" "${PROJECT_TOOLS_PATH}/tnl-bindir" @ONLY ) ADD_EXECUTABLE(tnl-grid-setup tnl-grid-setup.cpp ) ADD_EXECUTABLE(tnl-grid-to-mesh tnl-grid-to-mesh.cpp ) ADD_EXECUTABLE(tnl-mesh-converter tnl-mesh-converter.cpp ) +ADD_EXECUTABLE(tnl-game-of-life tnl-game-of-life.cpp ) ADD_EXECUTABLE(tnl-init tnl-init.cpp ) ADD_EXECUTABLE(tnl-view tnl-view.cpp ) ADD_EXECUTABLE(tnl-diff tnl-diff.cpp ) @@ -24,6 +25,47 @@ if( DCMTK_FOUND ) target_link_libraries(tnl-dicom-reader ${DCMTK_LIBRARIES} ) endif() +find_package( ZLIB ) +if( ZLIB_FOUND ) + target_compile_definitions(tnl-view PUBLIC "-DHAVE_ZLIB") + target_include_directories(tnl-view PUBLIC ${ZLIB_INCLUDE_DIRS}) + target_link_libraries(tnl-view ${ZLIB_LIBRARIES}) + + target_compile_definitions(tnl-mesh-converter PUBLIC "-DHAVE_ZLIB") + target_include_directories(tnl-mesh-converter PUBLIC ${ZLIB_INCLUDE_DIRS}) + target_link_libraries(tnl-mesh-converter ${ZLIB_LIBRARIES}) + + target_compile_definitions(tnl-game-of-life PUBLIC "-DHAVE_ZLIB") + target_include_directories(tnl-game-of-life PUBLIC ${ZLIB_INCLUDE_DIRS}) + target_link_libraries(tnl-game-of-life ${ZLIB_LIBRARIES}) +endif() + +find_package( tinyxml2 QUIET ) +if( tinyxml2_FOUND ) + target_compile_definitions(tnl-mesh-converter PUBLIC "-DHAVE_TINYXML2") + target_link_libraries(tnl-mesh-converter tinyxml2::tinyxml2) + + target_compile_definitions(tnl-game-of-life PUBLIC "-DHAVE_TINYXML2") + target_link_libraries(tnl-game-of-life tinyxml2::tinyxml2) +endif() + +find_package( METIS QUIET ) +if( METIS_FOUND ) + add_executable(tnl-decompose-mesh tnl-decompose-mesh.cpp) + target_include_directories(tnl-decompose-mesh PUBLIC ${METIS_INCLUDE_DIRS}) + target_link_libraries(tnl-decompose-mesh ${METIS_LIBRARIES}) + if( ZLIB_FOUND ) + target_compile_definitions(tnl-decompose-mesh PUBLIC "-DHAVE_ZLIB") + target_include_directories(tnl-decompose-mesh PUBLIC ${ZLIB_INCLUDE_DIRS}) + target_link_libraries(tnl-decompose-mesh ${ZLIB_LIBRARIES}) + endif() + if( tinyxml2_FOUND ) + target_compile_definitions(tnl-decompose-mesh PUBLIC "-DHAVE_TINYXML2") + target_link_libraries(tnl-decompose-mesh tinyxml2::tinyxml2) + endif() + install( TARGETS tnl-decompose-mesh DESTINATION bin ) +endif() + IF( BUILD_CUDA ) CUDA_ADD_EXECUTABLE( tnl-cuda-arch tnl-cuda-arch.cu ) INSTALL( TARGETS tnl-cuda-arch @@ -37,6 +79,7 @@ INSTALL( TARGETS tnl-init tnl-grid-setup tnl-grid-to-mesh tnl-mesh-converter + tnl-game-of-life tnl-dicom-reader tnl-image-converter tnl-lattice-init @@ -48,4 +91,3 @@ INSTALL( FILES ${PROJECT_TOOLS_PATH}/tnl-bindir tnl-log-to-html.py DESTINATION bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) - diff --git a/src/Tools/tnl-decompose-mesh.cpp b/src/Tools/tnl-decompose-mesh.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b1fb30d060cf834c31d205c61cde57cb1607b51c --- /dev/null +++ b/src/Tools/tnl-decompose-mesh.cpp @@ -0,0 +1,741 @@ +/*************************************************************************** + tnl-decompose-mesh.cpp - description + ------------------- + begin : Apr 1, 2020 + copyright : (C) 2020 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +// Implemented by: Jakub Klinkovský + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +using namespace TNL; + +struct DecomposeMeshConfigTag {}; + +namespace TNL { +namespace Meshes { +namespace BuildConfigTags { + +/**** + * Turn off all grids. + */ +template<> struct GridRealTag< DecomposeMeshConfigTag, float > { enum { enabled = false }; }; +template<> struct GridRealTag< DecomposeMeshConfigTag, double > { enum { enabled = false }; }; +template<> struct GridRealTag< DecomposeMeshConfigTag, long double > { enum { enabled = false }; }; + +/**** + * Unstructured meshes. + */ +template<> struct MeshCellTopologyTag< DecomposeMeshConfigTag, Topologies::Edge > { enum { enabled = true }; }; +template<> struct MeshCellTopologyTag< DecomposeMeshConfigTag, Topologies::Triangle > { enum { enabled = true }; }; +template<> struct MeshCellTopologyTag< DecomposeMeshConfigTag, Topologies::Quadrilateral > { enum { enabled = true }; }; +template<> struct MeshCellTopologyTag< DecomposeMeshConfigTag, Topologies::Tetrahedron > { enum { enabled = true }; }; +template<> struct MeshCellTopologyTag< DecomposeMeshConfigTag, 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< DecomposeMeshConfigTag, CellTopology, WorldDimension > +{ enum { enabled = ( WorldDimension == CellTopology::dimension ) }; }; + +// Meshes are enabled only for types explicitly listed below. +template<> struct MeshRealTag< DecomposeMeshConfigTag, float > { enum { enabled = false }; }; +template<> struct MeshRealTag< DecomposeMeshConfigTag, double > { enum { enabled = true }; }; +template<> struct MeshGlobalIndexTag< DecomposeMeshConfigTag, int > { enum { enabled = true }; }; +template<> struct MeshGlobalIndexTag< DecomposeMeshConfigTag, long int > { enum { enabled = false }; }; +template<> struct MeshLocalIndexTag< DecomposeMeshConfigTag, short int > { enum { enabled = true }; }; + +// Config tag specifying the MeshConfig template to use. +template<> +struct MeshConfigTemplateTag< DecomposeMeshConfigTag > +{ + template< typename Cell, + int WorldDimension = Cell::dimension, + typename Real = double, + typename GlobalIndex = int, + typename LocalIndex = GlobalIndex > + struct MeshConfig + { + 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 ) + { + // subvertices of faces are needed due to cell boundary tags + 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 ) + { + // superentities from faces to cells are needed due to cell boundary tags + return SuperentityDimension == meshDimension && EntityTopology::dimension == meshDimension - 1; + } + + template< typename EntityTopology > + static constexpr bool entityTagsStorage( EntityTopology ) + { + return EntityTopology::dimension >= meshDimension - 1; + } + + static constexpr bool dualGraphStorage() + { + return false; + } + }; +}; + +} // namespace BuildConfigTags +} // namespace Meshes +} // namespace TNL + + +void configSetup( Config::ConfigDescription& config ) +{ + config.addDelimiter( "General settings:" ); + config.addRequiredEntry< String >( "input-file", "Input file with the mesh." ); + config.addEntry< String >( "input-file-format", "Input mesh file format.", "auto" ); + config.addRequiredEntry< String >( "output-file", "Output mesh file in TNL or VTK format." ); + config.addRequiredEntry< unsigned >( "subdomains", "Number of subdomains to decompose the mesh." ); + config.addEntry< unsigned >( "ghost-levels", "Number of ghost levels by which the subdomains overlap.", 0 ); + config.addEntry< unsigned >( "min-common-vertices", + "Specifies the number of common nodes that two elements must have in order to put an " + "edge between them in the dual graph. By default it is equal to the mesh dimension." ); + config.addDelimiter( "METIS options:" ); + config.addEntry< String >( "metis-ptype", "Partitioning method.", "KWAY" ); + config.addEntryEnum( "KWAY" ); + config.addEntryEnum( "RB" ); + // NOTE: disabled because VOL requires the `vsize` array to be used +// config.addEntry< String >( "metis-objtype", "Type of the objective (used only by metis-ptype=KWAY).", "CUT" ); +// config.addEntryEnum( "CUT" ); +// config.addEntryEnum( "VOL" ); + config.addEntry< String >( "metis-ctype", "Matching scheme to be used during coarsening.", "RM" ); + config.addEntryEnum( "RM" ); + config.addEntryEnum( "SHEM" ); + config.addEntry< String >( "metis-iptype", "Algorithm used during initial partitioning.", "GROW" ); + config.addEntryEnum( "GROW" ); + config.addEntryEnum( "RANDOM" ); + config.addEntryEnum( "EDGE" ); + config.addEntryEnum( "NODE" ); + config.addEntry< String >( "metis-rtype", "Algorithm used for refinement.", "FM" ); + config.addEntryEnum( "FM" ); + config.addEntryEnum( "GREEDY" ); + config.addEntryEnum( "SEP2SIDED" ); + config.addEntryEnum( "SEP1SIDED" ); + config.addEntry< int >( "metis-no2hop", + "Specifies that the coarsening will not perform any 2–hop matchings when the standard " + "matching approach fails to sufficiently coarsen the graph. The 2–hop matching is very " + "effective for graphs with power-law degree distributions. " + "0 - Performs a 2–hop matching. 1 - Does not perform a 2–hop matching. ", + 0 ); + config.addEntryEnum( 0 ); + config.addEntryEnum( 1 ); + config.addEntry< unsigned >( "metis-ncuts", + "Specifies the number of different partitionings that it will compute. The final " + "partitioning is the one that achieves the best edgecut or communication volume.", + 1 ); + config.addEntry< unsigned >( "metis-niter", + "Specifies the number of iterations for the refinement algorithms at each stage of the " + "uncoarsening process.", + 10 ); + config.addEntry< unsigned >( "metis-ufactor", + "Specifies the maximum allowed load imbalance among the partitions. " + "The default value is 1 for metis-ptype=RB and 30 for metis-ptype=KWAY." ); + config.addEntry< int >( "metis-minconn", + "Specifies that the partitioning routines should try to minimize the maximum degree of the " + "subdomain graph. Note that the option applies only to metis-ptype=KWAY.", + 1 ); + config.addEntryEnum( 0 ); + config.addEntryEnum( 1 ); + config.addEntry< int >( "metis-contig", + "Specifies that the partitioning routines should try to produce partitions that are " + "contiguous. Note that if the input graph is not connected this option is ignored. " + "Note that the option applies only to metis-ptype=KWAY.", + 1 ); + config.addEntryEnum( 0 ); + config.addEntryEnum( 1 ); + config.addEntry< unsigned >( "metis-dbglvl", + "Specifies the amount of progress/debugging information will be printed during the execution " + "of the algorithms. The default value is 0 (no debugging/progress information). A non-zero " + "value can be supplied that is obtained by a bit-wise OR of the following values.\n" + " METIS_DBG_INFO (1) Prints various diagnostic messages.\n" + " METIS_DBG_TIME (2) Performs timing analysis.\n" + " METIS_DBG_COARSEN (4) Displays various statistics during coarsening.\n" + " METIS_DBG_REFINE (8) Displays various statistics during refinement.\n" + " METIS_DBG_IPART (16) Displays various statistics during initial partitioning.\n" + " METIS_DBG_MOVEINFO (32) Displays detailed information about vertex moves during refinement.\n" + " METIS_DBG_SEPINFO (64) Displays information about vertex separators.\n" + " METIS_DBG_CONNINFO (128) Displays information related to the minimization of subdomain connectivity.\n" + " METIS_DBG_CONTIGINFO (256) Displays information related to the elimination of connected components.", + 0 ); +} + +void setMETISoptions( idx_t options[METIS_NOPTIONS], const Config::ParameterContainer& parameters ) +{ + // partitioning method + const String ptype = parameters.getParameter< String >( "metis-ptype" ); + if( ptype == "KWAY" ) + options[METIS_OPTION_PTYPE] = METIS_PTYPE_KWAY; + if( ptype == "RB" ) + options[METIS_OPTION_PTYPE] = METIS_PTYPE_RB; + // type of the objective (used only by METIS_PTYPE_KWAY) + options[METIS_OPTION_OBJTYPE] = METIS_OBJTYPE_CUT; // or METIS_OBJTYPE_VOL (requires vsize to be used) + // matching scheme to be used during coarsening + const String ctype = parameters.getParameter< String >( "metis-ctype" ); + if( ctype == "RM" ) + options[METIS_OPTION_CTYPE] = METIS_CTYPE_RM; + if( ctype == "SHEM" ) + options[METIS_OPTION_CTYPE] = METIS_CTYPE_SHEM; + // algorithm used during initial partitioning + const String iptype = parameters.getParameter< String >( "metis-iptype" ); + if( iptype == "GROW" ) + options[METIS_OPTION_IPTYPE] = METIS_IPTYPE_GROW; + if( iptype == "RANDOM" ) + options[METIS_OPTION_IPTYPE] = METIS_IPTYPE_RANDOM; + if( iptype == "EDGE" ) + options[METIS_OPTION_IPTYPE] = METIS_IPTYPE_EDGE; + if( iptype == "NODE" ) + options[METIS_OPTION_IPTYPE] = METIS_IPTYPE_NODE; + // algorithm used for refinement + const String rtype = parameters.getParameter< String >( "metis-rtype" ); + if( rtype == "FM" ) + options[METIS_OPTION_RTYPE] = METIS_RTYPE_FM; + if( rtype == "GREEDY" ) + options[METIS_OPTION_RTYPE] = METIS_RTYPE_GREEDY; + if( rtype == "SEP2SIDED" ) + options[METIS_OPTION_RTYPE] = METIS_RTYPE_SEP2SIDED; + if( rtype == "SEP1SIDED" ) + options[METIS_OPTION_RTYPE] = METIS_RTYPE_SEP1SIDED; + // Specifies that the coarsening will not perform any 2–hop matchings when the standard + // matching approach fails to sufficiently coarsen the graph. The 2–hop matching is very + // effective for graphs with power-law degree distributions. + // 0 - Performs a 2–hop matching. 1 - Does not perform a 2–hop matching. + options[METIS_OPTION_NO2HOP] = parameters.getParameter< int >( "metis-no2hop" ); + // Specifies the number of different partitionings that it will compute. The final + // partitioning is the one that achieves the best edgecut or communication volume. + // Default is 1. + options[METIS_OPTION_NCUTS] = parameters.getParameter< unsigned >( "metis-ncuts" ); + // Specifies the number of iterations for the refinement algorithms at each stage of the + // uncoarsening process. Default is 10. + options[METIS_OPTION_NITER] = parameters.getParameter< unsigned >( "metis-niter" ); + // Specifies the maximum allowed load imbalance among the partitions. + // The default value is 1 for ptype=rb and 30 for ptype=kway. + if( parameters.checkParameter( "metis-ufactor" ) ) + options[METIS_OPTION_UFACTOR] = parameters.getParameter< unsigned >( "metis-ufactor" ); + // Specifies that the partitioning routines should try to minimize the maximum degree of the + // subdomain graph, i.e., the graph in which each partition is a node, and edges connect + // subdomains with a shared interface. + // 0 - Does not explicitly minimize the maximum connectivity. + // 1 - Explicitly minimize the maximum connectivity. + // NOTE: applies only to METIS_PTYPE_KWAY + options[METIS_OPTION_MINCONN] = parameters.getParameter< int >( "metis-minconn" ); + // Specifies that the partitioning routines should try to produce partitions that are + // contiguous. Note that if the input graph is not connected this option is ignored. + // NOTE: applies only to METIS_PTYPE_KWAY + options[METIS_OPTION_CONTIG] = parameters.getParameter< int >( "metis-contig" ); + // seed for the random number generator + options[METIS_OPTION_SEED] = std::chrono::system_clock::now().time_since_epoch().count(); + // numbering scheme for the adjacency structure of a graph or element-node structure of a mesh + // (0 is C-style, 1 is Fortran-style) + options[METIS_OPTION_NUMBERING] = 0; + // Specifies the amount of progress/debugging information will be printed during the execution + // of the algorithms. The default value is 0 (no debugging/progress information). A non-zero + // value can be supplied that is obtained by a bit-wise OR of the following values. + // METIS_DBG_INFO (1) Prints various diagnostic messages. + // METIS_DBG_TIME (2) Performs timing analysis. + // METIS_DBG_COARSEN (4) Displays various statistics during coarsening. + // METIS_DBG_REFINE (8) Displays various statistics during refinement. + // METIS_DBG_IPART (16) Displays various statistics during initial partitioning. + // METIS_DBG_MOVEINFO (32) Displays detailed information about vertex moves during refinement. + // METIS_DBG_SEPINFO (64) Displays information about vertex separators. + // METIS_DBG_CONNINFO (128) Displays information related to the minimization of subdomain connectivity. + // METIS_DBG_CONTIGINFO (256) Displays information related to the elimination of connected components. + options[METIS_OPTION_DBGLVL] = parameters.getParameter< unsigned >( "metis-dbglvl" ); +} + +template< typename Mesh > +struct DecomposeMesh +{ + using Index = typename Mesh::GlobalIndexType; + using IndexArray = Containers::Array< Index, Devices::Sequential, Index >; + using MetisIndexArray = Containers::Array< idx_t, Devices::Sequential, idx_t >; + + static bool run( const Mesh& mesh, const Config::ParameterContainer& parameters ) + { + // warn if input mesh has 64-bit indices, but METIS uses only 32-bit indices + if( IDXTYPEWIDTH == 32 && sizeof(Index) > 32 ) + std::cerr << "Warning: the input mesh uses 64-bit indices, but METIS was compiled only with 32-bit indices. " + "Decomposition may not work correctly if the index values overflow the 32-bit type." << std::endl; + + // get the mesh connectivity information in a format suitable for METIS. Actually, the same + // format is used by the XML-based VTK formats - the only difference is that METIS requires + // `offsets` to start with 0. + std::vector< idx_t > connectivity, offsets; + offsets.push_back(0); + const Index cellsCount = mesh.template getEntitiesCount< typename Mesh::Cell >(); + for( Index i = 0; i < cellsCount; i++ ) { + const auto& entity = mesh.template getEntity< typename Mesh::Cell >( i ); + const Index subvertices = entity.template getSubentitiesCount< 0 >(); + for( Index j = 0; j < subvertices; j++ ) + connectivity.push_back( entity.template getSubentityIndex< 0 >( j ) ); + offsets.push_back( connectivity.size() ); + } + + // number of elements (cells) + idx_t ne = mesh.template getEntitiesCount< typename Mesh::Cell >(); + // number of nodes (vertices) + idx_t nn = mesh.template getEntitiesCount< 0 >(); + // pointers to arrays storing the mesh in a CSR-like format + idx_t* eptr = offsets.data(); + idx_t* eind = connectivity.data(); + // Specifies the number of common nodes that two elements must have in order to put an edge + // between them in the dual graph. + idx_t ncommon = Mesh::getMeshDimension(); + if( parameters.checkParameter( "min-common-vertices" ) ) + ncommon = parameters.template getParameter< unsigned >( "min-common-vertices" ); + // numbering scheme for the adjacency structure of a graph or element-node structure of a mesh + // (0 is C-style, 1 is Fortran-style) + idx_t numflag = 0; + // These arrays store the adjacency structure of the generated dual graph. The format is of + // the adjacency structure is described in Section 5.5 of the METIS manual. Memory for these + // arrays is allocated by METIS’ API using the standard malloc function. It is the + // responsibility of the application to free this memory by calling free. METIS provides the + // METIS_Free that is a wrapper to C’s free function. + idx_t* xadj = nullptr; + idx_t* adjncy = nullptr; + + // We could use METIS_PartMeshDual directly instead of METIS_MeshToDual + METIS_PartGraph*, + // but we need to reuse the dual graph for the generation of ghost cells. + std::cout << "Running METIS_MeshToDual..." << std::endl; + int status = METIS_MeshToDual(&ne, &nn, eptr, eind, &ncommon, &numflag, &xadj, &adjncy); + + // wrap xadj and adjncy with shared_ptr + std::shared_ptr shared_xadj {xadj, METIS_Free}; + std::shared_ptr shared_adjncy {adjncy, METIS_Free}; + + switch( status ) + { + case METIS_OK: break; + case METIS_ERROR_INPUT: + throw std::runtime_error( "METIS_MeshToDual failed due to an input error." ); + case METIS_ERROR_MEMORY: + throw std::runtime_error( "METIS_MeshToDual failed due to a memory allocation error." ); + case METIS_ERROR: + default: + throw std::runtime_error( "METIS_MeshToDual failed with an unspecified error." ); + } + + // The number of vertices in the graph. + idx_t nvtxs = ne; + // The number of balancing constraints. It should be at least 1. + idx_t ncon = 1; + // An array of size `ne` specifying the weights of the elements. A NULL value can be passed + // to indicate that all elements have an equal weight. + idx_t* vwgt = nullptr; + // An array of size `ne` specifying the size of the elements that is used for computing the + // total communication volume as described in Section 5.7 of the METIS manual. A NULL value + // can be passed when the objective is cut or when all elements have an equal size. + idx_t* vsize = nullptr; + // The weights of the edges as described in Section 5.5 of the METIS manual. + idx_t* adjwgt = nullptr; // METIS_PartMeshDual uses NULL too + // The number of parts to partition the mesh. + idx_t nparts = parameters.template getParameter< unsigned >( "subdomains" ); + // This is an array of size nparts that specifies the desired weight for each partition. The + // target partition weight for the i-th partition is specified at tpwgts[i] (the numbering for + // the partitions starts from 0). The sum of the tpwgts[] entries must be 1.0. A NULL value + // can be passed to indicate that the graph should be equally divided among the partitions. + real_t* tpwgts = nullptr; + // This is an array of size ncon that specifies the allowed load imbalance tolerance for each + // constraint. For the i-th partition and j-th constraint the allowed weight is the + // ubvec[j]*tpwgts[i*ncon+j] fraction of the j-th’s constraint total weight. The load + // imbalances must be greater than 1.0. A NULL value can be passed indicating that the load + // imbalance tolerance for each constraint should be 1.001 (for ncon=1) or 1.01 (for ncon>1). + real_t* ubvec = nullptr; // METIS_PartMeshDual uses NULL too + // Upon successful completion, this variable stores either the edgecut or the total + // communication volume of the dual graph’s partitioning. + idx_t objval = 0; + // Array of size `ne` that upon successful completion stores the partition array for the + // elements of the mesh. + MetisIndexArray part_array( nvtxs ); + idx_t* part = part_array.getData(); + + // Array of METIS options as described in Section 5.4 of the METIS manual. + idx_t options[METIS_NOPTIONS]; + // future-proof (or just in case we forgot to set some options explicitly) + METIS_SetDefaultOptions(options); + // set METIS options from parameters + setMETISoptions(options, parameters); + + if( options[METIS_OPTION_PTYPE] == METIS_PTYPE_KWAY ) { + std::cout << "Running METIS_PartGraphKway..." << std::endl; + status = METIS_PartGraphKway(&nvtxs, &ncon, xadj, adjncy, vwgt, vsize, adjwgt, &nparts, tpwgts, ubvec, options, &objval, part); + } + else { + std::cout << "Running METIS_PartGraphRecursive..." << std::endl; + status = METIS_PartGraphRecursive(&nvtxs, &ncon, xadj, adjncy, vwgt, vsize, adjwgt, &nparts, tpwgts, ubvec, options, &objval, part); + } + + switch( status ) + { + case METIS_OK: break; + case METIS_ERROR_INPUT: + throw std::runtime_error( "METIS_PartGraph failed due to an input error." ); + case METIS_ERROR_MEMORY: + throw std::runtime_error( "METIS_PartGraph failed due to a memory allocation error." ); + case METIS_ERROR: + default: + throw std::runtime_error( "METIS_PartGraph failed with an unspecified error." ); + } + + // deallocate auxiliary vectors + connectivity.clear(); + connectivity.shrink_to_fit(); + offsets.clear(); + offsets.shrink_to_fit(); + + return decompose_and_save( mesh, parameters, part_array, shared_xadj, shared_adjncy, ncommon ); + } + + static bool + decompose_and_save( const Mesh& mesh, + const Config::ParameterContainer& parameters, + const MetisIndexArray& part, + const std::shared_ptr< idx_t > dual_xadj, + const std::shared_ptr< idx_t > dual_adjncy, + const Index ncommon ) + { + const unsigned nparts = parameters.template getParameter< unsigned >( "subdomains" ); + const unsigned ghost_levels = parameters.getParameter< unsigned >( "ghost-levels" ); + const Index cellsCount = mesh.template getEntitiesCount< typename Mesh::Cell >(); + const Index pointsCount = mesh.template getEntitiesCount< typename Mesh::Vertex >(); + + // count cells in each subdomain + IndexArray cells_counts( nparts ); + cells_counts.setValue( 0 ); + for( Index i = 0; i < cellsCount; i++ ) + ++cells_counts[ part[ i ] ]; + + // build offsets for the partitioned cell indices + IndexArray cells_offsets( nparts ); + cells_offsets[0] = 0; + for( unsigned p = 1; p < nparts; p++ ) + cells_offsets[p] = cells_offsets[p-1] + cells_counts[p-1]; + + // construct block-wise local-to-global mapping for cells + IndexArray cells_local_to_global( cellsCount ); + { + IndexArray offsets; offsets = cells_offsets; + for( Index i = 0; i < cellsCount; i++ ) { + const Index p = part[ i ]; + cells_local_to_global[ offsets[p]++ ] = i; + } + } + + auto is_ghost_neighbor = [&] ( const typename Mesh::Cell& cell ) + { + const Index neighbors_start = dual_xadj.get()[ cell.getIndex() ]; + const Index neighbors_end = dual_xadj.get()[ cell.getIndex() + 1 ]; + for( Index i = neighbors_start; i < neighbors_end; i++ ) { + const Index neighbor_idx = dual_adjncy.get()[ i ]; + if( part[ cell.getIndex() ] != part[ neighbor_idx ] ) + return true; + } + return false; + }; + + // construct global index permutation for cells + // convention: seed index = global index in the partitioned mesh, + // cell index = global index in the original mesh + IndexArray seed_to_cell_index( cellsCount ); + for( unsigned p = 0; p < nparts; p++ ) { + Index assigned = 0; + std::set< Index > boundary, ghost_neighbors; + for( Index local_idx = 0; local_idx < cells_counts[ p ]; local_idx++ ) { + const Index global_idx = cells_local_to_global[ cells_offsets[ p ] + local_idx ]; + const auto& cell = mesh.template getEntity< typename Mesh::Cell >( global_idx ); + // check global domain boundary first + if( mesh.template isBoundaryEntity< Mesh::getMeshDimension() >( cell.getIndex() ) ) + boundary.insert( global_idx ); + // check subdomain boundary + else if( is_ghost_neighbor( cell ) ) + ghost_neighbors.insert( global_idx ); + // otherwise subdomain interior - assign index now + else + seed_to_cell_index[ cells_offsets[ p ] + assigned++ ] = global_idx; + } + for( auto global_idx : boundary ) + seed_to_cell_index[ cells_offsets[ p ] + assigned++ ] = global_idx; + for( auto global_idx : ghost_neighbors ) + seed_to_cell_index[ cells_offsets[ p ] + assigned++ ] = global_idx; + TNL_ASSERT_EQ( assigned, cells_counts[ p ], "bug in the global index permutation generator" ); + } + cells_local_to_global.reset(); + // cell_to_seed_index is an inverse permutation of seed_to_cell_index + IndexArray cell_to_seed_index( cellsCount ); + for( Index i = 0; i < cellsCount; i++ ) + cell_to_seed_index[ seed_to_cell_index[ i ] ] = i; + + // construct global index permutation for points + // convention: points at subdomain boundaries are assigned to the subdomain with the higher number + IndexArray point_old_to_new_global_index( pointsCount ); + IndexArray points_counts( nparts ); + points_counts.setValue( 0 ); + { + // first assign points to subdomains - the subdomain with the highest number takes the point + IndexArray point_to_subdomain( pointsCount ); + point_to_subdomain.setValue( 0 ); + for( unsigned p = 0; p < nparts; p++ ) { + for( Index local_idx = 0; local_idx < cells_counts[ p ]; local_idx++ ) { + const Index global_idx = seed_to_cell_index[ cells_offsets[ p ] + local_idx ]; + const auto& cell = mesh.template getEntity< typename Mesh::Cell >( global_idx ); + const Index subvertices = cell.template getSubentitiesCount< 0 >(); + for( Index j = 0; j < subvertices; j++ ) { + const Index v = cell.template getSubentityIndex< 0 >( j ); + if( (Index) p > point_to_subdomain[ v ] ) + point_to_subdomain[ v ] = p; + } + } + } + // assign global indices to points + Index pointIdx = 0; + for( unsigned p = 0; p < nparts; p++ ) { + for( Index local_idx = 0; local_idx < cells_counts[ p ]; local_idx++ ) { + const Index global_idx = seed_to_cell_index[ cells_offsets[ p ] + local_idx ]; + const auto& cell = mesh.template getEntity< typename Mesh::Cell >( global_idx ); + const Index subvertices = cell.template getSubentitiesCount< 0 >(); + for( Index j = 0; j < subvertices; j++ ) { + const Index v = cell.template getSubentityIndex< 0 >( j ); + if( point_to_subdomain[ v ] == (Index) p ) { + // assign index + point_old_to_new_global_index[ v ] = pointIdx++; + // mark as assigned + point_to_subdomain[ v ] = nparts; + // increase count + ++points_counts[ p ]; + } + } + } + } + } + + // build offsets for the partitioned point indices + IndexArray points_offsets( nparts ); + points_offsets[0] = 0; + for( unsigned p = 1; p < nparts; p++ ) + points_offsets[p] = points_offsets[p-1] + points_counts[p-1]; + + // write a .pvtu file + using PVTU = Meshes::Writers::PVTUWriter< Mesh >; + const std::string pvtuFileName = parameters.template getParameter< String >( "output-file" ); + std::ofstream file( pvtuFileName ); + PVTU pvtu( file ); + pvtu.template writeEntities< Mesh::getMeshDimension() >( Mesh{}, ghost_levels, ncommon ); + if( ghost_levels > 0 ) { + // the PointData and CellData from the individual files should be added here + pvtu.template writePPointData< std::uint8_t >( Meshes::VTK::ghostArrayName() ); + pvtu.template writePPointData< Index >( "GlobalIndex" ); + pvtu.template writePCellData< std::uint8_t >( Meshes::VTK::ghostArrayName() ); + pvtu.template writePCellData< Index >( "GlobalIndex" ); + } + + std::cout << "Writing subdomains..." << std::endl; + for( unsigned p = 0; p < nparts; p++ ) { + const std::string outputFileName = pvtu.addPiece( pvtuFileName, p ); + std::cout << outputFileName << std::endl; + + // Due to ghost levels, we don't know the number of cells, let alone points, in each + // subdomain ahead of time. Hence, we use dynamic data structures instead of MeshBuilder. + using CellSeed = typename Mesh::MeshTraitsType::CellSeedType; + std::vector< CellSeed > seeds; + std::vector< Index > seeds_global_indices; + std::set< Index > cell_global_indices; + + // We'll need global-to-local index mapping for vertices. Here we also record which + // points are actually needed. + std::map< Index, Index > vertex_global_to_local; + auto add_cell = [&] ( const auto& cell ) + { + if( cell_global_indices.count( cell.getIndex() ) != 0 ) + return false; + CellSeed seed; + for( Index v = 0; v < cell.template getSubentitiesCount< 0 >(); v++ ) { + const Index global_idx = cell.template getSubentityIndex< 0 >( v ); + if( vertex_global_to_local.count(global_idx) == 0 ) + vertex_global_to_local.insert( {global_idx, vertex_global_to_local.size()} ); + seed.setCornerId( v, vertex_global_to_local[ global_idx ] ); + } + seeds.push_back( seed ); + seeds_global_indices.push_back( cell_to_seed_index[ cell.getIndex() ] ); + cell_global_indices.insert( cell.getIndex() ); + return true; + }; + + // iterate over local cells, add only local points (to ensure that ghost points are ordered after all local points) + for( Index local_idx = 0; local_idx < cells_counts[ p ]; local_idx++ ) { + const Index global_idx = seed_to_cell_index[ cells_offsets[ p ] + local_idx ]; + const auto& cell = mesh.template getEntity< typename Mesh::Cell >( global_idx ); + for( Index v = 0; v < cell.template getSubentitiesCount< 0 >(); v++ ) { + const Index global_vert_idx = cell.template getSubentityIndex< 0 >( v ); + if( point_old_to_new_global_index[global_vert_idx] >= points_offsets[p] && + point_old_to_new_global_index[global_vert_idx] < points_offsets[p] + points_counts[p] ) + { + if( vertex_global_to_local.count(global_vert_idx) == 0 ) + vertex_global_to_local.insert( {global_vert_idx, vertex_global_to_local.size()} ); + } + } + } + TNL_ASSERT_EQ( (Index) vertex_global_to_local.size(), points_counts[p], + "some local points were not added in the first pass" ); + // iterate over local cells, create seeds and record ghost neighbor indices + std::vector< Index > ghost_neighbors; + for( Index local_idx = 0; local_idx < cells_counts[ p ]; local_idx++ ) { + const Index global_idx = seed_to_cell_index[ cells_offsets[ p ] + local_idx ]; + const auto& cell = mesh.template getEntity< typename Mesh::Cell >( global_idx ); + add_cell( cell ); + if( is_ghost_neighbor( cell ) ) + ghost_neighbors.push_back( global_idx ); + } + + // collect seed indices of ghost cells + std::vector< Index > ghost_seed_indices; + for( unsigned gl = 0; gl < ghost_levels; gl++ ) { + std::vector< Index > new_ghosts; + for( auto global_idx : ghost_neighbors ) { + const Index neighbors_start = dual_xadj.get()[ global_idx ]; + const Index neighbors_end = dual_xadj.get()[ global_idx + 1 ]; + for( Index i = neighbors_start; i < neighbors_end; i++ ) { + const Index neighbor_idx = dual_adjncy.get()[ i ]; + new_ghosts.push_back( neighbor_idx ); + const Index neighbor_seed_idx = cell_to_seed_index[ neighbor_idx ]; + ghost_seed_indices.push_back( neighbor_seed_idx ); + } + } + std::swap( ghost_neighbors, new_ghosts ); + } + ghost_neighbors.clear(); + ghost_neighbors.shrink_to_fit(); + + // sort ghosts by their seed index (i.g. global index on the decomposed mesh) + std::sort( ghost_seed_indices.begin(), ghost_seed_indices.end() ); + + // add ghost cells + for( auto idx : ghost_seed_indices ) { + // the ghost_seed_indices array may contain duplicates and even local + // cells, but add_cell takes care of uniqueness, so we don't have to + // care about that + const auto& cell = mesh.template getEntity< typename Mesh::Cell >( seed_to_cell_index[ idx ] ); + add_cell( cell ); + } + ghost_seed_indices.clear(); + ghost_seed_indices.shrink_to_fit(); + cell_global_indices.clear(); + + // set points needed for the subdomain + using PointArrayType = typename Mesh::MeshTraitsType::PointArrayType; + PointArrayType points( vertex_global_to_local.size() ); + // create "GlobalIndex" PointData array + IndexArray pointsGlobalIndices( vertex_global_to_local.size() ); + for( auto it : vertex_global_to_local ) { + points[ it.second ] = mesh.getPoint( it.first ); + pointsGlobalIndices[ it.second ] = point_old_to_new_global_index[ it.first ]; + } + vertex_global_to_local.clear(); + + // copy cell seeds into a TNL array which is used by the mesh initializer + using CellSeedArrayType = typename Mesh::MeshTraitsType::CellSeedArrayType; + CellSeedArrayType cellSeeds = seeds; + seeds.clear(); + seeds.shrink_to_fit(); + + // create "GlobalIndex" CellData array + IndexArray cellsGlobalIndices = seeds_global_indices; + seeds_global_indices.clear(); + seeds_global_indices.shrink_to_fit(); + + // create "vtkGhostType" CellData and PointData arrays - see https://blog.kitware.com/ghost-and-blanking-visibility-changes/ + Containers::Array< std::uint8_t, Devices::Sequential, Index > cellGhosts( cellSeeds.getSize() ), pointGhosts( points.getSize() ); + for( Index i = 0; i < cells_counts[ p ]; i++ ) + cellGhosts[ i ] = 0; + for( Index i = cells_counts[ p ]; i < cellSeeds.getSize(); i++ ) + cellGhosts[ i ] = (std::uint8_t) Meshes::VTK::CellGhostTypes::DUPLICATECELL; + // point ghosts are more tricky because they were assigned to the subdomain with higher number + for( Index i = 0; i < points.getSize(); i++ ) { + const Index global_idx = pointsGlobalIndices[ i ]; + if( global_idx < points_offsets[ p ] || global_idx >= points_offsets[ p ] + points_counts[ p ] ) + pointGhosts[ i ] = (std::uint8_t) Meshes::VTK::PointGhostTypes::DUPLICATEPOINT; + else + pointGhosts[ i ] = 0; + } + + // init mesh for the subdomain + Mesh subdomain; + subdomain.init( points, cellSeeds ); + + // write the subdomain + using Writer = Meshes::Writers::VTUWriter< Mesh >; + std::ofstream file( outputFileName ); + Writer writer( file ); + writer.template writeEntities< Mesh::getMeshDimension() >( subdomain ); + if( ghost_levels > 0 ) { + writer.writePointData( pointGhosts, Meshes::VTK::ghostArrayName() ); + writer.writePointData( pointsGlobalIndices, "GlobalIndex" ); + writer.writeCellData( cellGhosts, Meshes::VTK::ghostArrayName() ); + writer.writeCellData( cellsGlobalIndices, "GlobalIndex" ); + } + } + + return true; + } +}; + +int main( int argc, char* argv[] ) +{ + Config::ParameterContainer parameters; + Config::ConfigDescription conf_desc; + + configSetup( conf_desc ); + + if( ! parseCommandLine( argc, argv, conf_desc, parameters ) ) + return EXIT_FAILURE; + + const String inputFileName = parameters.getParameter< String >( "input-file" ); + const String inputFileFormat = parameters.getParameter< String >( "input-file-format" ); + const String outputFile = parameters.template getParameter< String >( "output-file" ); + if( ! outputFile.endsWith( ".pvtu" ) ) { + std::cerr << "Error: the output file must have a '.pvtu' extension." << std::endl; + return EXIT_FAILURE; + } + + auto wrapper = [&] ( const auto& reader, auto&& mesh ) + { + using MeshType = std::decay_t< decltype(mesh) >; + return DecomposeMesh< MeshType >::run( std::forward(mesh), parameters ); + }; + return ! Meshes::resolveAndLoadMesh< DecomposeMeshConfigTag, Devices::Host >( wrapper, inputFileName, inputFileFormat ); +} diff --git a/src/Tools/tnl-diff.cpp b/src/Tools/tnl-diff.cpp index 84d7a08096e8b89e3ecc8278f82bcb586f5cbfe6..b55013c9ebdc166d439583b3c0e7eed9c7808251 100644 --- a/src/Tools/tnl-diff.cpp +++ b/src/Tools/tnl-diff.cpp @@ -10,7 +10,6 @@ #include "tnl-diff.h" #include -#include #include void setupConfig( Config::ConfigDescription& config ) @@ -41,12 +40,6 @@ int main( int argc, char* argv[] ) return EXIT_FAILURE; String meshFile = parameters.getParameter< String >( "mesh" ); - /*if( meshFile == "" ) - { - if( ! processFiles< DummyMesh< double, Devices::Host, int > >( parameters ) ) - return EXIT_FAILURE; - return EXIT_SUCCESS; - }*/ const String meshType = getObjectType( meshFile ); std::cout << meshType << " detected in " << meshFile << " file." << std::endl; const std::vector< String > parsedMeshType = parseObjectType( meshType ); diff --git a/src/Tools/tnl-game-of-life.cpp b/src/Tools/tnl-game-of-life.cpp new file mode 100644 index 0000000000000000000000000000000000000000..748f0a4bf139c4d6d1e7c4c7b7e8dd4f0f6cf636 --- /dev/null +++ b/src/Tools/tnl-game-of-life.cpp @@ -0,0 +1,381 @@ +/*************************************************************************** + tnl-game-of-life.cpp - description + ------------------- + begin : Apr 17, 2020 + copyright : (C) 2020 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace TNL; + +using CommunicatorType = Communicators::MpiCommunicator; + +struct MyConfigTag {}; + +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. + */ +//template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Edge > { enum { enabled = true }; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Triangle > { enum { enabled = true }; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Quadrilateral > { 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 = GlobalIndex > + struct MeshConfig + { + 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; + } + + template< typename EntityTopology > + static constexpr bool superentityStorage( EntityTopology, int SuperentityDimension ) + { +// return false; + return (EntityTopology::dimension == 0 || EntityTopology::dimension == meshDimension - 1) && SuperentityDimension == meshDimension; + } + + template< typename EntityTopology > + static constexpr bool entityTagsStorage( EntityTopology ) + { +// return false; + return EntityTopology::dimension == 0 || EntityTopology::dimension >= meshDimension - 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 ) +{ + using LocalMesh = typename Mesh::MeshType; + using Index = typename Mesh::GlobalIndexType; + + const LocalMesh& localMesh = mesh.getLocalMesh(); + + // print basic mesh info + mesh.printInfo( std::cout ); + + // test 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() >(); + +/* + // 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); + sync.synchronize( f_in ); +*/ + // find the rank which contains most points in the box between (0.45, 0.45) and (0.55, 0.55) + typename LocalMesh::PointType c1 = {0.48, 0.42}; + typename LocalMesh::PointType c2 = {0.58, 0.52}; + Index count = 0; + for( Index i = 0; i < pointsCount; i++ ) { + auto p = localMesh.getPoint(i); + if( p.x() >= c1.x() && p.y() >= c1.y() && p.x() <= c2.x() && p.y() <= c2.y() ) { + count++; + } + } + Index max_count; + CommunicatorType::Allreduce( &count, &max_count, 1, MPI_MAX, mesh.getCommunicationGroup() ); + std::cout << "Rank " << CommunicatorType::GetRank() << ": count=" << count << ", max_count=" << max_count << std::endl; + Index reference_cell = 0; + if( count == max_count ) { + // find cell which has all points in the central box + for( Index i = 0; i < cellsCount; i++ ) { + const Index subvertices = localMesh.template getSubentitiesCount< LocalMesh::getMeshDimension(), 0 >( i ); + int in_box = 0; + for( Index j = 0; j < subvertices; j++ ) { + auto p = localMesh.getPoint( localMesh.template getSubentityIndex< LocalMesh::getMeshDimension(), 0 >( i, j ) ); + if( p.x() >= c1.x() && p.y() >= c1.y() && p.x() <= c2.x() && p.y() <= c2.y() ) + in_box++; + } + if( in_box == subvertices ) { + reference_cell = i; + } + } + } + // R-pentomino (stabilizes after 1103 iterations) + const Index max_iter = 1103; + if( count == max_count ) { + auto& v = f_in.getData(); + v[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; + } +/* + // Acorn (stabilizes after 5206 iterations) + const Index max_iter = 5206; + if( count == max_count ) { + auto& v = f_in.getData(); + v[reference_cell] = 1; + Index n1 = localMesh.getCellNeighborIndex(reference_cell,4); + v[n1] = 1; + Index s1 = localMesh.getCellNeighborIndex(n1,4); + Index s2 = localMesh.getCellNeighborIndex(s1,4); + Index n2 = localMesh.getCellNeighborIndex(s2,4); + v[n2] = 1; + Index n3 = localMesh.getCellNeighborIndex(n2,4); + v[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; + } +*/ + + auto make_snapshot = [&] ( Index iteration ) + { + // create a .pvtu file (only rank 0 actually writes to the file) + const std::string mainFilePath = "GoL." + std::to_string(iteration) + ".pvtu"; + std::ofstream file; + if( CommunicatorType::GetRank() == 0 ) + file.open( mainFilePath ); + using PVTU = Meshes::Writers::PVTUWriter< LocalMesh >; + PVTU pvtu( file ); + pvtu.template writeEntities< Mesh::getMeshDimension() >( mesh ); + pvtu.writeMetadata( iteration, iteration ); + // 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" ); + const std::string subfilePath = pvtu.template addPiece< CommunicatorType >( mainFilePath, mesh.getCommunicationGroup() ); + + // create a .vtu file for local data + using Writer = Meshes::Writers::VTUWriter< LocalMesh >; + std::ofstream subfile( subfilePath ); + 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" ); + }; + + // 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(); + Pointers::DevicePointer< const LocalMesh > localMeshDevicePointer( localMesh ); + const LocalMesh* localMeshPointer = &localMeshDevicePointer.template getData< typename LocalMesh::DeviceType >(); + + bool all_done = false; + Index iteration = 0; + do { + iteration++; + if( CommunicatorType::GetRank() == 0 ) + std::cout << "Computing iteration " << iteration << "..." << std::endl; + + // iterate over all local entities + 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; + for( Index n = 0; n < localMeshPointer->getCellNeighborsCount( i ); n++ ) { + const Index neighbor = localMeshPointer->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; + } + }; + localMesh.template forLocal< MyMeshFunction::getEntitiesDimension() >( kernel ); + + // synchronize + sync.synchronize( f_out ); + // swap input and output arrays + f_in.getData().swap( f_out.getData() ); + // 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(); + CommunicatorType::Allreduce( &done, &all_done, 1, MPI_LAND, mesh.getCommunicationGroup() ); + } + while( all_done == false ); + + return true; +} + +void configSetup( Config::ConfigDescription& config ) +{ + config.addDelimiter( "General settings:" ); + config.addRequiredEntry< String >( "input-file", "Input file with the mesh." ); + config.addEntry< String >( "input-file-format", "Input mesh file format.", "auto" ); + config.addDelimiter( "MPI settings:" ); + CommunicatorType::configSetup( config ); +} + +int main( int argc, char* argv[] ) +{ + Config::ParameterContainer parameters; + Config::ConfigDescription conf_desc; + + configSetup( conf_desc ); + + Communicators::ScopedInitializer< CommunicatorType > scopedInit(argc, argv); + + if( ! parseCommandLine( argc, argv, conf_desc, parameters ) ) + return EXIT_FAILURE; + + if( ! CommunicatorType::setup( parameters ) ) + return EXIT_FAILURE; + + const String inputFileName = parameters.getParameter< String >( "input-file" ); + const String inputFileFormat = parameters.getParameter< String >( "input-file-format" ); + + auto wrapper = [&] ( auto& reader, auto&& mesh ) -> bool + { + using MeshType = std::decay_t< decltype(mesh) >; + return runGameOfLife( std::forward(mesh) ); + }; + return ! Meshes::resolveAndLoadDistributedMesh< MyConfigTag, Devices::Host >( wrapper, inputFileName, inputFileFormat ); +} diff --git a/src/Tools/tnl-grid-to-mesh.cpp b/src/Tools/tnl-grid-to-mesh.cpp index d2d03a29a850c38b8bb940102ae1a1f235d193ab..9ff091975a2e26a75358bd871eaae3ef11d649db 100644 --- a/src/Tools/tnl-grid-to-mesh.cpp +++ b/src/Tools/tnl-grid-to-mesh.cpp @@ -1,4 +1,20 @@ +/*************************************************************************** + tnl-grid-to-mesh.cpp - description + ------------------- + begin : Oct 24, 2017 + copyright : (C) 2017 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +#include #include +#include +#include +#include + +using namespace TNL; struct GridToMeshConfigTag {}; @@ -26,8 +42,7 @@ template<> struct GridIndexTag< GridToMeshConfigTag, long int >{ enum { enabled } // namespace Meshes } // namespace TNL - -// FIXME: can't be deduced from GridType +// cannot be deduced from GridType using LocalIndexType = short int; template< typename Mesh > @@ -43,24 +58,23 @@ struct MeshCreator }; template< typename Real, typename Device, typename Index > -struct MeshCreator< TNL::Meshes::Grid< 1, Real, Device, Index > > +struct MeshCreator< Meshes::Grid< 1, Real, Device, Index > > { - using GridType = TNL::Meshes::Grid< 1, Real, Device, Index >; - using CellTopology = TNL::Meshes::Topologies::Edge; - using MeshConfig = TNL::Meshes::DefaultConfig< CellTopology, + using GridType = Meshes::Grid< 1, Real, Device, Index >; + using CellTopology = Meshes::Topologies::Edge; + using MeshConfig = Meshes::DefaultConfig< CellTopology, CellTopology::dimension, typename GridType::RealType, typename GridType::GlobalIndexType, - LocalIndexType, - typename GridType::GlobalIndexType >; - using MeshType = TNL::Meshes::Mesh< MeshConfig >; + LocalIndexType >; + using MeshType = Meshes::Mesh< MeshConfig >; static bool run( const GridType& grid, MeshType& mesh ) { const Index numberOfVertices = grid.template getEntitiesCount< typename GridType::Vertex >(); const Index numberOfCells = grid.template getEntitiesCount< typename GridType::Cell >(); - TNL::Meshes::MeshBuilder< MeshType > meshBuilder; + Meshes::MeshBuilder< MeshType > meshBuilder; meshBuilder.setPointsCount( numberOfVertices ); meshBuilder.setCellsCount( numberOfCells ); @@ -81,24 +95,23 @@ struct MeshCreator< TNL::Meshes::Grid< 1, Real, Device, Index > > }; template< typename Real, typename Device, typename Index > -struct MeshCreator< TNL::Meshes::Grid< 2, Real, Device, Index > > +struct MeshCreator< Meshes::Grid< 2, Real, Device, Index > > { - using GridType = TNL::Meshes::Grid< 2, Real, Device, Index >; - using CellTopology = TNL::Meshes::Topologies::Quadrilateral; - using MeshConfig = TNL::Meshes::DefaultConfig< CellTopology, + using GridType = Meshes::Grid< 2, Real, Device, Index >; + using CellTopology = Meshes::Topologies::Quadrilateral; + using MeshConfig = Meshes::DefaultConfig< CellTopology, CellTopology::dimension, typename GridType::RealType, typename GridType::GlobalIndexType, - LocalIndexType, - typename GridType::GlobalIndexType >; - using MeshType = TNL::Meshes::Mesh< MeshConfig >; + LocalIndexType >; + using MeshType = Meshes::Mesh< MeshConfig >; static bool run( const GridType& grid, MeshType& mesh ) { const Index numberOfVertices = grid.template getEntitiesCount< typename GridType::Vertex >(); const Index numberOfCells = grid.template getEntitiesCount< typename GridType::Cell >(); - TNL::Meshes::MeshBuilder< MeshType > meshBuilder; + Meshes::MeshBuilder< MeshType > meshBuilder; meshBuilder.setPointsCount( numberOfVertices ); meshBuilder.setCellsCount( numberOfCells ); @@ -121,24 +134,23 @@ struct MeshCreator< TNL::Meshes::Grid< 2, Real, Device, Index > > }; template< typename Real, typename Device, typename Index > -struct MeshCreator< TNL::Meshes::Grid< 3, Real, Device, Index > > +struct MeshCreator< Meshes::Grid< 3, Real, Device, Index > > { - using GridType = TNL::Meshes::Grid< 3, Real, Device, Index >; - using CellTopology = TNL::Meshes::Topologies::Hexahedron; - using MeshConfig = TNL::Meshes::DefaultConfig< CellTopology, + using GridType = Meshes::Grid< 3, Real, Device, Index >; + using CellTopology = Meshes::Topologies::Hexahedron; + using MeshConfig = Meshes::DefaultConfig< CellTopology, CellTopology::dimension, typename GridType::RealType, typename GridType::GlobalIndexType, - LocalIndexType, - typename GridType::GlobalIndexType >; - using MeshType = TNL::Meshes::Mesh< MeshConfig >; + LocalIndexType >; + using MeshType = Meshes::Mesh< MeshConfig >; static bool run( const GridType& grid, MeshType& mesh ) { const Index numberOfVertices = grid.template getEntitiesCount< typename GridType::Vertex >(); const Index numberOfCells = grid.template getEntitiesCount< typename GridType::Cell >(); - TNL::Meshes::MeshBuilder< MeshType > meshBuilder; + Meshes::MeshBuilder< MeshType > meshBuilder; meshBuilder.setPointsCount( numberOfVertices ); meshBuilder.setCellsCount( numberOfCells ); @@ -165,57 +177,70 @@ struct MeshCreator< TNL::Meshes::Grid< 3, Real, Device, Index > > }; template< typename Grid > -struct GridConverter +bool convertGrid( Grid& grid, const String& fileName, const String& outputFileName, const String& outputFormat ) { - static bool run( const TNL::String& fileName, const TNL::String& outputFileName ) - { - using MeshCreator = MeshCreator< Grid >; - using Mesh = typename MeshCreator::MeshType; + using MeshCreator = MeshCreator< Grid >; + using Mesh = typename MeshCreator::MeshType; - Grid grid; - Mesh mesh; + grid.load( fileName ); - TNL::Meshes::Readers::TNLReader reader; - if( ! reader.readMesh( fileName, grid ) ) { - std::cerr << "Failed to load grid from file '" << fileName << "'." << std::endl; - return false; - } + Mesh mesh; + if( ! MeshCreator::run( grid, mesh ) ) { + std::cerr << "Unable to build mesh from grid." << std::endl; + return false; + } - if( ! MeshCreator::run( grid, mesh ) ) { - std::cerr << "Unable to build mesh from grid." << std::endl; - return false; - } + if( outputFormat == "vtk" ) { + using VTKWriter = Meshes::Writers::VTKWriter< Mesh >; + std::ofstream file( outputFileName.getString() ); + VTKWriter writer( file ); + writer.template writeEntities< Mesh::getMeshDimension() >( mesh ); + } + else if( outputFormat == "vtu" ) { + using VTKWriter = Meshes::Writers::VTUWriter< Mesh >; + std::ofstream file( outputFileName.getString() ); + VTKWriter writer( file ); + writer.template writeEntities< Mesh::getMeshDimension() >( mesh ); + } + else if( outputFormat == "netgen" ) { + using NetgenWriter = Meshes::Writers::NetgenWriter< Mesh >; + std::fstream file( outputFileName.getString() ); + NetgenWriter::writeMesh( mesh, file ); + } - try - { - mesh.save( outputFileName ); - } - catch(...) - { - std::cerr << "Failed to save the mesh to file '" << outputFileName << "'." << std::endl; - return false; - } + return true; +} - return true; - } -}; +void configSetup( Config::ConfigDescription& config ) +{ + config.addDelimiter( "General settings:" ); + config.addRequiredEntry< String >( "input-file", "Input file with the mesh." ); + config.addRequiredEntry< String >( "output-file", "Output mesh file in TNL or VTK format." ); + config.addEntry< String >( "output-file-format", "Output mesh file format." ); + config.addEntryEnum( "tnl" ); + config.addEntryEnum( "vtk" ); + config.addEntryEnum( "vtu" ); + config.addEntryEnum( "netgen" ); +} int main( int argc, char* argv[] ) { - using namespace TNL; + Config::ParameterContainer parameters; + Config::ConfigDescription conf_desc; + + configSetup( conf_desc ); - if( argc < 3 ) { - std::cerr << "Usage: " << argv[ 0 ] << " input-grid.tnl output-mesh.tnl" << std::endl; + if( ! parseCommandLine( argc, argv, conf_desc, parameters ) ) return EXIT_FAILURE; - } - String fileName( argv[ 1 ] ); - String outputFileName( argv[ 2 ] ); + const String inputFileName = parameters.getParameter< String >( "input-file" ); + const String outputFileName = parameters.getParameter< String >( "output-file" ); + const String outputFileFormat = parameters.getParameter< String >( "output-file-format" ); - return ! Meshes::resolveMeshType< GridToMeshConfigTag, Devices::Host, GridConverter > - ( fileName, - fileName, // passed to GridConverter::run - outputFileName - ); + auto wrapper = [&] ( const auto& reader, auto&& grid ) + { + return convertGrid( grid, inputFileName, outputFileName, outputFileFormat ); + }; + return ! Meshes::resolveMeshType< GridToMeshConfigTag, Devices::Host >( wrapper, inputFileName ); } diff --git a/src/Tools/tnl-init.cpp b/src/Tools/tnl-init.cpp index 220874ebc9d549b5923078f33b9bd119d5a29af5..1a7769b5c89911018127c0fb4e105f3931f6a7a1 100644 --- a/src/Tools/tnl-init.cpp +++ b/src/Tools/tnl-init.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include diff --git a/src/Tools/tnl-mesh-converter.cpp b/src/Tools/tnl-mesh-converter.cpp index ac8f9921966ba96d915c479056111729fd9b2dad..8049e2a235ab295660543cdf781e7421c2bd1082 100644 --- a/src/Tools/tnl-mesh-converter.cpp +++ b/src/Tools/tnl-mesh-converter.cpp @@ -11,11 +11,9 @@ #include #include #include +#include #include -#include -#include - using namespace TNL; struct MeshConverterConfigTag {}; @@ -51,15 +49,61 @@ struct MeshWorldDimensionTag< MeshConverterConfigTag, CellTopology, WorldDimensi { enum { enabled = ( WorldDimension == CellTopology::dimension ) }; }; // Meshes are enabled only for types explicitly listed below. -template< typename Real > struct MeshRealTag< MeshConverterConfigTag, Real > { enum { enabled = false }; }; -template< typename GlobalIndex > struct MeshGlobalIndexTag< MeshConverterConfigTag, GlobalIndex > { enum { enabled = false }; }; -template< typename LocalIndex > struct MeshLocalIndexTag< MeshConverterConfigTag, LocalIndex > { enum { enabled = false }; }; -template< typename GlobalIndex, typename Id > struct MeshIdTag< MeshConverterConfigTag, GlobalIndex, Id > { enum { enabled = false }; }; +template<> struct MeshRealTag< MeshConverterConfigTag, float > { enum { enabled = false }; }; template<> struct MeshRealTag< MeshConverterConfigTag, double > { enum { enabled = true }; }; template<> struct MeshGlobalIndexTag< MeshConverterConfigTag, int > { enum { enabled = true }; }; +template<> struct MeshGlobalIndexTag< MeshConverterConfigTag, long int > { enum { enabled = false }; }; template<> struct MeshLocalIndexTag< MeshConverterConfigTag, short int > { enum { enabled = true }; }; -template< typename GlobalIndex > struct MeshIdTag< MeshConverterConfigTag, GlobalIndex, void > { enum { enabled = false }; }; -template< typename GlobalIndex > struct MeshIdTag< MeshConverterConfigTag, GlobalIndex, GlobalIndex > { enum { enabled = true }; }; + +// Config tag specifying the MeshConfig template to use. +template<> +struct MeshConfigTemplateTag< MeshConverterConfigTag > +{ + template< typename Cell, + int WorldDimension = Cell::dimension, + typename Real = double, + typename GlobalIndex = int, + typename LocalIndex = GlobalIndex > + struct MeshConfig + { + 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; + } + + template< typename EntityTopology > + static constexpr bool subentityOrientationStorage( EntityTopology, int SubentityDimension ) + { + return false; + } + + template< typename EntityTopology > + static constexpr bool superentityStorage( EntityTopology, int SuperentityDimension ) + { + return false; + } + + template< typename EntityTopology > + static constexpr bool entityTagsStorage( EntityTopology ) + { + return false; + } + + static constexpr bool dualGraphStorage() + { + return false; + } + }; +}; } // namespace BuildConfigTags } // namespace Meshes @@ -67,53 +111,52 @@ template< typename GlobalIndex > struct MeshIdTag< MeshConverterConfigTag, Globa template< typename Mesh > -struct MeshConverter +bool convertMesh( const Mesh& mesh, const String& inputFileName, const String& outputFileName, const String& outputFormat ) { - static bool run( const String& inputFileName, const String& outputFileName, const String& outputFormat ) + if( outputFormat == "tnl" ) { - Mesh mesh; - Meshes::DistributedMeshes::DistributedMesh distributedMesh; - if( ! Meshes::loadMesh( inputFileName, mesh, distributedMesh ) ) { - std::cerr << "Failed to load mesh from file '" << inputFileName << "'." << std::endl; - return false; - } - - if( outputFormat == "tnl" ) + try { - try - { - mesh.save( outputFileName ); - } - catch(...) - { - std::cerr << "Failed to save the mesh to file '" << outputFileName << "'." << std::endl; - return false; - } + mesh.save( outputFileName ); } - else if( outputFormat == "vtk" ) { - using VTKWriter = Meshes::Writers::VTKWriter< Mesh >; - std::fstream file( outputFileName.getString() ); - VTKWriter::template writeEntities< Mesh::getMeshDimension() >( mesh, file ); + catch(...) + { + std::cerr << "Failed to save the mesh to file '" << outputFileName << "'." << std::endl; + return false; } - // FIXME: NetgenWriter is not specialized for grids -// else if( outputFormat == "netgen" ) { -// using NetgenWriter = Meshes::Writers::NetgenWriter< Mesh >; -// std::fstream file( outputFileName.getString() ); -// NetgenWriter::writeMesh( mesh, file ); -// } - - return true; } -}; + else if( outputFormat == "vtk" ) { + using VTKWriter = Meshes::Writers::VTKWriter< Mesh >; + std::ofstream file( outputFileName.getString() ); + VTKWriter writer( file ); + writer.template writeEntities< Mesh::getMeshDimension() >( mesh ); + } + else if( outputFormat == "vtu" ) { + using VTKWriter = Meshes::Writers::VTUWriter< Mesh >; + std::ofstream file( outputFileName.getString() ); + VTKWriter writer( file ); + writer.template writeEntities< Mesh::getMeshDimension() >( mesh ); + } + // FIXME: NetgenWriter is not specialized for grids +// else if( outputFormat == "netgen" ) { +// using NetgenWriter = Meshes::Writers::NetgenWriter< Mesh >; +// std::fstream file( outputFileName.getString() ); +// NetgenWriter::writeMesh( mesh, file ); +// } + + return true; +} void configSetup( Config::ConfigDescription& config ) { config.addDelimiter( "General settings:" ); config.addRequiredEntry< String >( "input-file", "Input file with the mesh." ); + config.addEntry< String >( "input-file-format", "Input mesh file format.", "auto" ); config.addRequiredEntry< String >( "output-file", "Output mesh file in TNL or VTK format." ); - config.addEntry< String >( "output-format", "Output mesh file format." ); + config.addRequiredEntry< String >( "output-file-format", "Output mesh file format." ); config.addEntryEnum( "tnl" ); config.addEntryEnum( "vtk" ); + config.addEntryEnum( "vtu" ); // config.addEntryEnum( "netgen" ); } @@ -121,20 +164,20 @@ int main( int argc, char* argv[] ) { Config::ParameterContainer parameters; Config::ConfigDescription conf_desc; - + configSetup( conf_desc ); if( ! parseCommandLine( argc, argv, conf_desc, parameters ) ) return EXIT_FAILURE; const String inputFileName = parameters.getParameter< String >( "input-file" ); + const String inputFileFormat = parameters.getParameter< String >( "input-file-format" ); const String outputFileName = parameters.getParameter< String >( "output-file" ); - const String outputFormat = parameters.getParameter< String >( "output-format" ); - - return ! Meshes::resolveMeshType< MeshConverterConfigTag, Devices::Host, MeshConverter > - ( inputFileName, - inputFileName, // passed to MeshConverter::run - outputFileName, - outputFormat - ); + const String outputFileFormat = parameters.getParameter< String >( "output-file-format" ); + + auto wrapper = [&] ( auto& reader, auto&& mesh ) -> bool + { + return convertMesh( mesh, inputFileName, outputFileName, outputFileFormat ); + }; + return ! Meshes::resolveAndLoadMesh< MeshConverterConfigTag, Devices::Host >( wrapper, inputFileName, inputFileFormat ); } diff --git a/src/Tools/tnl-view.cpp b/src/Tools/tnl-view.cpp index 239a60e47b49ad3caaddbd25a1937fd1d2fd9d9b..97cfa7a97ec454279ee1ff2b8b279df8d2ea575a 100644 --- a/src/Tools/tnl-view.cpp +++ b/src/Tools/tnl-view.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include @@ -47,15 +46,11 @@ struct MeshWorldDimensionTag< TNLViewBuildConfigTag, CellTopology, WorldDimensio { enum { enabled = ( WorldDimension == CellTopology::dimension ) }; }; // Meshes are enabled only for types explicitly listed below. -template< typename Real > struct MeshRealTag< TNLViewBuildConfigTag, Real > { enum { enabled = false }; }; -template< typename GlobalIndex > struct MeshGlobalIndexTag< TNLViewBuildConfigTag, GlobalIndex > { enum { enabled = false }; }; -template< typename LocalIndex > struct MeshLocalIndexTag< TNLViewBuildConfigTag, LocalIndex > { enum { enabled = false }; }; -template< typename GlobalIndex, typename Id > struct MeshIdTag< TNLViewBuildConfigTag, GlobalIndex, Id > { enum { enabled = false }; }; +template<> struct MeshRealTag< TNLViewBuildConfigTag, float > { enum { enabled = false }; }; template<> struct MeshRealTag< TNLViewBuildConfigTag, double > { enum { enabled = true }; }; template<> struct MeshGlobalIndexTag< TNLViewBuildConfigTag, int > { enum { enabled = true }; }; +template<> struct MeshGlobalIndexTag< TNLViewBuildConfigTag, long int > { enum { enabled = false }; }; template<> struct MeshLocalIndexTag< TNLViewBuildConfigTag, short int > { enum { enabled = true }; }; -template< typename GlobalIndex > struct MeshIdTag< TNLViewBuildConfigTag, GlobalIndex, void > { enum { enabled = false }; }; -template< typename GlobalIndex > struct MeshIdTag< TNLViewBuildConfigTag, GlobalIndex, GlobalIndex > { enum { enabled = true }; }; } // namespace BuildConfigTags } // namespace Meshes @@ -65,6 +60,7 @@ void setupConfig( Config::ConfigDescription& config ) { config.addDelimiter( "General settings:" ); config.addEntry < String >( "mesh", "Mesh file.", "mesh.tnl" ); + config.addEntry < String >( "mesh-format", "Mesh file format.", "auto" ); config.addRequiredList < String >( "input-files", "Input files." ); // config.addList < String >( "output-files", "Output files." ); config.addEntry < bool > ( "check-output-file", "If the output file already exists, do not recreate it.", false ); @@ -77,6 +73,7 @@ void setupConfig( Config::ConfigDescription& config ) config.addEntry < String >( "output-format", "Output file format.", "gnuplot" ); config.addEntryEnum< String > ( "gnuplot" ); config.addEntryEnum< String > ( "vtk" ); + config.addEntryEnum< String > ( "vtu" ); config.addEntry < int > ( "verbose", "Set the verbosity of the program.", 1 ); } @@ -89,8 +86,11 @@ int main( int argc, char* argv[] ) return EXIT_FAILURE; const String meshFile = parameters.getParameter< String >( "mesh" ); - return ! TNL::Meshes::resolveMeshType< TNLViewBuildConfigTag, - Devices::Host, - FilesProcessor > - ( meshFile, parameters ); + const String meshFileFormat = parameters.getParameter< String >( "mesh-format" ); + auto wrapper = [&] ( const auto& reader, auto&& mesh ) + { + using MeshType = std::decay_t< decltype(mesh) >; + return processFiles< MeshType >( parameters ); + }; + return ! TNL::Meshes::resolveMeshType< TNLViewBuildConfigTag, Devices::Host >( wrapper, meshFile, meshFileFormat ); } diff --git a/src/Tools/tnl-view.h b/src/Tools/tnl-view.h index 84b3681b9906f0324daa7af4e82197d35c20affa..bfda4f960092553dc58ce596d4718d2d9a86ee72 100644 --- a/src/Tools/tnl-view.h +++ b/src/Tools/tnl-view.h @@ -8,8 +8,7 @@ /* See Copyright Notice in tnl/Copyright */ -#ifndef TNL_VIEW_H_ -#define TNL_VIEW_H_ +#pragma once #include #include @@ -21,28 +20,17 @@ #include #include -#include #include using namespace TNL; -bool getOutputFileName( const String& inputFileName, - const String& outputFormat, - String& outputFileName ) +String getOutputFileName( const String& inputFileName, + const String& outputFormat ) { - outputFileName = removeFileNameExtension( inputFileName ); + String outputFileName = removeFileNameExtension( inputFileName ); if( outputFormat == "gnuplot" ) - { - outputFileName += ".gplt"; - return true; - } - if( outputFormat == "vtk" ) - { - outputFileName += ".vtk"; - return true; - } - std::cerr << "Unknown file format " << outputFormat << "."; - return false; + return outputFileName + ".gplt"; + return outputFileName + "." + outputFormat; } @@ -64,13 +52,9 @@ bool writeMeshFunction( const typename MeshFunction::MeshPointer& meshPointer, return false; } - int verbose = parameters. getParameter< int >( "verbose"); - String outputFormat = parameters. getParameter< String >( "output-format" ); - String outputFileName; - if( ! getOutputFileName( inputFileName, - outputFormat, - outputFileName ) ) - return false; + int verbose = parameters.getParameter< int >( "verbose"); + String outputFormat = parameters.getParameter< String >( "output-format" ); + String outputFileName = getOutputFileName( inputFileName, outputFormat ); if( verbose ) std::cout << " writing to " << outputFileName << " ... " << std::flush; @@ -95,13 +79,9 @@ bool writeVectorField( const typename VectorField::FunctionType::MeshPointer& me return false; } - int verbose = parameters. getParameter< int >( "verbose"); - String outputFormat = parameters. getParameter< String >( "output-format" ); - String outputFileName; - if( ! getOutputFileName( inputFileName, - outputFormat, - outputFileName ) ) - return false; + int verbose = parameters.getParameter< int >( "verbose"); + String outputFormat = parameters.getParameter< String >( "output-format" ); + String outputFileName = getOutputFileName( inputFileName, outputFormat ); if( verbose ) std::cout << " writing to " << outputFileName << " ... " << std::flush; @@ -238,13 +218,9 @@ bool convertObject( const MeshPointer& meshPointer, const std::vector< String >& parsedObjectType, const Config::ParameterContainer& parameters ) { - int verbose = parameters. getParameter< int >( "verbose"); - String outputFormat = parameters. getParameter< String >( "output-format" ); - String outputFileName; - if( ! getOutputFileName( inputFileName, - outputFormat, - outputFileName ) ) - return false; + int verbose = parameters.getParameter< int >( "verbose"); + String outputFormat = parameters.getParameter< String >( "output-format" ); + String outputFileName = getOutputFileName( inputFileName, outputFormat ); if( verbose ) std::cout << " writing to " << outputFileName << " ... " << std::flush; @@ -306,57 +282,6 @@ bool setIndexType( const MeshPointer& meshPointer, return false; } -template< typename MeshPointer > -bool setTupleType( const MeshPointer& meshPointer, - const String& inputFileName, - const std::vector< String >& parsedObjectType, - const std::vector< String >& parsedValueType, - const Config::ParameterContainer& parameters ) -{ - int dimensions = atoi( parsedValueType[ 1 ].getString() ); - String dataType = parsedValueType[ 2 ]; - if( dataType == "float" ) - switch( dimensions ) - { - case 1: - return setIndexType< MeshPointer, Containers::StaticVector< 1, float >, float >( meshPointer, inputFileName, parsedObjectType, parameters ); - break; - case 2: - return setIndexType< MeshPointer, Containers::StaticVector< 2, float >, float >( meshPointer, inputFileName, parsedObjectType, parameters ); - break; - case 3: - return setIndexType< MeshPointer, Containers::StaticVector< 3, float >, float >( meshPointer, inputFileName, parsedObjectType, parameters ); - break; - } - if( dataType == "double" ) - switch( dimensions ) - { - case 1: - return setIndexType< MeshPointer, Containers::StaticVector< 1, double >, double >( meshPointer, inputFileName, parsedObjectType, parameters ); - break; - case 2: - return setIndexType< MeshPointer, Containers::StaticVector< 2, double >, double >( meshPointer, inputFileName, parsedObjectType, parameters ); - break; - case 3: - return setIndexType< MeshPointer, Containers::StaticVector< 3, double >, double >( meshPointer, inputFileName, parsedObjectType, parameters ); - break; - } -// if( dataType == "long double" ) -// switch( dimensions ) -// { -// case 1: -// return setIndexType< MeshPointer, Containers::StaticVector< 1, long double >, long double >( meshPointer, inputFileName, parsedObjectType, parameters ); -// break; -// case 2: -// return setIndexType< MeshPointer, Containers::StaticVector< 2, long double >, long double >( meshPointer, inputFileName, parsedObjectType, parameters ); -// break; -// case 3: -// return setIndexType< MeshPointer, Containers::StaticVector< 3, long double >, long double >( meshPointer, inputFileName, parsedObjectType, parameters ); -// break; -// } - return false; -} - template< typename MeshPointer > bool setValueType( const MeshPointer& meshPointer, const String& inputFileName, @@ -382,99 +307,74 @@ bool setValueType( const MeshPointer& meshPointer, if( elementType == "bool" ) return setIndexType< MeshPointer, bool, bool >( meshPointer, inputFileName, parsedObjectType, parameters ); - const std::vector< String > parsedValueType = parseObjectType( elementType ); - if( ! parsedValueType.size() ) - { - std::cerr << "Unable to parse object type " << elementType << "." << std::endl; - return false; - } - if( parsedValueType[ 0 ] == "Containers::StaticVector" ) - return setTupleType< MeshPointer >( meshPointer, inputFileName, parsedObjectType, parsedValueType, parameters ); - std::cerr << "Unknown element type " << elementType << "." << std::endl; return false; } template< typename Mesh > -struct FilesProcessor +bool processFiles( const Config::ParameterContainer& parameters ) { - static bool run( const Config::ParameterContainer& parameters ) + int verbose = parameters.getParameter< int >( "verbose"); + const String meshFile = parameters.getParameter< String >( "mesh" ); + const String meshFileFormat = parameters.getParameter< String >( "mesh-format" ); + + typedef Pointers::SharedPointer< Mesh > MeshPointer; + MeshPointer meshPointer; + if( ! Meshes::loadMesh( *meshPointer, meshFile, meshFileFormat ) ) + return false; + + bool checkOutputFile = parameters.getParameter< bool >( "check-output-file" ); + std::vector< String > inputFiles = parameters.getParameter< std::vector< String > >( "input-files" ); + bool error( false ); +//#ifdef HAVE_OPENMP +//#pragma omp parallel for +//#endif + for( int i = 0; i < (int) inputFiles.size(); i++ ) { - int verbose = parameters. getParameter< int >( "verbose"); - String meshFile = parameters. getParameter< String >( "mesh" ); + if( verbose ) + std::cout << "Processing file " << inputFiles[ i ] << " ... " << std::flush; - typedef Pointers::SharedPointer< Mesh > MeshPointer; - MeshPointer meshPointer; - - if( meshFile != "" ) + String outputFormat = parameters.getParameter< String >( "output-format" ); + String outputFileName = getOutputFileName( inputFiles[ i ], outputFormat ); + if( checkOutputFile && fileExists( outputFileName ) ) { - Meshes::DistributedMeshes::DistributedMesh distributedMesh; - if( ! Meshes::loadMesh( meshFile, *meshPointer, distributedMesh ) ) - return false; + if( verbose ) + std::cout << " file already exists. Skipping. \r" << std::flush; + continue; } - bool checkOutputFile = parameters. getParameter< bool >( "check-output-file" ); - std::vector< String > inputFiles = parameters.getParameter< std::vector< String > >( "input-files" ); - bool error( false ); - //#ifdef HAVE_OPENMP - //#pragma omp parallel for - //#endif - for( int i = 0; i < (int) inputFiles.size(); i++ ) + String objectType; + try { - if( verbose ) - std::cout << "Processing file " << inputFiles[ i ] << " ... " << std::flush; - - String outputFormat = parameters. getParameter< String >( "output-format" ); - String outputFileName; - if( ! getOutputFileName( inputFiles[ i ], - outputFormat, - outputFileName ) ) - { - error = true; - continue; - } - if( checkOutputFile && fileExists( outputFileName ) ) - { - if( verbose ) - std::cout << " file already exists. Skipping. \r" << std::flush; - continue; - } - - String objectType; - try - { - objectType = getObjectType( inputFiles[ i ] ); - } - catch(...) - { - std::cerr << "unknown object ... SKIPPING!" << std::endl; - continue; - } - - if( verbose ) - std::cout << objectType << " detected ... "; - - const std::vector< String > parsedObjectType = parseObjectType( objectType ); - if( ! parsedObjectType.size() ) - { - std::cerr << "Unable to parse object type " << objectType << "." << std::endl; - error = true; - continue; - } - if( parsedObjectType[ 0 ] == "Containers::Array" || - parsedObjectType[ 0 ] == "Containers::Vector" ) // TODO: remove deprecated names (Vector is saved as Array) - setValueType< MeshPointer >( meshPointer, inputFiles[ i ], parsedObjectType, parameters ); - if( parsedObjectType[ 0 ] == "Functions::MeshFunction" ) - setMeshFunction< MeshPointer >( meshPointer, inputFiles[ i ], parsedObjectType, parameters ); - if( parsedObjectType[ 0 ] == "Functions::VectorField" ) - setVectorFieldSize< MeshPointer >( meshPointer, inputFiles[ i ], parsedObjectType, parameters ); - if( verbose ) - std::cout << "[ OK ]. " << std::endl; + objectType = getObjectType( inputFiles[ i ] ); } + catch(...) + { + std::cerr << "unknown object ... SKIPPING!" << std::endl; + continue; + } + if( verbose ) - std::cout << std::endl; - return ! error; - } -}; + std::cout << objectType << " detected ... "; -#endif /* TNL_VIEW_H_ */ + const std::vector< String > parsedObjectType = parseObjectType( objectType ); + if( ! parsedObjectType.size() ) + { + std::cerr << "Unable to parse object type " << objectType << "." << std::endl; + error = true; + continue; + } + if( parsedObjectType[ 0 ] == "Containers::Array" || + parsedObjectType[ 0 ] == "Containers::Vector" ) // TODO: remove deprecated names (Vector is saved as Array) + setValueType< MeshPointer >( meshPointer, inputFiles[ i ], parsedObjectType, parameters ); + if( parsedObjectType[ 0 ] == "Functions::MeshFunction" ) + setMeshFunction< MeshPointer >( meshPointer, inputFiles[ i ], parsedObjectType, parameters ); + if( parsedObjectType[ 0 ] == "Functions::VectorField" ) + setVectorFieldSize< MeshPointer >( meshPointer, inputFiles[ i ], parsedObjectType, parameters ); + if( verbose ) + std::cout << "[ OK ]. " << std::endl; + } + if( verbose ) + std::cout << std::endl; + return ! error; +} diff --git a/src/UnitTests/Containers/CMakeLists.txt b/src/UnitTests/Containers/CMakeLists.txt index 21be3ded15eb27f75534c1374e9936026a5196ec..ae3c1b0e712acde15fcbc3deb15fc374cb118148 100644 --- a/src/UnitTests/Containers/CMakeLists.txt +++ b/src/UnitTests/Containers/CMakeLists.txt @@ -108,7 +108,6 @@ ADD_TEST( StaticVectorOperationsTest ${EXECUTABLE_OUTPUT_PATH}/StaticVectorOpera ADD_TEST( StaticVectorOfStaticVectorsTest ${EXECUTABLE_OUTPUT_PATH}/StaticVectorOfStaticVectorsTest${CMAKE_EXECUTABLE_SUFFIX} ) -ADD_SUBDIRECTORY( Multimaps ) ADD_SUBDIRECTORY( ndarray ) diff --git a/src/UnitTests/Containers/Multimaps/CMakeLists.txt b/src/UnitTests/Containers/Multimaps/CMakeLists.txt deleted file mode 100644 index 0cf3ae746674680d5858c7e6f11f2ba42b7f0437..0000000000000000000000000000000000000000 --- a/src/UnitTests/Containers/Multimaps/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -ADD_EXECUTABLE( MultimapTest MultimapTest.cpp ) -TARGET_COMPILE_OPTIONS( MultimapTest PRIVATE ${CXX_TESTS_FLAGS} ) -TARGET_LINK_LIBRARIES( MultimapTest ${GTEST_BOTH_LIBRARIES} ) - -ADD_EXECUTABLE( StaticMultimapTest StaticMultimapTest.cpp ) -TARGET_COMPILE_OPTIONS( StaticMultimapTest PRIVATE ${CXX_TESTS_FLAGS} ) -TARGET_LINK_LIBRARIES( StaticMultimapTest ${GTEST_BOTH_LIBRARIES} ) - -ADD_TEST( MultimapTest ${EXECUTABLE_OUTPUT_PATH}/MultimapTest${CMAKE_EXECUTABLE_SUFFIX} ) -ADD_TEST( StaticMultimapTest ${EXECUTABLE_OUTPUT_PATH}/MultimapTest${CMAKE_EXECUTABLE_SUFFIX} ) diff --git a/src/UnitTests/Containers/Multimaps/MultimapTest.cpp b/src/UnitTests/Containers/Multimaps/MultimapTest.cpp deleted file mode 100644 index 0ba26092d37f5f2026839d605cad4cf8e53b4e48..0000000000000000000000000000000000000000 --- a/src/UnitTests/Containers/Multimaps/MultimapTest.cpp +++ /dev/null @@ -1,154 +0,0 @@ -#include - -using namespace TNL; -using namespace TNL::Containers::Multimaps; - -using IndexType = int; -using Device = Devices::Host; -using LocalIndexType = short; - -static const char* TEST_FILE_NAME = "test_MultimapTest.tnl"; - -#ifdef HAVE_GTEST -#include - -TEST( MultimapTest, TestTypedefs ) -{ - using MultimapType = EllpackIndexMultimap< IndexType, Device, LocalIndexType >; - const bool same_index = std::is_same< typename MultimapType::IndexType, IndexType >::value; - ASSERT_TRUE( same_index ); - const bool same_device = std::is_same< typename MultimapType::DeviceType, Device >::value; - ASSERT_TRUE( same_device ); - const bool same_localindex = std::is_same< typename MultimapType::LocalIndexType, LocalIndexType >::value; - ASSERT_TRUE( same_localindex ); -} - -TEST( MultimapTest, TestSettingSizes ) -{ - using MultimapType = EllpackIndexMultimap< IndexType, Device, LocalIndexType >; - - IndexType inputs = 10; - LocalIndexType valuesGlobalMax = 3; - LocalIndexType valuesLocalMax = 2; - - MultimapType map; - map.setKeysRange( inputs ); - ASSERT_EQ( map.getKeysRange(), inputs ); - - typename MultimapType::ValuesAllocationVectorType allocationRanges; - allocationRanges.setSize( inputs ); - allocationRanges.setValue( valuesGlobalMax ); - allocationRanges[ 0 ] = 0; - allocationRanges[ 1 ] = 1; - map.allocate( allocationRanges ); - - for( IndexType i = 0; i < inputs; i++ ) { - auto values = map.getValues( i ); - const auto constValues = ( (const MultimapType&) map ).getValues( i ); - - // uninitialized should be equal to the value from the allocation vector - ASSERT_EQ( values.getSize(), allocationRanges[ i ] ); - // This does not work with Array deep copy constructor - ASSERT_EQ( constValues.getSize(), allocationRanges[ i ] ); - - // setting lower sizes - values.setSize( valuesLocalMax ); - ASSERT_EQ( values.getSize(), valuesLocalMax ); - // This does not work with Array deep copy constructor - ASSERT_EQ( constValues.getSize(), valuesLocalMax ); - - // setting global max - values.setSize( valuesGlobalMax ); - ASSERT_EQ( values.getSize(), valuesGlobalMax ); - // This does not work with Array deep copy constructor - ASSERT_EQ( constValues.getSize(), valuesGlobalMax ); - } -} - -TEST( MultimapTest, TestSettingValues ) -{ - using MultimapType = EllpackIndexMultimap< IndexType, Device, LocalIndexType >; - - IndexType inputs = 10; - LocalIndexType allocatedValues = 2; - - MultimapType map; - map.setKeysRange( inputs ); - ASSERT_EQ( map.getKeysRange(), inputs ); - - typename MultimapType::ValuesAllocationVectorType allocationRanges; - allocationRanges.setSize( inputs ); - allocationRanges.setValue( allocatedValues ); - map.allocate( allocationRanges ); - - for( IndexType i = 0; i < inputs; i++ ) { - auto values = map.getValues( i ); - const auto constValues = ( (const MultimapType&) map ).getValues( i ); - - values.setSize( allocatedValues ); - - for( LocalIndexType o = 0; o < allocatedValues; o++ ) - values.setValue( o, i + o ); - - for( LocalIndexType o = 0; o < allocatedValues; o++ ) { - ASSERT_EQ( values.getValue( o ), i + o ); - ASSERT_EQ( values[ o ], i + o ); - ASSERT_EQ( constValues.getValue( o ), i + o ); - ASSERT_EQ( constValues[ o ], i + o ); - } - - for( LocalIndexType o = 0; o < allocatedValues; o++ ) - values[ o ] = i * o; - - for( LocalIndexType o = 0; o < allocatedValues; o++ ) { - ASSERT_EQ( values.getValue( o ), i * o ); - ASSERT_EQ( values[ o ], i * o ); - ASSERT_EQ( constValues.getValue( o ), i * o ); - ASSERT_EQ( constValues[ o ], i * o ); - } - } -} - -TEST( MultimapTest, TestSaveAndLoad ) -{ - using MultimapType = EllpackIndexMultimap< IndexType, Device, LocalIndexType >; - - IndexType inputs = 10; - LocalIndexType allocatedValues = 2; - - MultimapType map, map2; - map.setKeysRange( inputs ); - ASSERT_EQ( map.getKeysRange(), inputs ); - - typename MultimapType::ValuesAllocationVectorType allocationRanges; - allocationRanges.setSize( inputs ); - allocationRanges.setValue( allocatedValues ); - map.allocate( allocationRanges ); - - for( IndexType i = 0; i < inputs; i++ ) { - auto values = map.getValues( i ); - for( LocalIndexType o = 0; o < allocatedValues; o++ ) - values.setValue( o, i + o ); - } - - map.save( TEST_FILE_NAME ); - map2.load( TEST_FILE_NAME ); - - EXPECT_EQ( map, map2 ); - EXPECT_EQ( map.getKeysRange(), map2.getKeysRange() ); - - for( IndexType i = 0; i < inputs; i++ ) { - auto values = map.getValues( i ); - auto values2 = map2.getValues( i ); - - for( LocalIndexType o = 0; o < allocatedValues; o++ ) { - ASSERT_EQ( values[ o ], i + o ); - ASSERT_EQ( values2[ o ], i + o ); - } - } - - EXPECT_EQ( std::remove( TEST_FILE_NAME ), 0 ); -} -#endif - -#include "../../main.h" diff --git a/src/UnitTests/Containers/Multimaps/StaticMultimapTest.cpp b/src/UnitTests/Containers/Multimaps/StaticMultimapTest.cpp deleted file mode 100644 index 86d5c0cb4bc9d21dedd2972db51eada2479faa8d..0000000000000000000000000000000000000000 --- a/src/UnitTests/Containers/Multimaps/StaticMultimapTest.cpp +++ /dev/null @@ -1,102 +0,0 @@ -#include - -using namespace TNL; -using namespace TNL::Containers::Multimaps; - -using IndexType = int; -using Device = Devices::Host; -using LocalIndexType = short; - -static const char* TEST_FILE_NAME = "test_StaticMultimapTest.tnl"; - -#ifdef HAVE_GTEST -#include - -TEST( MultimapTest, TestTypedefs ) -{ - using MultimapType = StaticEllpackIndexMultimap< 4, IndexType, Device, LocalIndexType >; - const bool same_index = std::is_same< typename MultimapType::IndexType, IndexType >::value; - ASSERT_TRUE( same_index ); - const bool same_device = std::is_same< typename MultimapType::DeviceType, Device >::value; - ASSERT_TRUE( same_device ); - const bool same_localindex = std::is_same< typename MultimapType::LocalIndexType, LocalIndexType >::value; - ASSERT_TRUE( same_localindex ); -} - -TEST( MultimapTest, TestSettingValues ) -{ - using MultimapType = StaticEllpackIndexMultimap< 4, IndexType, Device, LocalIndexType >; - - const IndexType inputs = 10; - const LocalIndexType allocatedValues = 4; - - MultimapType map; - map.setKeysRange( inputs ); - ASSERT_EQ( map.getKeysRange(), inputs ); - map.allocate(); - - for( IndexType i = 0; i < inputs; i++ ) { - auto values = map.getValues( i ); - const auto constValues = ( (const MultimapType) map ).getValues( i ); - - for( LocalIndexType o = 0; o < allocatedValues; o++ ) - values.setValue( o, i + o ); - - for( LocalIndexType o = 0; o < allocatedValues; o++ ) { - ASSERT_EQ( values.getValue( o ), i + o ); - ASSERT_EQ( values[ o ], i + o ); - ASSERT_EQ( constValues.getValue( o ), i + o ); - ASSERT_EQ( constValues[ o ], i + o ); - } - - for( LocalIndexType o = 0; o < allocatedValues; o++ ) - values[ o ] = i * o; - - for( LocalIndexType o = 0; o < allocatedValues; o++ ) { - ASSERT_EQ( values.getValue( o ), i * o ); - ASSERT_EQ( values[ o ], i * o ); - ASSERT_EQ( constValues.getValue( o ), i * o ); - ASSERT_EQ( constValues[ o ], i * o ); - } - } -} - -TEST( MultimapTest, TestSaveAndLoad ) -{ - using MultimapType = StaticEllpackIndexMultimap< 4, IndexType, Device, LocalIndexType >; - - const IndexType inputs = 10; - const LocalIndexType allocatedValues = 4; - - MultimapType map, map2; - map.setKeysRange( inputs ); - ASSERT_EQ( map.getKeysRange(), inputs ); - map.allocate(); - - for( IndexType i = 0; i < inputs; i++ ) { - auto values = map.getValues( i ); - for( LocalIndexType o = 0; o < allocatedValues; o++ ) - values.setValue( o, i + o ); - } - - map.save( TEST_FILE_NAME ); - map2.load( TEST_FILE_NAME ); - - EXPECT_EQ( map, map2 ); - EXPECT_EQ( map.getKeysRange(), map2.getKeysRange() ); - - for( IndexType i = 0; i < inputs; i++ ) { - auto values = map.getValues( i ); - auto values2 = map2.getValues( i ); - - for( LocalIndexType o = 0; o < allocatedValues; o++ ) { - ASSERT_EQ( values[ o ], i + o ); - ASSERT_EQ( values2[ o ], i + o ); - } - } - - EXPECT_EQ( std::remove( TEST_FILE_NAME ), 0 ); -} -#endif - -#include "../../main.h" diff --git a/src/UnitTests/Meshes/BoundaryTagsTest.cpp b/src/UnitTests/Meshes/BoundaryTagsTest.cpp deleted file mode 100644 index c83e407526e93910c8d5a8f5213307a0895a021a..0000000000000000000000000000000000000000 --- a/src/UnitTests/Meshes/BoundaryTagsTest.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "BoundaryTagsTest.h" -#include "../main.h" diff --git a/src/UnitTests/Meshes/BoundaryTagsTest.h b/src/UnitTests/Meshes/BoundaryTagsTest.h deleted file mode 100644 index 6bc07adabac4a0e1470c13238acf947f1b97f8fb..0000000000000000000000000000000000000000 --- a/src/UnitTests/Meshes/BoundaryTagsTest.h +++ /dev/null @@ -1,118 +0,0 @@ -#pragma once - -#ifdef HAVE_GTEST -#include - -#include - -#include -#include -#include -#include -#include - -namespace BoundaryTagsTest { - -using namespace TNL; -using namespace TNL::Meshes; - -using RealType = double; -using Device = Devices::Host; -using IndexType = int; - -class TestQuadrilateralMeshConfig : public DefaultConfig< Topologies::Quadrilateral > -{ -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; } -}; - -TEST( MeshTest, RegularMeshOfQuadrilateralsTest ) -{ - using QuadrilateralMeshEntityType = MeshEntity< TestQuadrilateralMeshConfig, Devices::Host, Topologies::Quadrilateral >; - using EdgeMeshEntityType = typename QuadrilateralMeshEntityType::SubentityTraits< 1 >::SubentityType; - using VertexMeshEntityType = typename QuadrilateralMeshEntityType::SubentityTraits< 0 >::SubentityType; - - using PointType = typename VertexMeshEntityType::PointType; - static_assert( std::is_same< PointType, Containers::StaticVector< 2, RealType > >::value, - "unexpected PointType" ); - - const IndexType xSize( 3 ), ySize( 4 ); - const RealType width( 1.0 ), height( 1.0 ); - const RealType hx( width / ( RealType ) xSize ), - hy( height / ( RealType ) ySize ); - const IndexType numberOfCells = xSize * ySize; - const IndexType numberOfVertices = ( xSize + 1 ) * ( ySize + 1 ); - - typedef Mesh< TestQuadrilateralMeshConfig > TestQuadrilateralMesh; - TestQuadrilateralMesh mesh, mesh2; - MeshBuilder< TestQuadrilateralMesh > meshBuilder; - meshBuilder.setPointsCount( numberOfVertices ); - meshBuilder.setCellsCount( numberOfCells ); - - /**** - * Setup vertices - */ - for( IndexType j = 0; j <= ySize; j++ ) - for( IndexType i = 0; i <= xSize; i++ ) - meshBuilder.setPoint( j * ( xSize + 1 ) + i, PointType( i * hx, j * hy ) ); - - /**** - * Setup cells - */ - IndexType cellIdx( 0 ); - for( IndexType j = 0; j < ySize; j++ ) - for( IndexType i = 0; i < xSize; i++ ) - { - const IndexType vertex0 = j * ( xSize + 1 ) + i; - const IndexType vertex1 = j * ( xSize + 1 ) + i + 1; - const IndexType vertex2 = ( j + 1 ) * ( xSize + 1 ) + i + 1; - const IndexType vertex3 = ( j + 1 ) * ( xSize + 1 ) + i; - - meshBuilder.getCellSeed( cellIdx ).setCornerId( 0, vertex0 ); - meshBuilder.getCellSeed( cellIdx ).setCornerId( 1, vertex1 ); - meshBuilder.getCellSeed( cellIdx ).setCornerId( 2, vertex2 ); - meshBuilder.getCellSeed( cellIdx++ ).setCornerId( 3, vertex3 ); - } - - ASSERT_TRUE( meshBuilder.build( mesh ) ); - - std::vector< IndexType > boundaryCells = {0, 1, 2, 3, 5, 6, 8, 9, 10, 11}; - std::vector< IndexType > interiorCells = {4, 7}; - - // Test boundary cells - EXPECT_EQ( mesh.template getBoundaryEntitiesCount< 2 >(), (int) boundaryCells.size() ); - for( size_t i = 0; i < boundaryCells.size(); i++ ) { - EXPECT_TRUE( mesh.template isBoundaryEntity< 2 >( boundaryCells[ i ] ) ); - EXPECT_EQ( mesh.template getBoundaryEntityIndex< 2 >( i ), boundaryCells[ i ] ); - } - // Test interior cells - EXPECT_EQ( mesh.template getInteriorEntitiesCount< 2 >(), (int) interiorCells.size() ); - for( size_t i = 0; i < interiorCells.size(); i++ ) { - EXPECT_FALSE( mesh.template isBoundaryEntity< 2 >( interiorCells[ i ] ) ); - EXPECT_EQ( mesh.template getInteriorEntityIndex< 2 >( i ), interiorCells[ i ] ); - } - - std::vector< IndexType > boundaryFaces = {0, 3, 4, 7, 8, 12, 15, 19, 22, 25, 26, 28, 29, 30}; - std::vector< IndexType > interiorFaces = {1, 2, 5, 6, 9, 10, 11, 13, 14, 16, 17, 18, 20, 21, 23, 24, 27}; - - // Test boundary faces - EXPECT_EQ( mesh.template getBoundaryEntitiesCount< 1 >(), (int) boundaryFaces.size() ); - for( size_t i = 0; i < boundaryFaces.size(); i++ ) { - EXPECT_TRUE( mesh.template isBoundaryEntity< 1 >( boundaryFaces[ i ] ) ); - EXPECT_EQ( mesh.template getBoundaryEntityIndex< 1 >( i ), boundaryFaces[ i ] ); - } - // Test interior faces - EXPECT_EQ( mesh.template getInteriorEntitiesCount< 1 >(), (int) interiorFaces.size() ); - for( size_t i = 0; i < interiorFaces.size(); i++ ) { - EXPECT_FALSE( mesh.template isBoundaryEntity< 1 >( interiorFaces[ i ] ) ); - EXPECT_EQ( mesh.template getInteriorEntityIndex< 1 >( i ), interiorFaces[ i ] ); - } -} - -} // namespace BoundaryTagsTest - -#endif diff --git a/src/UnitTests/Meshes/CMakeLists.txt b/src/UnitTests/Meshes/CMakeLists.txt index 91bf37215b772df499a4ecc7d32cd4fdfe335f05..75fab220ef6ea23dd96bbae02831bdbcf5d92462 100644 --- a/src/UnitTests/Meshes/CMakeLists.txt +++ b/src/UnitTests/Meshes/CMakeLists.txt @@ -1,8 +1,8 @@ ADD_SUBDIRECTORY( DistributedMeshes ) -ADD_EXECUTABLE( BoundaryTagsTest BoundaryTagsTest.cpp ) -TARGET_COMPILE_OPTIONS( BoundaryTagsTest PRIVATE ${CXX_TESTS_FLAGS} ) -TARGET_LINK_LIBRARIES( BoundaryTagsTest ${GTEST_BOTH_LIBRARIES} ) +ADD_EXECUTABLE( EntityTagsTest EntityTagsTest.cpp ) +TARGET_COMPILE_OPTIONS( EntityTagsTest PRIVATE ${CXX_TESTS_FLAGS} ) +TARGET_LINK_LIBRARIES( EntityTagsTest ${GTEST_BOTH_LIBRARIES} ) # Mesh cannot be compiled by nvcc < 9 due to bugs in the compiler if( ${BUILD_CUDA} AND ${CUDA_VERSION_MAJOR} GREATER_EQUAL 9 ) @@ -31,45 +31,16 @@ else() TARGET_LINK_LIBRARIES( MeshOrderingTest ${GTEST_BOTH_LIBRARIES} ) endif() -ADD_EXECUTABLE( MeshEntityTest MeshEntityTest.cpp ) -TARGET_COMPILE_OPTIONS( MeshEntityTest PRIVATE ${CXX_TESTS_FLAGS} ) -TARGET_LINK_LIBRARIES( MeshEntityTest ${GTEST_BOTH_LIBRARIES} ) - - -ADD_TEST( BoundaryTagsTest ${EXECUTABLE_OUTPUT_PATH}/BoundaryTagsTest${CMAKE_EXECUTABLE_SUFFIX} ) +ADD_TEST( EntityTagsTest ${EXECUTABLE_OUTPUT_PATH}/EntityTagsTest${CMAKE_EXECUTABLE_SUFFIX} ) ADD_TEST( MeshTest ${EXECUTABLE_OUTPUT_PATH}/MeshTest${CMAKE_EXECUTABLE_SUFFIX} ) ADD_TEST( MeshTraverserTest ${EXECUTABLE_OUTPUT_PATH}/MeshTraverserTest${CMAKE_EXECUTABLE_SUFFIX} ) ADD_TEST( MeshOrderingTest ${EXECUTABLE_OUTPUT_PATH}/MeshOrderingTest${CMAKE_EXECUTABLE_SUFFIX} ) -ADD_TEST( MeshEntityTest ${EXECUTABLE_OUTPUT_PATH}/MeshEntityTest${CMAKE_EXECUTABLE_SUFFIX} ) - - - -## -## Tests with VTK -## -#find_package( VTK ) -#if( VTK_FOUND ) -# include(${VTK_USE_FILE}) -# -# AddCompilerFlag( "-DHAVE_VTK " ) -# SET( VTK_COMMON_LIBRARIES vtkCommonCore ; vtkIOLegacy ) -#endif( VTK_FOUND ) -## MeshReaderTest is not a unit test so we disable it, because it takes -## a long time to compile. -## -## Mesh cannot be compiled by nvcc < 9 due to bugs in the compiler -#if( ${BUILD_CUDA} AND ${CUDA_VERSION_MAJOR} GREATER_EQUAL 9 ) -# CUDA_ADD_EXECUTABLE( MeshReaderTest MeshReaderTest.cu -# OPTIONS ${CXX_TESTS_FLAGS} ) -# TARGET_LINK_LIBRARIES( MeshReaderTest -# ${GTEST_BOTH_LIBRARIES} -# ${VTK_COMMON_LIBRARIES} ) -#else() -# ADD_EXECUTABLE( MeshReaderTest MeshReaderTest.cpp ) -# TARGET_COMPILE_OPTIONS( MeshReaderTest PRIVATE ${CXX_TESTS_FLAGS} ) -# TARGET_LINK_LIBRARIES( MeshReaderTest -# ${GTEST_BOTH_LIBRARIES} -# ${VTK_COMMON_LIBRARIES} ) -#endif() +## 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 ${EXECUTABLE_OUTPUT_PATH}/MeshEntityTest${CMAKE_EXECUTABLE_SUFFIX} ) diff --git a/src/UnitTests/Meshes/DistributedMeshes/CMakeLists.txt b/src/UnitTests/Meshes/DistributedMeshes/CMakeLists.txt index bd294bf3650bf8066191e722c014f6592197a2a6..dad8a3c7fb07d2923313739477e02c68f6ce7f46 100644 --- a/src/UnitTests/Meshes/DistributedMeshes/CMakeLists.txt +++ b/src/UnitTests/Meshes/DistributedMeshes/CMakeLists.txt @@ -54,9 +54,24 @@ ELSE( BUILD_CUDA ) ADD_EXECUTABLE( DistributedGridIOTest DistributedGridIOTest.cpp ) TARGET_COMPILE_OPTIONS( DistributedGridIOTest PRIVATE ${CXX_TESTS_FLAGS} ) TARGET_LINK_LIBRARIES( DistributedGridIOTest ${GTEST_BOTH_LIBRARIES} ) - ENDIF( BUILD_CUDA ) +if( BUILD_CUDA ) + cuda_add_executable( DistributedMeshTest DistributedMeshTest.cu OPTIONS ${CXX_TESTS_FLAGS}) + target_link_libraries( DistributedMeshTest ${GTEST_BOTH_LIBRARIES} ) +else() + add_executable( DistributedMeshTest DistributedMeshTest.cpp ) + target_compile_options( DistributedMeshTest PRIVATE ${CXX_TESTS_FLAGS} ) + target_link_libraries( DistributedMeshTest ${GTEST_BOTH_LIBRARIES} ) +endif() + +find_package( tinyxml2 QUIET ) +if( tinyxml2_FOUND ) + target_compile_definitions(DistributedMeshTest PUBLIC "-DHAVE_TINYXML2") + target_link_libraries(DistributedMeshTest tinyxml2::tinyxml2) +endif() + + SET (mpi_test_parameters_1d -np 4 -H localhost:4 "${EXECUTABLE_OUTPUT_PATH}/DistributedGridTest_1D${CMAKE_EXECUTABLE_SUFFIX}") ADD_TEST( NAME DistributedGridTest_1D COMMAND "mpirun" ${mpi_test_parameters_1d}) @@ -85,4 +100,8 @@ SET (mpi_test_parameters_CutDistributedMeshFunctionTest -np 12 -H localhost:12 " #SET (mpi_test_parameters_DistributedVectorFieldIO_MPIIOTest -np 4 -H localhost:4 "${EXECUTABLE_OUTPUT_PATH}/DistributedVectorFieldIO_MPIIOTest ${CMAKE_EXECUTABLE_SUFFIX}") #ADD_TEST( NAME DistributedVectorFieldIO_MPIIOTest COMMAND "mpirun" ${mpi_test_parameters_IOMPIIO}) +add_test( NAME DistributedMeshTest_2x2 COMMAND mpirun -np 4 -H localhost:4 ${EXECUTABLE_OUTPUT_PATH}/DistributedMeshTest${CMAKE_EXECUTABLE_SUFFIX} ) +add_test( NAME DistributedMeshTest_3x3 COMMAND mpirun -np 9 -H localhost:9 ${EXECUTABLE_OUTPUT_PATH}/DistributedMeshTest${CMAKE_EXECUTABLE_SUFFIX} ) +add_test( NAME DistributedMeshTest_4x4 COMMAND mpirun -np 16 -H localhost:16 ${EXECUTABLE_OUTPUT_PATH}/DistributedMeshTest${CMAKE_EXECUTABLE_SUFFIX} ) + endif() diff --git a/src/UnitTests/Meshes/DistributedMeshes/CutDistributedMeshFunctionTest.cpp b/src/UnitTests/Meshes/DistributedMeshes/CutDistributedMeshFunctionTest.cpp index 12e70676890b8015d62062995a67f3c76e05bc9e..4d5bb4baf66abbabcf6f989e4ed8389acf0d7b9a 100644 --- a/src/UnitTests/Meshes/DistributedMeshes/CutDistributedMeshFunctionTest.cpp +++ b/src/UnitTests/Meshes/DistributedMeshes/CutDistributedMeshFunctionTest.cpp @@ -71,7 +71,7 @@ TEST(CutDistributedMeshFunction, 2D_Data) Pointers::SharedPointer< LinearFunctionType, Host > linearFunctionPtr; linearFunctionEvaluator.evaluateAllEntities(meshFunctionptr , linearFunctionPtr); - DistributedMeshSynchronizer< MeshFunctionView > synchronizer; + DistributedMeshSynchronizer< DistributedMeshType > synchronizer; synchronizer.setDistributedGrid( &distributedGrid ); synchronizer.template synchronize( *meshFunctionptr ); @@ -152,7 +152,7 @@ TEST(CutDistributedMeshFunction, 3D_1_Data) Pointers::SharedPointer< LinearFunctionType, Host > linearFunctionPtr; linearFunctionEvaluator.evaluateAllEntities(meshFunctionptr , linearFunctionPtr); - DistributedMeshSynchronizer< MeshFunctionView > synchronizer; + DistributedMeshSynchronizer< DistributedMeshType > synchronizer; synchronizer.setDistributedGrid( &distributedGrid ); synchronizer.template synchronize( *meshFunctionptr ); @@ -233,7 +233,7 @@ TEST(CutDistributedMeshFunction, 3D_2_Data) Pointers::SharedPointer< LinearFunctionType, Host > linearFunctionPtr; linearFunctionEvaluator.evaluateAllEntities(meshFunctionptr , linearFunctionPtr); - DistributedMeshSynchronizer< MeshFunctionView > synchronizer; + DistributedMeshSynchronizer< DistributedMeshType > synchronizer; synchronizer.setDistributedGrid( &distributedGrid ); synchronizer.template synchronize( *meshFunctionptr ); @@ -336,7 +336,7 @@ TEST(CutDistributedMeshFunction, 2D_Synchronization) MeshFunctionView cutMeshFunction; cutMeshFunction.bind(cutGrid,cutDof); - DistributedMeshSynchronizer< MeshFunctionView > synchronizer; + DistributedMeshSynchronizer< CutDistributedMeshType > synchronizer; synchronizer.setDistributedGrid( &cutDistributedGrid ); synchronizer.template synchronize( cutMeshFunction ); @@ -421,7 +421,7 @@ TEST(CutDistributedMeshFunction, 3D_1_Synchronization) MeshFunctionView cutMeshFunction; cutMeshFunction.bind(cutGrid,cutDof); - DistributedMeshSynchronizer< MeshFunctionView > synchronizer; + DistributedMeshSynchronizer< CutDistributedMeshType > synchronizer; synchronizer.setDistributedGrid( &cutDistributedGrid ); synchronizer.template synchronize( cutMeshFunction ); @@ -510,7 +510,7 @@ TEST(CutDistributedMeshFunction, 3D_2_Synchronization) MeshFunctionView cutMeshFunction; cutMeshFunction.bind(cutGrid,cutDof); - DistributedMeshSynchronizer< MeshFunctionView > synchronizer; + DistributedMeshSynchronizer< CutDistributedMeshType > synchronizer; synchronizer.setDistributedGrid( &cutDistributedGrid ); synchronizer.template synchronize( cutMeshFunction ); diff --git a/src/UnitTests/Meshes/DistributedMeshes/DistributedGridIOTest.h b/src/UnitTests/Meshes/DistributedMeshes/DistributedGridIOTest.h index 9eaf279958514e3de72060cf374aea0b939bf21f..6b7c489af4acdf0596426409586b49fd22424178 100644 --- a/src/UnitTests/Meshes/DistributedMeshes/DistributedGridIOTest.h +++ b/src/UnitTests/Meshes/DistributedMeshes/DistributedGridIOTest.h @@ -353,7 +353,7 @@ class TestDistributedGridIO DistributedGridIO ::load(fileName, *loadMeshFunctionptr ); - DistributedMeshSynchronizer< MeshFunctionType > synchronizer; + DistributedMeshSynchronizer< DistributedGridType > synchronizer; synchronizer.setDistributedGrid( &distributedGrid ); synchronizer.template synchronize( *loadMeshFunctionptr ); //need synchronization for overlaps to be filled corectly in loadDof diff --git a/src/UnitTests/Meshes/DistributedMeshes/DistributedGridIO_MPIIOTest.h b/src/UnitTests/Meshes/DistributedMeshes/DistributedGridIO_MPIIOTest.h index 9cd77cf92ea768ad0551833173f9acb99c6b2d73..5bbad8f03b46fe69ec3237136503452f5f090b40 100644 --- a/src/UnitTests/Meshes/DistributedMeshes/DistributedGridIO_MPIIOTest.h +++ b/src/UnitTests/Meshes/DistributedMeshes/DistributedGridIO_MPIIOTest.h @@ -163,7 +163,7 @@ class TestDistributedGridMPIIO{ DistributedGridIO ::load(FileName, *loadMeshFunctionptr ); - DistributedMeshSynchronizer< MeshFunctionType > synchronizer; + DistributedMeshSynchronizer< DistributedGridType > synchronizer; synchronizer.setDistributedGrid( &distributedGrid ); synchronizer.template synchronize( *loadMeshFunctionptr ); //need synchronization for overlaps to be filled corectly in loadDof diff --git a/src/UnitTests/Meshes/DistributedMeshes/DistributedGridTest_1D.cpp b/src/UnitTests/Meshes/DistributedMeshes/DistributedGridTest_1D.cpp index 707a99d8363ecf84ab642419e59469faa6aa5635..7cb44ef229fa58399572a0916c6b76dcaaaea527 100644 --- a/src/UnitTests/Meshes/DistributedMeshes/DistributedGridTest_1D.cpp +++ b/src/UnitTests/Meshes/DistributedMeshes/DistributedGridTest_1D.cpp @@ -97,7 +97,7 @@ typedef typename GridType::Cell Cell; typedef typename GridType::IndexType IndexType; typedef typename GridType::PointType PointType; typedef DistributedMesh DistributedGridType; -using Synchronizer = DistributedMeshSynchronizer< MeshFunctionType >; +using Synchronizer = DistributedMeshSynchronizer< DistributedGridType >; class DistributedGridTest_1D : public ::testing::Test { diff --git a/src/UnitTests/Meshes/DistributedMeshes/DistributedGridTest_2D.cpp b/src/UnitTests/Meshes/DistributedMeshes/DistributedGridTest_2D.cpp index b69a85b1584350d1819c87de143caa1c1bcca3dc..71370cae2d0530f871cc4148ac0f06b58696e3c0 100644 --- a/src/UnitTests/Meshes/DistributedMeshes/DistributedGridTest_2D.cpp +++ b/src/UnitTests/Meshes/DistributedMeshes/DistributedGridTest_2D.cpp @@ -323,7 +323,7 @@ typedef typename GridType::Cell Cell; typedef typename GridType::IndexType IndexType; typedef typename GridType::PointType PointType; typedef DistributedMesh DistributedGridType; -using Synchronizer = DistributedMeshSynchronizer< MeshFunctionType >; +using Synchronizer = DistributedMeshSynchronizer< DistributedGridType >; class DistributedGridTest_2D : public ::testing::Test { diff --git a/src/UnitTests/Meshes/DistributedMeshes/DistributedGridTest_3D.cpp b/src/UnitTests/Meshes/DistributedMeshes/DistributedGridTest_3D.cpp index 8b0ac5aecf2ddc21588dc7f002e62c19396d5a42..765341c1e5c2361c113ee0c2a83a0e435a3ebf3c 100644 --- a/src/UnitTests/Meshes/DistributedMeshes/DistributedGridTest_3D.cpp +++ b/src/UnitTests/Meshes/DistributedMeshes/DistributedGridTest_3D.cpp @@ -601,7 +601,7 @@ typedef typename GridType::Cell Cell; typedef typename GridType::IndexType IndexType; typedef typename GridType::PointType PointType; typedef DistributedMesh DistributedGridType; -using Synchronizer = DistributedMeshSynchronizer< MeshFunctionType >; +using Synchronizer = DistributedMeshSynchronizer< DistributedGridType >; class DistributedGirdTest_3D : public ::testing::Test { diff --git a/src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.cpp b/src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e2fd04487534bed49728e85865298713a79231e5 --- /dev/null +++ b/src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.cpp @@ -0,0 +1,2 @@ +#include "DistributedMeshTest.h" +#include "../../main_mpi.h" diff --git a/src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.cu b/src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.cu new file mode 100644 index 0000000000000000000000000000000000000000..e2fd04487534bed49728e85865298713a79231e5 --- /dev/null +++ b/src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.cu @@ -0,0 +1,2 @@ +#include "DistributedMeshTest.h" +#include "../../main_mpi.h" diff --git a/src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.h b/src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.h new file mode 100644 index 0000000000000000000000000000000000000000..641d3af39d580e943b975329c2216738f0aa3873 --- /dev/null +++ b/src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.h @@ -0,0 +1,751 @@ +#pragma once + +#ifdef HAVE_GTEST +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DistributedMeshTest { + +namespace fs = std::experimental::filesystem; + +using namespace TNL; +using namespace TNL::Meshes; +using namespace TNL::Meshes::DistributedMeshes; + +// cannot be deduced from the grid +using LocalIndexType = short int; +// we test only with MPI +using CommunicatorType = Communicators::MpiCommunicator; +using CommunicationGroup = typename CommunicatorType::CommunicationGroup; + +template< typename Mesh > +struct GridDistributor; + +template< typename Real, typename Device, typename Index > +struct GridDistributor< TNL::Meshes::Grid< 2, Real, Device, Index > > +{ + using GridType = TNL::Meshes::Grid< 2, Real, Device, Index >; + using CoordinatesType = typename GridType::CoordinatesType; + using CellTopology = TNL::Meshes::Topologies::Quadrilateral; + using MeshConfig = TNL::Meshes::DefaultConfig< CellTopology, + CellTopology::dimension, + typename GridType::RealType, + typename GridType::GlobalIndexType, + LocalIndexType >; + using LocalMeshType = TNL::Meshes::Mesh< MeshConfig >; + + GridDistributor() = delete; + + GridDistributor( CoordinatesType rank_sizes, CommunicationGroup group ) + : rank(CommunicatorType::GetRank(group)), + nproc(CommunicatorType::GetSize(group)), + rank_sizes(rank_sizes), + group(group) + {} + + void decompose( const GridType& grid, + DistributedMesh< LocalMeshType >& mesh, + int overlap ) + { + ASSERT_EQ( nproc, product(rank_sizes) ); + + // subdomain coordinates + const CoordinatesType rank_coordinates = {rank % rank_sizes.x(), rank / rank_sizes.x()}; + + // local mesh + local_size = grid.getDimensions() / rank_sizes; + ASSERT_EQ( local_size * rank_sizes, grid.getDimensions() ); + // ranges for local (owned) cells + cell_begin = rank_coordinates * local_size; + cell_end = (rank_coordinates + 1) * local_size; + ASSERT_LE( cell_begin, grid.getDimensions() ); + ASSERT_LE( cell_end, grid.getDimensions() ); + // ranges for local (owned) vertices + vert_begin = cell_begin + 1; + if( rank_coordinates.x() == 0 ) vert_begin.x()--; + if( rank_coordinates.y() == 0 ) vert_begin.y()--; + vert_end = cell_end + 1; + + // count local (owned) entities + localVerticesCount = product(vert_end - vert_begin); + localCellsCount = product(cell_end - cell_begin); + // set entities counts on the subdomain + verticesCount = 0; + for( Index y = vert_begin.y() - (rank_coordinates.y() > 0) * (overlap + (overlap>0)); y < vert_end.y() + (rank_coordinates.y() < rank_sizes.y() - 1) * overlap; y++ ) + for( Index x = vert_begin.x() - (rank_coordinates.x() > 0) * (overlap + (overlap>0)); x < vert_end.x() + (rank_coordinates.x() < rank_sizes.x() - 1) * overlap; x++ ) + verticesCount++; + cellsCount = 0; + for( Index y = cell_begin.y() - (rank_coordinates.y() > 0) * overlap; y < cell_end.y() + (rank_coordinates.y() < rank_sizes.y() - 1) * overlap; y++ ) + for( Index x = cell_begin.x() - (rank_coordinates.x() > 0) * overlap; x < cell_end.x() + (rank_coordinates.x() < rank_sizes.x() - 1) * overlap; x++ ) + cellsCount++; + TNL::Meshes::MeshBuilder< LocalMeshType > meshBuilder; + meshBuilder.setPointsCount( verticesCount ); + meshBuilder.setCellsCount( cellsCount ); + + // mappings for vertex indices + std::unordered_map< Index, Index > vert_global_to_local, cell_global_to_local; + + Index idx = 0; + auto add_vertex = [&] ( Index x, Index y ) + { + if( x < 0 || x > grid.getDimensions().x() ) return; + if( y < 0 || y > grid.getDimensions().y() ) return; + typename GridType::Vertex vertex( grid ); + vertex.setCoordinates( {x, y} ); + vertex.refresh(); + if( vert_global_to_local.count( vertex.getIndex() ) == 0 ) { + meshBuilder.setPoint( idx, vertex.getCenter() ); + vert_global_to_local.insert( {vertex.getIndex(), idx} ); + idx++; + } + }; + auto add_cell = [&] ( Index x, Index y ) + { + if( x < 0 || x >= grid.getDimensions().x() ) return; + if( y < 0 || y >= grid.getDimensions().y() ) return; + typename GridType::Cell cell( grid ); + cell.setCoordinates( {x, y} ); + cell.refresh(); + if( cell_global_to_local.count( cell.getIndex() ) == 0 ) { + const auto neighbors = cell.template getNeighborEntities< 0 >(); + meshBuilder.getCellSeed( idx ).setCornerId( 0, vert_global_to_local[ neighbors.template getEntityIndex< -1, -1 >() ] ); + meshBuilder.getCellSeed( idx ).setCornerId( 1, vert_global_to_local[ neighbors.template getEntityIndex< 1, -1 >() ] ); + meshBuilder.getCellSeed( idx ).setCornerId( 2, vert_global_to_local[ neighbors.template getEntityIndex< 1, 1 >() ] ); + meshBuilder.getCellSeed( idx ).setCornerId( 3, vert_global_to_local[ neighbors.template getEntityIndex< -1, 1 >() ] ); + cell_global_to_local.insert( {cell.getIndex(), idx} ); + idx++; + } + }; + + // add all local (owned) vertices + for( Index y = vert_begin.y(); y < vert_end.y(); y++ ) + for( Index x = vert_begin.x(); x < vert_end.x(); x++ ) + add_vertex( x, y ); + // add remaining vertices that will be needed for overlaps +// for( Index y = vert_begin.y() - (rank_coordinates.y() > 0) * (overlap + (overlap>0)); y < vert_end.y() + (rank_coordinates.y() < rank_sizes.y() - 1) * overlap; y++ ) +// for( Index x = vert_begin.x() - (rank_coordinates.x() > 0) * (overlap + (overlap>0)); x < vert_end.x() + (rank_coordinates.x() < rank_sizes.x() - 1) * overlap; x++ ) +// add_vertex( x, y ); + // - bottom left + for( Index y = vert_begin.y() - (rank_coordinates.y() > 0) * (overlap + (overlap>0)); y < vert_begin.y(); y++ ) + for( Index x = vert_begin.x() - (rank_coordinates.x() > 0) * (overlap + (overlap>0)); x < vert_begin.x(); x++ ) + add_vertex( x, y ); + // - bottom + for( Index y = vert_begin.y() - (rank_coordinates.y() > 0) * (overlap + (overlap>0)); y < vert_begin.y(); y++ ) + for( Index x = vert_begin.x(); x < vert_end.x(); x++ ) + add_vertex( x, y ); + // - bottom right + for( Index y = vert_begin.y() - (rank_coordinates.y() > 0) * (overlap + (overlap>0)); y < vert_begin.y(); y++ ) + for( Index x = vert_end.x(); x < vert_end.x() + (rank_coordinates.x() < rank_sizes.x() - 1) * overlap; x++ ) + add_vertex( x, y ); + // - left + for( Index y = vert_begin.y(); y < vert_end.y(); y++ ) + for( Index x = vert_begin.x() - (rank_coordinates.x() > 0) * (overlap + (overlap>0)); x < vert_begin.x(); x++ ) + add_vertex( x, y ); + // - right + for( Index y = vert_begin.y(); y < vert_end.y(); y++ ) + for( Index x = vert_end.x(); x < vert_end.x() + (rank_coordinates.x() < rank_sizes.x() - 1) * overlap; x++ ) + add_vertex( x, y ); + // - top left + for( Index y = vert_end.y(); y < vert_end.y() + (rank_coordinates.y() < rank_sizes.y() - 1) * overlap; y++ ) + for( Index x = vert_begin.x() - (rank_coordinates.x() > 0) * (overlap + (overlap>0)); x < vert_begin.x(); x++ ) + add_vertex( x, y ); + // - top + for( Index y = vert_end.y(); y < vert_end.y() + (rank_coordinates.y() < rank_sizes.y() - 1) * overlap; y++ ) + for( Index x = vert_begin.x(); x < vert_end.x(); x++ ) + add_vertex( x, y ); + // - top right + for( Index y = vert_end.y(); y < vert_end.y() + (rank_coordinates.y() < rank_sizes.y() - 1) * overlap; y++ ) + for( Index x = vert_end.x(); x < vert_end.x() + (rank_coordinates.x() < rank_sizes.x() - 1) * overlap; x++ ) + add_vertex( x, y ); + + // reset counter + idx = 0; + + // add local cells + for( Index y = cell_begin.y(); y < cell_end.y(); y++ ) + for( Index x = cell_begin.x(); x < cell_end.x(); x++ ) + add_cell( x, y ); + // add ghost cells +// for( Index y = cell_begin.y() - (rank_coordinates.y() > 0) * overlap; y < cell_end.y() + (rank_coordinates.y() < rank_sizes.y() - 1) * overlap; y++ ) +// for( Index x = cell_begin.x() - (rank_coordinates.x() > 0) * overlap; x < cell_end.x() + (rank_coordinates.x() < rank_sizes.x() - 1) * overlap; x++ ) +// add_cell( x, y ); + // - bottom left + for( Index y = cell_begin.y() - (rank_coordinates.y() > 0) * overlap; y < cell_begin.y(); y++ ) + for( Index x = cell_begin.x() - (rank_coordinates.x() > 0) * overlap; x < cell_begin.x(); x++ ) + add_cell( x, y ); + // - bottom + for( Index y = cell_begin.y() - (rank_coordinates.y() > 0) * overlap; y < cell_begin.y(); y++ ) + for( Index x = cell_begin.x(); x < cell_end.x(); x++ ) + add_cell( x, y ); + // - bottom right + for( Index y = cell_begin.y() - (rank_coordinates.y() > 0) * overlap; y < cell_begin.y(); y++ ) + for( Index x = cell_end.x(); x < cell_end.x() + (rank_coordinates.x() < rank_sizes.x() - 1) * overlap; x++ ) + add_cell( x, y ); + // - left + for( Index y = cell_begin.y(); y < cell_end.y(); y++ ) + for( Index x = cell_begin.x() - (rank_coordinates.x() > 0) * overlap; x < cell_begin.x(); x++ ) + add_cell( x, y ); + // - right + for( Index y = cell_begin.y(); y < cell_end.y(); y++ ) + for( Index x = cell_end.x(); x < cell_end.x() + (rank_coordinates.x() < rank_sizes.x() - 1) * overlap; x++ ) + add_cell( x, y ); + // - top left + for( Index y = cell_end.y(); y < cell_end.y() + (rank_coordinates.y() < rank_sizes.y() - 1) * overlap; y++ ) + for( Index x = cell_begin.x() - (rank_coordinates.x() > 0) * overlap; x < cell_begin.x(); x++ ) + add_cell( x, y ); + // - top + for( Index y = cell_end.y(); y < cell_end.y() + (rank_coordinates.y() < rank_sizes.y() - 1) * overlap; y++ ) + for( Index x = cell_begin.x(); x < cell_end.x(); x++ ) + add_cell( x, y ); + // - top right + for( Index y = cell_end.y(); y < cell_end.y() + (rank_coordinates.y() < rank_sizes.y() - 1) * overlap; y++ ) + for( Index x = cell_end.x(); x < cell_end.x() + (rank_coordinates.x() < rank_sizes.x() - 1) * overlap; x++ ) + add_cell( x, y ); + + ASSERT_TRUE( meshBuilder.build( mesh.getLocalMesh() ) ); + + // set ghost levels + mesh.setGhostLevels( overlap ); + + if( overlap > 0 ) { + // assign point ghost tags + mesh.vtkPointGhostTypes().setSize( verticesCount ); + for( Index i = 0; i < localVerticesCount; i++ ) + mesh.vtkPointGhostTypes()[ i ] = 0; + for( Index i = localVerticesCount; i < verticesCount; i++ ) { + mesh.vtkPointGhostTypes()[ i ] = (std::uint8_t) VTK::PointGhostTypes::DUPLICATEPOINT; + mesh.getLocalMesh().template addEntityTag< 0 >( i, EntityTags::GhostEntity ); + } + + // assign cell ghost tags + mesh.vtkCellGhostTypes().setSize( cellsCount ); + for( Index i = 0; i < localCellsCount; i++ ) + mesh.vtkCellGhostTypes()[ i ] = 0; + for( Index i = localCellsCount; i < cellsCount; i++ ) { + mesh.vtkCellGhostTypes()[ i ] = (std::uint8_t) VTK::CellGhostTypes::DUPLICATECELL; + mesh.getLocalMesh().template addEntityTag< 2 >( i, EntityTags::GhostEntity ); + } + + // update the entity tags layers after setting ghost indices + mesh.getLocalMesh().template updateEntityTagsLayer< 0 >(); + mesh.getLocalMesh().template updateEntityTagsLayer< 2 >(); + + // assign global indices + auto& points_indices = mesh.template getGlobalIndices< 0 >(); + auto& cells_indices = mesh.template getGlobalIndices< 2 >(); + points_indices.setSize( verticesCount ); + cells_indices.setSize( cellsCount ); + // compute mapping between old and new global indices (global indices on the distributed mesh != global indices on the original grid) + const auto vert_new_global_indices = renumberVertices( grid, rank_sizes ); + const auto cell_new_global_indices = renumberCells( grid, rank_sizes ); + for( auto pair : vert_global_to_local ) + points_indices[ pair.second ] = vert_new_global_indices.at( pair.first ); + for( auto pair : cell_global_to_local ) + cells_indices[ pair.second ] = cell_new_global_indices.at( pair.first ); + } + + // set the communication group + mesh.setCommunicationGroup( group ); + + if( overlap > 0 ) { + // distribute faces + distributeSubentities< 1 >( mesh ); + } + } + + static std::map< Index, Index > renumberVertices( const GridType& grid, CoordinatesType rank_sizes ) + { + std::map< Index, Index > result; + Index idx = 0; + const CoordinatesType local_size = grid.getDimensions() / rank_sizes; + CoordinatesType rank_coordinates; + for( rank_coordinates.y() = 0; rank_coordinates.y() < rank_sizes.y(); rank_coordinates.y()++ ) + for( rank_coordinates.x() = 0; rank_coordinates.x() < rank_sizes.x(); rank_coordinates.x()++ ) + { + const CoordinatesType cell_begin = rank_coordinates * local_size; + const CoordinatesType cell_end = (rank_coordinates + 1) * local_size; + CoordinatesType vert_begin = cell_begin + 1; + if( rank_coordinates.x() == 0 ) vert_begin.x()--; + if( rank_coordinates.y() == 0 ) vert_begin.y()--; + const CoordinatesType vert_end = cell_end + 1; + + for( Index y = vert_begin.y(); y < vert_end.y(); y++ ) + for( Index x = vert_begin.x(); x < vert_end.x(); x++ ) + { + typename GridType::Vertex vertex( grid ); + vertex.setCoordinates( {x, y} ); + vertex.refresh(); + result.insert( {vertex.getIndex(), idx} ); + idx++; + } + } + return result; + } + + static std::map< Index, Index > renumberCells( const GridType& grid, CoordinatesType rank_sizes ) + { + Index idx = 0; + std::map< Index, Index > result; + const CoordinatesType local_size = grid.getDimensions() / rank_sizes; + CoordinatesType rank_coordinates; + for( rank_coordinates.y() = 0; rank_coordinates.y() < rank_sizes.y(); rank_coordinates.y()++ ) + for( rank_coordinates.x() = 0; rank_coordinates.x() < rank_sizes.x(); rank_coordinates.x()++ ) + { + const CoordinatesType cell_begin = rank_coordinates * local_size; + const CoordinatesType cell_end = (rank_coordinates + 1) * local_size; + + for( Index y = cell_begin.y(); y < cell_end.y(); y++ ) + for( Index x = cell_begin.x(); x < cell_end.x(); x++ ) + { + typename GridType::Cell cell( grid ); + cell.setCoordinates( {x, y} ); + cell.refresh(); + result.insert( {cell.getIndex(), idx} ); + idx++; + } + } + return result; + } + + // input parameters + int rank, nproc; + CoordinatesType rank_sizes; + CommunicationGroup group; + // output attributes (byproduct of the decomposition, useful for testing) + CoordinatesType rank_coordinates, local_size, vert_begin, vert_end, cell_begin, cell_end; + Index verticesCount, cellsCount, localVerticesCount, localCellsCount; +}; + +template< typename Mesh, typename Distributor > +void validateMesh( const Mesh& mesh, const Distributor& distributor, int ghostLevels ) +{ + using Index = typename Mesh::GlobalIndexType; + using Device = typename Mesh::DeviceType; + + // check basic interface + EXPECT_EQ( mesh.getCommunicationGroup(), CommunicatorType::AllGroup ); + EXPECT_EQ( mesh.getGhostLevels(), ghostLevels ); + if( ghostLevels > 0 ) { + EXPECT_EQ( mesh.template getGlobalIndices< 0 >().getSize(), mesh.getLocalMesh().template getEntitiesCount< 0 >() ); + EXPECT_EQ( mesh.template getGlobalIndices< 2 >().getSize(), mesh.getLocalMesh().template getEntitiesCount< 2 >() ); + EXPECT_EQ( mesh.vtkPointGhostTypes().getSize(), mesh.getLocalMesh().template getEntitiesCount< 0 >() ); + EXPECT_EQ( mesh.vtkCellGhostTypes().getSize(), mesh.getLocalMesh().template getEntitiesCount< 2 >() ); + } + else { + EXPECT_EQ( mesh.template getGlobalIndices< 0 >().getSize(), 0 ); + EXPECT_EQ( mesh.template getGlobalIndices< 2 >().getSize(), 0 ); + EXPECT_EQ( mesh.vtkPointGhostTypes().getSize(), 0 ); + EXPECT_EQ( mesh.vtkCellGhostTypes().getSize(), 0 ); + } + + // check entities counts + EXPECT_EQ( mesh.getLocalMesh().template getEntitiesCount< 0 >(), distributor.verticesCount ); + EXPECT_EQ( mesh.getLocalMesh().template getEntitiesCount< 2 >(), distributor.cellsCount ); + EXPECT_EQ( mesh.getLocalMesh().template getGhostEntitiesCount< 0 >(), distributor.verticesCount - distributor.localVerticesCount ); + EXPECT_EQ( mesh.getLocalMesh().template getGhostEntitiesCount< 2 >(), distributor.cellsCount - distributor.localCellsCount ); + EXPECT_EQ( mesh.getLocalMesh().template getGhostEntitiesOffset< 0 >(), distributor.localVerticesCount ); + EXPECT_EQ( mesh.getLocalMesh().template getGhostEntitiesOffset< 2 >(), distributor.localCellsCount ); + + if( ghostLevels > 0 ) { + // check that vtkPointGhostTypes is consistent with the tags array + for( Index i = 0; i < distributor.verticesCount; i++ ) { + EXPECT_EQ( mesh.getLocalMesh().template isGhostEntity< 0 >( i ), mesh.vtkPointGhostTypes()[ i ] & (std::uint8_t) VTK::PointGhostTypes::DUPLICATEPOINT ) << "vertex idx = " << i; + } + // check that vtkPointGhostTypes and vtkCellGhostTypes are consistent with the entities tags arrays + for( Index i = 0; i < distributor.cellsCount; i++ ) { + EXPECT_EQ( mesh.getLocalMesh().template isGhostEntity< 2 >( i ), mesh.vtkCellGhostTypes()[ i ] & (std::uint8_t) VTK::CellGhostTypes::DUPLICATECELL ) << "cell idx = " << i; + } + } + + // check decomposition: check that ghost tags are set on correct entities + for( Index i = 0; i < distributor.localVerticesCount; i++ ) { + EXPECT_FALSE( mesh.getLocalMesh().template isGhostEntity< 0 >( i ) ) << "vertex idx = " << i; + } + if( ghostLevels > 0 ) + for( Index i = distributor.localVerticesCount; i < distributor.verticesCount; i++ ) { + EXPECT_TRUE( mesh.getLocalMesh().template isGhostEntity< 0 >( i ) ) << "vertex idx = " << i; + } + for( Index i = 0; i < distributor.localCellsCount; i++ ) { + EXPECT_FALSE( mesh.getLocalMesh().template isGhostEntity< 2 >( i ) ) << "cell idx = " << i; + } + if( ghostLevels > 0 ) + for( Index i = distributor.localCellsCount; i < distributor.cellsCount; i++ ) { + EXPECT_TRUE( mesh.getLocalMesh().template isGhostEntity< 2 >( i ) ) << "cell idx = " << i; + } + + if( ghostLevels > 0 ) { + // exchange local vertices and cells counts, exclusive scan to compute offsets + Containers::Vector< Index, Device > vert_offsets( distributor.nproc + 1 ), cell_offsets( distributor.nproc + 1 ); + { + Containers::Array< Index, Device > vert_sendbuf( distributor.nproc ), cell_sendbuf( distributor.nproc ); + vert_sendbuf.setValue( distributor.localVerticesCount ); + cell_sendbuf.setValue( distributor.localCellsCount ); + CommunicatorType::Alltoall( vert_sendbuf.getData(), 1, + vert_offsets.getData(), 1, + distributor.group ); + CommunicatorType::Alltoall( cell_sendbuf.getData(), 1, + cell_offsets.getData(), 1, + distributor.group ); + } + vert_offsets.setElement( distributor.nproc, 0 ); + cell_offsets.setElement( distributor.nproc, 0 ); + vert_offsets.template scan< Algorithms::ScanType::Exclusive >(); + cell_offsets.template scan< Algorithms::ScanType::Exclusive >(); + EXPECT_EQ( vert_offsets[ distributor.rank ], mesh.template getGlobalIndices< 0 >()[ 0 ] ); + EXPECT_EQ( cell_offsets[ distributor.rank ], mesh.template getGlobalIndices< 2 >()[ 0 ] ); + + // check global indices of ghost entities + for( Index i = 0; i < distributor.localVerticesCount; i++ ) { + EXPECT_EQ( mesh.template getGlobalIndices< 0 >()[ i ], vert_offsets[ distributor.rank ] + i ) << "vertex idx = " << i; + } + for( Index i = distributor.localVerticesCount; i < distributor.verticesCount; i++ ) { + EXPECT_TRUE( mesh.template getGlobalIndices< 0 >()[ i ] < vert_offsets[ distributor.rank ] || + mesh.template getGlobalIndices< 0 >()[ i ] >= vert_offsets[ distributor.rank + 1 ] ) << "vertex idx = " << i; + } + for( Index i = 0; i < distributor.localCellsCount; i++ ) { + EXPECT_EQ( mesh.template getGlobalIndices< 2 >()[ i ], cell_offsets[ distributor.rank ] + i ) << "cell idx = " << i; + } + for( Index i = distributor.localCellsCount; i < distributor.cellsCount; i++ ) { + EXPECT_TRUE( mesh.template getGlobalIndices< 2 >()[ i ] < cell_offsets[ distributor.rank ] || + mesh.template getGlobalIndices< 2 >()[ i ] >= cell_offsets[ distributor.rank + 1 ] ) << "cell idx = " << i; + } + } +} + +struct TestEntitiesProcessor +{ + template< typename Mesh, typename UserData, typename Entity > + __cuda_callable__ + static void processEntity( const Mesh& mesh, UserData& userData, const Entity& entity ) + { + userData[ entity.getIndex() ] += 1; + } +}; + +template< typename Device, typename EntityType, typename MeshType, typename HostArray > +void testTraverserOnDevice( const MeshType& mesh, + const HostArray& expected_array_boundary, + const HostArray& expected_array_interior, + const HostArray& expected_array_all, + const HostArray& expected_array_ghost, + const HostArray& expected_array_local ) +{ + using DeviceMesh = Mesh< typename MeshType::Config, Device >; + Traverser< DeviceMesh, EntityType > traverser; + Pointers::SharedPointer< DeviceMesh > meshPointer; + *meshPointer = mesh; + + Containers::Array< int, Device > array_boundary( mesh.template getEntitiesCount< EntityType >() ); + Containers::Array< int, Device > array_interior( mesh.template getEntitiesCount< EntityType >() ); + Containers::Array< int, Device > array_all ( mesh.template getEntitiesCount< EntityType >() ); + Containers::Array< int, Device > array_ghost ( mesh.template getEntitiesCount< EntityType >() ); + Containers::Array< int, Device > array_local ( mesh.template getEntitiesCount< EntityType >() ); + + array_boundary.setValue( 0 ); + array_interior.setValue( 0 ); + array_all .setValue( 0 ); + array_ghost .setValue( 0 ); + array_local .setValue( 0 ); + + traverser.template processBoundaryEntities< TestEntitiesProcessor >( meshPointer, array_boundary.getView() ); + traverser.template processInteriorEntities< TestEntitiesProcessor >( meshPointer, array_interior.getView() ); + traverser.template processAllEntities < TestEntitiesProcessor >( meshPointer, array_all.getView() ); + traverser.template processGhostEntities < TestEntitiesProcessor >( meshPointer, array_ghost.getView() ); + traverser.template processLocalEntities < TestEntitiesProcessor >( meshPointer, array_local.getView() ); + + EXPECT_EQ( array_boundary, expected_array_boundary ); + EXPECT_EQ( array_interior, expected_array_interior ); + EXPECT_EQ( array_all, expected_array_all ); + EXPECT_EQ( array_ghost, expected_array_ghost ); + EXPECT_EQ( array_local, expected_array_local ); + + // test iteration methods: forAll, forBoundary, forInterior + array_boundary.setValue( 0 ); + array_interior.setValue( 0 ); + array_all .setValue( 0 ); + array_ghost .setValue( 0 ); + array_local .setValue( 0 ); + + auto view_boundary = array_boundary.getView(); + auto view_interior = array_interior.getView(); + auto view_all = array_all.getView(); + auto view_ghost = array_ghost.getView(); + auto view_local = array_local.getView(); + + auto f_boundary = [view_boundary] __cuda_callable__ ( typename MeshType::GlobalIndexType i ) mutable { view_boundary[i] += 1; }; + auto f_interior = [view_interior] __cuda_callable__ ( typename MeshType::GlobalIndexType i ) mutable { view_interior[i] += 1; }; + auto f_all = [view_all] __cuda_callable__ ( typename MeshType::GlobalIndexType i ) mutable { view_all[i] += 1; }; + auto f_ghost = [view_ghost] __cuda_callable__ ( typename MeshType::GlobalIndexType i ) mutable { view_ghost[i] += 1; }; + auto f_local = [view_local] __cuda_callable__ ( typename MeshType::GlobalIndexType i ) mutable { view_local[i] += 1; }; + + meshPointer->template forBoundary< EntityType::getEntityDimension() >( f_boundary ); + meshPointer->template forInterior< EntityType::getEntityDimension() >( f_interior ); + meshPointer->template forAll < EntityType::getEntityDimension() >( f_all ); + meshPointer->template forGhost < EntityType::getEntityDimension() >( f_ghost ); + meshPointer->template forLocal < EntityType::getEntityDimension() >( f_local ); + + EXPECT_EQ( array_boundary, expected_array_boundary ); + EXPECT_EQ( array_interior, expected_array_interior ); + EXPECT_EQ( array_all, expected_array_all ); + EXPECT_EQ( array_ghost, expected_array_ghost ); + EXPECT_EQ( array_local, expected_array_local ); +} + +template< typename Mesh > +void testTraverser( const Mesh& mesh ) +{ + const auto cellsCount = mesh.getLocalMesh().template getEntitiesCount< 2 >(); + const auto verticesCount = mesh.getLocalMesh().template getEntitiesCount< 0 >(); + + // create arrays for all test cases + Containers::Array< int > array_cells_boundary( cellsCount ); + Containers::Array< int > array_cells_interior( cellsCount ); + Containers::Array< int > array_cells_all ( cellsCount ); + Containers::Array< int > array_cells_ghost ( cellsCount ); + Containers::Array< int > array_cells_local ( cellsCount ); + + Containers::Array< int > array_vertices_boundary( verticesCount ); + Containers::Array< int > array_vertices_interior( verticesCount ); + Containers::Array< int > array_vertices_all ( verticesCount ); + Containers::Array< int > array_vertices_ghost ( verticesCount ); + Containers::Array< int > array_vertices_local ( verticesCount ); + + // set expected values + for( int i = 0; i < cellsCount; i++ ) { + array_cells_all[ i ] = 1; + if( mesh.getLocalMesh().template isBoundaryEntity< 2 >( i ) ) { + array_cells_boundary[ i ] = 1; + array_cells_interior[ i ] = 0; + } + else { + array_cells_boundary[ i ] = 0; + array_cells_interior[ i ] = 1; + } + if( mesh.getLocalMesh().template isGhostEntity< 2 >( i ) ) { + array_cells_ghost[ i ] = 1; + array_cells_local[ i ] = 0; + } + else { + array_cells_ghost[ i ] = 0; + array_cells_local[ i ] = 1; + } + } + for( int i = 0; i < verticesCount; i++ ) { + array_vertices_all[ i ] = 1; + if( mesh.getLocalMesh().template isBoundaryEntity< 0 >( i ) ) { + array_vertices_boundary[ i ] = 1; + array_vertices_interior[ i ] = 0; + } + else { + array_vertices_boundary[ i ] = 0; + array_vertices_interior[ i ] = 1; + } + if( mesh.getLocalMesh().template isGhostEntity< 0 >( i ) ) { + array_vertices_ghost[ i ] = 1; + array_vertices_local[ i ] = 0; + } + else { + array_vertices_ghost[ i ] = 0; + array_vertices_local[ i ] = 1; + } + } + + // test + testTraverserOnDevice< Devices::Host, typename Mesh::Cell >( mesh.getLocalMesh(), array_cells_boundary, array_cells_interior, array_cells_all, array_cells_ghost, array_cells_local ); + testTraverserOnDevice< Devices::Host, typename Mesh::Vertex >( mesh.getLocalMesh(), array_vertices_boundary, array_vertices_interior, array_vertices_all, array_vertices_ghost, array_vertices_local ); +#ifdef HAVE_CUDA + testTraverserOnDevice< Devices::Cuda, typename Mesh::Cell >( mesh.getLocalMesh(), array_cells_boundary, array_cells_interior, array_cells_all, array_cells_ghost, array_cells_local ); + testTraverserOnDevice< Devices::Cuda, typename Mesh::Vertex >( mesh.getLocalMesh(), array_vertices_boundary, array_vertices_interior, array_vertices_all, array_vertices_ghost, array_vertices_local ); +#endif +} + +template< typename Device, typename EntityType, typename MeshType > +void testSynchronizerOnDevice( const MeshType& mesh ) +{ + using LocalMesh = Mesh< typename MeshType::Config, Device >; + using DeviceMesh = DistributedMesh< LocalMesh >; + using IndexType = typename MeshType::GlobalIndexType; + using MeshFunction = Functions::MeshFunction< LocalMesh, EntityType::getEntityDimension(), IndexType >; + using Synchronizer = DistributedMeshes::DistributedMeshSynchronizer< DeviceMesh, EntityType::getEntityDimension() >; + + // initialize + DeviceMesh deviceMesh; + deviceMesh = mesh; + Pointers::SharedPointer< LocalMesh > localMeshPointer; + *localMeshPointer = deviceMesh.getLocalMesh(); + MeshFunction f( localMeshPointer ); + f.getData().setValue( -1 ); + + // set global indices of local entities + for( IndexType i = 0; i < mesh.getLocalMesh().template getEntitiesCount< EntityType >(); i++ ) + if( ! mesh.getLocalMesh().template isGhostEntity< EntityType::getEntityDimension() >( i ) ) + f.getData().setElement( i, mesh.template getGlobalIndices< EntityType::getEntityDimension() >()[ i ] ); + + // synchronize + Synchronizer sync; + sync.initialize( deviceMesh ); + sync.synchronize( f ); + + // check all global indices + EXPECT_EQ( f.getData(), mesh.template getGlobalIndices< EntityType::getEntityDimension() >() ); +} + +template< typename Mesh > +void testSynchronizer( const Mesh& mesh ) +{ + testSynchronizerOnDevice< Devices::Host, typename Mesh::Cell >( mesh ); + testSynchronizerOnDevice< Devices::Host, typename Mesh::Vertex >( mesh ); + if( mesh.template getGlobalIndices< 1 >().getSize() > 0 ) + testSynchronizerOnDevice< Devices::Host, typename Mesh::Face >( mesh ); +#ifdef HAVE_CUDA + testSynchronizerOnDevice< Devices::Cuda, typename Mesh::Cell >( mesh ); + testSynchronizerOnDevice< Devices::Cuda, typename Mesh::Vertex >( mesh ); + if( mesh.template getGlobalIndices< 1 >().getSize() > 0 ) + testSynchronizerOnDevice< Devices::Cuda, typename Mesh::Face >( mesh ); +#endif +} + +TEST( DistributedMeshTest, 2D_ghostLevel0 ) +{ + using GridType = TNL::Meshes::Grid< 2, float, Devices::Host, int >; + using LocalMesh = typename GridDistributor< GridType >::LocalMeshType; + using Mesh = DistributedMesh< LocalMesh >; + GridType grid; + grid.setDomain( {0, 0}, {1, 1} ); + const int nproc = CommunicatorType::GetSize(); + grid.setDimensions( nproc, nproc ); + Mesh mesh; + GridDistributor< GridType > distributor( std::sqrt(nproc), CommunicatorType::AllGroup ); + const int ghostLevels = 0; + distributor.decompose( grid, mesh, ghostLevels ); + validateMesh( mesh, distributor, ghostLevels ); + testTraverser( mesh ); +} + +TEST( DistributedMeshTest, 2D_ghostLevel1 ) +{ + using GridType = TNL::Meshes::Grid< 2, float, Devices::Host, int >; + using LocalMesh = typename GridDistributor< GridType >::LocalMeshType; + using Mesh = DistributedMesh< LocalMesh >; + GridType grid; + grid.setDomain( {0, 0}, {1, 1} ); + const int nproc = CommunicatorType::GetSize(); + grid.setDimensions( nproc, nproc ); + Mesh mesh; + GridDistributor< GridType > distributor( std::sqrt(nproc), CommunicatorType::AllGroup ); + const int ghostLevels = 1; + distributor.decompose( grid, mesh, ghostLevels ); + validateMesh( mesh, distributor, ghostLevels ); + testTraverser( mesh ); + testSynchronizer( mesh ); +} + +TEST( DistributedMeshTest, 2D_ghostLevel2 ) +{ + using GridType = TNL::Meshes::Grid< 2, float, Devices::Host, int >; + using LocalMesh = typename GridDistributor< GridType >::LocalMeshType; + using Mesh = DistributedMesh< LocalMesh >; + GridType grid; + grid.setDomain( {0, 0}, {1, 1} ); + const int nproc = CommunicatorType::GetSize(); + grid.setDimensions( nproc, nproc ); + Mesh mesh; + GridDistributor< GridType > distributor( std::sqrt(nproc), CommunicatorType::AllGroup ); + const int ghostLevels = 2; + distributor.decompose( grid, mesh, ghostLevels ); + validateMesh( mesh, distributor, ghostLevels ); + testTraverser( mesh ); + testSynchronizer( mesh ); +} + +TEST( DistributedMeshTest, PVTUWriterReader ) +{ + using GridType = TNL::Meshes::Grid< 2, float, Devices::Host, int >; + using LocalMesh = typename GridDistributor< GridType >::LocalMeshType; + using Mesh = DistributedMesh< LocalMesh >; + GridType grid; + grid.setDomain( {0, 0}, {1, 1} ); + const int nproc = CommunicatorType::GetSize(); + grid.setDimensions( nproc, nproc ); + Mesh mesh; + GridDistributor< GridType > distributor( std::sqrt(nproc), CommunicatorType::AllGroup ); + const int ghostLevels = 2; + distributor.decompose( grid, mesh, ghostLevels ); + + // create a .pvtu file (only rank 0 actually writes to the file) + const std::string baseName = "DistributedMeshTest_" + std::to_string(nproc) + "proc"; + const std::string mainFilePath = baseName + ".pvtu"; + std::string subfilePath; + { + std::ofstream file; + if( CommunicatorType::GetRank() == 0 ) + file.open( mainFilePath ); + using PVTU = Meshes::Writers::PVTUWriter< LocalMesh >; + PVTU pvtu( file ); + pvtu.template writeEntities< Mesh::getMeshDimension() >( mesh ); + if( mesh.getGhostLevels() > 0 ) { + pvtu.template writePPointData< std::uint8_t >( Meshes::VTK::ghostArrayName() ); + pvtu.template writePPointData< typename Mesh::GlobalIndexType >( "GlobalIndex" ); + pvtu.template writePCellData< std::uint8_t >( Meshes::VTK::ghostArrayName() ); + pvtu.template writePCellData< typename Mesh::GlobalIndexType >( "GlobalIndex" ); + } + subfilePath = pvtu.template addPiece< CommunicatorType >( mainFilePath, mesh.getCommunicationGroup() ); + + // create a .vtu file for local data + using Writer = Meshes::Writers::VTUWriter< LocalMesh >; + std::ofstream subfile( subfilePath ); + Writer writer( subfile ); + writer.template writeEntities< LocalMesh::getMeshDimension() >( mesh.getLocalMesh() ); + if( mesh.getGhostLevels() > 0 ) { + writer.writePointData( mesh.vtkPointGhostTypes(), Meshes::VTK::ghostArrayName() ); + writer.writePointData( mesh.template getGlobalIndices< 0 >(), "GlobalIndex" ); + writer.writeCellData( mesh.vtkCellGhostTypes(), Meshes::VTK::ghostArrayName() ); + writer.writeCellData( mesh.template getGlobalIndices< 2 >(), "GlobalIndex" ); + } + + // end of scope closes the files + } + + // load and test + CommunicatorType::Barrier(); + Readers::PVTUReader reader( mainFilePath ); + reader.detectMesh(); + EXPECT_EQ( reader.getMeshType(), "Meshes::DistributedMesh" ); + Mesh loadedMesh; + reader.loadMesh( loadedMesh ); + // decomposition of faces is not stored in the VTK files + if( mesh.getGhostLevels() > 0 ) { + distributeSubentities< 1 >( loadedMesh ); + } + EXPECT_EQ( loadedMesh, mesh ); + + // cleanup + EXPECT_EQ( fs::remove( subfilePath ), true ); + CommunicatorType::Barrier(); + if( CommunicatorType::GetRank() == 0 ) { + EXPECT_EQ( fs::remove( mainFilePath ), true ); + EXPECT_EQ( fs::remove( baseName ), true ); + } +} + +} // namespace DistributedMeshTest + +#endif diff --git a/src/UnitTests/Meshes/DistributedMeshes/DistributedVectorFieldIO_MPIIOTestBase.h b/src/UnitTests/Meshes/DistributedMeshes/DistributedVectorFieldIO_MPIIOTestBase.h index 3b82492e043ae03a3dfb2f10ceb599a1a5404f9b..f35ec8e089621027f065ef764631494b87500982 100644 --- a/src/UnitTests/Meshes/DistributedMeshes/DistributedVectorFieldIO_MPIIOTestBase.h +++ b/src/UnitTests/Meshes/DistributedMeshes/DistributedVectorFieldIO_MPIIOTestBase.h @@ -165,7 +165,7 @@ class TestDistributedVectorFieldMPIIO{ DistributedGridIO_VectorField ::load(FileName, loadVectorField ); - DistributedMeshSynchronizer< MeshFunctionType > synchronizer; + DistributedMeshSynchronizer< DistributedGridType > synchronizer; synchronizer.setDistributedGrid( &distributedGrid ); for(int i=0;i + +#include + +#include +#include +#include +#include +#include + +namespace EntityTagsTest { + +using namespace TNL; +using namespace TNL::Meshes; + +using RealType = double; +using Device = Devices::Host; +using IndexType = int; + +class TestQuadrilateralMeshConfig : public DefaultConfig< Topologies::Quadrilateral > +{ +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; } +}; + +TEST( MeshTest, RegularMeshOfQuadrilateralsTest ) +{ + using QuadrilateralMeshEntityType = MeshEntity< TestQuadrilateralMeshConfig, Devices::Host, Topologies::Quadrilateral >; + using EdgeMeshEntityType = typename QuadrilateralMeshEntityType::SubentityTraits< 1 >::SubentityType; + using VertexMeshEntityType = typename QuadrilateralMeshEntityType::SubentityTraits< 0 >::SubentityType; + + using PointType = typename VertexMeshEntityType::PointType; + static_assert( std::is_same< PointType, Containers::StaticVector< 2, RealType > >::value, + "unexpected PointType" ); + + const IndexType xSize( 3 ), ySize( 4 ); + const RealType width( 1.0 ), height( 1.0 ); + const RealType hx( width / ( RealType ) xSize ), + hy( height / ( RealType ) ySize ); + const IndexType numberOfCells = xSize * ySize; + const IndexType numberOfVertices = ( xSize + 1 ) * ( ySize + 1 ); + + typedef Mesh< TestQuadrilateralMeshConfig > TestQuadrilateralMesh; + TestQuadrilateralMesh mesh, mesh2; + MeshBuilder< TestQuadrilateralMesh > meshBuilder; + meshBuilder.setPointsCount( numberOfVertices ); + meshBuilder.setCellsCount( numberOfCells ); + + /**** + * Setup vertices + */ + for( IndexType j = 0; j <= ySize; j++ ) + for( IndexType i = 0; i <= xSize; i++ ) + meshBuilder.setPoint( j * ( xSize + 1 ) + i, PointType( i * hx, j * hy ) ); + + /**** + * Setup cells + */ + IndexType cellIdx( 0 ); + for( IndexType j = 0; j < ySize; j++ ) + for( IndexType i = 0; i < xSize; i++ ) + { + const IndexType vertex0 = j * ( xSize + 1 ) + i; + const IndexType vertex1 = j * ( xSize + 1 ) + i + 1; + const IndexType vertex2 = ( j + 1 ) * ( xSize + 1 ) + i + 1; + const IndexType vertex3 = ( j + 1 ) * ( xSize + 1 ) + i; + + meshBuilder.getCellSeed( cellIdx ).setCornerId( 0, vertex0 ); + meshBuilder.getCellSeed( cellIdx ).setCornerId( 1, vertex1 ); + meshBuilder.getCellSeed( cellIdx ).setCornerId( 2, vertex2 ); + meshBuilder.getCellSeed( cellIdx++ ).setCornerId( 3, vertex3 ); + } + + ASSERT_TRUE( meshBuilder.build( mesh ) ); + + std::vector< IndexType > boundaryCells = {0, 1, 2, 3, 5, 6, 8, 9, 10, 11}; + std::vector< IndexType > interiorCells = {4, 7}; + + // Test boundary cells + EXPECT_EQ( mesh.template getBoundaryIndices< 2 >().getSize(), (int) boundaryCells.size() ); + for( size_t i = 0; i < boundaryCells.size(); i++ ) { + EXPECT_TRUE( mesh.template isBoundaryEntity< 2 >( boundaryCells[ i ] ) ); + EXPECT_EQ( mesh.template getBoundaryIndices< 2 >()[ i ], boundaryCells[ i ] ); + } + // Test interior cells + EXPECT_EQ( mesh.template getInteriorIndices< 2 >().getSize(), (int) interiorCells.size() ); + for( size_t i = 0; i < interiorCells.size(); i++ ) { + EXPECT_FALSE( mesh.template isBoundaryEntity< 2 >( interiorCells[ i ] ) ); + EXPECT_EQ( mesh.template getInteriorIndices< 2 >()[ i ], interiorCells[ i ] ); + } + + // Test setting other tags + for( size_t i = 0; i < boundaryCells.size(); i++ ) { + mesh.template addEntityTag< 2 >( boundaryCells[ i ], Meshes::EntityTags::GhostEntity ); + EXPECT_EQ( mesh.template getEntityTag< 2 >( boundaryCells[ i ] ), Meshes::EntityTags::BoundaryEntity | Meshes::EntityTags::GhostEntity ); + EXPECT_TRUE( mesh.template isBoundaryEntity< 2 >( boundaryCells[ i ] ) ); + mesh.template removeEntityTag< 2 >( boundaryCells[ i ], Meshes::EntityTags::GhostEntity ); + EXPECT_EQ( mesh.template getEntityTag< 2 >( boundaryCells[ i ] ), Meshes::EntityTags::BoundaryEntity ); + } + for( size_t i = 0; i < interiorCells.size(); i++ ) { + mesh.template addEntityTag< 2 >( interiorCells[ i ], Meshes::EntityTags::GhostEntity ); + EXPECT_EQ( mesh.template getEntityTag< 2 >( interiorCells[ i ] ), Meshes::EntityTags::GhostEntity ); + EXPECT_FALSE( mesh.template isBoundaryEntity< 2 >( interiorCells[ i ] ) ); + mesh.template removeEntityTag< 2 >( interiorCells[ i ], Meshes::EntityTags::GhostEntity ); + EXPECT_EQ( mesh.template getEntityTag< 2 >( interiorCells[ i ] ), 0 ); + } + + std::vector< IndexType > boundaryFaces = {0, 3, 4, 7, 8, 12, 15, 19, 22, 25, 26, 28, 29, 30}; + std::vector< IndexType > interiorFaces = {1, 2, 5, 6, 9, 10, 11, 13, 14, 16, 17, 18, 20, 21, 23, 24, 27}; + + // Test boundary faces + EXPECT_EQ( mesh.template getBoundaryIndices< 1 >().getSize(), (int) boundaryFaces.size() ); + for( size_t i = 0; i < boundaryFaces.size(); i++ ) { + EXPECT_TRUE( mesh.template isBoundaryEntity< 1 >( boundaryFaces[ i ] ) ); + EXPECT_EQ( mesh.template getBoundaryIndices< 1 >()[ i ], boundaryFaces[ i ] ); + } + // Test interior faces + EXPECT_EQ( mesh.template getInteriorIndices< 1 >().getSize(), (int) interiorFaces.size() ); + for( size_t i = 0; i < interiorFaces.size(); i++ ) { + EXPECT_FALSE( mesh.template isBoundaryEntity< 1 >( interiorFaces[ i ] ) ); + EXPECT_EQ( mesh.template getInteriorIndices< 1 >()[ i ], interiorFaces[ i ] ); + } + + // Test setting other tags + for( size_t i = 0; i < boundaryFaces.size(); i++ ) { + mesh.template addEntityTag< 1 >( boundaryFaces[ i ], Meshes::EntityTags::GhostEntity ); + EXPECT_EQ( mesh.template getEntityTag< 1 >( boundaryFaces[ i ] ), Meshes::EntityTags::BoundaryEntity | Meshes::EntityTags::GhostEntity ); + EXPECT_TRUE( mesh.template isBoundaryEntity< 1 >( boundaryFaces[ i ] ) ); + mesh.template removeEntityTag< 1 >( boundaryFaces[ i ], Meshes::EntityTags::GhostEntity ); + EXPECT_EQ( mesh.template getEntityTag< 1 >( boundaryFaces[ i ] ), Meshes::EntityTags::BoundaryEntity ); + } + for( size_t i = 0; i < interiorFaces.size(); i++ ) { + mesh.template addEntityTag< 1 >( interiorFaces[ i ], Meshes::EntityTags::GhostEntity ); + EXPECT_EQ( mesh.template getEntityTag< 1 >( interiorFaces[ i ] ), Meshes::EntityTags::GhostEntity ); + EXPECT_FALSE( mesh.template isBoundaryEntity< 1 >( interiorFaces[ i ] ) ); + mesh.template removeEntityTag< 1 >( interiorFaces[ i ], Meshes::EntityTags::GhostEntity ); + EXPECT_EQ( mesh.template getEntityTag< 1 >( interiorFaces[ i ] ), 0 ); + } +} + +} // namespace EntityTagsTest + +#endif diff --git a/src/UnitTests/Meshes/MeshOrderingTest.h b/src/UnitTests/Meshes/MeshOrderingTest.h index 054e4309eedd85563d95bf18ba5e9de74beff01d..634f9445411e35cf45debd60f9c23d361c532207 100644 --- a/src/UnitTests/Meshes/MeshOrderingTest.h +++ b/src/UnitTests/Meshes/MeshOrderingTest.h @@ -17,7 +17,7 @@ using namespace TNL; using namespace TNL::Meshes; class TestTriangleMeshConfig - : public DefaultConfig< Topologies::Triangle, 2, double, int, short int, int > + : public DefaultConfig< Topologies::Triangle, 2, double, int, short int > { public: static constexpr bool entityStorage( int dimensions ) { return true; } @@ -85,11 +85,11 @@ bool buildTriangleMesh( Mesh< TestTriangleMeshConfig, Device >& mesh ) return meshBuilder.build( mesh ); } -template< typename PermutationVector > +template< typename PermutationArray > void testMesh( const Mesh< TestTriangleMeshConfig, Devices::Host >& mesh, - const PermutationVector& vertexPermutation, - const PermutationVector& edgePermutation, - const PermutationVector& cellPermutation ) + const PermutationArray& vertexPermutation, + const PermutationArray& edgePermutation, + const PermutationArray& cellPermutation ) { using MeshType = Mesh< TestTriangleMeshConfig, Devices::Host >; using PointType = typename MeshType::PointType; @@ -124,16 +124,16 @@ void testMesh( const Mesh< TestTriangleMeshConfig, Devices::Host >& mesh, // test subentities - EXPECT_EQ( mesh.template getEntity< 1 >( edgePermutation[ 0 ] ).getVertexIndex( 0 ), vertexPermutation[ 1 ] ); - EXPECT_EQ( mesh.template getEntity< 1 >( edgePermutation[ 0 ] ).getVertexIndex( 1 ), vertexPermutation[ 2 ] ); - EXPECT_EQ( mesh.template getEntity< 1 >( edgePermutation[ 1 ] ).getVertexIndex( 0 ), vertexPermutation[ 2 ] ); - EXPECT_EQ( mesh.template getEntity< 1 >( edgePermutation[ 1 ] ).getVertexIndex( 1 ), vertexPermutation[ 0 ] ); - EXPECT_EQ( mesh.template getEntity< 1 >( edgePermutation[ 2 ] ).getVertexIndex( 0 ), vertexPermutation[ 0 ] ); - EXPECT_EQ( mesh.template getEntity< 1 >( edgePermutation[ 2 ] ).getVertexIndex( 1 ), vertexPermutation[ 1 ] ); - EXPECT_EQ( mesh.template getEntity< 1 >( edgePermutation[ 3 ] ).getVertexIndex( 0 ), vertexPermutation[ 2 ] ); - EXPECT_EQ( mesh.template getEntity< 1 >( edgePermutation[ 3 ] ).getVertexIndex( 1 ), vertexPermutation[ 3 ] ); - EXPECT_EQ( mesh.template getEntity< 1 >( edgePermutation[ 4 ] ).getVertexIndex( 0 ), vertexPermutation[ 3 ] ); - EXPECT_EQ( mesh.template getEntity< 1 >( edgePermutation[ 4 ] ).getVertexIndex( 1 ), vertexPermutation[ 1 ] ); + EXPECT_EQ( mesh.template getEntity< 1 >( edgePermutation[ 0 ] ).template getSubentityIndex< 0 >( 0 ), vertexPermutation[ 1 ] ); + EXPECT_EQ( mesh.template getEntity< 1 >( edgePermutation[ 0 ] ).template getSubentityIndex< 0 >( 1 ), vertexPermutation[ 2 ] ); + EXPECT_EQ( mesh.template getEntity< 1 >( edgePermutation[ 1 ] ).template getSubentityIndex< 0 >( 0 ), vertexPermutation[ 2 ] ); + EXPECT_EQ( mesh.template getEntity< 1 >( edgePermutation[ 1 ] ).template getSubentityIndex< 0 >( 1 ), vertexPermutation[ 0 ] ); + EXPECT_EQ( mesh.template getEntity< 1 >( edgePermutation[ 2 ] ).template getSubentityIndex< 0 >( 0 ), vertexPermutation[ 0 ] ); + EXPECT_EQ( mesh.template getEntity< 1 >( edgePermutation[ 2 ] ).template getSubentityIndex< 0 >( 1 ), vertexPermutation[ 1 ] ); + EXPECT_EQ( mesh.template getEntity< 1 >( edgePermutation[ 3 ] ).template getSubentityIndex< 0 >( 0 ), vertexPermutation[ 2 ] ); + EXPECT_EQ( mesh.template getEntity< 1 >( edgePermutation[ 3 ] ).template getSubentityIndex< 0 >( 1 ), vertexPermutation[ 3 ] ); + EXPECT_EQ( mesh.template getEntity< 1 >( edgePermutation[ 4 ] ).template getSubentityIndex< 0 >( 0 ), vertexPermutation[ 3 ] ); + EXPECT_EQ( mesh.template getEntity< 1 >( edgePermutation[ 4 ] ).template getSubentityIndex< 0 >( 1 ), vertexPermutation[ 1 ] ); EXPECT_EQ( mesh.template getEntity< 2 >( cellPermutation[ 0 ] ).template getSubentityIndex< 0 >( 0 ), vertexPermutation[ 0 ] ); EXPECT_EQ( mesh.template getEntity< 2 >( cellPermutation[ 0 ] ).template getSubentityIndex< 0 >( 1 ), vertexPermutation[ 1 ] ); @@ -204,19 +204,25 @@ void testMesh( const Mesh< TestTriangleMeshConfig, Devices::Host >& mesh, // test boundary tags const std::vector< int > boundaryFaces = {1, 2, 3, 4}; const std::vector< int > interiorFaces = {0}; - EXPECT_EQ( mesh.template getBoundaryEntitiesCount< 1 >(), (int) boundaryFaces.size() ); + EXPECT_EQ( mesh.template getBoundaryIndices< 1 >().getSize(), (int) boundaryFaces.size() ); for( size_t i = 0; i < boundaryFaces.size(); i++ ) { EXPECT_TRUE( mesh.template isBoundaryEntity< 1 >( edgePermutation[ boundaryFaces[ i ] ] ) ); // boundary indices are always sorted so we can't test this -// EXPECT_EQ( mesh.template getBoundaryEntityIndex< 1 >( i ), edgePermutation[ boundaryFaces[ i ] ] ); +// EXPECT_EQ( mesh.template getBoundaryIndices< 1 >()[ i ], edgePermutation[ boundaryFaces[ i ] ] ); } // Test interior faces - EXPECT_EQ( mesh.template getInteriorEntitiesCount< 1 >(), (int) interiorFaces.size() ); + EXPECT_EQ( mesh.template getInteriorIndices< 1 >().getSize(), (int) interiorFaces.size() ); for( size_t i = 0; i < interiorFaces.size(); i++ ) { EXPECT_FALSE( mesh.template isBoundaryEntity< 1 >( edgePermutation[ interiorFaces[ i ] ] ) ); // boundary indices are always sorted so we can't test this -// EXPECT_EQ( mesh.template getInteriorEntityIndex< 1 >( i ), edgePermutation[ interiorFaces[ i ] ] ); +// EXPECT_EQ( mesh.template getInteriorIndices< 1 >()[ i ], edgePermutation[ interiorFaces[ i ] ] ); } + + // tests for the dual graph layer + ASSERT_EQ( mesh.getCellNeighborsCount( cellPermutation[ 0 ] ), 1 ); + ASSERT_EQ( mesh.getCellNeighborsCount( cellPermutation[ 1 ] ), 1 ); + EXPECT_EQ( mesh.getCellNeighborIndex( cellPermutation[ 0 ], 0 ), cellPermutation[ 1 ] ); + EXPECT_EQ( mesh.getCellNeighborIndex( cellPermutation[ 1 ], 0 ), cellPermutation[ 0 ] ); } // hack due to TNL::Containers::Vector not supporting initilizer lists @@ -247,10 +253,10 @@ TEST( MeshOrderingTest, OrderingOnHost ) MeshHost mesh; ASSERT_TRUE( buildTriangleMesh( mesh ) ); - using PermutationVector = typename MeshHost::GlobalIndexVector; - PermutationVector vertexIdentity, edgeIdentity, cellIdentity, - vertexPermutation, edgePermutation, cellPermutation, - vertexInversePermutation, edgeInversePermutation, cellInversePermutation; + using PermutationArray = typename MeshHost::GlobalIndexArray; + PermutationArray vertexIdentity, edgeIdentity, cellIdentity, + vertexPermutation, edgePermutation, cellPermutation, + vertexInversePermutation, edgeInversePermutation, cellInversePermutation; setPermutation( vertexIdentity, _vertexIdentity ); setPermutation( edgeIdentity, _edgeIdentity ); setPermutation( cellIdentity, _cellIdentity ); @@ -282,7 +288,7 @@ TEST( MeshOrderingTest, OrderingOnCuda ) ASSERT_TRUE( buildTriangleMesh( meshHost ) ); mesh = meshHost; - using PermutationCuda = typename MeshCuda::GlobalIndexVector; + using PermutationCuda = typename MeshCuda::GlobalIndexArray; PermutationCuda vertexIdentity, edgeIdentity, cellIdentity, vertexPermutation, edgePermutation, cellPermutation, vertexInversePermutation, edgeInversePermutation, cellInversePermutation; @@ -303,10 +309,10 @@ TEST( MeshOrderingTest, OrderingOnCuda ) // test is on host { // local scope so we can use the same names - using PermutationVector = typename MeshHost::GlobalIndexVector; - PermutationVector vertexIdentity, edgeIdentity, cellIdentity, - vertexPermutation, edgePermutation, cellPermutation, - vertexInversePermutation, edgeInversePermutation, cellInversePermutation; + using PermutationArray = typename MeshHost::GlobalIndexArray; + PermutationArray vertexIdentity, edgeIdentity, cellIdentity, + vertexPermutation, edgePermutation, cellPermutation, + vertexInversePermutation, edgeInversePermutation, cellInversePermutation; setPermutation( vertexIdentity, _vertexIdentity ); setPermutation( edgeIdentity, _edgeIdentity ); setPermutation( cellIdentity, _cellIdentity ); diff --git a/src/UnitTests/Meshes/MeshReaderTest.cpp b/src/UnitTests/Meshes/MeshReaderTest.cpp deleted file mode 100644 index ac1b0314a166f798dd4c518b278844c2ecbcb964..0000000000000000000000000000000000000000 --- a/src/UnitTests/Meshes/MeshReaderTest.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "MeshReaderTest.h" diff --git a/src/UnitTests/Meshes/MeshReaderTest.cu b/src/UnitTests/Meshes/MeshReaderTest.cu deleted file mode 100644 index ac1b0314a166f798dd4c518b278844c2ecbcb964..0000000000000000000000000000000000000000 --- a/src/UnitTests/Meshes/MeshReaderTest.cu +++ /dev/null @@ -1 +0,0 @@ -#include "MeshReaderTest.h" diff --git a/src/UnitTests/Meshes/MeshReaderTest.h b/src/UnitTests/Meshes/MeshReaderTest.h deleted file mode 100644 index 17bc6a3178fbd5395a7964150bc67ebf89373516..0000000000000000000000000000000000000000 --- a/src/UnitTests/Meshes/MeshReaderTest.h +++ /dev/null @@ -1,196 +0,0 @@ -#include -#include -#include -#include -#include - -#ifdef HAVE_VTK -#include -#endif - -#include - -#include "MeshTest.h" - -// TODO: remove this after refactoring with clang-rename -#include "MeshEntityTest.h" -#include "BoundaryTagsTest.h" - -using namespace TNL; -using namespace TNL::Meshes; - - -template< typename Cell, - int WorldDimension = Cell::dimension, - typename Real = double, - typename GlobalIndex = int, - typename LocalIndex = GlobalIndex, - typename Id = void > -struct MyMeshConfig - : public DefaultConfig< Cell, WorldDimension, Real, GlobalIndex, LocalIndex, Id > -{ - static constexpr bool entityStorage( int dimension ) - { - return true; -// return dimension == 0 || dimension == Cell::dimension; - } - - template< typename EntityTopology > - static constexpr bool subentityStorage( EntityTopology, int SubentityDimension ) - { -// return entityStorage( EntityTopology::dimension ); - return entityStorage( EntityTopology::dimension ) && - SubentityDimension == 0; - } - - template< typename EntityTopology > - static constexpr bool subentityOrientationStorage( EntityTopology, int SubentityDimension ) - { - return false; - } - - template< typename EntityTopology > - static constexpr bool superentityStorage( EntityTopology, int SuperentityDimension ) - { -// return entityStorage( EntityTopology::dimension ); - return entityStorage( EntityTopology::dimension ) && - SuperentityDimension == Cell::dimension; - } - - template< typename EntityTopology > - static constexpr bool boundaryTagsStorage( EntityTopology ) - { - using BaseType = DefaultConfig< Cell, WorldDimension, Real, GlobalIndex, LocalIndex, Id >; - using FaceTopology = typename Topologies::Subtopology< Cell, BaseType::meshDimension - 1 >::Topology; - return entityStorage( BaseType::meshDimension - 1 ) && - superentityStorage( FaceTopology(), BaseType::meshDimension ) && - ( EntityTopology::dimension >= BaseType::meshDimension - 1 || subentityStorage( FaceTopology(), EntityTopology::dimension ) ); - //return false; - } -}; - - -// specialization of BuildConfigTags -struct MyBuildConfigTag {}; - -namespace TNL { -namespace Meshes { -namespace BuildConfigTags { - -// disable grids -template< int Dimension, typename Real, typename Device, typename Index > -struct GridTag< MyBuildConfigTag, Grid< Dimension, Real, Device, Index > > -{ enum { enabled = false }; }; - -// enable all cell topologies -template<> struct MeshCellTopologyTag< MyBuildConfigTag, Topologies::Edge > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MyBuildConfigTag, Topologies::Triangle > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MyBuildConfigTag, Topologies::Quadrilateral > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MyBuildConfigTag, Topologies::Tetrahedron > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MyBuildConfigTag, Topologies::Hexahedron > { enum { enabled = true }; }; - -template< typename CellTopology, int WorldDimension > -struct MeshWorldDimensionTag< MyBuildConfigTag, CellTopology, WorldDimension > -{ enum { enabled = ( WorldDimension == CellTopology::dimension ) }; }; - -template< typename Real > struct MeshRealTag< MyBuildConfigTag, Real > { enum { enabled = false }; }; -template<> struct MeshRealTag< MyBuildConfigTag, float > { enum { enabled = true }; }; -template<> struct MeshRealTag< MyBuildConfigTag, double > { enum { enabled = true }; }; - -template< typename GlobalIndex > struct MeshGlobalIndexTag< MyBuildConfigTag, GlobalIndex > { enum { enabled = false }; }; -template<> struct MeshGlobalIndexTag< MyBuildConfigTag, int > { enum { enabled = true }; }; - -template< typename LocalIndex > struct MeshLocalIndexTag< MyBuildConfigTag, LocalIndex > { enum { enabled = false }; }; -template<> struct MeshLocalIndexTag< MyBuildConfigTag, short int > { enum { enabled = true }; }; - -template<> -struct MeshConfigTemplateTag< MyBuildConfigTag > -{ - template< typename Cell, int WorldDimension, typename Real, typename GlobalIndex, typename LocalIndex, typename Id > - using MeshConfig = MyMeshConfig< Cell, WorldDimension, Real, GlobalIndex, LocalIndex, Id >; -}; - -} // namespace BuildConfigTags -} // namespace Meshes -} // namespace TNL - - -template< typename MeshType > -class MeshTester -{ -public: - static bool run( const String& fileName ) - { - std::cout << "pre-init\t"; - Debugging::printMemoryUsage(); - -#ifdef HAVE_VTK - MeshType mesh_libvtk; - Readers::VTKReader_libvtk<> reader; - if( ! reader.readMesh( fileName, mesh_libvtk ) ) - return false; - - std::cout << "libvtk vertices: " << mesh_libvtk.template getEntitiesCount< 0 >() << std::endl; - std::cout << "libvtk faces: " << mesh_libvtk.template getEntitiesCount< MeshType::getMeshDimension() - 1 >() << std::endl; - std::cout << "libvtk cells: " << mesh_libvtk.template getEntitiesCount< MeshType::getMeshDimension() >() << std::endl; -#endif - - Timer timer; - timer.start(); - - MeshType mesh; - Meshes::DistributedMeshes::DistributedMesh distributedMesh; - if( ! loadMesh( fileName, mesh, distributedMesh ) ) - return false; - - timer.stop(); - std::cout << "Loading took " << timer.getRealTime() << " seconds." << std::endl; - - std::cout << "vertices: " << mesh.template getEntitiesCount< 0 >() << std::endl; - std::cout << "faces: " << mesh.template getEntitiesCount< MeshType::getMeshDimension() - 1 >() << std::endl; - std::cout << "cells: " << mesh.template getEntitiesCount< MeshType::getMeshDimension() >() << std::endl; - - std::cout << "post-init\t"; - Debugging::printMemoryUsage(); - -#ifdef HAVE_VTK - std::cout << "mesh_libvtk == mesh: " << std::boolalpha << (mesh_libvtk == mesh) << std::endl; - if( mesh_libvtk != mesh ) { - std::cerr << "mesh_libvtk:\n" << mesh_libvtk << "\n\nmesh:\n" << mesh << std::endl; - return false; - } -#endif - -#ifdef HAVE_GTEST - std::cout << "Running basic I/O tests..." << std::endl; - MeshTest::testFinishedMesh( mesh ); -#endif -// mesh.save( "mesh-test.tnl" ); - - return true; - } -}; - -int main( int argc, char* argv[] ) -{ - if( argc < 2 ) { - std::cerr << "Usage: " << argv[ 0 ] << " filename.[tnl|ng|vtk] ..." << std::endl; - return EXIT_FAILURE; - } - - bool result = true; - - for( int i = 1; i < argc; i++ ) { - String fileName = argv[ i ]; - - result &= resolveMeshType< MyBuildConfigTag, Devices::Host, MeshTester > - ( fileName, - fileName // passed to MeshTester::run - ); - } - - std::cout << "final\t"; - Debugging::printMemoryUsage(); - - return ! result; -} diff --git a/src/UnitTests/Meshes/MeshTest.h b/src/UnitTests/Meshes/MeshTest.h index 5c95221ed8ad611f61265209fcc5d25b7cd5bb59..e1ac5af08fd083ff55e0f5bc6305790b15a5c562 100644 --- a/src/UnitTests/Meshes/MeshTest.h +++ b/src/UnitTests/Meshes/MeshTest.h @@ -97,6 +97,7 @@ void testMeshOnCuda( const Mesh& mesh ) DeviceMesh dmesh2; dmesh2 = mesh; EXPECT_EQ( dmesh2, mesh ); + compareStringRepresentation( dmesh2, mesh ); // test CUDA->CUDA copy testCopyAssignment( dmesh1 ); @@ -129,8 +130,8 @@ void testEntities( const Mesh& mesh ) // test that superentity accessors have been correctly bound for( IndexType i = 0; i < mesh.template getEntitiesCount< 0 >(); i++ ) { - auto v1 = mesh.template getEntity< 0 >( i ); - auto& v2 = mesh.template getEntity< 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() >() ); @@ -141,8 +142,8 @@ void testEntities( const Mesh& mesh ) // test that subentity accessors have been correctly bound for( IndexType i = 0; i < mesh.template getEntitiesCount< Mesh::getMeshDimension() >(); i++ ) { - auto c1 = mesh.template getEntity< Mesh::getMeshDimension() >( i ); - auto& c2 = mesh.template getEntity< 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 >() ); @@ -234,16 +235,16 @@ TEST( MeshTest, TwoTrianglesTest ) EXPECT_EQ( mesh.template getEntity< 0 >( 2 ).getPoint(), point2 ); EXPECT_EQ( mesh.template getEntity< 0 >( 3 ).getPoint(), point3 ); - EXPECT_EQ( mesh.template getEntity< 1 >( 0 ).getVertexIndex( 0 ), 1 ); - EXPECT_EQ( mesh.template getEntity< 1 >( 0 ).getVertexIndex( 1 ), 2 ); - EXPECT_EQ( mesh.template getEntity< 1 >( 1 ).getVertexIndex( 0 ), 2 ); - EXPECT_EQ( mesh.template getEntity< 1 >( 1 ).getVertexIndex( 1 ), 0 ); - EXPECT_EQ( mesh.template getEntity< 1 >( 2 ).getVertexIndex( 0 ), 0 ); - EXPECT_EQ( mesh.template getEntity< 1 >( 2 ).getVertexIndex( 1 ), 1 ); - EXPECT_EQ( mesh.template getEntity< 1 >( 3 ).getVertexIndex( 0 ), 2 ); - EXPECT_EQ( mesh.template getEntity< 1 >( 3 ).getVertexIndex( 1 ), 3 ); - EXPECT_EQ( mesh.template getEntity< 1 >( 4 ).getVertexIndex( 0 ), 3 ); - EXPECT_EQ( mesh.template getEntity< 1 >( 4 ).getVertexIndex( 1 ), 1 ); + EXPECT_EQ( mesh.template getEntity< 1 >( 0 ).template getSubentityIndex< 0 >( 0 ), 1 ); + EXPECT_EQ( mesh.template getEntity< 1 >( 0 ).template getSubentityIndex< 0 >( 1 ), 2 ); + EXPECT_EQ( mesh.template getEntity< 1 >( 1 ).template getSubentityIndex< 0 >( 0 ), 2 ); + EXPECT_EQ( mesh.template getEntity< 1 >( 1 ).template getSubentityIndex< 0 >( 1 ), 0 ); + EXPECT_EQ( mesh.template getEntity< 1 >( 2 ).template getSubentityIndex< 0 >( 0 ), 0 ); + EXPECT_EQ( mesh.template getEntity< 1 >( 2 ).template getSubentityIndex< 0 >( 1 ), 1 ); + EXPECT_EQ( mesh.template getEntity< 1 >( 3 ).template getSubentityIndex< 0 >( 0 ), 2 ); + EXPECT_EQ( mesh.template getEntity< 1 >( 3 ).template getSubentityIndex< 0 >( 1 ), 3 ); + EXPECT_EQ( mesh.template getEntity< 1 >( 4 ).template getSubentityIndex< 0 >( 0 ), 3 ); + EXPECT_EQ( mesh.template getEntity< 1 >( 4 ).template getSubentityIndex< 0 >( 1 ), 1 ); EXPECT_EQ( mesh.template getEntity< 2 >( 0 ).template getSubentityIndex< 0 >( 0 ), 0 ); EXPECT_EQ( mesh.template getEntity< 2 >( 0 ).template getSubentityIndex< 0 >( 1 ), 1 ); @@ -258,9 +259,7 @@ TEST( MeshTest, TwoTrianglesTest ) EXPECT_EQ( mesh.template getEntity< 2 >( 1 ).template getSubentityIndex< 1 >( 1 ), 4 ); EXPECT_EQ( mesh.template getEntity< 2 >( 1 ).template getSubentityIndex< 1 >( 2 ), 0 ); - /* - * Tests for the superentities layer. - */ + // tests for the superentities layer ASSERT_EQ( mesh.template getEntity< 0 >( 0 ).template getSuperentitiesCount< 1 >(), 2 ); EXPECT_EQ( mesh.template getEntity< 0 >( 0 ).template getSuperentityIndex< 1 >( 0 ), 1 ); EXPECT_EQ( mesh.template getEntity< 0 >( 0 ).template getSuperentityIndex< 1 >( 1 ), 2 ); @@ -278,6 +277,11 @@ TEST( MeshTest, TwoTrianglesTest ) EXPECT_EQ( mesh.template getEntity< 1 >( 0 ).template getSuperentityIndex< 2 >( 0 ), 0 ); EXPECT_EQ( mesh.template getEntity< 1 >( 0 ).template getSuperentityIndex< 2 >( 1 ), 1 ); + // tests for the dual graph layer + ASSERT_EQ( mesh.getCellNeighborsCount( 0 ), 1 ); + ASSERT_EQ( mesh.getCellNeighborsCount( 1 ), 1 ); + EXPECT_EQ( mesh.getCellNeighborIndex( 0, 0 ), 1 ); + EXPECT_EQ( mesh.getCellNeighborIndex( 1, 0 ), 0 ); testFinishedMesh( mesh ); }; @@ -645,6 +649,40 @@ TEST( MeshTest, RegularMeshOfQuadrilateralsTest ) } } + // Tests for the dual graph layer + ASSERT_EQ( mesh.getNeighborCounts().getSize(), numberOfCells ); + cellIdx = 0; + for( IndexType j = 0; j < ySize; j++ ) + for( IndexType i = 0; i < xSize; i++ ) + { + IndexType nnbrs = 4; + if( i == 0 || i == xSize - 1 ) + --nnbrs; + if( j == 0 || j == ySize - 1 ) + --nnbrs; + + EXPECT_EQ( mesh.getCellNeighborsCount( cellIdx ), nnbrs ); + std::set< IndexType > neighbors; + for( IndexType n = 0; n < nnbrs; n++ ) + neighbors.insert( mesh.getDualGraph().getRow( cellIdx ).getColumnIndex( n ) ); + + // the cell itself should not be its own neighbor + EXPECT_EQ( (IndexType) neighbors.count( cellIdx ), 0 ); + auto check_neighbor = [&]( IndexType i, IndexType j ) + { + if( i >= 0 && i < xSize && j >= 0 && j < ySize ) { + EXPECT_EQ( (IndexType) neighbors.count( j * xSize + i ), 1 ); + } + }; + // check neighbors over face + check_neighbor( i - 1, j ); + check_neighbor( i + 1, j ); + check_neighbor( i, j - 1 ); + check_neighbor( i, j + 1 ); + + ++cellIdx; + } + testFinishedMesh( mesh ); } @@ -810,6 +848,177 @@ TEST( MeshTest, RegularMeshOfHexahedronsTest ) } } + // Tests for the dual graph layer + ASSERT_EQ( mesh.getNeighborCounts().getSize(), numberOfCells ); + cellIdx = 0; + for( IndexType k = 0; k < zSize; k++ ) + for( IndexType j = 0; j < ySize; j++ ) + for( IndexType i = 0; i < xSize; i++ ) + { + IndexType nnbrs = 6; + if( i == 0 || i == xSize - 1 ) + --nnbrs; + if( j == 0 || j == ySize - 1 ) + --nnbrs; + if( k == 0 || k == zSize - 1 ) + --nnbrs; + + EXPECT_EQ( mesh.getCellNeighborsCount( cellIdx ), nnbrs ); + std::set< IndexType > neighbors; + for( IndexType n = 0; n < nnbrs; n++ ) + neighbors.insert( mesh.getDualGraph().getRow( cellIdx ).getColumnIndex( n ) ); + + // the cell itself should not be its own neighbor + EXPECT_EQ( (IndexType) neighbors.count( cellIdx ), 0 ); + auto check_neighbor = [&]( IndexType i, IndexType j, IndexType k ) + { + if( i >= 0 && i < xSize && j >= 0 && j < ySize && k >= 0 && k < zSize ) { + EXPECT_EQ( (IndexType) neighbors.count( k * xSize * ySize + j * xSize + i ), 1 ); + } + }; + // check neighbors over face + check_neighbor( i - 1, j, k ); + check_neighbor( i + 1, j, k ); + check_neighbor( i, j - 1, k ); + check_neighbor( i, j + 1, k ); + check_neighbor( i, j, k - 1 ); + check_neighbor( i, j, k + 1 ); + + ++cellIdx; + } + + // Tests for the dual graph layer - with minCommonVertices override + mesh.initializeDualGraph( mesh, 2 ); + ASSERT_EQ( mesh.getNeighborCounts().getSize(), numberOfCells ); + cellIdx = 0; + for( IndexType k = 0; k < zSize; k++ ) + for( IndexType j = 0; j < ySize; j++ ) + for( IndexType i = 0; i < xSize; i++ ) + { + IndexType nnbrs = 18; + if( i == 0 || i == xSize - 1 ) + nnbrs -= 5; + if( j == 0 || j == ySize - 1 ) + nnbrs -= 5; + if( k == 0 || k == zSize - 1 ) + nnbrs -= 5; + if( (i == 0 || i == xSize - 1) && (j == 0 || j == ySize - 1) ) + ++nnbrs; + if( (i == 0 || i == xSize - 1) && (k == 0 || k == zSize - 1) ) + ++nnbrs; + if( (j == 0 || j == ySize - 1) && (k == 0 || k == zSize - 1) ) + ++nnbrs; + + EXPECT_EQ( mesh.getCellNeighborsCount( cellIdx ), nnbrs ); + std::set< IndexType > neighbors; + for( IndexType n = 0; n < nnbrs; n++ ) + neighbors.insert( mesh.getDualGraph().getRow( cellIdx ).getColumnIndex( n ) ); + + // the cell itself should not be its own neighbor + EXPECT_EQ( (IndexType) neighbors.count( cellIdx ), 0 ); + auto check_neighbor = [&]( IndexType i, IndexType j, IndexType k ) + { + if( i >= 0 && i < xSize && j >= 0 && j < ySize && k >= 0 && k < zSize ) { + EXPECT_EQ( (IndexType) neighbors.count( k * xSize * ySize + j * xSize + i ), 1 ); + } + }; + // check neighbors over face + check_neighbor( i - 1, j, k ); + check_neighbor( i + 1, j, k ); + check_neighbor( i, j - 1, k ); + check_neighbor( i, j + 1, k ); + check_neighbor( i, j, k - 1 ); + check_neighbor( i, j, k + 1 ); + // check neighbors over edge + check_neighbor( i - 1, j - 1, k ); + check_neighbor( i - 1, j + 1, k ); + check_neighbor( i + 1, j - 1, k ); + check_neighbor( i + 1, j + 1, k ); + check_neighbor( i - 1, j, k - 1 ); + check_neighbor( i - 1, j, k + 1 ); + check_neighbor( i + 1, j, k - 1 ); + check_neighbor( i + 1, j, k + 1 ); + check_neighbor( i, j - 1, k - 1 ); + check_neighbor( i, j - 1, k + 1 ); + check_neighbor( i, j + 1, k - 1 ); + check_neighbor( i, j + 1, k + 1 ); + + ++cellIdx; + } + + // Tests for the dual graph layer - with minCommonVertices override + mesh.initializeDualGraph( mesh, 1 ); + ASSERT_EQ( mesh.getNeighborCounts().getSize(), numberOfCells ); + cellIdx = 0; + for( IndexType k = 0; k < zSize; k++ ) + for( IndexType j = 0; j < ySize; j++ ) + for( IndexType i = 0; i < xSize; i++ ) + { + IndexType nnbrs = 26; + if( i == 0 || i == xSize - 1 ) + nnbrs -= 9; + if( j == 0 || j == ySize - 1 ) + nnbrs -= 9; + if( k == 0 || k == zSize - 1 ) + nnbrs -= 9; + if( (i == 0 || i == xSize - 1) && (j == 0 || j == ySize - 1) ) + nnbrs += 3; + if( (i == 0 || i == xSize - 1) && (k == 0 || k == zSize - 1) ) + nnbrs += 3; + if( (j == 0 || j == ySize - 1) && (k == 0 || k == zSize - 1) ) + nnbrs += 3; + if( (i == 0 || i == xSize - 1) && (j == 0 || j == ySize - 1) && (k == 0 || k == zSize - 1) ) + --nnbrs; + + EXPECT_EQ( mesh.getCellNeighborsCount( cellIdx ), nnbrs ); + std::set< IndexType > neighbors; + for( IndexType n = 0; n < nnbrs; n++ ) + neighbors.insert( mesh.getDualGraph().getRow( cellIdx ).getColumnIndex( n ) ); + + // the cell itself should not be its own neighbor + EXPECT_EQ( (IndexType) neighbors.count( cellIdx ), 0 ); + auto check_neighbor = [&]( IndexType i, IndexType j, IndexType k ) + { + if( i >= 0 && i < xSize && j >= 0 && j < ySize && k >= 0 && k < zSize ) { + EXPECT_EQ( (IndexType) neighbors.count( k * xSize * ySize + j * xSize + i ), 1 ); + } + }; + // check neighbors over face + check_neighbor( i - 1, j, k ); + check_neighbor( i + 1, j, k ); + check_neighbor( i, j - 1, k ); + check_neighbor( i, j + 1, k ); + check_neighbor( i, j, k - 1 ); + check_neighbor( i, j, k + 1 ); + // check neighbors over edge + check_neighbor( i - 1, j - 1, k ); + check_neighbor( i - 1, j + 1, k ); + check_neighbor( i + 1, j - 1, k ); + check_neighbor( i + 1, j + 1, k ); + check_neighbor( i - 1, j, k - 1 ); + check_neighbor( i - 1, j, k + 1 ); + check_neighbor( i + 1, j, k - 1 ); + check_neighbor( i + 1, j, k + 1 ); + check_neighbor( i, j - 1, k - 1 ); + check_neighbor( i, j - 1, k + 1 ); + check_neighbor( i, j + 1, k - 1 ); + check_neighbor( i, j + 1, k + 1 ); + // check neighbors over vertex + check_neighbor( i - 1, j - 1, k - 1 ); + check_neighbor( i - 1, j - 1, k + 1 ); + check_neighbor( i - 1, j + 1, k - 1 ); + check_neighbor( i - 1, j + 1, k + 1 ); + check_neighbor( i + 1, j - 1, k - 1 ); + check_neighbor( i + 1, j - 1, k + 1 ); + check_neighbor( i + 1, j + 1, k - 1 ); + check_neighbor( i + 1, j + 1, k + 1 ); + + ++cellIdx; + } + + // reset dual graph back to its default state + mesh.initializeDualGraph( mesh ); + testFinishedMesh( mesh ); } diff --git a/src/UnitTests/Meshes/MeshTraverserTest.h b/src/UnitTests/Meshes/MeshTraverserTest.h index b5b45f8e5558c2c7ee0944a492761fc9f9ac07c5..785bc40e78f2473f4a66c8a67552faca0737d53e 100644 --- a/src/UnitTests/Meshes/MeshTraverserTest.h +++ b/src/UnitTests/Meshes/MeshTraverserTest.h @@ -20,9 +20,7 @@ using RealType = double; using Device = Devices::Host; using IndexType = int; -// FIXME: Traverser does not work with Id = void -//class TestQuadrilateralMeshConfig : public DefaultConfig< Topologies::Quadrilateral > -class TestQuadrilateralMeshConfig : public DefaultConfig< Topologies::Quadrilateral, 2, double, int, int, int > +class TestQuadrilateralMeshConfig : public DefaultConfig< Topologies::Quadrilateral > { public: static constexpr bool entityStorage( int dimensions ) { return true; } @@ -31,9 +29,7 @@ public: template< typename EntityTopology > static constexpr bool superentityStorage( EntityTopology, int SuperentityDimensions ) { return true; } }; -// FIXME: Traverser does not work with Id = void -//class TestHexahedronMeshConfig : public DefaultConfig< Topologies::Hexahedron > -class TestHexahedronMeshConfig : public DefaultConfig< Topologies::Hexahedron, 3, double, int, int, int > +class TestHexahedronMeshConfig : public DefaultConfig< Topologies::Hexahedron > { public: static constexpr bool entityStorage( int dimensions ) { return true; } @@ -53,17 +49,19 @@ struct TestEntitiesProcessor }; template< typename EntityType, typename DeviceMeshPointer, typename HostArray > -void testCudaTraverser( const DeviceMeshPointer& deviceMeshPointer, - const HostArray& host_array_boundary, - const HostArray& host_array_interior, - const HostArray& host_array_all ) +void testTraverser( const DeviceMeshPointer& deviceMeshPointer, + const HostArray& host_array_boundary, + const HostArray& host_array_interior, + const HostArray& host_array_all ) { using MeshType = typename DeviceMeshPointer::ObjectType; + using DeviceType = typename MeshType::DeviceType; + static_assert( std::is_same< DeviceType, typename DeviceMeshPointer::DeviceType >::value, "devices must be the same" ); Traverser< MeshType, EntityType > traverser; - Containers::Array< int, Devices::Cuda > array_boundary( deviceMeshPointer->template getEntitiesCount< EntityType >() ); - Containers::Array< int, Devices::Cuda > array_interior( deviceMeshPointer->template getEntitiesCount< EntityType >() ); - Containers::Array< int, Devices::Cuda > array_all ( deviceMeshPointer->template getEntitiesCount< EntityType >() ); + Containers::Array< int, DeviceType > array_boundary( deviceMeshPointer->template getEntitiesCount< EntityType >() ); + Containers::Array< int, DeviceType > array_interior( deviceMeshPointer->template getEntitiesCount< EntityType >() ); + Containers::Array< int, DeviceType > array_all ( deviceMeshPointer->template getEntitiesCount< EntityType >() ); array_boundary.setValue( 0 ); array_interior.setValue( 0 ); @@ -76,6 +74,27 @@ void testCudaTraverser( const DeviceMeshPointer& deviceMeshPointer, EXPECT_EQ( array_boundary, host_array_boundary ); EXPECT_EQ( array_interior, host_array_interior ); EXPECT_EQ( array_all, host_array_all ); + + // test iteration methods: forAll, forBoundary, forInterior + array_boundary.setValue( 0 ); + array_interior.setValue( 0 ); + array_all .setValue( 0 ); + + auto view_boundary = array_boundary.getView(); + auto view_interior = array_interior.getView(); + auto view_all = array_all.getView(); + + auto f_boundary = [view_boundary] __cuda_callable__ ( typename MeshType::GlobalIndexType i ) mutable { view_boundary[i] += 1; }; + auto f_interior = [view_interior] __cuda_callable__ ( typename MeshType::GlobalIndexType i ) mutable { view_interior[i] += 1; }; + auto f_all = [view_all] __cuda_callable__ ( typename MeshType::GlobalIndexType i ) mutable { view_all[i] += 1; }; + + deviceMeshPointer->template forBoundary< EntityType::getEntityDimension() >( f_boundary ); + deviceMeshPointer->template forInterior< EntityType::getEntityDimension() >( f_interior ); + deviceMeshPointer->template forAll < EntityType::getEntityDimension() >( f_all ); + + EXPECT_EQ( array_boundary, host_array_boundary ); + EXPECT_EQ( array_interior, host_array_interior ); + EXPECT_EQ( array_all, host_array_all ); } TEST( MeshTest, RegularMeshOfQuadrilateralsTest ) @@ -128,11 +147,6 @@ TEST( MeshTest, RegularMeshOfQuadrilateralsTest ) ASSERT_TRUE( meshBuilder.build( *meshPointer ) ); - // traversers for all test cases - Traverser< TestQuadrilateralMesh, QuadrilateralMeshEntityType > traverser_cells; - Traverser< TestQuadrilateralMesh, EdgeMeshEntityType > traverser_edges; - Traverser< TestQuadrilateralMesh, VertexMeshEntityType > traverser_vertices; - // arrays for all test cases Containers::Array< int > array_cells_boundary( meshPointer->template getEntitiesCount< 2 >() ); Containers::Array< int > array_cells_interior( meshPointer->template getEntitiesCount< 2 >() ); @@ -159,75 +173,63 @@ TEST( MeshTest, RegularMeshOfQuadrilateralsTest ) array_vertices_interior.setValue( 0 ); array_vertices_all .setValue( 0 ); - // traverse for all test cases - traverser_cells.template processBoundaryEntities< TestEntitiesProcessor >( meshPointer, array_cells_boundary.getView() ); - traverser_cells.template processInteriorEntities< TestEntitiesProcessor >( meshPointer, array_cells_interior.getView() ); - traverser_cells.template processAllEntities < TestEntitiesProcessor >( meshPointer, array_cells_all.getView() ); - - traverser_edges.template processBoundaryEntities< TestEntitiesProcessor >( meshPointer, array_edges_boundary.getView() ); - traverser_edges.template processInteriorEntities< TestEntitiesProcessor >( meshPointer, array_edges_interior.getView() ); - traverser_edges.template processAllEntities < TestEntitiesProcessor >( meshPointer, array_edges_all.getView() ); - - traverser_vertices.template processBoundaryEntities< TestEntitiesProcessor >( meshPointer, array_vertices_boundary.getView() ); - traverser_vertices.template processInteriorEntities< TestEntitiesProcessor >( meshPointer, array_vertices_interior.getView() ); - traverser_vertices.template processAllEntities < TestEntitiesProcessor >( meshPointer, array_vertices_all.getView() ); - - // test traversing cells + // set expected values for( IndexType j = 0; j < ySize; j++ ) for( IndexType i = 0; i < xSize; i++ ) { const IndexType idx = j * xSize + i; if( j == 0 || j == ySize - 1 || i == 0 || i == xSize - 1 ) { - EXPECT_EQ( array_cells_boundary[ idx ], 1 ); - EXPECT_EQ( array_cells_interior[ idx ], 0 ); + array_cells_boundary[ idx ] = 1; + array_cells_interior[ idx ] = 0; } else { - EXPECT_EQ( array_cells_boundary[ idx ], 0 ); - EXPECT_EQ( array_cells_interior[ idx ], 1 ); + array_cells_boundary[ idx ] = 0; + array_cells_interior[ idx ] = 1; } - EXPECT_EQ( array_cells_all[ idx ], 1 ); + array_cells_all[ idx ] = 1; } - - // test traversing edges // (edges are not numbered systematically, so we just compare with isBoundaryEntity) for( IndexType idx = 0; idx < meshPointer->template getEntitiesCount< 1 >(); idx++ ) { if( meshPointer->template isBoundaryEntity< 1 >( idx ) ) { - EXPECT_EQ( array_edges_boundary[ idx ], 1 ); - EXPECT_EQ( array_edges_interior[ idx ], 0 ); + array_edges_boundary[ idx ] = 1; + array_edges_interior[ idx ] = 0; } else { - EXPECT_EQ( array_edges_boundary[ idx ], 0 ); - EXPECT_EQ( array_edges_interior[ idx ], 1 ); + array_edges_boundary[ idx ] = 0; + array_edges_interior[ idx ] = 1; } - EXPECT_EQ( array_edges_all[ idx ], 1 ); + array_edges_all[ idx ] = 1; } - - // test traversing vertices for( IndexType j = 0; j <= ySize; j++ ) for( IndexType i = 0; i <= xSize; i++ ) { const IndexType idx = j * (xSize + 1) + i; if( j == 0 || j == ySize || i == 0 || i == xSize ) { - EXPECT_EQ( array_vertices_boundary[ idx ], 1 ); - EXPECT_EQ( array_vertices_interior[ idx ], 0 ); + array_vertices_boundary[ idx ] = 1; + array_vertices_interior[ idx ] = 0; } else { - EXPECT_EQ( array_vertices_boundary[ idx ], 0 ); - EXPECT_EQ( array_vertices_interior[ idx ], 1 ); + array_vertices_boundary[ idx ] = 0; + array_vertices_interior[ idx ] = 1; } - EXPECT_EQ( array_vertices_all[ idx ], 1 ); + array_vertices_all[ idx ] = 1; } + // test traverser with host + testTraverser< QuadrilateralMeshEntityType >( meshPointer, array_cells_boundary, array_cells_interior, array_cells_all ); + testTraverser< EdgeMeshEntityType >( meshPointer, array_edges_boundary, array_edges_interior, array_edges_all ); + testTraverser< VertexMeshEntityType >( meshPointer, array_vertices_boundary, array_vertices_interior, array_vertices_all ); + // test traverser with CUDA #ifdef HAVE_CUDA using DeviceMesh = Mesh< TestQuadrilateralMeshConfig, Devices::Cuda >; Pointers::SharedPointer< DeviceMesh > deviceMeshPointer; *deviceMeshPointer = *meshPointer; - testCudaTraverser< QuadrilateralMeshEntityType >( deviceMeshPointer, array_cells_boundary, array_cells_interior, array_cells_all ); - testCudaTraverser< EdgeMeshEntityType >( deviceMeshPointer, array_edges_boundary, array_edges_interior, array_edges_all ); - testCudaTraverser< VertexMeshEntityType >( deviceMeshPointer, array_vertices_boundary, array_vertices_interior, array_vertices_all ); + testTraverser< QuadrilateralMeshEntityType >( deviceMeshPointer, array_cells_boundary, array_cells_interior, array_cells_all ); + testTraverser< EdgeMeshEntityType >( deviceMeshPointer, array_edges_boundary, array_edges_interior, array_edges_all ); + testTraverser< VertexMeshEntityType >( deviceMeshPointer, array_vertices_boundary, array_vertices_interior, array_vertices_all ); #endif } @@ -293,12 +295,6 @@ TEST( MeshTest, RegularMeshOfHexahedronsTest ) ASSERT_TRUE( meshBuilder.build( *meshPointer ) ); - // traversers for all test cases - Traverser< TestHexahedronMesh, HexahedronMeshEntityType > traverser_cells; - Traverser< TestHexahedronMesh, QuadrilateralMeshEntityType > traverser_faces; - Traverser< TestHexahedronMesh, EdgeMeshEntityType > traverser_edges; - Traverser< TestHexahedronMesh, VertexMeshEntityType > traverser_vertices; - // arrays for all test cases Containers::Array< int > array_cells_boundary( meshPointer->template getEntitiesCount< 3 >() ); Containers::Array< int > array_cells_interior( meshPointer->template getEntitiesCount< 3 >() ); @@ -333,97 +329,80 @@ TEST( MeshTest, RegularMeshOfHexahedronsTest ) array_vertices_interior.setValue( 0 ); array_vertices_all .setValue( 0 ); - // traverse for all test cases - traverser_cells.template processBoundaryEntities< TestEntitiesProcessor >( meshPointer, array_cells_boundary.getView() ); - traverser_cells.template processInteriorEntities< TestEntitiesProcessor >( meshPointer, array_cells_interior.getView() ); - traverser_cells.template processAllEntities < TestEntitiesProcessor >( meshPointer, array_cells_all.getView() ); - - traverser_faces.template processBoundaryEntities< TestEntitiesProcessor >( meshPointer, array_faces_boundary.getView() ); - traverser_faces.template processInteriorEntities< TestEntitiesProcessor >( meshPointer, array_faces_interior.getView() ); - traverser_faces.template processAllEntities < TestEntitiesProcessor >( meshPointer, array_faces_all.getView() ); - - traverser_edges.template processBoundaryEntities< TestEntitiesProcessor >( meshPointer, array_edges_boundary.getView() ); - traverser_edges.template processInteriorEntities< TestEntitiesProcessor >( meshPointer, array_edges_interior.getView() ); - traverser_edges.template processAllEntities < TestEntitiesProcessor >( meshPointer, array_edges_all.getView() ); - - traverser_vertices.template processBoundaryEntities< TestEntitiesProcessor >( meshPointer, array_vertices_boundary.getView() ); - traverser_vertices.template processInteriorEntities< TestEntitiesProcessor >( meshPointer, array_vertices_interior.getView() ); - traverser_vertices.template processAllEntities < TestEntitiesProcessor >( meshPointer, array_vertices_all.getView() ); - - // test traversing cells + // set expected values for( IndexType k = 0; k < zSize; k++ ) for( IndexType j = 0; j < ySize; j++ ) for( IndexType i = 0; i < xSize; i++ ) { const IndexType idx = k * xSize * ySize + j * xSize + i; if( k == 0 || k == zSize - 1 || j == 0 || j == ySize - 1 || i == 0 || i == xSize - 1 ) { - EXPECT_EQ( array_cells_boundary[ idx ], 1 ); - EXPECT_EQ( array_cells_interior[ idx ], 0 ); + array_cells_boundary[ idx ] = 1; + array_cells_interior[ idx ] = 0; } else { - EXPECT_EQ( array_cells_boundary[ idx ], 0 ); - EXPECT_EQ( array_cells_interior[ idx ], 1 ); + array_cells_boundary[ idx ] = 0; + array_cells_interior[ idx ] = 1; } - EXPECT_EQ( array_cells_all[ idx ], 1 ); + array_cells_all[ idx ] = 1; } - - // test traversing faces // (faces are not numbered systematically, so we just compare with isBoundaryEntity) for( IndexType idx = 0; idx < meshPointer->template getEntitiesCount< 2 >(); idx++ ) { if( meshPointer->template isBoundaryEntity< 2 >( idx ) ) { - EXPECT_EQ( array_faces_boundary[ idx ], 1 ); - EXPECT_EQ( array_faces_interior[ idx ], 0 ); + array_faces_boundary[ idx ] = 1; + array_faces_interior[ idx ] = 0; } else { - EXPECT_EQ( array_faces_boundary[ idx ], 0 ); - EXPECT_EQ( array_faces_interior[ idx ], 1 ); + array_faces_boundary[ idx ] = 0; + array_faces_interior[ idx ] = 1; } - EXPECT_EQ( array_faces_all[ idx ], 1 ); + array_faces_all[ idx ] = 1; } - - // test traversing edges // (edges are not numbered systematically, so we just compare with isBoundaryEntity) for( IndexType idx = 0; idx < meshPointer->template getEntitiesCount< 1 >(); idx++ ) { if( meshPointer->template isBoundaryEntity< 1 >( idx ) ) { - EXPECT_EQ( array_edges_boundary[ idx ], 1 ); - EXPECT_EQ( array_edges_interior[ idx ], 0 ); + array_edges_boundary[ idx ] = 1; + array_edges_interior[ idx ] = 0; } else { - EXPECT_EQ( array_edges_boundary[ idx ], 0 ); - EXPECT_EQ( array_edges_interior[ idx ], 1 ); + array_edges_boundary[ idx ] = 0; + array_edges_interior[ idx ] = 1; } - EXPECT_EQ( array_edges_all[ idx ], 1 ); + array_edges_all[ idx ] = 1; } - - // test traversing vertices for( IndexType k = 0; k <= zSize; k++ ) for( IndexType j = 0; j <= ySize; j++ ) for( IndexType i = 0; i <= xSize; i++ ) { const IndexType idx = k * (xSize + 1) * (ySize + 1) + j * (xSize + 1) + i; if( k == 0 || k == zSize || j == 0 || j == ySize || i == 0 || i == xSize ) { - EXPECT_EQ( array_vertices_boundary[ idx ], 1 ); - EXPECT_EQ( array_vertices_interior[ idx ], 0 ); + array_vertices_boundary[ idx ] = 1; + array_vertices_interior[ idx ] = 0; } else { - EXPECT_EQ( array_vertices_boundary[ idx ], 0 ); - EXPECT_EQ( array_vertices_interior[ idx ], 1 ); + array_vertices_boundary[ idx ] = 0; + array_vertices_interior[ idx ] = 1; } - EXPECT_EQ( array_vertices_all[ idx ], 1 ); + array_vertices_all[ idx ] = 1; } + // test traverser with host + testTraverser< HexahedronMeshEntityType >( meshPointer, array_cells_boundary, array_cells_interior, array_cells_all ); + testTraverser< QuadrilateralMeshEntityType >( meshPointer, array_faces_boundary, array_faces_interior, array_faces_all ); + testTraverser< EdgeMeshEntityType >( meshPointer, array_edges_boundary, array_edges_interior, array_edges_all ); + testTraverser< VertexMeshEntityType >( meshPointer, array_vertices_boundary, array_vertices_interior, array_vertices_all ); + // test traverser with CUDA #ifdef HAVE_CUDA using DeviceMesh = Mesh< TestHexahedronMeshConfig, Devices::Cuda >; Pointers::SharedPointer< DeviceMesh > deviceMeshPointer; *deviceMeshPointer = *meshPointer; - testCudaTraverser< HexahedronMeshEntityType >( deviceMeshPointer, array_cells_boundary, array_cells_interior, array_cells_all ); - testCudaTraverser< QuadrilateralMeshEntityType >( deviceMeshPointer, array_faces_boundary, array_faces_interior, array_faces_all ); - testCudaTraverser< EdgeMeshEntityType >( deviceMeshPointer, array_edges_boundary, array_edges_interior, array_edges_all ); - testCudaTraverser< VertexMeshEntityType >( deviceMeshPointer, array_vertices_boundary, array_vertices_interior, array_vertices_all ); + testTraverser< HexahedronMeshEntityType >( deviceMeshPointer, array_cells_boundary, array_cells_interior, array_cells_all ); + testTraverser< QuadrilateralMeshEntityType >( deviceMeshPointer, array_faces_boundary, array_faces_interior, array_faces_all ); + testTraverser< EdgeMeshEntityType >( deviceMeshPointer, array_edges_boundary, array_edges_interior, array_edges_all ); + testTraverser< VertexMeshEntityType >( deviceMeshPointer, array_vertices_boundary, array_vertices_interior, array_vertices_all ); #endif } diff --git a/src/UnitTests/main_mpi.h b/src/UnitTests/main_mpi.h index 9fe75c8505625ed92e6c07d2014ec84a3fe3398f..0f8f4b059a119fb42d5bbb78be01540a50cbdf9b 100644 --- a/src/UnitTests/main_mpi.h +++ b/src/UnitTests/main_mpi.h @@ -8,7 +8,7 @@ #if (defined(HAVE_GTEST) && defined(HAVE_MPI)) #include #include -using CommunicatorType = Communicators::MpiCommunicator; +using CommunicatorType = TNL::Communicators::MpiCommunicator; #include @@ -58,7 +58,7 @@ int main( int argc, char* argv[] ) delete listeners.Release(listeners.default_result_printer()); listeners.Append(new MinimalistBufferedPrinter); - Communicators::ScopedInitializer< CommunicatorType > mpi(argc, argv); + TNL::Communicators::ScopedInitializer< CommunicatorType > mpi(argc, argv); #endif return RUN_ALL_TESTS(); #else