From 604ad17204caef00b3cc43d62f7495a3177d1f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Wed, 11 Mar 2020 11:34:56 +0100 Subject: [PATCH 01/73] Added getOutwardNormalVector.h --- .../Meshes/Geometry/getOutwardNormalVector.h | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 src/TNL/Meshes/Geometry/getOutwardNormalVector.h diff --git a/src/TNL/Meshes/Geometry/getOutwardNormalVector.h b/src/TNL/Meshes/Geometry/getOutwardNormalVector.h new file mode 100644 index 000000000..536800862 --- /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 -- GitLab From 271ca3480718abe697befca86601df3a823a8dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Thu, 19 Mar 2020 21:56:10 +0100 Subject: [PATCH 02/73] Grids: added public getters for numberOfNxFaces and numberOfNxAndNyFaces --- src/TNL/Meshes/GridDetails/Grid2D.h | 6 ++++++ src/TNL/Meshes/GridDetails/Grid2D_impl.h | 11 +++++++++++ src/TNL/Meshes/GridDetails/Grid3D.h | 12 ++++++++++++ src/TNL/Meshes/GridDetails/Grid3D_impl.h | 22 ++++++++++++++++++++++ 4 files changed, 51 insertions(+) diff --git a/src/TNL/Meshes/GridDetails/Grid2D.h b/src/TNL/Meshes/GridDetails/Grid2D.h index 3d7335053..3fd9194bd 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 4f9f8fdd5..3d8a4c194 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 7e3f0d5e8..91357ba12 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 dd91a7ed8..0b1f86d6b 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 > -- GitLab From a234eefefc2d2dc80d96f02722ddc22387c28fd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Wed, 11 Mar 2020 11:33:20 +0100 Subject: [PATCH 03/73] Refactoring VTKWriter to support the binary format Fixes #6 --- src/TNL/Endianness.h | 70 +++++ src/TNL/Functions/MeshFunctionVTKWriter.h | 38 ++- src/TNL/Meshes/Writers/VTKWriter.h | 20 +- src/TNL/Meshes/Writers/VTKWriter_impl.h | 306 +++++++++++++++------- 4 files changed, 324 insertions(+), 110 deletions(-) create mode 100644 src/TNL/Endianness.h diff --git a/src/TNL/Endianness.h b/src/TNL/Endianness.h new file mode 100644 index 000000000..c2cfc19d9 --- /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/Functions/MeshFunctionVTKWriter.h b/src/TNL/Functions/MeshFunctionVTKWriter.h index 10dd997a8..be0a9ee95 100644 --- a/src/TNL/Functions/MeshFunctionVTKWriter.h +++ b/src/TNL/Functions/MeshFunctionVTKWriter.h @@ -15,7 +15,8 @@ namespace TNL { namespace Functions { -template< typename MeshFunction > +template< typename MeshFunction, + bool = std::is_fundamental< typename MeshFunction::RealType >::value > class MeshFunctionVTKWriter { using MeshType = typename MeshFunction::MeshType; @@ -26,11 +27,12 @@ class MeshFunctionVTKWriter public: static bool write( const MeshFunction& function, std::ostream& str, - const String& functionName = "cellFunctionValues" ) + const String& functionName = "cellFunctionValues", + Meshes::Writers::VTKFileFormat format = Meshes::Writers::VTKFileFormat::ASCII ) { const MeshType& mesh = function.getMesh(); - MeshWriter::template writeEntities< MeshFunction::getEntitiesDimension() >( mesh, str ); - appendFunction( function, str, functionName ); + MeshWriter::template writeEntities< MeshFunction::getEntitiesDimension() >( mesh, str, format ); + appendFunction( function, str, format, functionName ); return true; } @@ -39,6 +41,7 @@ public: // with different function name. static void appendFunction( const MeshFunction& function, std::ostream& str, + Meshes::Writers::VTKFileFormat format, const String& functionName ) { const MeshType& mesh = function.getMesh(); @@ -47,10 +50,35 @@ public: 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"; + const typename MeshFunction::RealType value = function.getData().getElement( i ); + using Meshes::Writers::__impl::writeReal; + writeReal( format, str, value ); + if( format == Meshes::Writers::VTKFileFormat::ASCII ) + str << "\n"; } } }; +template< typename MeshFunction > +class MeshFunctionVTKWriter< MeshFunction, false > +{ +public: + static bool write( const MeshFunction& function, + std::ostream& str, + const String& functionName = "cellFunctionValues", + Meshes::Writers::VTKFileFormat format = Meshes::Writers::VTKFileFormat::ASCII ) + { + throw std::logic_error( "Unsupported RealType - VTKWriter supports only fundamental types." ); + } + + static void appendFunction( const MeshFunction& function, + std::ostream& str, + Meshes::Writers::VTKFileFormat format, + const String& functionName ) + { + throw std::logic_error( "Unsupported RealType - VTKWriter supports only fundamental types." ); + } +}; + } // namespace Functions } // namespace TNL diff --git a/src/TNL/Meshes/Writers/VTKWriter.h b/src/TNL/Meshes/Writers/VTKWriter.h index 4d5ad00e8..0aa9dd1b7 100644 --- a/src/TNL/Meshes/Writers/VTKWriter.h +++ b/src/TNL/Meshes/Writers/VTKWriter.h @@ -19,8 +19,20 @@ namespace TNL { namespace Meshes { namespace Writers { +enum class VTKFileFormat +{ + ASCII, + BINARY +}; + namespace __impl { +// TODO: 64-bit integers are most likely not supported in the BINARY format +inline void writeInt( VTKFileFormat format, std::ostream& str, int value ); + +template< typename Real > +void writeReal( VTKFileFormat format, std::ostream& str, Real value ); + template< typename Mesh, int EntityDimension > struct MeshEntitiesVTKWriter; template< typename Mesh, int EntityDimension > struct MeshEntityTypesVTKWriter; @@ -42,15 +54,15 @@ class VTKWriter public: using Index = typename Mesh::GlobalIndexType; - static void writeAllEntities( const Mesh& mesh, std::ostream& str ); + static void writeAllEntities( const Mesh& mesh, std::ostream& str, VTKFileFormat format = VTKFileFormat::ASCII ); template< int EntityDimension = Mesh::getMeshDimension() > - static void writeEntities( const Mesh& mesh, std::ostream& str ); + static void writeEntities( const Mesh& mesh, std::ostream& str, VTKFileFormat format = VTKFileFormat::ASCII ); protected: - static void writeHeader( const Mesh& mesh, std::ostream& str ); + static void writeHeader( const Mesh& mesh, std::ostream& str, VTKFileFormat format ); - static void writePoints( const Mesh& mesh, std::ostream& str ); + static void writePoints( const Mesh& mesh, std::ostream& str, VTKFileFormat format ); }; } // namespace Writers diff --git a/src/TNL/Meshes/Writers/VTKWriter_impl.h b/src/TNL/Meshes/Writers/VTKWriter_impl.h index 83cf95ec4..69108a7bf 100644 --- a/src/TNL/Meshes/Writers/VTKWriter_impl.h +++ b/src/TNL/Meshes/Writers/VTKWriter_impl.h @@ -12,6 +12,7 @@ #include +#include #include #include @@ -36,6 +37,33 @@ struct has_entity_topology< T, typename enable_if_type< typename T::EntityTopolo {}; +inline void +writeInt( VTKFileFormat format, std::ostream& str, int value ) +{ + if( format == VTKFileFormat::BINARY ) { + value = forceBigEndian( value ); + str.write( reinterpret_cast(&value), sizeof(int) ); + } + else { + str << value << " "; + } +} + +template< typename Real > +void +writeReal( VTKFileFormat format, std::ostream& str, Real value ) +{ + if( format == VTKFileFormat::BINARY ) { + value = forceBigEndian( value ); + str.write( reinterpret_cast(&value), sizeof(Real) ); + } + else { + str.precision( std::numeric_limits< Real >::digits10 ); + str << value << " "; + } +} + + template< typename Entity, bool _is_mesh_entity = has_entity_topology< Entity >::value > struct VerticesPerEntity @@ -127,7 +155,7 @@ getCellsListSize( const Mesh& mesh, DimensionTag = DimensionTag() ) template< typename Mesh, int EntityDimension > struct MeshEntitiesVTKWriter { - static void exec( const Mesh& mesh, std::ostream& str ) + static void exec( const Mesh& mesh, std::ostream& str, VTKFileFormat format ) { using EntityType = typename Mesh::template EntityType< EntityDimension >; using Index = typename Mesh::GlobalIndexType; @@ -136,10 +164,11 @@ struct MeshEntitiesVTKWriter const Index verticesPerEntity = VerticesPerEntity< EntityType >::count;; for( Index i = 0; i < entitiesCount; i++ ) { const auto& entity = mesh.template getEntity< EntityType >( i ); - str << verticesPerEntity; + writeInt( format, str, verticesPerEntity ); for( Index j = 0; j < verticesPerEntity; j++ ) - str << " " << entity.template getSubentityIndex< 0 >( j ); - str << "\n"; + writeInt( format, str, entity.template getSubentityIndex< 0 >( j ) ); + if( format == VTKFileFormat::ASCII ) + str << "\n"; } } }; @@ -148,15 +177,19 @@ struct MeshEntitiesVTKWriter template< typename Mesh > struct MeshEntitiesVTKWriter< Mesh, 0 > { - static void exec( const Mesh& mesh, std::ostream& str ) + static void exec( const Mesh& mesh, std::ostream& str, VTKFileFormat 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++ ) { - str << verticesPerEntity << " " << i << "\n"; + for( Index i = 0; i < entitiesCount; i++ ) + { + writeInt( format, str, verticesPerEntity ); + writeInt( format, str, i ); + if( format == VTKFileFormat::ASCII ) + str << "\n"; } } }; @@ -169,10 +202,16 @@ 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 ) + static void exec( const MeshType& mesh, std::ostream& str, VTKFileFormat format ) { for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) - str << "2 " << i << " " << i+1 << "\n"; + { + writeInt( format, str, 2 ); + writeInt( format, str, i ); + writeInt( format, str, i+1 ); + if( format == VTKFileFormat::ASCII ) + str << "\n"; + } } }; @@ -184,10 +223,15 @@ 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 ) + static void exec( const MeshType& mesh, std::ostream& str, VTKFileFormat format ) { for( MeshIndex i = 0; i < mesh.getDimensions().x() + 1; i++ ) - str << "1 " << i << "\n"; + { + writeInt( format, str, 1 ); + writeInt( format, str, i ); + if( format == VTKFileFormat::ASCII ) + str << "\n"; + } } }; @@ -199,14 +243,19 @@ 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 ) + static void exec( const MeshType& mesh, std::ostream& str, VTKFileFormat format ) { 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"; + 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 == VTKFileFormat::ASCII ) + str << "\n"; + } } }; @@ -218,17 +267,27 @@ 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 ) + static void exec( const MeshType& mesh, std::ostream& str, VTKFileFormat format ) { 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 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 == VTKFileFormat::ASCII ) + str << "\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"; + 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 == VTKFileFormat::ASCII ) + str << "\n"; + } } }; @@ -240,11 +299,16 @@ 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 ) + static void exec( const MeshType& mesh, std::ostream& str, VTKFileFormat format ) { 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"; + for( MeshIndex i = 0; i < ( mesh.getDimensions().x() + 1 ); i++ ) + { + writeInt( format, str, 1 ); + writeInt( format, str, j * mesh.getDimensions().x() + i ); + if( format == VTKFileFormat::ASCII ) + str << "\n"; + } } }; @@ -256,19 +320,24 @@ 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 ) + static void exec( const MeshType& mesh, std::ostream& str, VTKFileFormat 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++ ) - 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"; + 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 == VTKFileFormat::ASCII ) + str << "\n"; + } } }; @@ -280,31 +349,46 @@ 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 ) + static void exec( const MeshType& mesh, std::ostream& str, VTKFileFormat 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++ ) - 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 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 == VTKFileFormat::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++ ) - 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 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 == VTKFileFormat::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++ ) - 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"; + 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 == VTKFileFormat::ASCII ) + str << "\n"; + } } }; @@ -316,25 +400,40 @@ 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 ) + static void exec( const MeshType& mesh, std::ostream& str, VTKFileFormat 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++ ) - 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 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 == VTKFileFormat::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++ ) - 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 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 == VTKFileFormat::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++ ) - 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"; + 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 == VTKFileFormat::ASCII ) + str << "\n"; + } } }; @@ -346,12 +445,17 @@ 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 ) + static void exec( const MeshType& mesh, std::ostream& str, VTKFileFormat 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++ ) - str << "1 " << k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i << "\n"; + 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 == VTKFileFormat::ASCII ) + str << "\n"; + } } }; @@ -360,7 +464,7 @@ struct MeshEntitiesVTKWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 0 template< typename Mesh, int EntityDimension > struct MeshEntityTypesVTKWriter { - static void exec( const Mesh& mesh, std::ostream& str ) + static void exec( const Mesh& mesh, std::ostream& str, VTKFileFormat format ) { using EntityType = typename Mesh::template EntityType< EntityDimension >; using Index = typename Mesh::GlobalIndexType; @@ -368,7 +472,9 @@ struct MeshEntityTypesVTKWriter 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"; + writeInt( format, str, type ); + if( format == VTKFileFormat::ASCII ) + str << "\n"; } } }; @@ -382,14 +488,16 @@ struct MeshEntityTypesVTKWriter< Grid< Dimension, MeshReal, Device, MeshIndex >, { using MeshType = Grid< Dimension, MeshReal, Device, MeshIndex >; - static void exec( const MeshType& mesh, std::ostream& str ) + static void exec( const MeshType& mesh, std::ostream& str, VTKFileFormat 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) __impl::GridEntityShape< EntityType >::shape; - str << type << "\n"; + writeInt( format, str, type ); + if( format == VTKFileFormat::ASCII ) + str << "\n"; } } }; @@ -398,28 +506,28 @@ struct MeshEntityTypesVTKWriter< Grid< Dimension, MeshReal, Device, MeshIndex >, template< typename Mesh > void -VTKWriter< Mesh >::writeAllEntities( const Mesh& mesh, std::ostream& str ) +VTKWriter< Mesh >::writeAllEntities( const Mesh& mesh, std::ostream& str, VTKFileFormat format ) { - writeHeader( mesh, str ); - writePoints( mesh, str ); + writeHeader( mesh, str, format ); + writePoints( mesh, str, format ); 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 ); + Algorithms::TemplateStaticFor< int, 0, Mesh::getMeshDimension() + 1, EntitiesWriter >::exec( mesh, str, format ); str << std::endl << "CELL_TYPES " << allEntitiesCount << std::endl; - Algorithms::TemplateStaticFor< int, 0, Mesh::getMeshDimension() + 1, EntityTypesWriter >::exec( mesh, str ); + Algorithms::TemplateStaticFor< int, 0, Mesh::getMeshDimension() + 1, EntityTypesWriter >::exec( mesh, str, format ); } template< typename Mesh > template< int EntityDimension > void -VTKWriter< Mesh >::writeEntities( const Mesh& mesh, std::ostream& str ) +VTKWriter< Mesh >::writeEntities( const Mesh& mesh, std::ostream& str, VTKFileFormat format ) { - writeHeader( mesh, str ); - writePoints( mesh, str ); + writeHeader( mesh, str, format ); + writePoints( mesh, str, format ); using EntityType = typename Mesh::template EntityType< EntityDimension >; const Index entitiesCount = mesh.template getEntitiesCount< EntityType >(); @@ -427,43 +535,39 @@ VTKWriter< Mesh >::writeEntities( const Mesh& mesh, std::ostream& str ) const Index cellsListSize = entitiesCount * ( verticesPerEntity + 1 ); str << std::endl << "CELLS " << entitiesCount << " " << cellsListSize << std::endl; - EntitiesWriter< EntityDimension >::exec( mesh, str ); + EntitiesWriter< EntityDimension >::exec( mesh, str, format ); str << std::endl << "CELL_TYPES " << entitiesCount << std::endl; - EntityTypesWriter< EntityDimension >::exec( mesh, str ); + EntityTypesWriter< EntityDimension >::exec( mesh, str, format ); } template< typename Mesh > void -VTKWriter< Mesh >::writeHeader( const Mesh& mesh, std::ostream& str ) +VTKWriter< Mesh >::writeHeader( const Mesh& mesh, std::ostream& str, VTKFileFormat format ) { str << "# vtk DataFile Version 2.0\n" << "TNL DATA\n" - << "ASCII\n" + << ((format == VTKFileFormat::ASCII) ? "ASCII\n" : "BINARY\n") << "DATASET UNSTRUCTURED_GRID\n"; } template< typename Mesh > void -VTKWriter< Mesh >::writePoints( const Mesh& mesh, std::ostream& str ) +VTKWriter< Mesh >::writePoints( const Mesh& mesh, std::ostream& str, VTKFileFormat format ) { + using __impl::writeReal; 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 << " "; - } + for( Index j = 0; j < point.getSize(); j++ ) + writeReal( format, str, point[ j ] ); // VTK needs zeros for unused dimensions for( Index j = 0; j < 3 - point.getSize(); j++ ) - str << " 0"; - str << "\n"; + writeReal( format, str, (typename Mesh::PointType::RealType) 0 ); + if( format == VTKFileFormat::ASCII ) + str << "\n"; } } -- GitLab From f6fb8bd31e4bc64edbecd966a1a407e381444416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Wed, 18 Mar 2020 19:34:32 +0100 Subject: [PATCH 04/73] Refactoring VTKWriter --- src/TNL/Functions/MeshFunction.hpp | 6 +- src/TNL/Functions/MeshFunctionVTKWriter.h | 56 ++++----- src/TNL/Functions/VectorField.h | 6 +- src/TNL/Functions/VectorFieldVTKWriter.h | 37 +++--- src/TNL/Meshes/Writers/VTKWriter.h | 53 +++++++-- .../{VTKWriter_impl.h => VTKWriter.hpp} | 106 ++++++++++++++---- src/Tools/tnl-mesh-converter.cpp | 3 +- 7 files changed, 183 insertions(+), 84 deletions(-) rename src/TNL/Meshes/Writers/{VTKWriter_impl.h => VTKWriter.hpp} (83%) diff --git a/src/TNL/Functions/MeshFunction.hpp b/src/TNL/Functions/MeshFunction.hpp index 028bc19df..df8bd7ddf 100644 --- a/src/TNL/Functions/MeshFunction.hpp +++ b/src/TNL/Functions/MeshFunction.hpp @@ -405,8 +405,10 @@ 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" ) { + MeshFunctionVTKWriter< MeshFunction > writer( file ); + writer.write( *this ); + } 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 index be0a9ee95..c2c22a3e2 100644 --- a/src/TNL/Functions/MeshFunctionVTKWriter.h +++ b/src/TNL/Functions/MeshFunctionVTKWriter.h @@ -18,44 +18,36 @@ namespace Functions { template< typename MeshFunction, bool = std::is_fundamental< typename MeshFunction::RealType >::value > class MeshFunctionVTKWriter +: protected Meshes::Writers::VTKWriter< typename MeshFunction::MeshType > { 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", - Meshes::Writers::VTKFileFormat format = Meshes::Writers::VTKFileFormat::ASCII ) + MeshFunctionVTKWriter( std::ostream& str, + Meshes::Writers::VTKFileFormat format = Meshes::Writers::VTKFileFormat::ASCII ) + : Meshes::Writers::VTKWriter< MeshType >( str, format ) + {} + + void write( const MeshFunction& function, + const String& functionName = "cellFunctionValues" ) { const MeshType& mesh = function.getMesh(); - MeshWriter::template writeEntities< MeshFunction::getEntitiesDimension() >( mesh, str, format ); - appendFunction( function, str, format, functionName ); - return true; + this->template writeEntities< MeshFunction::getEntitiesDimension() >( mesh ); + appendFunction( function, functionName ); } // 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, - Meshes::Writers::VTKFileFormat format, - const String& functionName ) + void appendFunction( const MeshFunction& function, + 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++ ) { - const typename MeshFunction::RealType value = function.getData().getElement( i ); - using Meshes::Writers::__impl::writeReal; - writeReal( format, str, value ); - if( format == Meshes::Writers::VTKFileFormat::ASCII ) - str << "\n"; - } + if( MeshFunction::getEntitiesDimension() == 0 ) + this->writeDataArray( function.getData(), functionName, 1, Meshes::Writers::VTKDataType::PointData ); + else + this->writeDataArray( function.getData(), functionName, 1, Meshes::Writers::VTKDataType::CellData ); } }; @@ -63,18 +55,18 @@ template< typename MeshFunction > class MeshFunctionVTKWriter< MeshFunction, false > { public: - static bool write( const MeshFunction& function, - std::ostream& str, - const String& functionName = "cellFunctionValues", - Meshes::Writers::VTKFileFormat format = Meshes::Writers::VTKFileFormat::ASCII ) + MeshFunctionVTKWriter( std::ostream& str, + Meshes::Writers::VTKFileFormat format = Meshes::Writers::VTKFileFormat::ASCII ) + {} + + bool write( const MeshFunction& function, + const String& functionName = "cellFunctionValues" ) { throw std::logic_error( "Unsupported RealType - VTKWriter supports only fundamental types." ); } - static void appendFunction( const MeshFunction& function, - std::ostream& str, - Meshes::Writers::VTKFileFormat format, - const String& functionName ) + void appendFunction( const MeshFunction& function, + const String& functionName ) { throw std::logic_error( "Unsupported RealType - VTKWriter supports only fundamental types." ); } diff --git a/src/TNL/Functions/VectorField.h b/src/TNL/Functions/VectorField.h index e9a99113e..6765aae06 100644 --- a/src/TNL/Functions/VectorField.h +++ b/src/TNL/Functions/VectorField.h @@ -504,8 +504,10 @@ 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" ) { + VectorFieldVTKWriter< VectorField > writer( file ); + writer.write( *this ); + } else if( format == "gnuplot" ) return VectorFieldGnuplotWriter< VectorField >::write( *this, file ); else { diff --git a/src/TNL/Functions/VectorFieldVTKWriter.h b/src/TNL/Functions/VectorFieldVTKWriter.h index c3ccc4b94..a80943ac3 100644 --- a/src/TNL/Functions/VectorFieldVTKWriter.h +++ b/src/TNL/Functions/VectorFieldVTKWriter.h @@ -17,6 +17,7 @@ namespace Functions { template< typename VectorField > class VectorFieldVTKWriter +: protected Meshes::Writers::VTKWriter< typename VectorField::MeshType > { using MeshType = typename VectorField::MeshType; using MeshWriter = Meshes::Writers::VTKWriter< MeshType >; @@ -24,34 +25,44 @@ class VectorFieldVTKWriter using GlobalIndex = typename MeshType::GlobalIndexType; public: - static bool write( const VectorField& field, - std::ostream& str, - const String& fieldName = "cellVectorFieldValues" ) + VectorFieldVTKWriter( std::ostream& str, + Meshes::Writers::VTKFileFormat format = Meshes::Writers::VTKFileFormat::ASCII ) + : Meshes::Writers::VTKWriter< MeshType >( str, format ) + {} + + void write( const VectorField& field, + const String& fieldName = "cellVectorFieldValues" ) { const MeshType& mesh = field.getMesh(); - MeshWriter::template writeEntities< VectorField::getEntitiesDimension() >( mesh, str ); - appendField( field, str, fieldName ); - return true; + this->template writeEntities< VectorField::getEntitiesDimension() >( mesh ); + appendField( field, fieldName ); } // 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 ) + void appendField( const VectorField& field, + 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; + + // copy all values from the vector field into a contiguous array + using BufferType = Containers::Array< typename VectorField::RealType, Devices::Host, GlobalIndex >; + BufferType buffer( 3 * entitiesCount ); + GlobalIndex j = 0; 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"; + buffer[ j++ ] = ( i < vector.getSize() ? vector[ i ] : 0 ); } + + // write the buffer + if( VectorField::getEntitiesDimension() == 0 ) + this->writeDataArray( buffer, fieldName, 3, Meshes::Writers::VTKDataType::PointData ); + else + this->writeDataArray( buffer, fieldName, 3, Meshes::Writers::VTKDataType::CellData ); } }; diff --git a/src/TNL/Meshes/Writers/VTKWriter.h b/src/TNL/Meshes/Writers/VTKWriter.h index 0aa9dd1b7..83e8a8e62 100644 --- a/src/TNL/Meshes/Writers/VTKWriter.h +++ b/src/TNL/Meshes/Writers/VTKWriter.h @@ -25,13 +25,13 @@ enum class VTKFileFormat BINARY }; -namespace __impl { - -// TODO: 64-bit integers are most likely not supported in the BINARY format -inline void writeInt( VTKFileFormat format, std::ostream& str, int value ); +enum class VTKDataType +{ + CellData, + PointData +}; -template< typename Real > -void writeReal( VTKFileFormat format, std::ostream& str, Real value ); +namespace __impl { template< typename Mesh, int EntityDimension > struct MeshEntitiesVTKWriter; template< typename Mesh, int EntityDimension > struct MeshEntityTypesVTKWriter; @@ -52,21 +52,50 @@ class VTKWriter using EntityTypesWriter = __impl::MeshEntityTypesVTKWriter< Mesh, EntityDimension >; public: - using Index = typename Mesh::GlobalIndexType; + using IndexType = typename Mesh::GlobalIndexType; + + VTKWriter() = delete; + + VTKWriter( std::ostream& str, VTKFileFormat format = VTKFileFormat::ASCII ) + : str(str), format(format) + {} - static void writeAllEntities( const Mesh& mesh, std::ostream& str, VTKFileFormat format = VTKFileFormat::ASCII ); + void writeAllEntities( const Mesh& mesh ); template< int EntityDimension = Mesh::getMeshDimension() > - static void writeEntities( const Mesh& mesh, std::ostream& str, VTKFileFormat format = VTKFileFormat::ASCII ); + void writeEntities( const Mesh& mesh ); + + template< typename Array > + void writeDataArray( const Array& array, + const String& name, + const int numberOfComponents = 1, + VTKDataType dataType = VTKDataType::CellData ); protected: - static void writeHeader( const Mesh& mesh, std::ostream& str, VTKFileFormat format ); + void writeHeader( const Mesh& mesh ); + + void writePoints( const Mesh& mesh ); + + std::ostream& str; + + VTKFileFormat 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; + + // number of data arrays written in each section + int cellDataArrays = 0; + int pointDataArrays = 0; - static void writePoints( const Mesh& mesh, std::ostream& str, VTKFileFormat format ); + // indicator of the current section + VTKDataType currentSection = VTKDataType::CellData; }; } // namespace Writers } // namespace Meshes } // namespace TNL -#include +#include diff --git a/src/TNL/Meshes/Writers/VTKWriter_impl.h b/src/TNL/Meshes/Writers/VTKWriter.hpp similarity index 83% rename from src/TNL/Meshes/Writers/VTKWriter_impl.h rename to src/TNL/Meshes/Writers/VTKWriter.hpp index 69108a7bf..70ab2b057 100644 --- a/src/TNL/Meshes/Writers/VTKWriter_impl.h +++ b/src/TNL/Meshes/Writers/VTKWriter.hpp @@ -37,6 +37,7 @@ struct has_entity_topology< T, typename enable_if_type< typename T::EntityTopolo {}; +// TODO: 64-bit integers are most likely not supported in the BINARY format inline void writeInt( VTKFileFormat format, std::ostream& str, int value ) { @@ -506,44 +507,105 @@ struct MeshEntityTypesVTKWriter< Grid< Dimension, MeshReal, Device, MeshIndex >, template< typename Mesh > void -VTKWriter< Mesh >::writeAllEntities( const Mesh& mesh, std::ostream& str, VTKFileFormat format ) +VTKWriter< Mesh >::writeAllEntities( const Mesh& mesh ) { - writeHeader( mesh, str, format ); - writePoints( mesh, str, format ); + writeHeader( mesh ); + writePoints( mesh ); - const Index allEntitiesCount = __impl::getAllMeshEntitiesCount( mesh ); - const Index cellsListSize = __impl::getCellsListSize( mesh ); + cellsCount = __impl::getAllMeshEntitiesCount( mesh ); + const IndexType cellsListSize = __impl::getCellsListSize( mesh ); - str << std::endl << "CELLS " << allEntitiesCount << " " << cellsListSize << std::endl; + str << std::endl << "CELLS " << cellsCount << " " << cellsListSize << std::endl; Algorithms::TemplateStaticFor< int, 0, Mesh::getMeshDimension() + 1, EntitiesWriter >::exec( mesh, str, format ); - str << std::endl << "CELL_TYPES " << allEntitiesCount << std::endl; + str << std::endl << "CELL_TYPES " << cellsCount << std::endl; Algorithms::TemplateStaticFor< int, 0, Mesh::getMeshDimension() + 1, EntityTypesWriter >::exec( mesh, str, format ); } template< typename Mesh > template< int EntityDimension > void -VTKWriter< Mesh >::writeEntities( const Mesh& mesh, std::ostream& str, VTKFileFormat format ) +VTKWriter< Mesh >::writeEntities( const Mesh& mesh ) { - writeHeader( mesh, str, format ); - writePoints( mesh, str, format ); + writeHeader( mesh ); + writePoints( mesh ); 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 ); + cellsCount = mesh.template getEntitiesCount< EntityType >(); + const IndexType verticesPerEntity = __impl::VerticesPerEntity< EntityType >::count; + const IndexType cellsListSize = cellsCount * ( verticesPerEntity + 1 ); - str << std::endl << "CELLS " << entitiesCount << " " << cellsListSize << std::endl; + str << std::endl << "CELLS " << cellsCount << " " << cellsListSize << std::endl; EntitiesWriter< EntityDimension >::exec( mesh, str, format ); - str << std::endl << "CELL_TYPES " << entitiesCount << std::endl; + str << std::endl << "CELL_TYPES " << cellsCount << std::endl; EntityTypesWriter< EntityDimension >::exec( mesh, str, format ); } template< typename Mesh > + template< typename Array > void -VTKWriter< Mesh >::writeHeader( const Mesh& mesh, std::ostream& str, VTKFileFormat format ) +VTKWriter< Mesh >::writeDataArray( const Array& array, + const String& name, + const int numberOfComponents, + VTKDataType dataType ) +{ + // 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, dataType ); + return; + } + + if( numberOfComponents != 1 && numberOfComponents != 3 ) + throw std::logic_error("Unsupported numberOfComponents parameter: " + std::to_string(numberOfComponents)); + + if( dataType == VTKDataType::CellData ) + 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)"); + if( dataType == VTKDataType::PointData ) + 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( dataType != currentSection && cellDataArrays * pointDataArrays != 0 ) + throw std::logic_error("The requested data section is not the current section and it has already been written."); + + // start the appropriate section if necessary + if( dataType == VTKDataType::CellData && cellDataArrays == 0 ) { + str << std::endl << "CELL_DATA " << cellsCount << std::endl; + ++cellDataArrays; + } + if( dataType == VTKDataType::PointData && pointDataArrays == 0 ) { + str << std::endl << "POINT_DATA " << pointsCount << std::endl; + ++pointDataArrays; + } + + // 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::__impl::writeReal; + for( IndexType i = 0; i < array.getSize(); i++ ) { + writeReal( format, str, array[i] ); + if( format == Meshes::Writers::VTKFileFormat::ASCII ) + str << "\n"; + } +} + +template< typename Mesh > +void +VTKWriter< Mesh >::writeHeader( const Mesh& mesh ) { str << "# vtk DataFile Version 2.0\n" << "TNL DATA\n" @@ -553,18 +615,18 @@ VTKWriter< Mesh >::writeHeader( const Mesh& mesh, std::ostream& str, VTKFileForm template< typename Mesh > void -VTKWriter< Mesh >::writePoints( const Mesh& mesh, std::ostream& str, VTKFileFormat format ) +VTKWriter< Mesh >::writePoints( const Mesh& mesh ) { using __impl::writeReal; - const Index verticesCount = mesh.template getEntitiesCount< typename Mesh::Vertex >(); - str << "POINTS " << verticesCount << " " << getType< typename Mesh::RealType >() << std::endl; - for( Index i = 0; i < verticesCount; i++ ) { + 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( Index j = 0; j < point.getSize(); j++ ) + for( IndexType j = 0; j < point.getSize(); j++ ) writeReal( format, str, point[ j ] ); // VTK needs zeros for unused dimensions - for( Index j = 0; j < 3 - point.getSize(); j++ ) + for( IndexType j = 0; j < 3 - point.getSize(); j++ ) writeReal( format, str, (typename Mesh::PointType::RealType) 0 ); if( format == VTKFileFormat::ASCII ) str << "\n"; diff --git a/src/Tools/tnl-mesh-converter.cpp b/src/Tools/tnl-mesh-converter.cpp index ac8f99219..2a4c2f29c 100644 --- a/src/Tools/tnl-mesh-converter.cpp +++ b/src/Tools/tnl-mesh-converter.cpp @@ -93,7 +93,8 @@ struct MeshConverter else if( outputFormat == "vtk" ) { using VTKWriter = Meshes::Writers::VTKWriter< Mesh >; std::fstream file( outputFileName.getString() ); - VTKWriter::template writeEntities< Mesh::getMeshDimension() >( mesh, file ); + VTKWriter writer( file ); + writer.template writeEntities< Mesh::getMeshDimension() >( mesh ); } // FIXME: NetgenWriter is not specialized for grids // else if( outputFormat == "netgen" ) { -- GitLab From 748aa4b7eca47734939272ffd07b2d91e21bd1d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Wed, 18 Mar 2020 20:09:14 +0100 Subject: [PATCH 05/73] Fixed typo in tnl-mesh-converter --- src/Tools/tnl-mesh-converter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tools/tnl-mesh-converter.cpp b/src/Tools/tnl-mesh-converter.cpp index 2a4c2f29c..ec72bc3a4 100644 --- a/src/Tools/tnl-mesh-converter.cpp +++ b/src/Tools/tnl-mesh-converter.cpp @@ -92,7 +92,7 @@ struct MeshConverter } else if( outputFormat == "vtk" ) { using VTKWriter = Meshes::Writers::VTKWriter< Mesh >; - std::fstream file( outputFileName.getString() ); + std::ofstream file( outputFileName.getString() ); VTKWriter writer( file ); writer.template writeEntities< Mesh::getMeshDimension() >( mesh ); } -- GitLab From 9248a370fa54baf232338e41a63d9915923d6693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Wed, 8 Apr 2020 20:52:41 +0200 Subject: [PATCH 06/73] Removed dummy DistributedMesh from tnl-mesh-converter --- src/Tools/tnl-mesh-converter.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Tools/tnl-mesh-converter.cpp b/src/Tools/tnl-mesh-converter.cpp index ec72bc3a4..618f753ff 100644 --- a/src/Tools/tnl-mesh-converter.cpp +++ b/src/Tools/tnl-mesh-converter.cpp @@ -13,9 +13,6 @@ #include #include -#include -#include - using namespace TNL; struct MeshConverterConfigTag {}; @@ -72,8 +69,7 @@ struct MeshConverter static bool run( const String& inputFileName, const String& outputFileName, const String& outputFormat ) { Mesh mesh; - Meshes::DistributedMeshes::DistributedMesh distributedMesh; - if( ! Meshes::loadMesh( inputFileName, mesh, distributedMesh ) ) { + if( ! Meshes::loadMesh( inputFileName, mesh ) ) { std::cerr << "Failed to load mesh from file '" << inputFileName << "'." << std::endl; return false; } @@ -122,7 +118,7 @@ int main( int argc, char* argv[] ) { Config::ParameterContainer parameters; Config::ConfigDescription conf_desc; - + configSetup( conf_desc ); if( ! parseCommandLine( argc, argv, conf_desc, parameters ) ) -- GitLab From 52d44ee0d191adf20476cdc87fe624f379e9e13e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Wed, 18 Mar 2020 20:09:42 +0100 Subject: [PATCH 07/73] Refactoring namespace TNL::Meshes::Writers::__impl --- src/TNL/Meshes/Readers/EntityShape.h | 18 +++++ src/TNL/Meshes/Writers/VTKWriter.h | 8 +-- src/TNL/Meshes/Writers/VTKWriter.hpp | 81 +++------------------- src/TNL/Meshes/Writers/VerticesPerEntity.h | 71 +++++++++++++++++++ 4 files changed, 103 insertions(+), 75 deletions(-) create mode 100644 src/TNL/Meshes/Writers/VerticesPerEntity.h diff --git a/src/TNL/Meshes/Readers/EntityShape.h b/src/TNL/Meshes/Readers/EntityShape.h index c2c39eae5..b0c7c4501 100644 --- a/src/TNL/Meshes/Readers/EntityShape.h +++ b/src/TNL/Meshes/Readers/EntityShape.h @@ -11,6 +11,7 @@ #pragma once #include +#include #include #include @@ -26,6 +27,7 @@ namespace Readers { * Enumeration of entity shapes, inspired by the VTK library. */ enum class EntityShape +: std::uint8_t { Vertex = 1, PolyVertex = 2, @@ -128,6 +130,22 @@ template<> struct TopologyToEntityShape< Topologies::Quadrilateral > { static c 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; +}; + } // namespace Readers } // namespace Meshes } // namespace TNL diff --git a/src/TNL/Meshes/Writers/VTKWriter.h b/src/TNL/Meshes/Writers/VTKWriter.h index 83e8a8e62..e25e95727 100644 --- a/src/TNL/Meshes/Writers/VTKWriter.h +++ b/src/TNL/Meshes/Writers/VTKWriter.h @@ -31,12 +31,12 @@ enum class VTKDataType PointData }; -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 @@ -46,10 +46,10 @@ 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 IndexType = typename Mesh::GlobalIndexType; diff --git a/src/TNL/Meshes/Writers/VTKWriter.hpp b/src/TNL/Meshes/Writers/VTKWriter.hpp index 70ab2b057..4a5b98dcb 100644 --- a/src/TNL/Meshes/Writers/VTKWriter.hpp +++ b/src/TNL/Meshes/Writers/VTKWriter.hpp @@ -10,32 +10,16 @@ #pragma once -#include - #include #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 -{}; - +namespace details { // TODO: 64-bit integers are most likely not supported in the BINARY format inline void @@ -65,51 +49,6 @@ writeReal( VTKFileFormat format, std::ostream& str, Real value ) } -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 > ) @@ -472,7 +411,7 @@ struct MeshEntityTypesVTKWriter 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; + const int type = (int) Readers::TopologyToEntityShape< typename EntityType::EntityTopology >::shape; writeInt( format, str, type ); if( format == VTKFileFormat::ASCII ) str << "\n"; @@ -495,7 +434,7 @@ struct MeshEntityTypesVTKWriter< Grid< Dimension, MeshReal, Device, MeshIndex >, const MeshIndex entitiesCount = mesh.template getEntitiesCount< EntityType >(); for( MeshIndex i = 0; i < entitiesCount; i++ ) { - const int type = (int) __impl::GridEntityShape< EntityType >::shape; + const int type = (int) Readers::GridEntityShape< EntityType >::shape; writeInt( format, str, type ); if( format == VTKFileFormat::ASCII ) str << "\n"; @@ -503,7 +442,7 @@ struct MeshEntityTypesVTKWriter< Grid< Dimension, MeshReal, Device, MeshIndex >, } }; -} // namespace __impl +} // namespace details template< typename Mesh > void @@ -512,8 +451,8 @@ VTKWriter< Mesh >::writeAllEntities( const Mesh& mesh ) writeHeader( mesh ); writePoints( mesh ); - cellsCount = __impl::getAllMeshEntitiesCount( mesh ); - const IndexType cellsListSize = __impl::getCellsListSize( mesh ); + cellsCount = details::getAllMeshEntitiesCount( mesh ); + const IndexType cellsListSize = details::getCellsListSize( mesh ); str << std::endl << "CELLS " << cellsCount << " " << cellsListSize << std::endl; Algorithms::TemplateStaticFor< int, 0, Mesh::getMeshDimension() + 1, EntitiesWriter >::exec( mesh, str, format ); @@ -532,7 +471,7 @@ VTKWriter< Mesh >::writeEntities( const Mesh& mesh ) using EntityType = typename Mesh::template EntityType< EntityDimension >; cellsCount = mesh.template getEntitiesCount< EntityType >(); - const IndexType verticesPerEntity = __impl::VerticesPerEntity< EntityType >::count; + const IndexType verticesPerEntity = VerticesPerEntity< EntityType >::count; const IndexType cellsListSize = cellsCount * ( verticesPerEntity + 1 ); str << std::endl << "CELLS " << cellsCount << " " << cellsListSize << std::endl; @@ -595,7 +534,7 @@ VTKWriter< Mesh >::writeDataArray( const Array& array, str << "VECTORS " << name << " " << getType< typename Array::ValueType >() << " 1" << std::endl; } - using Meshes::Writers::__impl::writeReal; + using Meshes::Writers::details::writeReal; for( IndexType i = 0; i < array.getSize(); i++ ) { writeReal( format, str, array[i] ); if( format == Meshes::Writers::VTKFileFormat::ASCII ) @@ -617,7 +556,7 @@ template< typename Mesh > void VTKWriter< Mesh >::writePoints( const Mesh& mesh ) { - using __impl::writeReal; + 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++ ) { diff --git a/src/TNL/Meshes/Writers/VerticesPerEntity.h b/src/TNL/Meshes/Writers/VerticesPerEntity.h new file mode 100644 index 000000000..50a71eae5 --- /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 = 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; +}; + +} // namespace Writers +} // namespace Meshes +} // namespace TNL -- GitLab From 222138110666e509ee66e33e6199ef1bf01ac531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Thu, 19 Mar 2020 12:50:22 +0100 Subject: [PATCH 08/73] Refactoring: Meshes/Readers/EntityShape.h moved to Meshes/VTKTraits.h --- src/TNL/Functions/MeshFunctionVTKWriter.h | 8 +- src/TNL/Functions/VectorFieldVTKWriter.h | 6 +- src/TNL/Meshes/Readers/NetgenReader.h | 22 ++--- src/TNL/Meshes/Readers/TNLReader.h | 24 ++--- src/TNL/Meshes/Readers/VTKReader.h | 20 ++-- src/TNL/Meshes/Readers/VTKReader_libvtk.h | 12 +-- .../TypeResolver/MeshTypeResolver_impl.h | 15 ++- .../{Readers/EntityShape.h => VTKTraits.h} | 89 +++++++++-------- src/TNL/Meshes/Writers/VTKWriter.h | 28 ++---- src/TNL/Meshes/Writers/VTKWriter.hpp | 95 ++++++++++--------- 10 files changed, 160 insertions(+), 159 deletions(-) rename src/TNL/Meshes/{Readers/EntityShape.h => VTKTraits.h} (71%) diff --git a/src/TNL/Functions/MeshFunctionVTKWriter.h b/src/TNL/Functions/MeshFunctionVTKWriter.h index c2c22a3e2..e13c0f725 100644 --- a/src/TNL/Functions/MeshFunctionVTKWriter.h +++ b/src/TNL/Functions/MeshFunctionVTKWriter.h @@ -26,7 +26,7 @@ class MeshFunctionVTKWriter public: MeshFunctionVTKWriter( std::ostream& str, - Meshes::Writers::VTKFileFormat format = Meshes::Writers::VTKFileFormat::ASCII ) + Meshes::VTK::FileFormat format = Meshes::VTK::FileFormat::ascii ) : Meshes::Writers::VTKWriter< MeshType >( str, format ) {} @@ -45,9 +45,9 @@ public: const String& functionName ) { if( MeshFunction::getEntitiesDimension() == 0 ) - this->writeDataArray( function.getData(), functionName, 1, Meshes::Writers::VTKDataType::PointData ); + this->writeDataArray( function.getData(), functionName, 1, Meshes::VTK::DataType::PointData ); else - this->writeDataArray( function.getData(), functionName, 1, Meshes::Writers::VTKDataType::CellData ); + this->writeDataArray( function.getData(), functionName, 1, Meshes::VTK::DataType::CellData ); } }; @@ -56,7 +56,7 @@ class MeshFunctionVTKWriter< MeshFunction, false > { public: MeshFunctionVTKWriter( std::ostream& str, - Meshes::Writers::VTKFileFormat format = Meshes::Writers::VTKFileFormat::ASCII ) + Meshes::VTK::FileFormat format = Meshes::VTK::FileFormat::ascii ) {} bool write( const MeshFunction& function, diff --git a/src/TNL/Functions/VectorFieldVTKWriter.h b/src/TNL/Functions/VectorFieldVTKWriter.h index a80943ac3..6cfeb80c7 100644 --- a/src/TNL/Functions/VectorFieldVTKWriter.h +++ b/src/TNL/Functions/VectorFieldVTKWriter.h @@ -26,7 +26,7 @@ class VectorFieldVTKWriter public: VectorFieldVTKWriter( std::ostream& str, - Meshes::Writers::VTKFileFormat format = Meshes::Writers::VTKFileFormat::ASCII ) + Meshes::VTK::FileFormat format = Meshes::VTK::FileFormat::ascii ) : Meshes::Writers::VTKWriter< MeshType >( str, format ) {} @@ -60,9 +60,9 @@ public: // write the buffer if( VectorField::getEntitiesDimension() == 0 ) - this->writeDataArray( buffer, fieldName, 3, Meshes::Writers::VTKDataType::PointData ); + this->writeDataArray( buffer, fieldName, 3, Meshes::VTK::DataType::PointData ); else - this->writeDataArray( buffer, fieldName, 3, Meshes::Writers::VTKDataType::CellData ); + this->writeDataArray( buffer, fieldName, 3, Meshes::VTK::DataType::CellData ); } }; diff --git a/src/TNL/Meshes/Readers/NetgenReader.h b/src/TNL/Meshes/Readers/NetgenReader.h index f781d3da4..abf56c03e 100644 --- a/src/TNL/Meshes/Readers/NetgenReader.h +++ b/src/TNL/Meshes/Readers/NetgenReader.h @@ -21,7 +21,7 @@ #include #include -#include +#include namespace TNL { namespace Meshes { @@ -122,22 +122,22 @@ public: verticesInCell++; } //cout << "There are " << verticesInCell << " vertices in cell ..." << std::endl; - + if( meshDimension == 1 && verticesInCell == 2 ) - cellShape = EntityShape::Line; + cellShape = VTK::EntityShape::Line; else if( meshDimension == 2 ) { if( verticesInCell == 3 ) - cellShape = EntityShape::Triangle; + cellShape = VTK::EntityShape::Triangle; else if( verticesInCell == 4 ) - cellShape = EntityShape::Quad; + cellShape = VTK::EntityShape::Quad; } else if( meshDimension == 3 ) { if( verticesInCell == 4 ) - cellShape = EntityShape::Tetra; + cellShape = VTK::EntityShape::Tetra; else if( verticesInCell == 8 ) - cellShape = EntityShape::Hexahedron; + cellShape = VTK::EntityShape::Hexahedron; } - if( cellShape == EntityShape::Vertex ) { + if( cellShape == VTK::EntityShape::Vertex ) { std::cerr << "Unknown cell topology: mesh dimension is " << meshDimension << ", number of vertices in cells is " << verticesInCell << "." << std::endl; return false; } @@ -251,7 +251,7 @@ public: return worldDimension; } - EntityShape + VTK::EntityShape getCellShape() const { return cellShape; @@ -288,13 +288,13 @@ public: protected: String fileName; int meshDimension, worldDimension; - EntityShape cellShape = EntityShape::Vertex; + VTK::EntityShape cellShape = VTK::EntityShape::Vertex; void reset() { fileName = ""; meshDimension = worldDimension = 0; - cellShape = EntityShape::Vertex; + cellShape = VTK::EntityShape::Vertex; } }; diff --git a/src/TNL/Meshes/Readers/TNLReader.h b/src/TNL/Meshes/Readers/TNLReader.h index 88869ffc1..b7cdc54c9 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 { @@ -42,11 +42,11 @@ public: globalIndexType = localIndexType = idType = 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 ] ); @@ -72,15 +72,15 @@ public: idType = parsedMeshConfig[ 6 ]; if( topology == "MeshEdgeTopology" ) - cellShape = EntityShape::Line; + cellShape = VTK::EntityShape::Line; else if( topology == "MeshTriangleTopology" ) - cellShape = EntityShape::Triangle; + cellShape = VTK::EntityShape::Triangle; else if( topology == "MeshQuadrilateralTopology" ) - cellShape = EntityShape::Quad; + cellShape = VTK::EntityShape::Quad; else if( topology == "MeshTetrahedronTopology" ) - cellShape = EntityShape::Tetra; + cellShape = VTK::EntityShape::Tetra; else if( topology == "MeshHexahedronTopology" ) - cellShape = EntityShape::Hexahedron; + cellShape = VTK::EntityShape::Hexahedron; else { std::cerr << "Detected topology '" << topology << "' is not supported." << std::endl; return false; @@ -120,7 +120,7 @@ public: return worldDimension; } - EntityShape + VTK::EntityShape getCellShape() const { return cellShape; @@ -155,7 +155,7 @@ protected: String meshType; int meshDimension = 0; int worldDimension = 0; - EntityShape cellShape = EntityShape::Vertex; + VTK::EntityShape cellShape = VTK::EntityShape::Vertex; String realType; String globalIndexType; String localIndexType; @@ -166,7 +166,7 @@ protected: fileName = ""; meshType = ""; meshDimension = worldDimension = 0; - cellShape = EntityShape::Vertex; + cellShape = VTK::EntityShape::Vertex; realType = localIndexType = globalIndexType = idType = ""; } }; diff --git a/src/TNL/Meshes/Readers/VTKReader.h b/src/TNL/Meshes/Readers/VTKReader.h index d78796ba2..438d0b546 100644 --- a/src/TNL/Meshes/Readers/VTKReader.h +++ b/src/TNL/Meshes/Readers/VTKReader.h @@ -16,7 +16,7 @@ #include #include -#include +#include #include namespace TNL { @@ -107,7 +107,7 @@ public: // read entity types long int entitiesRead = 0; - std::map< int, EntityShape > entityTypes; + std::map< int, VTK::EntityShape > entityTypes; while( entitiesRead < numberOfEntities ) { if( ! inputFile ) { std::cerr << "VTKReader: unable to read enough entity types, the file may be invalid or corrupted." << std::endl; @@ -120,7 +120,7 @@ public: iss.clear(); iss.str( line ); iss >> typeId; - const EntityShape type = (EntityShape) typeId; + const VTK::EntityShape type = (VTK::EntityShape) typeId; const int dimension = getEntityDimension( type ); // check entity type @@ -128,7 +128,7 @@ public: 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 << ". " + << " with type " << VTK::getShapeName( entityTypes[ dimension ] ) << " and " << VTK::getShapeName( type ) << ". " << "The type of all entities with the same dimension must be the same." << std::endl; this->reset(); return false; @@ -156,7 +156,7 @@ public: using PointType = typename MeshType::PointType; using CellSeedType = typename MeshBuilder::CellSeedType; - const EntityShape cellType = TopologyToEntityShape< typename MeshType::template EntityTraits< MeshType::getMeshDimension() >::EntityTopology >::shape; + const VTK::EntityShape cellType = VTK::TopologyToEntityShape< typename MeshType::template EntityTraits< MeshType::getMeshDimension() >::EntityTopology >::shape; MeshBuilder meshBuilder; std::ifstream inputFile( fileName.getString() ); @@ -232,7 +232,7 @@ public: iss >> numberOfEntities; // read entity types, count cells - std::vector< EntityShape > entityTypes; + std::vector< VTK::EntityShape > entityTypes; entityTypes.resize( numberOfEntities ); IndexType numberOfCells = 0; for( IndexType entityIndex = 0; entityIndex < numberOfEntities; entityIndex++ ) { @@ -247,7 +247,7 @@ public: iss.clear(); iss.str( line ); iss >> typeId; - entityTypes[ entityIndex ] = (EntityShape) typeId; + entityTypes[ entityIndex ] = (VTK::EntityShape) typeId; const int dimension = getEntityDimension( entityTypes[ entityIndex ] ); if( dimension == MeshType::getMeshDimension() ) numberOfCells++; @@ -310,7 +310,7 @@ public: return worldDimension; } - EntityShape + VTK::EntityShape getCellShape() const { return cellShape; @@ -350,14 +350,14 @@ protected: String fileName; int meshDimension, worldDimension; - EntityShape cellShape = EntityShape::Vertex; + VTK::EntityShape cellShape = VTK::EntityShape::Vertex; std::string realType; void reset() { fileName = ""; meshDimension = worldDimension = 0; - cellShape = EntityShape::Vertex; + cellShape = VTK::EntityShape::Vertex; realType = ""; } diff --git a/src/TNL/Meshes/Readers/VTKReader_libvtk.h b/src/TNL/Meshes/Readers/VTKReader_libvtk.h index 0e2b40421..46945e00f 100644 --- a/src/TNL/Meshes/Readers/VTKReader_libvtk.h +++ b/src/TNL/Meshes/Readers/VTKReader_libvtk.h @@ -20,7 +20,7 @@ #include #include #include -#include +#include #ifdef HAVE_VTK #include @@ -135,7 +135,7 @@ public: return this->meshDimension; } - EntityShape + VTK::EntityShape getCellShape() const { return this->entityTypes.at( this->meshDimension ); @@ -147,7 +147,7 @@ public: // return this->verticesInEntities.at( this->getMeshDimension() ); // } -// EntityShape +// VTK::EntityShape // getEntityType( int entityDimension ) const // { // return this->entityTypes.at( entityDimension ); @@ -201,7 +201,7 @@ protected: 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; + std::unordered_map< int, VTK::EntityShape > entityTypes; void reset() { @@ -273,7 +273,7 @@ protected: vtkCell* cell = vtkMesh.GetCell( i ); const int dimension = cell->GetCellDimension(); const int points = cell->GetNumberOfPoints(); - const EntityShape type = (EntityShape) cell->GetCellType(); + const VTK::EntityShape type = (VTK::EntityShape) cell->GetCellType(); // number of vertices in entities if( this->verticesInEntities.find( dimension ) == this->verticesInEntities.cend() ) @@ -291,7 +291,7 @@ protected: 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 + << " with type " << VTK::getShapeName( this->entityTypes[ dimension ] ) << " and " << VTK::getShapeName( type ) << ". The type of all entities with the same dimension must be the same." << std::endl; this->reset(); return false; diff --git a/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h b/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h index b92148fa9..f59838a30 100644 --- a/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h +++ b/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h @@ -15,7 +15,7 @@ #include #include #include -#include +#include namespace TNL { namespace Meshes { @@ -43,21 +43,20 @@ MeshTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... resolveCellTopology( const Reader& reader, ProblemSetterArgs&&... problemSetterArgs ) { - using Readers::EntityShape; switch( reader.getCellShape() ) { - case EntityShape::Line: + case VTK::EntityShape::Line: return resolveWorldDimension< Topologies::Edge >( reader, std::forward(problemSetterArgs)... ); - case EntityShape::Triangle: + case VTK::EntityShape::Triangle: return resolveWorldDimension< Topologies::Triangle >( reader, std::forward(problemSetterArgs)... ); - case EntityShape::Quad: + case VTK::EntityShape::Quad: return resolveWorldDimension< Topologies::Quadrilateral >( reader, std::forward(problemSetterArgs)... ); - case EntityShape::Tetra: + case VTK::EntityShape::Tetra: return resolveWorldDimension< Topologies::Tetrahedron >( reader, std::forward(problemSetterArgs)... ); - case EntityShape::Hexahedron: + case VTK::EntityShape::Hexahedron: return resolveWorldDimension< Topologies::Hexahedron >( reader, std::forward(problemSetterArgs)... ); default: - std::cerr << "unsupported cell topology: " << reader.getCellShape() << std::endl; + std::cerr << "unsupported cell topology: " << VTK::getShapeName( reader.getCellShape() ) << std::endl; return false; } } diff --git a/src/TNL/Meshes/Readers/EntityShape.h b/src/TNL/Meshes/VTKTraits.h similarity index 71% rename from src/TNL/Meshes/Readers/EntityShape.h rename to src/TNL/Meshes/VTKTraits.h index b0c7c4501..38f93db98 100644 --- a/src/TNL/Meshes/Readers/EntityShape.h +++ b/src/TNL/Meshes/VTKTraits.h @@ -1,5 +1,5 @@ /*************************************************************************** - EntityShape.h - description + VTKTraits.h - description ------------------- begin : Nov 22, 2016 copyright : (C) 2016 by Tomas Oberhuber et al. @@ -10,7 +10,7 @@ #pragma once -#include +#include #include #include @@ -21,11 +21,26 @@ namespace TNL { namespace Meshes { -namespace Readers { +namespace VTK { -/* - * Enumeration of entity shapes, inspired by the VTK library. - */ +// 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 { @@ -45,56 +60,40 @@ enum class EntityShape Pyramid = 14 }; -inline std::ostream& operator<<( std::ostream& str, EntityShape shape ) +inline std::string getShapeName( EntityShape shape ) { switch( shape ) { case EntityShape::Vertex: - str << "Entity::Vertex"; - break; + return "Vertex"; case EntityShape::PolyVertex: - str << "Entity::PolyVertex"; - break; + return "PolyVertex"; case EntityShape::Line: - str << "Entity::Line"; - break; + return "Line"; case EntityShape::PolyLine: - str << "Entity::PolyLine"; - break; + return "PolyLine"; case EntityShape::Triangle: - str << "Entity::Triangle"; - break; + return "Triangle"; case EntityShape::TriangleStrip: - str << "Entity::TriangleStrip"; - break; + return "TriangleStrip"; case EntityShape::Polygon: - str << "Entity::Polygon"; - break; + return "Polygon"; case EntityShape::Pixel: - str << "Entity::Pixel"; - break; + return "Pixel"; case EntityShape::Quad: - str << "Entity::Quad"; - break; + return "Quad"; case EntityShape::Tetra: - str << "Entity::Tetra"; - break; + return "Tetra"; case EntityShape::Voxel: - str << "Entity::Voxel"; - break; + return "Voxel"; case EntityShape::Hexahedron: - str << "Entity::Hexahedron"; - break; + return "Hexahedron"; case EntityShape::Wedge: - str << "Entity::Wedge"; - break; + return "Wedge"; case EntityShape::Pyramid: - str << "Entity::Pyramid"; - break; - default: - str << ""; + return "Pyramid"; } - return str; + return ""; } inline int getEntityDimension( EntityShape shape ) @@ -146,6 +145,18 @@ public: EntityShape::Voxel; }; -} // namespace Readers +// 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"; } + +} // namespace VTK } // namespace Meshes } // namespace TNL diff --git a/src/TNL/Meshes/Writers/VTKWriter.h b/src/TNL/Meshes/Writers/VTKWriter.h index e25e95727..92d4dc6e5 100644 --- a/src/TNL/Meshes/Writers/VTKWriter.h +++ b/src/TNL/Meshes/Writers/VTKWriter.h @@ -10,27 +10,14 @@ #pragma once -#include - #include #include +#include namespace TNL { namespace Meshes { namespace Writers { -enum class VTKFileFormat -{ - ASCII, - BINARY -}; - -enum class VTKDataType -{ - CellData, - PointData -}; - namespace details { template< typename Mesh, int EntityDimension > struct MeshEntitiesVTKWriter; @@ -56,9 +43,12 @@ public: VTKWriter() = delete; - VTKWriter( std::ostream& str, VTKFileFormat format = VTKFileFormat::ASCII ) + VTKWriter( std::ostream& str, VTK::FileFormat format = VTK::FileFormat::ascii ) : 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."); + } void writeAllEntities( const Mesh& mesh ); @@ -69,7 +59,7 @@ public: void writeDataArray( const Array& array, const String& name, const int numberOfComponents = 1, - VTKDataType dataType = VTKDataType::CellData ); + VTK::DataType dataType = VTK::DataType::CellData ); protected: void writeHeader( const Mesh& mesh ); @@ -78,7 +68,7 @@ protected: std::ostream& str; - VTKFileFormat format; + VTK::FileFormat format; // number of cells (in the VTK sense) written to the file IndexType cellsCount = 0; @@ -91,7 +81,7 @@ protected: int pointDataArrays = 0; // indicator of the current section - VTKDataType currentSection = VTKDataType::CellData; + VTK::DataType currentSection = VTK::DataType::CellData; }; } // namespace Writers diff --git a/src/TNL/Meshes/Writers/VTKWriter.hpp b/src/TNL/Meshes/Writers/VTKWriter.hpp index 4a5b98dcb..c7bf476ec 100644 --- a/src/TNL/Meshes/Writers/VTKWriter.hpp +++ b/src/TNL/Meshes/Writers/VTKWriter.hpp @@ -1,5 +1,5 @@ /*************************************************************************** - VTKWriter.h - description + VTKWriter.hpp - description ------------------- begin : Mar 04, 2017 copyright : (C) 2017 by Tomas Oberhuber et al. @@ -10,9 +10,10 @@ #pragma once +#include + #include #include -#include #include namespace TNL { @@ -23,9 +24,9 @@ namespace details { // TODO: 64-bit integers are most likely not supported in the BINARY format inline void -writeInt( VTKFileFormat format, std::ostream& str, int value ) +writeInt( VTK::FileFormat format, std::ostream& str, int value ) { - if( format == VTKFileFormat::BINARY ) { + if( format == VTK::FileFormat::binary ) { value = forceBigEndian( value ); str.write( reinterpret_cast(&value), sizeof(int) ); } @@ -36,9 +37,9 @@ writeInt( VTKFileFormat format, std::ostream& str, int value ) template< typename Real > void -writeReal( VTKFileFormat format, std::ostream& str, Real value ) +writeReal( VTK::FileFormat format, std::ostream& str, Real value ) { - if( format == VTKFileFormat::BINARY ) { + if( format == VTK::FileFormat::binary ) { value = forceBigEndian( value ); str.write( reinterpret_cast(&value), sizeof(Real) ); } @@ -95,7 +96,7 @@ getCellsListSize( const Mesh& mesh, DimensionTag = DimensionTag() ) template< typename Mesh, int EntityDimension > struct MeshEntitiesVTKWriter { - static void exec( const Mesh& mesh, std::ostream& str, VTKFileFormat format ) + static void exec( const Mesh& mesh, std::ostream& str, VTK::FileFormat format ) { using EntityType = typename Mesh::template EntityType< EntityDimension >; using Index = typename Mesh::GlobalIndexType; @@ -107,7 +108,7 @@ struct MeshEntitiesVTKWriter writeInt( format, str, verticesPerEntity ); for( Index j = 0; j < verticesPerEntity; j++ ) writeInt( format, str, entity.template getSubentityIndex< 0 >( j ) ); - if( format == VTKFileFormat::ASCII ) + if( format == VTK::FileFormat::ascii ) str << "\n"; } } @@ -117,7 +118,7 @@ struct MeshEntitiesVTKWriter template< typename Mesh > struct MeshEntitiesVTKWriter< Mesh, 0 > { - static void exec( const Mesh& mesh, std::ostream& str, VTKFileFormat format ) + static void exec( const Mesh& mesh, std::ostream& str, VTK::FileFormat format ) { using EntityType = typename Mesh::template EntityType< 0 >; using Index = typename Mesh::GlobalIndexType; @@ -128,7 +129,7 @@ struct MeshEntitiesVTKWriter< Mesh, 0 > { writeInt( format, str, verticesPerEntity ); writeInt( format, str, i ); - if( format == VTKFileFormat::ASCII ) + if( format == VTK::FileFormat::ascii ) str << "\n"; } } @@ -142,14 +143,14 @@ 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, VTKFileFormat format ) + 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 == VTKFileFormat::ASCII ) + if( format == VTK::FileFormat::ascii ) str << "\n"; } } @@ -163,13 +164,13 @@ 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, VTKFileFormat format ) + 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 == VTKFileFormat::ASCII ) + if( format == VTK::FileFormat::ascii ) str << "\n"; } } @@ -183,7 +184,7 @@ 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, VTKFileFormat format ) + 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++ ) @@ -193,7 +194,7 @@ struct MeshEntitiesVTKWriter< Meshes::Grid< 2, MeshReal, Device, MeshIndex >, 2 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 == VTKFileFormat::ASCII ) + if( format == VTK::FileFormat::ascii ) str << "\n"; } } @@ -207,7 +208,7 @@ 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, VTKFileFormat format ) + 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++ ) @@ -215,7 +216,7 @@ struct MeshEntitiesVTKWriter< Meshes::Grid< 2, MeshReal, Device, MeshIndex >, 1 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 == VTKFileFormat::ASCII ) + if( format == VTK::FileFormat::ascii ) str << "\n"; } @@ -225,7 +226,7 @@ struct MeshEntitiesVTKWriter< Meshes::Grid< 2, MeshReal, Device, MeshIndex >, 1 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 == VTKFileFormat::ASCII ) + if( format == VTK::FileFormat::ascii ) str << "\n"; } } @@ -239,14 +240,14 @@ 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, VTKFileFormat format ) + 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 == VTKFileFormat::ASCII ) + if( format == VTK::FileFormat::ascii ) str << "\n"; } } @@ -260,7 +261,7 @@ 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, VTKFileFormat format ) + 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++ ) @@ -275,7 +276,7 @@ struct MeshEntitiesVTKWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 3 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 == VTKFileFormat::ASCII ) + if( format == VTK::FileFormat::ascii ) str << "\n"; } } @@ -289,7 +290,7 @@ 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, VTKFileFormat format ) + 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++ ) @@ -300,7 +301,7 @@ struct MeshEntitiesVTKWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 2 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 == VTKFileFormat::ASCII ) + if( format == VTK::FileFormat::ascii ) str << "\n"; } @@ -313,7 +314,7 @@ struct MeshEntitiesVTKWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 2 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 == VTKFileFormat::ASCII ) + if( format == VTK::FileFormat::ascii ) str << "\n"; } @@ -326,7 +327,7 @@ struct MeshEntitiesVTKWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 2 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 == VTKFileFormat::ASCII ) + if( format == VTK::FileFormat::ascii ) str << "\n"; } } @@ -340,7 +341,7 @@ 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, VTKFileFormat format ) + 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++ ) @@ -349,7 +350,7 @@ struct MeshEntitiesVTKWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 1 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 == VTKFileFormat::ASCII ) + if( format == VTK::FileFormat::ascii ) str << "\n"; } @@ -360,7 +361,7 @@ struct MeshEntitiesVTKWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 1 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 == VTKFileFormat::ASCII ) + if( format == VTK::FileFormat::ascii ) str << "\n"; } @@ -371,7 +372,7 @@ struct MeshEntitiesVTKWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 1 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 == VTKFileFormat::ASCII ) + if( format == VTK::FileFormat::ascii ) str << "\n"; } } @@ -385,7 +386,7 @@ 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, VTKFileFormat format ) + 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++ ) @@ -393,7 +394,7 @@ struct MeshEntitiesVTKWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 0 { writeInt( format, str, 1 ); writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); - if( format == VTKFileFormat::ASCII ) + if( format == VTK::FileFormat::ascii ) str << "\n"; } } @@ -404,16 +405,16 @@ struct MeshEntitiesVTKWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 0 template< typename Mesh, int EntityDimension > struct MeshEntityTypesVTKWriter { - static void exec( const Mesh& mesh, std::ostream& str, VTKFileFormat format ) + 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) Readers::TopologyToEntityShape< typename EntityType::EntityTopology >::shape; + const int type = (int) VTK::TopologyToEntityShape< typename EntityType::EntityTopology >::shape; writeInt( format, str, type ); - if( format == VTKFileFormat::ASCII ) + if( format == VTK::FileFormat::ascii ) str << "\n"; } } @@ -428,15 +429,15 @@ struct MeshEntityTypesVTKWriter< Grid< Dimension, MeshReal, Device, MeshIndex >, { using MeshType = Grid< Dimension, MeshReal, Device, MeshIndex >; - static void exec( const MeshType& mesh, std::ostream& str, VTKFileFormat format ) + 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) Readers::GridEntityShape< EntityType >::shape; + const int type = (int) VTK::GridEntityShape< EntityType >::shape; writeInt( format, str, type ); - if( format == VTKFileFormat::ASCII ) + if( format == VTK::FileFormat::ascii ) str << "\n"; } } @@ -487,7 +488,7 @@ void VTKWriter< Mesh >::writeDataArray( const Array& array, const String& name, const int numberOfComponents, - VTKDataType dataType ) + VTK::DataType dataType ) { // use a host buffer if direct access to the array elements is not possible if( std::is_same< typename Array::DeviceType, Devices::Cuda >::value ) @@ -502,11 +503,11 @@ VTKWriter< Mesh >::writeDataArray( const Array& array, if( numberOfComponents != 1 && numberOfComponents != 3 ) throw std::logic_error("Unsupported numberOfComponents parameter: " + std::to_string(numberOfComponents)); - if( dataType == VTKDataType::CellData ) + if( dataType == VTK::DataType::CellData ) 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)"); - if( dataType == VTKDataType::PointData ) + if( dataType == VTK::DataType::PointData ) 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)"); @@ -516,11 +517,11 @@ VTKWriter< Mesh >::writeDataArray( const Array& array, throw std::logic_error("The requested data section is not the current section and it has already been written."); // start the appropriate section if necessary - if( dataType == VTKDataType::CellData && cellDataArrays == 0 ) { + if( dataType == VTK::DataType::CellData && cellDataArrays == 0 ) { str << std::endl << "CELL_DATA " << cellsCount << std::endl; ++cellDataArrays; } - if( dataType == VTKDataType::PointData && pointDataArrays == 0 ) { + if( dataType == VTK::DataType::PointData && pointDataArrays == 0 ) { str << std::endl << "POINT_DATA " << pointsCount << std::endl; ++pointDataArrays; } @@ -537,7 +538,7 @@ VTKWriter< Mesh >::writeDataArray( const Array& array, using Meshes::Writers::details::writeReal; for( IndexType i = 0; i < array.getSize(); i++ ) { writeReal( format, str, array[i] ); - if( format == Meshes::Writers::VTKFileFormat::ASCII ) + if( format == VTK::FileFormat::ascii ) str << "\n"; } } @@ -548,7 +549,7 @@ VTKWriter< Mesh >::writeHeader( const Mesh& mesh ) { str << "# vtk DataFile Version 2.0\n" << "TNL DATA\n" - << ((format == VTKFileFormat::ASCII) ? "ASCII\n" : "BINARY\n") + << ((format == VTK::FileFormat::ascii) ? "ASCII\n" : "BINARY\n") << "DATASET UNSTRUCTURED_GRID\n"; } @@ -567,7 +568,7 @@ VTKWriter< Mesh >::writePoints( const Mesh& mesh ) // 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 == VTKFileFormat::ASCII ) + if( format == VTK::FileFormat::ascii ) str << "\n"; } } -- GitLab From 7bb0d6913e6ad4f4471e29a23359e2b23d21314b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Thu, 19 Mar 2020 15:23:03 +0100 Subject: [PATCH 09/73] Refactoring: removed writeAllEntities from VTKWriter The method was unused (and does not make much sense for the format anyway). --- src/TNL/Meshes/Writers/AsymptoteWriter.h | 12 ----- src/TNL/Meshes/Writers/VTKWriter.h | 2 - src/TNL/Meshes/Writers/VTKWriter.hpp | 58 ------------------------ 3 files changed, 72 deletions(-) diff --git a/src/TNL/Meshes/Writers/AsymptoteWriter.h b/src/TNL/Meshes/Writers/AsymptoteWriter.h index 62a9f073d..5f0663782 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/VTKWriter.h b/src/TNL/Meshes/Writers/VTKWriter.h index 92d4dc6e5..9e3d1002a 100644 --- a/src/TNL/Meshes/Writers/VTKWriter.h +++ b/src/TNL/Meshes/Writers/VTKWriter.h @@ -50,8 +50,6 @@ public: throw std::domain_error("The Legacy VTK file formats support only ASCII and BINARY formats."); } - void writeAllEntities( const Mesh& mesh ); - template< int EntityDimension = Mesh::getMeshDimension() > void writeEntities( const Mesh& mesh ); diff --git a/src/TNL/Meshes/Writers/VTKWriter.hpp b/src/TNL/Meshes/Writers/VTKWriter.hpp index c7bf476ec..44b70665f 100644 --- a/src/TNL/Meshes/Writers/VTKWriter.hpp +++ b/src/TNL/Meshes/Writers/VTKWriter.hpp @@ -50,47 +50,6 @@ writeReal( VTK::FileFormat format, std::ostream& str, Real value ) } -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 > @@ -445,23 +404,6 @@ struct MeshEntityTypesVTKWriter< Grid< Dimension, MeshReal, Device, MeshIndex >, } // namespace details -template< typename Mesh > -void -VTKWriter< Mesh >::writeAllEntities( const Mesh& mesh ) -{ - writeHeader( mesh ); - writePoints( mesh ); - - cellsCount = details::getAllMeshEntitiesCount( mesh ); - const IndexType cellsListSize = details::getCellsListSize( mesh ); - - str << std::endl << "CELLS " << cellsCount << " " << cellsListSize << std::endl; - Algorithms::TemplateStaticFor< int, 0, Mesh::getMeshDimension() + 1, EntitiesWriter >::exec( mesh, str, format ); - - str << std::endl << "CELL_TYPES " << cellsCount << std::endl; - Algorithms::TemplateStaticFor< int, 0, Mesh::getMeshDimension() + 1, EntityTypesWriter >::exec( mesh, str, format ); -} - template< typename Mesh > template< int EntityDimension > void -- GitLab From 8f4924062d0590adb8d90e47e927e2ae6703b627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Thu, 19 Mar 2020 17:13:58 +0100 Subject: [PATCH 10/73] Refactoring: removed tuple types from tnl-view --- src/TNL/Functions/MeshFunctionVTKWriter.h | 24 +-------- src/Tools/tnl-view.h | 60 ----------------------- 2 files changed, 1 insertion(+), 83 deletions(-) diff --git a/src/TNL/Functions/MeshFunctionVTKWriter.h b/src/TNL/Functions/MeshFunctionVTKWriter.h index e13c0f725..83e6dd1a4 100644 --- a/src/TNL/Functions/MeshFunctionVTKWriter.h +++ b/src/TNL/Functions/MeshFunctionVTKWriter.h @@ -15,8 +15,7 @@ namespace TNL { namespace Functions { -template< typename MeshFunction, - bool = std::is_fundamental< typename MeshFunction::RealType >::value > +template< typename MeshFunction > class MeshFunctionVTKWriter : protected Meshes::Writers::VTKWriter< typename MeshFunction::MeshType > { @@ -51,26 +50,5 @@ public: } }; -template< typename MeshFunction > -class MeshFunctionVTKWriter< MeshFunction, false > -{ -public: - MeshFunctionVTKWriter( std::ostream& str, - Meshes::VTK::FileFormat format = Meshes::VTK::FileFormat::ascii ) - {} - - bool write( const MeshFunction& function, - const String& functionName = "cellFunctionValues" ) - { - throw std::logic_error( "Unsupported RealType - VTKWriter supports only fundamental types." ); - } - - void appendFunction( const MeshFunction& function, - const String& functionName ) - { - throw std::logic_error( "Unsupported RealType - VTKWriter supports only fundamental types." ); - } -}; - } // namespace Functions } // namespace TNL diff --git a/src/Tools/tnl-view.h b/src/Tools/tnl-view.h index 84b3681b9..e4ba96b9b 100644 --- a/src/Tools/tnl-view.h +++ b/src/Tools/tnl-view.h @@ -306,57 +306,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,15 +331,6 @@ 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; } -- GitLab From 14bb4359b5483355f605bde5da8a00fa5b020ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Thu, 19 Mar 2020 15:25:18 +0100 Subject: [PATCH 11/73] Added VTUWriter (for now ASCII format only) --- src/TNL/Meshes/Writers/VTUWriter.h | 99 +++++ src/TNL/Meshes/Writers/VTUWriter.hpp | 518 +++++++++++++++++++++++++++ src/Tools/tnl-mesh-converter.cpp | 8 + 3 files changed, 625 insertions(+) create mode 100644 src/TNL/Meshes/Writers/VTUWriter.h create mode 100644 src/TNL/Meshes/Writers/VTUWriter.hpp diff --git a/src/TNL/Meshes/Writers/VTUWriter.h b/src/TNL/Meshes/Writers/VTUWriter.h new file mode 100644 index 000000000..34bc27b87 --- /dev/null +++ b/src/TNL/Meshes/Writers/VTUWriter.h @@ -0,0 +1,99 @@ +/*************************************************************************** + 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::ascii ) + : str(str), format(format) + {} + + template< int EntityDimension = Mesh::getMeshDimension() > + void writeEntities( const Mesh& mesh ); + + // 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< typename Array > + void writeDataArray( const Array& array, + const String& name, + const int numberOfComponents = 1 ); + + ~VTUWriter(); + +protected: + void writeHeader( const Mesh& mesh ); + + void writeFooter(); + + void writePoints( const Mesh& mesh ); + + 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; + + // number of data arrays written in each section + int cellDataArrays = 0; + int pointDataArrays = 0; + + // indicator of the current section + VTK::DataType currentSection = VTK::DataType::CellData; +}; + +} // 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 000000000..6bf28a141 --- /dev/null +++ b/src/TNL/Meshes/Writers/VTUWriter.hpp @@ -0,0 +1,518 @@ +/*************************************************************************** + 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 + +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 > + 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( mesh ); + if( pieceOpen ) + str << "\n"; + 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 >::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"; + + if( format == 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"; + } + // TODO: binary format, compression + + // write DataArray footer + str << "\n"; +} + +template< typename Mesh > +void +VTUWriter< Mesh >::writeHeader( const Mesh& mesh ) +{ + str << "\n"; + str << "\n"; + str << "\n"; + + vtkfileOpen = true; +} + +template< typename Mesh > +void +VTUWriter< Mesh >::writeFooter() +{ + if( pieceOpen ) + str << "\n"; + str << "\n"; + str << "\n"; +} + +template< typename Mesh > +VTUWriter< Mesh >::~VTUWriter() +{ + if( vtkfileOpen ) + writeFooter(); +} + +template< typename Mesh > +void +VTUWriter< Mesh >::writeMetadata( int cycle, double time ) +{ + 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 > +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"; +} + +} // namespace Writers +} // namespace Meshes +} // namespace TNL diff --git a/src/Tools/tnl-mesh-converter.cpp b/src/Tools/tnl-mesh-converter.cpp index 618f753ff..9b96671b5 100644 --- a/src/Tools/tnl-mesh-converter.cpp +++ b/src/Tools/tnl-mesh-converter.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include using namespace TNL; @@ -92,6 +93,12 @@ struct MeshConverter 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 >; @@ -111,6 +118,7 @@ void configSetup( Config::ConfigDescription& config ) config.addEntry< String >( "output-format", "Output mesh file format." ); config.addEntryEnum( "tnl" ); config.addEntryEnum( "vtk" ); + config.addEntryEnum( "vtu" ); // config.addEntryEnum( "netgen" ); } -- GitLab From 18143ecb676cf66ec8d3f1d880949248396b9b6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Thu, 19 Mar 2020 17:22:03 +0100 Subject: [PATCH 12/73] Fixed VTUWriter::writeMetadata - it has to be called before writeEntities --- src/TNL/Meshes/Writers/VTUWriter.h | 8 ++--- src/TNL/Meshes/Writers/VTUWriter.hpp | 49 +++++++++++++++------------- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/TNL/Meshes/Writers/VTUWriter.h b/src/TNL/Meshes/Writers/VTUWriter.h index 34bc27b87..d91f0dba6 100644 --- a/src/TNL/Meshes/Writers/VTUWriter.h +++ b/src/TNL/Meshes/Writers/VTUWriter.h @@ -47,13 +47,13 @@ public: : str(str), format(format) {} - template< int EntityDimension = Mesh::getMeshDimension() > - void writeEntities( const Mesh& mesh ); - // 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 writeDataArray( const Array& array, const String& name, @@ -62,7 +62,7 @@ public: ~VTUWriter(); protected: - void writeHeader( const Mesh& mesh ); + void writeHeader(); void writeFooter(); diff --git a/src/TNL/Meshes/Writers/VTUWriter.hpp b/src/TNL/Meshes/Writers/VTUWriter.hpp index 6bf28a141..669e7c39d 100644 --- a/src/TNL/Meshes/Writers/VTUWriter.hpp +++ b/src/TNL/Meshes/Writers/VTUWriter.hpp @@ -352,6 +352,30 @@ struct MeshEntitiesVTUCollector< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, } // 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 @@ -363,7 +387,7 @@ VTUWriter< Mesh >::writeEntities( const Mesh& mesh ) cellsCount = mesh.template getEntitiesCount< EntityType >(); if( ! vtkfileOpen ) - writeHeader( mesh ); + writeHeader(); if( pieceOpen ) str << "\n"; str << "\n"; @@ -434,7 +458,7 @@ VTUWriter< Mesh >::writeDataArray( const Array& array, template< typename Mesh > void -VTUWriter< Mesh >::writeHeader( const Mesh& mesh ) +VTUWriter< Mesh >::writeHeader() { str << "\n"; str << "::~VTUWriter() writeFooter(); } -template< typename Mesh > -void -VTUWriter< Mesh >::writeMetadata( int cycle, double time ) -{ - 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 > void VTUWriter< Mesh >::writePoints( const Mesh& mesh ) -- GitLab From b65b6429236ab2a649bc4e04cdf2b8cd1ea2d7a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Thu, 19 Mar 2020 17:31:09 +0100 Subject: [PATCH 13/73] Added writeMetadata to VTKWriter --- src/TNL/Meshes/Writers/VTKWriter.h | 9 +++++++- src/TNL/Meshes/Writers/VTKWriter.hpp | 32 ++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/TNL/Meshes/Writers/VTKWriter.h b/src/TNL/Meshes/Writers/VTKWriter.h index 9e3d1002a..7111cd8c5 100644 --- a/src/TNL/Meshes/Writers/VTKWriter.h +++ b/src/TNL/Meshes/Writers/VTKWriter.h @@ -50,6 +50,10 @@ public: 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() > void writeEntities( const Mesh& mesh ); @@ -60,7 +64,7 @@ public: VTK::DataType dataType = VTK::DataType::CellData ); protected: - void writeHeader( const Mesh& mesh ); + void writeHeader(); void writePoints( const Mesh& mesh ); @@ -74,6 +78,9 @@ protected: // 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; diff --git a/src/TNL/Meshes/Writers/VTKWriter.hpp b/src/TNL/Meshes/Writers/VTKWriter.hpp index 44b70665f..8bad27c04 100644 --- a/src/TNL/Meshes/Writers/VTKWriter.hpp +++ b/src/TNL/Meshes/Writers/VTKWriter.hpp @@ -404,12 +404,39 @@ struct MeshEntityTypesVTKWriter< Grid< Dimension, MeshReal, Device, MeshIndex >, } // 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 ) { - writeHeader( mesh ); + if( ! headerWritten ) + writeHeader(); writePoints( mesh ); using EntityType = typename Mesh::template EntityType< EntityDimension >; @@ -487,12 +514,13 @@ VTKWriter< Mesh >::writeDataArray( const Array& array, template< typename Mesh > void -VTKWriter< Mesh >::writeHeader( const Mesh& mesh ) +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; } template< typename Mesh > -- GitLab From 800a8b6536a3439d2601af2b53e4034da9378c14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Thu, 19 Mar 2020 18:00:21 +0100 Subject: [PATCH 14/73] Refactoring PointData and CellData sections in VTK writers --- src/TNL/Functions/MeshFunction.hpp | 10 +- src/TNL/Functions/MeshFunctionVTKWriter.h | 54 --------- src/TNL/Functions/MeshFunctionView.hpp | 12 +- src/TNL/Functions/VectorField.h | 48 +++++++- src/TNL/Functions/VectorFieldVTKWriter.h | 70 ----------- src/TNL/Meshes/Writers/VTKWriter.h | 17 ++- src/TNL/Meshes/Writers/VTKWriter.hpp | 98 ++++++++++------ src/TNL/Meshes/Writers/VTUWriter.h | 32 +++-- src/TNL/Meshes/Writers/VTUWriter.hpp | 136 ++++++++++++++++++---- 9 files changed, 273 insertions(+), 204 deletions(-) delete mode 100644 src/TNL/Functions/MeshFunctionVTKWriter.h delete mode 100644 src/TNL/Functions/VectorFieldVTKWriter.h diff --git a/src/TNL/Functions/MeshFunction.hpp b/src/TNL/Functions/MeshFunction.hpp index df8bd7ddf..df4e77b1d 100644 --- a/src/TNL/Functions/MeshFunction.hpp +++ b/src/TNL/Functions/MeshFunction.hpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #pragma once @@ -406,8 +406,12 @@ write( const String& fileName, return false; } if( format == "vtk" ) { - MeshFunctionVTKWriter< MeshFunction > writer( file ); - writer.write( *this ); + 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 == "gnuplot" ) return MeshFunctionGnuplotWriter< MeshFunction >::write( *this, file ); diff --git a/src/TNL/Functions/MeshFunctionVTKWriter.h b/src/TNL/Functions/MeshFunctionVTKWriter.h deleted file mode 100644 index 83e6dd1a4..000000000 --- a/src/TNL/Functions/MeshFunctionVTKWriter.h +++ /dev/null @@ -1,54 +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 -: protected Meshes::Writers::VTKWriter< typename MeshFunction::MeshType > -{ - using MeshType = typename MeshFunction::MeshType; - using EntityType = typename MeshType::template EntityType< MeshFunction::getEntitiesDimension() >; - using GlobalIndex = typename MeshType::GlobalIndexType; - -public: - MeshFunctionVTKWriter( std::ostream& str, - Meshes::VTK::FileFormat format = Meshes::VTK::FileFormat::ascii ) - : Meshes::Writers::VTKWriter< MeshType >( str, format ) - {} - - void write( const MeshFunction& function, - const String& functionName = "cellFunctionValues" ) - { - const MeshType& mesh = function.getMesh(); - this->template writeEntities< MeshFunction::getEntitiesDimension() >( mesh ); - appendFunction( function, functionName ); - } - - // VTK supports writing multiple functions into the same file. - // You can call this after 'write', which initializes the mesh entities, - // with different function name. - void appendFunction( const MeshFunction& function, - const String& functionName ) - { - if( MeshFunction::getEntitiesDimension() == 0 ) - this->writeDataArray( function.getData(), functionName, 1, Meshes::VTK::DataType::PointData ); - else - this->writeDataArray( function.getData(), functionName, 1, Meshes::VTK::DataType::CellData ); - } -}; - -} // namespace Functions -} // namespace TNL diff --git a/src/TNL/Functions/MeshFunctionView.hpp b/src/TNL/Functions/MeshFunctionView.hpp index b1845362e..980978e72 100644 --- a/src/TNL/Functions/MeshFunctionView.hpp +++ b/src/TNL/Functions/MeshFunctionView.hpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #pragma once @@ -490,8 +490,14 @@ 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 == "gnuplot" ) return MeshFunctionGnuplotWriter< MeshFunctionView >::write( *this, file ); else { diff --git a/src/TNL/Functions/VectorField.h b/src/TNL/Functions/VectorField.h index 6765aae06..a18fa1b8f 100644 --- a/src/TNL/Functions/VectorField.h +++ b/src/TNL/Functions/VectorField.h @@ -16,7 +16,7 @@ #include #include #include -#include +#include namespace TNL { namespace Functions { @@ -259,8 +259,28 @@ 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" ) { + Meshes::Writers::VTKWriter< Mesh > writer( file ); + writer.template writeEntities< getEntitiesDimension() >( *getMeshPointer() ); + + // 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, "The VTK format supports only up to 3D vector fields." ); + for( int j = 0; j < 3; j++ ) + buffer[ k++ ] = ( j < vector.getSize() ? vector[ j ] : 0 ); + } + + // write the buffer + 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 { @@ -505,8 +525,26 @@ class VectorField< Size, MeshFunctionView< Mesh, MeshEntityDimension, Real > > return false; } if( format == "vtk" ) { - VectorFieldVTKWriter< VectorField > writer( file ); - writer.write( *this ); + Meshes::Writers::VTKWriter< Mesh > writer( file ); + writer.template writeEntities< getEntitiesDimension() >( *getMeshPointer() ); + + // 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, "The VTK format supports only up to 3D vector fields." ); + for( int j = 0; j < 3; j++ ) + buffer[ k++ ] = ( j < vector.getSize() ? vector[ j ] : 0 ); + } + + // write the buffer + if( getEntitiesDimension() == 0 ) + writer.writePointData( buffer, "cellVectorFieldValues", 3 ); + else + writer.writeCellData( buffer, "pointVectorFieldValues", 3 ); } else if( format == "gnuplot" ) return VectorFieldGnuplotWriter< VectorField >::write( *this, file ); diff --git a/src/TNL/Functions/VectorFieldVTKWriter.h b/src/TNL/Functions/VectorFieldVTKWriter.h deleted file mode 100644 index 6cfeb80c7..000000000 --- a/src/TNL/Functions/VectorFieldVTKWriter.h +++ /dev/null @@ -1,70 +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 -: protected Meshes::Writers::VTKWriter< typename VectorField::MeshType > -{ - 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: - VectorFieldVTKWriter( std::ostream& str, - Meshes::VTK::FileFormat format = Meshes::VTK::FileFormat::ascii ) - : Meshes::Writers::VTKWriter< MeshType >( str, format ) - {} - - void write( const VectorField& field, - const String& fieldName = "cellVectorFieldValues" ) - { - const MeshType& mesh = field.getMesh(); - this->template writeEntities< VectorField::getEntitiesDimension() >( mesh ); - appendField( field, fieldName ); - } - - // VTK supports writing multiple fields into the same file. - // You can call this after 'write', which initializes the mesh entities, - // with different field name. - void appendField( const VectorField& field, - const String& fieldName ) - { - const MeshType& mesh = field.getMesh(); - const GlobalIndex entitiesCount = mesh.template getEntitiesCount< EntityType >(); - - // copy all values from the vector field into a contiguous array - using BufferType = Containers::Array< typename VectorField::RealType, Devices::Host, GlobalIndex >; - BufferType buffer( 3 * entitiesCount ); - GlobalIndex j = 0; - 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++ ) - buffer[ j++ ] = ( i < vector.getSize() ? vector[ i ] : 0 ); - } - - // write the buffer - if( VectorField::getEntitiesDimension() == 0 ) - this->writeDataArray( buffer, fieldName, 3, Meshes::VTK::DataType::PointData ); - else - this->writeDataArray( buffer, fieldName, 3, Meshes::VTK::DataType::CellData ); - } -}; - -} // namespace Functions -} // namespace TNL diff --git a/src/TNL/Meshes/Writers/VTKWriter.h b/src/TNL/Meshes/Writers/VTKWriter.h index 7111cd8c5..3f68000aa 100644 --- a/src/TNL/Meshes/Writers/VTKWriter.h +++ b/src/TNL/Meshes/Writers/VTKWriter.h @@ -57,17 +57,26 @@ public: 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, - VTK::DataType dataType = VTK::DataType::CellData ); + const int numberOfComponents = 1 ); protected: - void writeHeader(); - void writePoints( const Mesh& mesh ); + void writeHeader(); + std::ostream& str; VTK::FileFormat format; diff --git a/src/TNL/Meshes/Writers/VTKWriter.hpp b/src/TNL/Meshes/Writers/VTKWriter.hpp index 8bad27c04..701d062de 100644 --- a/src/TNL/Meshes/Writers/VTKWriter.hpp +++ b/src/TNL/Meshes/Writers/VTKWriter.hpp @@ -451,13 +451,60 @@ VTKWriter< Mesh >::writeEntities( const Mesh& mesh ) 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, - VTK::DataType dataType ) + 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 ) @@ -465,36 +512,13 @@ VTKWriter< Mesh >::writeDataArray( const Array& array, using HostArray = typename Array::template Self< typename Array::ValueType, Devices::Host >; HostArray hostBuffer; hostBuffer = array; - writeDataArray( hostBuffer, name, numberOfComponents, dataType ); + writeDataArray( hostBuffer, name, numberOfComponents ); return; } if( numberOfComponents != 1 && numberOfComponents != 3 ) throw std::logic_error("Unsupported numberOfComponents parameter: " + std::to_string(numberOfComponents)); - if( dataType == VTK::DataType::CellData ) - 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)"); - if( dataType == VTK::DataType::PointData ) - 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( dataType != currentSection && cellDataArrays * pointDataArrays != 0 ) - throw std::logic_error("The requested data section is not the current section and it has already been written."); - - // start the appropriate section if necessary - if( dataType == VTK::DataType::CellData && cellDataArrays == 0 ) { - str << std::endl << "CELL_DATA " << cellsCount << std::endl; - ++cellDataArrays; - } - if( dataType == VTK::DataType::PointData && pointDataArrays == 0 ) { - str << std::endl << "POINT_DATA " << pointsCount << std::endl; - ++pointDataArrays; - } - // write DataArray header if( numberOfComponents == 1 ) { str << "SCALARS " << name << " " << getType< typename Array::ValueType >() << " 1" << std::endl; @@ -512,17 +536,6 @@ VTKWriter< Mesh >::writeDataArray( const Array& array, } } -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; -} - template< typename Mesh > void VTKWriter< Mesh >::writePoints( const Mesh& mesh ) @@ -543,6 +556,17 @@ VTKWriter< Mesh >::writePoints( const Mesh& mesh ) } } +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/VTUWriter.h b/src/TNL/Meshes/Writers/VTUWriter.h index d91f0dba6..4e61526ce 100644 --- a/src/TNL/Meshes/Writers/VTUWriter.h +++ b/src/TNL/Meshes/Writers/VTUWriter.h @@ -54,6 +54,16 @@ public: 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, @@ -62,12 +72,12 @@ public: ~VTUWriter(); protected: + void writePoints( const Mesh& mesh ); + void writeHeader(); void writeFooter(); - void writePoints( const Mesh& mesh ); - std::ostream& str; VTK::FileFormat format; @@ -84,12 +94,20 @@ protected: // indicator if a tag is open bool pieceOpen = false; - // number of data arrays written in each section - int cellDataArrays = 0; - int pointDataArrays = 0; + // 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(); - // indicator of the current section - VTK::DataType currentSection = VTK::DataType::CellData; + void closePiece(); }; } // namespace Writers diff --git a/src/TNL/Meshes/Writers/VTUWriter.hpp b/src/TNL/Meshes/Writers/VTUWriter.hpp index 669e7c39d..b72d3248c 100644 --- a/src/TNL/Meshes/Writers/VTUWriter.hpp +++ b/src/TNL/Meshes/Writers/VTUWriter.hpp @@ -388,8 +388,7 @@ VTUWriter< Mesh >::writeEntities( const Mesh& mesh ) if( ! vtkfileOpen ) writeHeader(); - if( pieceOpen ) - str << "\n"; + closePiece(); str << "\n"; pieceOpen = true; @@ -414,6 +413,38 @@ VTUWriter< Mesh >::writeEntities( const Mesh& mesh ) 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 @@ -456,6 +487,30 @@ VTUWriter< Mesh >::writeDataArray( const Array& array, 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() @@ -479,8 +534,7 @@ template< typename Mesh > void VTUWriter< Mesh >::writeFooter() { - if( pieceOpen ) - str << "\n"; + closePiece(); str << "\n"; str << "\n"; } @@ -494,26 +548,66 @@ VTUWriter< Mesh >::~VTUWriter() template< typename Mesh > void -VTUWriter< Mesh >::writePoints( const Mesh& mesh ) +VTUWriter< Mesh >::openCellData() { - // 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; + if( cellDataClosed ) + throw std::logic_error("The tag has already been closed in the current section."); + closePointData(); + if( ! cellDataOpen ) { + str << "\n"; + cellDataOpen = true; } +} - // write the buffer - str << "\n"; - writeDataArray( buffer, "Points", 3 ); - str << "\n"; +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 -- GitLab From 32d1c833e62f6073cec7e8babec91522de2f6023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Fri, 20 Mar 2020 21:28:40 +0100 Subject: [PATCH 15/73] Implemented binary and zlib-compressed data formats for VTUWriter --- src/TNL/Meshes/Writers/VTKWriter.hpp | 2 +- src/TNL/Meshes/Writers/VTUWriter.hpp | 38 +++- src/TNL/base64.h | 316 +++++++++++++++++++++++++++ src/TNL/zlib_compression.h | 185 ++++++++++++++++ 4 files changed, 530 insertions(+), 11 deletions(-) create mode 100644 src/TNL/base64.h create mode 100644 src/TNL/zlib_compression.h diff --git a/src/TNL/Meshes/Writers/VTKWriter.hpp b/src/TNL/Meshes/Writers/VTKWriter.hpp index 701d062de..125366d03 100644 --- a/src/TNL/Meshes/Writers/VTKWriter.hpp +++ b/src/TNL/Meshes/Writers/VTKWriter.hpp @@ -12,9 +12,9 @@ #include -#include #include #include +#include namespace TNL { namespace Meshes { diff --git a/src/TNL/Meshes/Writers/VTUWriter.hpp b/src/TNL/Meshes/Writers/VTUWriter.hpp index b72d3248c..b1dd5fc0e 100644 --- a/src/TNL/Meshes/Writers/VTUWriter.hpp +++ b/src/TNL/Meshes/Writers/VTUWriter.hpp @@ -14,9 +14,13 @@ #include -#include #include #include +#include +#include +#ifdef HAVE_ZLIB + #include +#endif namespace TNL { namespace Meshes { @@ -472,16 +476,30 @@ VTUWriter< Mesh >::writeDataArray( const Array& array, str << " NumberOfComponents=\"" << numberOfComponents << "\""; str << " format=\"" << ((format == VTK::FileFormat::ascii) ? "ascii" : "binary") << "\">\n"; - if( format == 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"; + 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::binary: + write_encoded_block< HeaderType >( array.getData(), array.getSize(), str ); + str << "\n"; + break; + case VTK::FileFormat::zlib_compressed: +#ifdef HAVE_ZLIB + write_compressed_block< HeaderType >( array.getData(), array.getSize(), str ); + str << "\n"; + break; +#else + throw std::runtime_error("The ZLIB compression algorithm is not available in this build. Please recompile the program with -DHAVE_ZLIB."); +#endif } - // TODO: binary format, compression // write DataArray footer str << "\n"; diff --git a/src/TNL/base64.h b/src/TNL/base64.h new file mode 100644 index 000000000..cab004bb2 --- /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 000000000..a9c423475 --- /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 -- GitLab From f81992386680ce5cf7ea43e0e559c8310f4248f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sat, 21 Mar 2020 09:04:49 +0100 Subject: [PATCH 16/73] Changed default formats for VTKWriter and VTUWriter to binary and compressed, compressed falls back to binary if zlib is not available --- src/TNL/Meshes/Writers/VTKWriter.h | 2 +- src/TNL/Meshes/Writers/VTUWriter.h | 2 +- src/TNL/Meshes/Writers/VTUWriter.hpp | 13 +++++++------ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/TNL/Meshes/Writers/VTKWriter.h b/src/TNL/Meshes/Writers/VTKWriter.h index 3f68000aa..8010317a3 100644 --- a/src/TNL/Meshes/Writers/VTKWriter.h +++ b/src/TNL/Meshes/Writers/VTKWriter.h @@ -43,7 +43,7 @@ public: VTKWriter() = delete; - VTKWriter( std::ostream& str, VTK::FileFormat format = VTK::FileFormat::ascii ) + VTKWriter( std::ostream& str, VTK::FileFormat format = VTK::FileFormat::binary ) : str(str), format(format) { if( format != VTK::FileFormat::ascii && format != VTK::FileFormat::binary ) diff --git a/src/TNL/Meshes/Writers/VTUWriter.h b/src/TNL/Meshes/Writers/VTUWriter.h index 4e61526ce..9f715dce6 100644 --- a/src/TNL/Meshes/Writers/VTUWriter.h +++ b/src/TNL/Meshes/Writers/VTUWriter.h @@ -43,7 +43,7 @@ public: VTUWriter() = delete; - VTUWriter( std::ostream& str, VTK::FileFormat format = VTK::FileFormat::ascii ) + VTUWriter( std::ostream& str, VTK::FileFormat format = VTK::FileFormat::zlib_compressed ) : str(str), format(format) {} diff --git a/src/TNL/Meshes/Writers/VTUWriter.hpp b/src/TNL/Meshes/Writers/VTUWriter.hpp index b1dd5fc0e..8d609f0a7 100644 --- a/src/TNL/Meshes/Writers/VTUWriter.hpp +++ b/src/TNL/Meshes/Writers/VTUWriter.hpp @@ -487,18 +487,17 @@ VTUWriter< Mesh >::writeDataArray( const Array& array, str << +array[i] << " "; str << "\n"; break; - case VTK::FileFormat::binary: - write_encoded_block< HeaderType >( array.getData(), array.getSize(), str ); - str << "\n"; - break; case VTK::FileFormat::zlib_compressed: #ifdef HAVE_ZLIB write_compressed_block< HeaderType >( array.getData(), array.getSize(), str ); str << "\n"; break; -#else - throw std::runtime_error("The ZLIB compression algorithm is not available in this build. Please recompile the program with -DHAVE_ZLIB."); #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 @@ -540,8 +539,10 @@ VTUWriter< Mesh >::writeHeader() else str << " byte_order=\"BigEndian\""; str << " header_type=\"" << VTK::getTypeName( HeaderType{} ) << "\""; +#ifdef HAVE_ZLIB if( format == VTK::FileFormat::zlib_compressed ) str << " compressor=\"vtkZLibDataCompressor\""; +#endif str << ">\n"; str << "\n"; -- GitLab From d6328b9f75a43d4411b6205f0c324a347159ae40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sat, 21 Mar 2020 09:21:20 +0100 Subject: [PATCH 17/73] Added VTU format to MeshFunction::write, VectorField::write and tnl-view --- src/TNL/Functions/MeshFunction.hpp | 9 ++++ src/TNL/Functions/MeshFunctionView.hpp | 9 ++++ src/TNL/Functions/VectorField.h | 67 +++++++++++++++--------- src/Tools/CMakeLists.txt | 7 +++ src/Tools/tnl-view.cpp | 1 + src/Tools/tnl-view.h | 72 +++++++------------------- 6 files changed, 88 insertions(+), 77 deletions(-) diff --git a/src/TNL/Functions/MeshFunction.hpp b/src/TNL/Functions/MeshFunction.hpp index df4e77b1d..6d8f91ebc 100644 --- a/src/TNL/Functions/MeshFunction.hpp +++ b/src/TNL/Functions/MeshFunction.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #pragma once @@ -413,6 +414,14 @@ write( const String& fileName, 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/MeshFunctionView.hpp b/src/TNL/Functions/MeshFunctionView.hpp index 980978e72..15b70c4ed 100644 --- a/src/TNL/Functions/MeshFunctionView.hpp +++ b/src/TNL/Functions/MeshFunctionView.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #pragma once @@ -498,6 +499,14 @@ write( const String& fileName, 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 a18fa1b8f..f0e875608 100644 --- a/src/TNL/Functions/VectorField.h +++ b/src/TNL/Functions/VectorField.h @@ -17,6 +17,7 @@ #include #include #include +#include namespace TNL { namespace Functions { @@ -259,10 +260,7 @@ class VectorField< Size, MeshFunction< Mesh, MeshEntityDimension, Real > > std::cerr << "Unable to open a file " << fileName << "." << std::endl; return false; } - if( format == "vtk" ) { - Meshes::Writers::VTKWriter< Mesh > writer( file ); - writer.template writeEntities< getEntitiesDimension() >( *getMeshPointer() ); - + 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() >(); @@ -270,16 +268,27 @@ class VectorField< Size, MeshFunction< Mesh, MeshEntityDimension, Real > > IndexType k = 0; for( IndexType i = 0; i < entitiesCount; i++ ) { const VectorType vector = getElement( i ); - static_assert( getVectorDimension() <= 3, "The VTK format supports only up to 3D vector fields." ); + 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 ); } - // write the buffer - if( getEntitiesDimension() == 0 ) - writer.writePointData( buffer, "cellVectorFieldValues", 3 ); - else - writer.writeCellData( buffer, "pointVectorFieldValues", 3 ); + 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 ); @@ -524,10 +533,7 @@ class VectorField< Size, MeshFunctionView< Mesh, MeshEntityDimension, Real > > std::cerr << "Unable to open a file " << fileName << "." << std::endl; return false; } - if( format == "vtk" ) { - Meshes::Writers::VTKWriter< Mesh > writer( file ); - writer.template writeEntities< getEntitiesDimension() >( *getMeshPointer() ); - + 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() >(); @@ -535,16 +541,27 @@ class VectorField< Size, MeshFunctionView< Mesh, MeshEntityDimension, Real > > IndexType k = 0; for( IndexType i = 0; i < entitiesCount; i++ ) { const VectorType vector = getElement( i ); - static_assert( getVectorDimension() <= 3, "The VTK format supports only up to 3D vector fields." ); + 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 ); } - // write the buffer - if( getEntitiesDimension() == 0 ) - writer.writePointData( buffer, "cellVectorFieldValues", 3 ); - else - writer.writeCellData( buffer, "pointVectorFieldValues", 3 ); + 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 ); @@ -554,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/Tools/CMakeLists.txt b/src/Tools/CMakeLists.txt index 787bf40bf..45d31e3e9 100644 --- a/src/Tools/CMakeLists.txt +++ b/src/Tools/CMakeLists.txt @@ -24,6 +24,13 @@ 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}) +endif() + IF( BUILD_CUDA ) CUDA_ADD_EXECUTABLE( tnl-cuda-arch tnl-cuda-arch.cu ) INSTALL( TARGETS tnl-cuda-arch diff --git a/src/Tools/tnl-view.cpp b/src/Tools/tnl-view.cpp index 239a60e47..202249202 100644 --- a/src/Tools/tnl-view.cpp +++ b/src/Tools/tnl-view.cpp @@ -77,6 +77,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 ); } diff --git a/src/Tools/tnl-view.h b/src/Tools/tnl-view.h index e4ba96b9b..5fdf53e37 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 @@ -26,23 +25,13 @@ 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 +53,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 +80,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 +219,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; @@ -340,8 +317,8 @@ struct FilesProcessor { static bool run( const Config::ParameterContainer& parameters ) { - int verbose = parameters. getParameter< int >( "verbose"); - String meshFile = parameters. getParameter< String >( "mesh" ); + int verbose = parameters.getParameter< int >( "verbose"); + String meshFile = parameters.getParameter< String >( "mesh" ); typedef Pointers::SharedPointer< Mesh > MeshPointer; MeshPointer meshPointer; @@ -353,7 +330,7 @@ struct FilesProcessor return false; } - bool checkOutputFile = parameters. getParameter< bool >( "check-output-file" ); + 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 @@ -364,15 +341,8 @@ struct FilesProcessor 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; - } + String outputFormat = parameters.getParameter< String >( "output-format" ); + String outputFileName = getOutputFileName( inputFiles[ i ], outputFormat ); if( checkOutputFile && fileExists( outputFileName ) ) { if( verbose ) @@ -416,5 +386,3 @@ struct FilesProcessor return ! error; } }; - -#endif /* TNL_VIEW_H_ */ -- GitLab From e65073adbfe554fb3fc29015bc82d0f218dd0b84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sun, 22 Mar 2020 16:15:45 +0100 Subject: [PATCH 18/73] MeshTypeResolver: enabled matching of more typedefs --- .../TypeResolver/MeshTypeResolver_impl.h | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h b/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h index f59838a30..5bdd34f9c 100644 --- a/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h +++ b/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h @@ -139,7 +139,7 @@ resolveReal( const Reader& reader, return resolveGlobalIndex< CellTopology, WorldDimension, float >( reader, std::forward(problemSetterArgs)... ); if( reader.getRealType() == "double" ) return resolveGlobalIndex< CellTopology, WorldDimension, double >( reader, std::forward(problemSetterArgs)... ); - if( reader.getRealType() == "long-double" ) + if( reader.getRealType() == "long double" ) return resolveGlobalIndex< CellTopology, WorldDimension, long double >( reader, std::forward(problemSetterArgs)... ); std::cerr << "Unsupported real type: " << reader.getRealType() << std::endl; return false; @@ -177,11 +177,17 @@ MeshTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... resolveGlobalIndex( const Reader& reader, ProblemSetterArgs&&... problemSetterArgs ) { - if( reader.getGlobalIndexType() == "short int" ) + if( reader.getGlobalIndexType() == "short int" || + reader.getGlobalIndexType() == "std::int16_t" || + reader.getGlobalIndexType() == "std::uint16_t" ) return resolveLocalIndex< CellTopology, WorldDimension, Real, short int >( reader, std::forward(problemSetterArgs)... ); - if( reader.getGlobalIndexType() == "int" ) + if( reader.getGlobalIndexType() == "int" || + reader.getGlobalIndexType() == "std::int32_t" || + reader.getGlobalIndexType() == "std::uint32_t" ) return resolveLocalIndex< CellTopology, WorldDimension, Real, int >( reader, std::forward(problemSetterArgs)... ); - if( reader.getGlobalIndexType() == "long int" ) + if( reader.getGlobalIndexType() == "long int" || + reader.getGlobalIndexType() == "std::int64_t" || + reader.getGlobalIndexType() == "std::uint64_t" ) return resolveLocalIndex< CellTopology, WorldDimension, Real, long int >( reader, std::forward(problemSetterArgs)... ); std::cerr << "Unsupported global index type: " << reader.getGlobalIndexType() << std::endl; return false; @@ -221,11 +227,17 @@ MeshTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... resolveLocalIndex( const Reader& reader, ProblemSetterArgs&&... problemSetterArgs ) { - if( reader.getLocalIndexType() == "short int" ) + if( reader.getLocalIndexType() == "short int" || + reader.getLocalIndexType() == "std::int16_t" || + reader.getLocalIndexType() == "std::uint16_t" ) return resolveId< CellTopology, WorldDimension, Real, GlobalIndex, short int >( reader, std::forward(problemSetterArgs)... ); - if( reader.getLocalIndexType() == "int" ) + if( reader.getLocalIndexType() == "int" || + reader.getLocalIndexType() == "std::int32_t" || + reader.getLocalIndexType() == "std::uint32_t" ) return resolveId< CellTopology, WorldDimension, Real, GlobalIndex, int >( reader, std::forward(problemSetterArgs)... ); - if( reader.getLocalIndexType() == "long int" ) + if( reader.getLocalIndexType() == "long int" || + reader.getLocalIndexType() == "std::int64_t" || + reader.getLocalIndexType() == "std::uint64_t" ) return resolveId< CellTopology, WorldDimension, Real, GlobalIndex, long int >( reader, std::forward(problemSetterArgs)... ); std::cerr << "Unsupported local index type: " << reader.getLocalIndexType() << std::endl; return false; @@ -267,11 +279,17 @@ MeshTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... resolveId( const Reader& reader, ProblemSetterArgs&&... problemSetterArgs ) { - if( reader.getIdType() == "short int" ) + if( reader.getIdType() == "short int" || + reader.getIdType() == "std::int16_t" || + reader.getIdType() == "std::uint16_t" ) return resolveMeshType< CellTopology, WorldDimension, Real, GlobalIndex, LocalIndex, short int >( reader, std::forward(problemSetterArgs)... ); - if( reader.getIdType() == "int" ) + if( reader.getIdType() == "int" || + reader.getIdType() == "std::int32_t" || + reader.getIdType() == "std::uint32_t" ) return resolveMeshType< CellTopology, WorldDimension, Real, GlobalIndex, LocalIndex, int >( reader, std::forward(problemSetterArgs)... ); - if( reader.getIdType() == "long int" ) + if( reader.getIdType() == "long int" || + reader.getIdType() == "std::int64_t" || + reader.getIdType() == "std::uint64_t" ) 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)... ); -- GitLab From 5ba08aff8e8874d944c4db4285b2ae7958cbfea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sun, 22 Mar 2020 16:16:32 +0100 Subject: [PATCH 19/73] Added VTUReader.h --- src/TNL/Meshes/Readers/VTUReader.h | 562 ++++++++++++++++++ .../Meshes/TypeResolver/TypeResolver_impl.h | 24 +- src/Tools/CMakeLists.txt | 10 + 3 files changed, 594 insertions(+), 2 deletions(-) create mode 100644 src/TNL/Meshes/Readers/VTUReader.h diff --git a/src/TNL/Meshes/Readers/VTUReader.h b/src/TNL/Meshes/Readers/VTUReader.h new file mode 100644 index 000000000..7c46694a8 --- /dev/null +++ b/src/TNL/Meshes/Readers/VTUReader.h @@ -0,0 +1,562 @@ +/*************************************************************************** + 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 */ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +#ifdef HAVE_ZLIB + #include +#endif + +#ifdef HAVE_TINYXML2 + #include +#endif + +namespace TNL { +namespace Meshes { +namespace Readers { + +struct XMLVTKError + : public std::runtime_error +{ + XMLVTKError( std::string msg ) + : std::runtime_error( "XMLVTKReader error: " + msg ) + {} +}; + +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 VTUReader +{ + 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 > >; + +#ifdef HAVE_TINYXML2 + static void verifyElement( const tinyxml2::XMLElement* elem, const std::string name ) + { + if( ! elem ) + throw XMLVTKError( "tag <" + name + "> not found" ); + if( elem->Name() != name ) + throw XMLVTKError( "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 XMLVTKError( "element " + parentName + " does not contain any child" ); + if( elem->NextSibling() ) + throw XMLVTKError( "<" + 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 XMLVTKError( "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 XMLVTKError( "element <" + std::string(elem->Name()) + "> does not have the attribute '" + name + "' or it could not be converted to int64_t" ); + 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 ) + { + verifyElement( elem, "DataArray" ); + // verify Name + getAttributeString( elem, "Name" ); + // verify type + const std::string type = getAttributeString( elem, "type" ); + if( VTKDataTypes.count( type ) == 0 ) + throw XMLVTKError( "unsupported DataArray type: " + type ); + // verify format + const std::string format = getAttributeString( elem, "format" ); + if( format != "ascii" && format != "binary" ) + throw XMLVTKError( "unsupported DataArray 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 XMLVTKError( "unsupported NumberOfComponents in DataArray: " + 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 XMLVTKError& ) {} + if( arrayName == name ) { + if( found == nullptr ) + found = child; + else + throw XMLVTKError( "the <" + std::string(parent->Name()) + "> tag contains multiple tags with the Name=\"" + name + "\" attribute" ); + } + child = child->NextSiblingElement( "DataArray" ); + } + if( found == nullptr ) + throw XMLVTKError( "the <" + std::string(parent->Name()) + "> tag does not contain any tag with the Name=\"" + name + "\" attribute" ); + verifyDataArray( found ); + return found; + } + + template< typename HeaderType > + std::size_t + readBlockSize( const char* block ) const + { + std::pair> decoded_data = decode_block( block, get_encoded_length(sizeof(HeaderType)) ); + if( decoded_data.first != sizeof(HeaderType) ) + throw XMLVTKError( "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 XMLVTKError( "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 XMLVTKError( "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 XMLVTKError( "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 XMLVTKError( "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 XMLVTKError( "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 XMLVTKError( "unsupported DataArray type: " + type ); + } + else + throw XMLVTKError( "unsupported DataArray format: " + format ); + } + + void readUnstructuredGrid( const tinyxml2::XMLElement* elem ) + { + using namespace tinyxml2; + const XMLElement* piece = getChildSafe( elem, "Piece" ); + if( piece->NextSiblingElement( "Piece" ) ) + // ambiguity - throw error, we don't know which piece to parse (or all of them?) + throw XMLVTKError( "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 XMLVTKError( "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 XMLVTKError( "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 XMLVTKError( "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 XMLVTKError( "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 XMLVTKError( "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 XMLVTKError( "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 XMLVTKError( "size of the offsets data array does not match the NumberOfCells attribute" ); + for( auto c : array ) { + if( c <= (decltype(c)) max_offset ) + throw XMLVTKError( "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 XMLVTKError( "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 XMLVTKError( "connectivity index " + std::to_string(c) + " is out of range" ); + } + }, + connectivityArray + ); + + } +#endif + +public: + bool detectMesh( const std::string& fileName ) + { +#ifdef HAVE_TINYXML2 + this->reset(); + this->fileName = fileName; + + using namespace tinyxml2; + + XMLDocument dom; + XMLError status; + + // load and verify XML + status = dom.LoadFile( fileName.c_str() ); + if( status != XML_SUCCESS ) + throw XMLVTKError( "VTUReader: failed to parse the file as an XML document." ); + + // verify root element + const XMLElement* elem = dom.FirstChildElement(); + verifyElement( elem, "VTKFile" ); + if( elem->NextSibling() ) + throw XMLVTKError( " 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 XMLVTKError( "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 XMLVTKError( "invalid header_type: " + headerType ); + headerType = VTKDataTypes.at( headerType ); + + // verify compressor + compressor = getAttributeString( elem, "compressor", "" ); + if( compressor == "" ) + compressor = ""; + if( compressor != "" && compressor != "vtkZLibDataCompressor" ) + throw XMLVTKError( "unsupported compressor type: " + compressor + " (only vtkZLibDataCompressor is supported)" ); + + // verify file type + fileType = getAttributeString( elem, "type" ); + elem = verifyHasOnlyOneChild( elem, fileType ); + if( fileType == "UnstructuredGrid" ) + readUnstructuredGrid( elem ); + else + // TODO: generalize the reader for other XML VTK formats + throw XMLVTKError( "parsing the " + fileType + " files is not implemented (yet)" ); + + return true; +#else + 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."); +#endif + } + + template< typename MeshType > + bool readMesh( const std::string& fileName, MeshType& mesh ) + { + // reading is actually done in detectMesh, but the mesh type resolver discards the first reader instance + if( ! detectMesh( fileName ) ) + return false; + + // check that the cell shape mathes + const VTK::EntityShape meshCellShape = VTK::TopologyToEntityShape< typename MeshType::template EntityTraits< MeshType::getMeshDimension() >::EntityTopology >::shape; + if( meshCellShape != cellShape ) + throw XMLVTKError( "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( [this, &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 = {}; + + return meshBuilder.build( mesh ); + } + + std::string + getMeshType() const + { + return "Meshes::Mesh"; + } + + int getMeshDimension() const + { + return this->meshDimension; + } + + int + getWorldDimension() const + { + return worldDimension; + } + + VTK::EntityShape + getCellShape() const + { + return cellShape; + } + + std::string + getRealType() const + { + return pointsType.c_str(); + } + + std::string + getGlobalIndexType() const + { + return connectivityType; + } + + std::string + getLocalIndexType() const + { + // not stored in the VTK file + return "short int"; + } + + std::string + getIdType() const + { + // not stored in the VTK file + return getGlobalIndexType(); + } + +protected: + std::string fileName; + + // VTK file type + std::string fileType; + + std::size_t NumberOfPoints, NumberOfCells; + int meshDimension, worldDimension; + VTK::EntityShape cellShape = VTK::EntityShape::Vertex; + std::string byteOrder, compressor, headerType; + + // arrays holding the s from the VTK file + VariantVector pointsArray, connectivityArray, offsetsArray, typesArray; + // string representation of each array's value type + std::string pointsType, connectivityType, offsetsType, typesType; + + void reset() + { + fileName = fileType = ""; + NumberOfPoints = NumberOfCells = 0; + meshDimension = worldDimension = 0; + cellShape = VTK::EntityShape::Vertex; + byteOrder = compressor = headerType = ""; + pointsArray = connectivityArray = offsetsArray = typesArray = {}; + pointsType = connectivityType = offsetsType = typesType = ""; + } +}; + +} // namespace Readers +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h b/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h index f18188b9c..a0f87b58a 100644 --- a/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h +++ b/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -105,8 +106,23 @@ bool resolveMeshType( const String& fileName_, return false; } } + else if( ends_with( fileName, ".vtu" ) ) { + // FIXME: The XML VTK files don't store local index and id 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::VTUReader reader; + if( ! reader.detectMesh( fileName_ ) ) + return false; + if( reader.getMeshType() == "Meshes::Mesh" ) + return MeshTypeResolver< decltype(reader), ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: + run( reader, std::forward(problemSetterArgs)... ); + 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; + std::cerr << "File '" << fileName << "' has unknown extension. Supported extensions are '.tnl', '.vtk', '.vtu' and '.ng'." << std::endl; return false; } } @@ -140,8 +156,12 @@ loadMesh( const String& fileName, Readers::VTKReader reader; status = reader.readMesh( fileName, mesh ); } + else if( ends_with( fileName_, ".vtu" ) ) { + Readers::VTUReader reader; + status = reader.readMesh( fileName, mesh ); + } else { - std::cerr << "File '" << fileName << "' has unknown extension. Supported extensions are '.tnl', '.vtk' and '.ng'." << std::endl; + std::cerr << "File '" << fileName << "' has unknown extension. Supported extensions are '.tnl', '.vtk', '.vtu' and '.ng'." << std::endl; return false; } diff --git a/src/Tools/CMakeLists.txt b/src/Tools/CMakeLists.txt index 45d31e3e9..fb34e032c 100644 --- a/src/Tools/CMakeLists.txt +++ b/src/Tools/CMakeLists.txt @@ -29,6 +29,16 @@ 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}) +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) endif() IF( BUILD_CUDA ) -- GitLab From 1575f02f421f65af8d5cd5894237d7eeeb6fdbd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Wed, 25 Mar 2020 16:57:06 +0100 Subject: [PATCH 20/73] Big mesh refactoring Main changes: - mesh pointer added to MeshEntity, all topology accessors redirected through it to the mesh - storage of points and subentity orientations moved from MeshEntity to Mesh - removed MeshEntityIndex - MeshEntity always stores its index as the GlobalIndexType - removed entity storage from Mesh - mesh entities can be generated on the fly Plus some minor related simplifications, mainly in the mesh initializer. --- src/Python/pytnl/tnl/Mesh.h | 23 +- src/Python/pytnl/typedefs.h | 9 +- .../Multimaps/EllpackIndexMultimap.h | 6 + .../Multimaps/EllpackIndexMultimap.hpp | 37 ++ .../Multimaps/StaticEllpackIndexMultimap.h | 6 + .../Multimaps/StaticEllpackIndexMultimap.hpp | 36 ++ src/TNL/Meshes/BuildConfigTags.h | 16 +- src/TNL/Meshes/DefaultConfig.h | 4 +- src/TNL/Meshes/Geometry/getEntityCenter.h | 2 +- src/TNL/Meshes/Mesh.h | 58 ++- .../EntityLayers/SubentityAccess.h | 419 ------------------ .../EntityLayers/SuperentityAccess.h | 288 ------------ .../MeshDetails/EntityStorageRebinder.h | 159 ------- .../MeshDetails/IndexPermutationApplier.h | 44 +- src/TNL/Meshes/MeshDetails/MeshEntityIndex.h | 75 ---- src/TNL/Meshes/MeshDetails/MeshEntity_impl.h | 275 +++--------- .../MeshDetails/MeshLayers/StorageLayer.h | 162 +++++-- .../MeshLayers/SubentityOrientationsLayer.h | 172 +++++++ .../MeshLayers/SubentityStorageLayer.h | 17 + .../MeshLayers/SuperentityStorageLayer.h | 17 + src/TNL/Meshes/MeshDetails/Mesh_impl.h | 104 +++-- src/TNL/Meshes/MeshDetails/Traverser_impl.h | 9 +- .../initializer/EntityInitializer.h | 37 +- .../MeshDetails/initializer/Initializer.h | 74 +--- .../initializer/SubentitySeedsCreator.h | 12 +- .../MeshDetails/traits/MeshSubentityTraits.h | 2 +- .../traits/MeshSuperentityTraits.h | 1 - src/TNL/Meshes/MeshEntity.h | 172 ++----- .../Meshes/TypeResolver/MeshTypeResolver.h | 33 +- .../TypeResolver/MeshTypeResolver_impl.h | 66 +-- src/TNL/Meshes/Writers/NetgenWriter.h | 2 +- src/TNL/Meshes/Writers/VerticesPerEntity.h | 2 +- src/Tools/tnl-grid-to-mesh.cpp | 9 +- src/Tools/tnl-mesh-converter.cpp | 3 - src/Tools/tnl-view.cpp | 3 - src/UnitTests/Meshes/CMakeLists.txt | 15 +- src/UnitTests/Meshes/MeshOrderingTest.h | 22 +- src/UnitTests/Meshes/MeshTest.h | 28 +- src/UnitTests/Meshes/MeshTraverserTest.h | 4 +- 39 files changed, 754 insertions(+), 1669 deletions(-) delete mode 100644 src/TNL/Meshes/MeshDetails/EntityLayers/SubentityAccess.h delete mode 100644 src/TNL/Meshes/MeshDetails/EntityLayers/SuperentityAccess.h delete mode 100644 src/TNL/Meshes/MeshDetails/EntityStorageRebinder.h delete mode 100644 src/TNL/Meshes/MeshDetails/MeshEntityIndex.h create mode 100644 src/TNL/Meshes/MeshDetails/MeshLayers/SubentityOrientationsLayer.h diff --git a/src/Python/pytnl/tnl/Mesh.h b/src/Python/pytnl/tnl/Mesh.h index c0207e243..6b40ce58c 100644 --- a/src/Python/pytnl/tnl/Mesh.h +++ b/src/Python/pytnl/tnl/Mesh.h @@ -13,13 +13,6 @@ namespace py = pybind11; #include -template< typename MeshEntity > -typename MeshEntity::MeshTraitsType::GlobalIndexType -getIndex( const MeshEntity& entity ) -{ - return entity.getIndex(); -}; - struct _general {}; struct _special : _general {}; @@ -88,9 +81,7 @@ 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 ; @@ -103,9 +94,9 @@ 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 +107,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 b06693ab6..7a74237f0 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 index 5e575cc21..8684ba3b1 100644 --- a/src/TNL/Containers/Multimaps/EllpackIndexMultimap.h +++ b/src/TNL/Containers/Multimaps/EllpackIndexMultimap.h @@ -70,6 +70,12 @@ class EllpackIndexMultimap __cuda_callable__ ConstValuesAccessorType getValues( const IndexType& inputIndex ) const; + __cuda_callable__ + LocalIndexType getValuesCount( const IndexType& inputIndex ) const; + + __cuda_callable__ + IndexType getValue( const IndexType& inputIndex, const LocalIndexType& portIndex ) const; + bool operator==( const EllpackIndexMultimap& other ) const; void save( File& file ) const; diff --git a/src/TNL/Containers/Multimaps/EllpackIndexMultimap.hpp b/src/TNL/Containers/Multimaps/EllpackIndexMultimap.hpp index c75250029..0872d8501 100644 --- a/src/TNL/Containers/Multimaps/EllpackIndexMultimap.hpp +++ b/src/TNL/Containers/Multimaps/EllpackIndexMultimap.hpp @@ -185,6 +185,43 @@ getValues( const IndexType& inputIndex ) const return ConstValuesAccessorType( &this->values[ offset ], &this->valuesCounts[ inputIndex ], this->maxValuesCount ); } +template< typename Index, + typename Device, + typename LocalIndex, + int SliceSize > +__cuda_callable__ +LocalIndex +EllpackIndexMultimap< Index, Device, LocalIndex, SliceSize >:: +getValuesCount( const IndexType& inputIndex ) const +{ + return valuesCounts[ inputIndex ]; +} + +template< typename Index, + typename Device, + typename LocalIndex, + int SliceSize > +__cuda_callable__ +Index +EllpackIndexMultimap< Index, Device, LocalIndex, SliceSize >:: +getValue( const IndexType& inputIndex, const LocalIndexType& portIndex ) 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 this->values[ offset + portIndex * SliceSize ]; +} + template< typename Index, typename Device, typename LocalIndex, diff --git a/src/TNL/Containers/Multimaps/StaticEllpackIndexMultimap.h b/src/TNL/Containers/Multimaps/StaticEllpackIndexMultimap.h index f816cabd2..c8f2aebaa 100644 --- a/src/TNL/Containers/Multimaps/StaticEllpackIndexMultimap.h +++ b/src/TNL/Containers/Multimaps/StaticEllpackIndexMultimap.h @@ -68,6 +68,12 @@ class StaticEllpackIndexMultimap __cuda_callable__ ConstValuesAccessorType getValues( const IndexType& inputIndex ) const; + __cuda_callable__ + constexpr LocalIndexType getValuesCount( const IndexType& inputIndex ) const; + + __cuda_callable__ + IndexType getValue( const IndexType& inputIndex, const LocalIndexType& portIndex ) const; + bool operator==( const StaticEllpackIndexMultimap& other ) const; void save( File& file ) const; diff --git a/src/TNL/Containers/Multimaps/StaticEllpackIndexMultimap.hpp b/src/TNL/Containers/Multimaps/StaticEllpackIndexMultimap.hpp index 7af150615..8654ac0a7 100644 --- a/src/TNL/Containers/Multimaps/StaticEllpackIndexMultimap.hpp +++ b/src/TNL/Containers/Multimaps/StaticEllpackIndexMultimap.hpp @@ -152,6 +152,42 @@ getValues( const IndexType& inputIndex ) const return ConstValuesAccessorType( &this->values[ offset ] ); } +template< int ValuesCount, + typename Index, + typename Device, + typename LocalIndex, + int SliceSize > +__cuda_callable__ +constexpr LocalIndex +StaticEllpackIndexMultimap< ValuesCount, Index, Device, LocalIndex, SliceSize >:: +getValuesCount( const IndexType& inputIndex ) const +{ + return ValuesCount; +} + +template< int ValuesCount, + typename Index, + typename Device, + typename LocalIndex, + int SliceSize > +__cuda_callable__ +Index +StaticEllpackIndexMultimap< ValuesCount, Index, Device, LocalIndex, SliceSize >:: +getValue( const IndexType& inputIndex, const LocalIndexType& portIndex ) 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 this->values[ offset + portIndex * SliceSize ]; +} + template< int ValuesCount, typename Index, typename Device, diff --git a/src/TNL/Meshes/BuildConfigTags.h b/src/TNL/Meshes/BuildConfigTags.h index 35aa2b2f8..0481f65a0 100644 --- a/src/TNL/Meshes/BuildConfigTags.h +++ b/src/TNL/Meshes/BuildConfigTags.h @@ -94,17 +94,12 @@ template< typename ConfigTag, typename GlobalIndex > struct MeshGlobalIndexTag { // 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 }; }; - // 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 +116,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 +127,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 366356474..800b92b4f 100644 --- a/src/TNL/Meshes/DefaultConfig.h +++ b/src/TNL/Meshes/DefaultConfig.h @@ -33,15 +33,13 @@ template< typename Cell, int WorldDimension = Cell::dimension, typename Real = double, typename GlobalIndex = int, - typename LocalIndex = GlobalIndex, - typename Id = void > + typename LocalIndex = GlobalIndex > 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; diff --git a/src/TNL/Meshes/Geometry/getEntityCenter.h b/src/TNL/Meshes/Geometry/getEntityCenter.h index 4cceff706..cdcf6cefb 100644 --- a/src/TNL/Meshes/Geometry/getEntityCenter.h +++ b/src/TNL/Meshes/Geometry/getEntityCenter.h @@ -50,7 +50,7 @@ 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; diff --git a/src/TNL/Meshes/Mesh.h b/src/TNL/Meshes/Mesh.h index 4d71e3ac9..488b0b103 100644 --- a/src/TNL/Meshes/Mesh.h +++ b/src/TNL/Meshes/Mesh.h @@ -20,7 +20,6 @@ #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; @@ -110,7 +114,9 @@ class Mesh virtual String getSerializationTypeVirtual() const; - + /** + * Entities + */ template< int Dimension > static constexpr bool entitiesAvailable(); @@ -120,12 +126,7 @@ class Mesh 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 +135,38 @@ class Mesh template< typename EntityType > __cuda_callable__ - EntityType& getEntity( const GlobalIndexType& entityIndex ); + EntityType getEntity( const GlobalIndexType entityIndex ) const; + + /** + * Points + */ + __cuda_callable__ + const PointType& getPoint( const GlobalIndexType vertexIndex ) const; - template< typename EntityType > __cuda_callable__ - const EntityType& getEntity( const GlobalIndexType& entityIndex ) const; + PointType& getPoint( const GlobalIndexType vertexIndex ); + + /** + * Subentities + */ + template< int EntityDimension, int SubentityDimension > + __cuda_callable__ + 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; /* @@ -169,8 +197,8 @@ class Mesh DistributedMeshes::DistributedMesh< Mesh >* getDistributedMesh(void) const { - return NULL; - }; + return nullptr; + } protected: // Methods for the mesh initializer @@ -180,8 +208,6 @@ class Mesh friend Initializer< MeshConfig >; - friend EntityStorageRebinder< Mesh >; - template< typename Mesh, int Dimension > friend struct IndexPermutationApplier; }; @@ -192,4 +218,6 @@ std::ostream& operator<<( std::ostream& str, const Mesh< MeshConfig, Device >& m } // namespace Meshes } // namespace TNL +#include + #include diff --git a/src/TNL/Meshes/MeshDetails/EntityLayers/SubentityAccess.h b/src/TNL/Meshes/MeshDetails/EntityLayers/SubentityAccess.h deleted file mode 100644 index 4adc400d4..000000000 --- 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 474571816..000000000 --- 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 c956d3169..000000000 --- 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 bd9c02411..079c08cf6 100644 --- a/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h +++ b/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h @@ -121,50 +121,60 @@ private: using SuperentitiesWorker = IndexPermutationApplierSuperentitiesWorker< Superdimension >; public: - static void exec( Mesh& mesh, - const GlobalIndexVector& perm, - const GlobalIndexVector& iperm ) + static void permutePoints( Mesh& mesh, + const GlobalIndexVector& perm, + const GlobalIndexVector& iperm ) { using IndexType = typename Mesh::GlobalIndexType; using DeviceType = typename Mesh::DeviceType; - using StorageArrayType = typename Mesh::template EntityTraits< Dimension >::StorageArrayType; + using PointArrayType = typename Mesh::MeshTraitsType::PointArrayType; - const IndexType entitiesCount = mesh.template getEntitiesCount< Dimension >(); + const IndexType pointsCount = mesh.template getEntitiesCount< 0 >(); - StorageArrayType entities; - entities.setSize( entitiesCount ); + PointArrayType points; + points.setSize( pointsCount ); // kernel to copy entities to new array, applying the permutation auto kernel1 = [] __cuda_callable__ ( IndexType i, const Mesh* mesh, - typename StorageArrayType::ValueType* entitiesArray, + typename PointArrayType::ValueType* pointsArray, const IndexType* perm ) { - entitiesArray[ i ] = mesh->template getEntity< Dimension >( perm[ i ] ); + pointsArray[ i ] = mesh->getPoint( perm[ i ] ); }; // kernel to copy permuted entities back to the mesh auto kernel2 = [] __cuda_callable__ ( IndexType i, Mesh* mesh, - const typename StorageArrayType::ValueType* entitiesArray ) + const typename PointArrayType::ValueType* pointsArray ) { - auto& entity = mesh->template getEntity< Dimension >( i ); - entity = entitiesArray[ i ]; - entity.setIndex( i ); + mesh->getPoint( i ) = pointsArray[ i ]; }; Pointers::DevicePointer< Mesh > meshPointer( mesh ); - Algorithms::ParallelFor< DeviceType >::exec( (IndexType) 0, entitiesCount, + Algorithms::ParallelFor< DeviceType >::exec( (IndexType) 0, pointsCount, kernel1, &meshPointer.template getData< DeviceType >(), - entities.getData(), + points.getData(), perm.getData() ); - Algorithms::ParallelFor< DeviceType >::exec( (IndexType) 0, entitiesCount, + Algorithms::ParallelFor< DeviceType >::exec( (IndexType) 0, pointsCount, kernel2, &meshPointer.template modifyData< DeviceType >(), - entities.getData() ); + points.getData() ); + } + + static void exec( Mesh& mesh, + const GlobalIndexVector& perm, + const GlobalIndexVector& iperm ) + { + using IndexType = typename Mesh::GlobalIndexType; + using DeviceType = typename Mesh::DeviceType; + using StorageArrayType = typename Mesh::template EntityTraits< Dimension >::StorageArrayType; + + if( Dimension == 0 ) + permutePoints( mesh, perm, iperm ); // permute superentities storage Algorithms::TemplateStaticFor< int, 0, Dimension, SubentitiesStorageWorker >::execHost( mesh, perm ); diff --git a/src/TNL/Meshes/MeshDetails/MeshEntityIndex.h b/src/TNL/Meshes/MeshDetails/MeshEntityIndex.h deleted file mode 100644 index 110fa9eef..000000000 --- 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 index 5c7414b42..b14e85427 100644 --- a/src/TNL/Meshes/MeshDetails/MeshEntity_impl.h +++ b/src/TNL/Meshes/MeshDetails/MeshEntity_impl.h @@ -26,309 +26,141 @@ template< typename MeshConfig, 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 ) +MeshEntity( const MeshType& mesh, const GlobalIndexType index ) +: meshPointer( &mesh ), + index( index ) { } -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 >& +bool MeshEntity< MeshConfig, Device, EntityTopology >:: -operator=( const MeshEntity& entity ) +operator==( const MeshEntity& entity ) const { - SubentityAccessLayerFamily< MeshConfig, Device, EntityTopology >::operator=( entity ); - SuperentityAccessLayerFamily< MeshConfig, Device, EntityTopology >::operator=( entity ); - MeshEntityIndex< typename MeshConfig::IdType >::operator=( entity ); - return *this; + return getIndex() == entity.getIndex(); } 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 +bool MeshEntity< MeshConfig, Device, EntityTopology >:: -getSerializationType() +operator!=( const MeshEntity& entity ) const { - return String( "MeshEntity<" ) + - TNL::getSerializationType< MeshConfig >() + ", " + - TNL::getSerializationType< EntityTopology >() + ">"; + return ! ( *this == entity ); } template< typename MeshConfig, typename Device, typename EntityTopology > -String +constexpr int MeshEntity< MeshConfig, Device, EntityTopology >:: -getSerializationTypeVirtual() const +getEntityDimension() { - return this->getSerializationType(); + return EntityTopology::dimension; } template< typename MeshConfig, typename Device, typename EntityTopology > -void +__cuda_callable__ +const Mesh< MeshConfig, Device >& MeshEntity< MeshConfig, Device, EntityTopology >:: -save( File& file ) const +getMesh() const { - // no I/O for subentities and superentities - not loaded anyway + return *meshPointer; } template< typename MeshConfig, typename Device, typename EntityTopology > -void +__cuda_callable__ +typename Mesh< MeshConfig, Device >::GlobalIndexType MeshEntity< MeshConfig, Device, EntityTopology >:: -load( File& file ) +getIndex() const { - // no I/O for subentities and superentities - Mesh::load has to rebind pointers + return index; } template< typename MeshConfig, typename Device, typename EntityTopology > -void +__cuda_callable__ +typename MeshEntity< MeshConfig, Device, EntityTopology >::PointType MeshEntity< MeshConfig, Device, EntityTopology >:: -print( std::ostream& str ) const +getPoint() const { - str << "\t Mesh entity dimension: " << EntityTopology::dimension << std::endl; - SubentityAccessLayerFamily< MeshConfig, Device, EntityTopology >::print( str ); - SuperentityAccessLayerFamily< MeshConfig, Device, EntityTopology >::print( str ); + 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__ -bool +typename MeshEntity< MeshConfig, Device, EntityTopology >::LocalIndexType MeshEntity< MeshConfig, Device, EntityTopology >:: -operator==( const MeshEntity& entity ) const +getSubentitiesCount() const { - return ( SubentityAccessLayerFamily< MeshConfig, Device, EntityTopology >::operator==( entity ) && - SuperentityAccessLayerFamily< MeshConfig, Device, EntityTopology >::operator==( entity ) && - MeshEntityIndex< typename MeshConfig::IdType >::operator==( entity ) ); + 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__ -bool +typename MeshEntity< MeshConfig, Device, EntityTopology >::GlobalIndexType MeshEntity< MeshConfig, Device, EntityTopology >:: -operator!=( const MeshEntity& entity ) const +getSubentityIndex( const LocalIndexType localIndex ) const { - return ! ( *this == entity ); + TNL_ASSERT_TRUE( meshPointer, "meshPointer was not set" ); + return meshPointer->template getSubentityIndex< getEntityDimension(), Subdimension >( this->getIndex(), localIndex ); } template< typename MeshConfig, typename Device, typename EntityTopology > -constexpr int + template< int Subdimension > +__cuda_callable__ +const typename MeshEntity< MeshConfig, Device, EntityTopology >::template SubentityTraits< Subdimension >::OrientationArrayType& MeshEntity< MeshConfig, Device, EntityTopology >:: -getEntityDimension() +getSubentityOrientation( const LocalIndexType localIndex ) const { - return EntityTopology::dimension; + TNL_ASSERT_TRUE( meshPointer, "meshPointer was not set" ); + return meshPointer->template getSubentityOrientation< getEntityDimension(), Subdimension >( this->getIndex(), localIndex ); } -/**** - * Subentities - */ template< typename MeshConfig, typename Device, typename EntityTopology > -constexpr typename MeshEntity< MeshConfig, Device, EntityTopology >::LocalIndexType + template< int Superdimension > +__cuda_callable__ +typename MeshEntity< MeshConfig, Device, EntityTopology >::LocalIndexType MeshEntity< MeshConfig, Device, EntityTopology >:: -getVerticesCount() +getSuperentitiesCount() const { - return SubentityTraits< 0 >::count; + 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 >:: -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 ) +getSuperentityIndex( const LocalIndexType localIndex ) const { - this->point = point; + TNL_ASSERT_TRUE( meshPointer, "meshPointer was not set" ); + return meshPointer->template getSuperentityIndex< getEntityDimension(), Superdimension >( this->getIndex(), localIndex ); } template< typename MeshConfig, @@ -336,8 +168,7 @@ template< typename MeshConfig, typename EntityTopology > std::ostream& operator<<( std::ostream& str, const MeshEntity< MeshConfig, Device, EntityTopology >& entity ) { - entity.print( str ); - return str; + return str << getType< decltype(entity) >() << "( , " << entity.getIndex() << " )"; } } // namespace Meshes diff --git a/src/TNL/Meshes/MeshDetails/MeshLayers/StorageLayer.h b/src/TNL/Meshes/MeshDetails/MeshLayers/StorageLayer.h index 86c19eeb2..ecd1f790e 100644 --- a/src/TNL/Meshes/MeshDetails/MeshLayers/StorageLayer.h +++ b/src/TNL/Meshes/MeshDetails/MeshLayers/StorageLayer.h @@ -19,6 +19,7 @@ #include #include #include +#include #include namespace TNL { @@ -40,20 +41,81 @@ class StorageLayerFamily 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=; + template< int Dimension, int Subdimension > + using SubentityTraits = typename MeshTraitsType::template SubentityTraits< typename EntityTraits< Dimension >::EntityTopology, Subdimension >; 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 ); + return *this; + } + + template< typename Device_ > + StorageLayerFamily& operator=( const StorageLayerFamily< MeshConfig, Device_ >& layer ) + { + points = layer.getPoints(); + BaseType::operator=( layer ); + return *this; + } + + bool operator==( const StorageLayerFamily& layer ) const + { + return ( points == layer.points && + BaseType::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; + } + 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 ); + if( Dimension == 0 ) + points.setSize( entitiesCount ); } template< int Dimension, int Subdimension > + __cuda_callable__ typename MeshTraitsType::template SubentityTraits< typename EntityTraits< Dimension >::EntityTopology, Subdimension >::StorageNetworkType& getSubentityStorageNetwork() { @@ -65,7 +127,21 @@ protected: return BaseType::template getSubentityStorageNetwork< Subdimension >(); } + template< int Dimension, int Subdimension > + __cuda_callable__ + const typename MeshTraitsType::template SubentityTraits< typename EntityTraits< Dimension >::EntityTopology, Subdimension >::StorageNetworkType& + getSubentityStorageNetwork() const + { + 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 > + __cuda_callable__ typename MeshTraitsType::template SuperentityTraits< typename EntityTraits< Dimension >::EntityTopology, Superdimension >::StorageNetworkType& getSuperentityStorageNetwork() { @@ -76,6 +152,37 @@ protected: typename EntityTraits< Dimension >::EntityTopology >; return BaseType::template getSuperentityStorageNetwork< Superdimension >(); } + + template< int Dimension, int Superdimension > + __cuda_callable__ + const typename MeshTraitsType::template SuperentityTraits< typename EntityTraits< Dimension >::EntityTopology, Superdimension >::StorageNetworkType& + getSuperentityStorageNetwork() const + { + 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< 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 ); + } }; @@ -89,6 +196,9 @@ class StorageLayer< MeshConfig, : public SubentityStorageLayerFamily< MeshConfig, Device, typename MeshTraits< MeshConfig, Device >::template EntityTraits< DimensionTag::value >::EntityTopology >, + public SubentityOrientationsLayerFamily< MeshConfig, + Device, + typename MeshTraits< MeshConfig, Device >::template EntityTraits< DimensionTag::value >::EntityTopology >, public SuperentityStorageLayerFamily< MeshConfig, Device, typename MeshTraits< MeshConfig, Device >::template EntityTraits< DimensionTag::value >::EntityTopology >, @@ -103,8 +213,12 @@ public: using EntityType = typename EntityTraitsType::EntityType; using EntityTopology = typename EntityTraitsType::EntityTopology; using SubentityStorageBaseType = SubentityStorageLayerFamily< MeshConfig, Device, EntityTopology >; + using SubentityOrientationsBaseType = SubentityOrientationsLayerFamily< MeshConfig, Device, EntityTopology >; using SuperentityStorageBaseType = SuperentityStorageLayerFamily< MeshConfig, Device, EntityTopology >; + using BaseType::subentityOrientationsArray; + using SubentityOrientationsBaseType::subentityOrientationsArray; + StorageLayer() = default; explicit StorageLayer( const StorageLayer& other ) @@ -120,8 +234,7 @@ public: StorageLayer& operator=( const StorageLayer& other ) { - entities.setLike( other.entities ); - entities = other.entities; + entitiesCount = other.entitiesCount; SubentityStorageBaseType::operator=( other ); SuperentityStorageBaseType::operator=( other ); BaseType::operator=( other ); @@ -131,8 +244,7 @@ public: template< typename Device_ > StorageLayer& operator=( const StorageLayer< MeshConfig, Device_, DimensionTag >& other ) { - entities.setLike( other.entities ); - entities = other.entities; + entitiesCount = other.getEntitiesCount( DimensionTag() ); SubentityStorageBaseType::operator=( other ); SuperentityStorageBaseType::operator=( other ); BaseType::operator=( other ); @@ -143,7 +255,7 @@ public: { SubentityStorageBaseType::save( file ); SuperentityStorageBaseType::save( file ); - file << this->entities; + file.save( &entitiesCount, 1 ); BaseType::save( file ); } @@ -151,15 +263,13 @@ public: { SubentityStorageBaseType::load( file ); SuperentityStorageBaseType::load( file ); - file >> this->entities; + file.load( &entitiesCount, 1 ); 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; + str << "Number of entities with dimension " << DimensionTag::value << ": " << entitiesCount << std::endl; SubentityStorageBaseType::print( str ); SuperentityStorageBaseType::print( str ); str << std::endl; @@ -168,7 +278,7 @@ public: bool operator==( const StorageLayer& meshLayer ) const { - return ( entities == meshLayer.entities && + return ( entitiesCount == meshLayer.entitiesCount && SubentityStorageBaseType::operator==( meshLayer ) && SuperentityStorageBaseType::operator==( meshLayer ) && BaseType::operator==( meshLayer ) ); @@ -179,34 +289,20 @@ public: __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 ]; + return this->entitiesCount; } protected: using BaseType::setEntitiesCount; void setEntitiesCount( DimensionTag, const GlobalIndexType& entitiesCount ) { - this->entities.setSize( entitiesCount ); + this->entitiesCount = entitiesCount; SubentityStorageBaseType::setEntitiesCount( entitiesCount ); + SubentityOrientationsBaseType::setEntitiesCount( entitiesCount ); SuperentityStorageBaseType::setEntitiesCount( entitiesCount ); } - StorageArrayType entities; + GlobalIndexType entitiesCount = 0; // friend class is needed for templated assignment operators template< typename MeshConfig_, typename Device_, typename DimensionTag_, bool Storage_ > @@ -254,9 +350,9 @@ protected: } + void subentityOrientationsArray() {} void setEntitiesCount() {} void getEntitiesCount() const {} - void getEntity() const {} void save( File& file ) const {} void load( File& file ) {} diff --git a/src/TNL/Meshes/MeshDetails/MeshLayers/SubentityOrientationsLayer.h b/src/TNL/Meshes/MeshDetails/MeshLayers/SubentityOrientationsLayer.h new file mode 100644 index 000000000..18a137547 --- /dev/null +++ b/src/TNL/Meshes/MeshDetails/MeshLayers/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/MeshLayers/SubentityStorageLayer.h index a598289e5..e64f944c7 100644 --- a/src/TNL/Meshes/MeshDetails/MeshLayers/SubentityStorageLayer.h +++ b/src/TNL/Meshes/MeshDetails/MeshLayers/SubentityStorageLayer.h @@ -47,12 +47,22 @@ public: protected: template< int Subdimension > + __cuda_callable__ typename MeshTraitsType::template SubentityTraits< EntityTopology, Subdimension >::StorageNetworkType& getSubentityStorageNetwork() { static_assert( EntityTopology::dimension > Subdimension, "Invalid combination of Dimension and Subdimension." ); return BaseType::getSubentityStorageNetwork( DimensionTag< Subdimension >() ); } + + template< int Subdimension > + __cuda_callable__ + const typename MeshTraitsType::template SubentityTraits< EntityTopology, Subdimension >::StorageNetworkType& + getSubentityStorageNetwork() const + { + static_assert( EntityTopology::dimension > Subdimension, "Invalid combination of Dimension and Subdimension." ); + return BaseType::getSubentityStorageNetwork( DimensionTag< Subdimension >() ); + } }; template< typename MeshConfig, @@ -139,11 +149,18 @@ protected: } using BaseType::getSubentityStorageNetwork; + __cuda_callable__ StorageNetworkType& getSubentityStorageNetwork( SubdimensionTag ) { return this->storageNetwork; } + __cuda_callable__ + const StorageNetworkType& getSubentityStorageNetwork( SubdimensionTag ) const + { + return this->storageNetwork; + } + private: StorageNetworkType storageNetwork; diff --git a/src/TNL/Meshes/MeshDetails/MeshLayers/SuperentityStorageLayer.h b/src/TNL/Meshes/MeshDetails/MeshLayers/SuperentityStorageLayer.h index 9cc1c5535..397f111b4 100644 --- a/src/TNL/Meshes/MeshDetails/MeshLayers/SuperentityStorageLayer.h +++ b/src/TNL/Meshes/MeshDetails/MeshLayers/SuperentityStorageLayer.h @@ -53,12 +53,22 @@ public: protected: template< int Superdimension > + __cuda_callable__ typename MeshTraitsType::template SuperentityTraits< EntityTopology, Superdimension >::StorageNetworkType& getSuperentityStorageNetwork() { static_assert( EntityTopology::dimension < Superdimension, "Invalid combination of Dimension and Superdimension." ); return BaseType::getSuperentityStorageNetwork( DimensionTag< Superdimension >() ); } + + template< int Superdimension > + __cuda_callable__ + const typename MeshTraitsType::template SuperentityTraits< EntityTopology, Superdimension >::StorageNetworkType& + getSuperentityStorageNetwork() const + { + static_assert( EntityTopology::dimension < Superdimension, "Invalid combination of Dimension and Superdimension." ); + return BaseType::getSuperentityStorageNetwork( DimensionTag< Superdimension >() ); + } }; template< typename MeshConfig, @@ -140,11 +150,18 @@ protected: } using BaseType::getSuperentityStorageNetwork; + __cuda_callable__ StorageNetworkType& getSuperentityStorageNetwork( SuperdimensionTag ) { return this->storageNetwork; } + __cuda_callable__ + const StorageNetworkType& getSuperentityStorageNetwork( SuperdimensionTag ) const + { + return this->storageNetwork; + } + private: StorageNetworkType storageNetwork; diff --git a/src/TNL/Meshes/MeshDetails/Mesh_impl.h b/src/TNL/Meshes/MeshDetails/Mesh_impl.h index 4b0488b2d..2e5c4e0d3 100644 --- a/src/TNL/Meshes/MeshDetails/Mesh_impl.h +++ b/src/TNL/Meshes/MeshDetails/Mesh_impl.h @@ -17,7 +17,6 @@ #pragma once #include -#include #include #include @@ -43,8 +42,6 @@ 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 > @@ -54,8 +51,6 @@ 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 > @@ -65,8 +60,6 @@ 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; } @@ -78,8 +71,6 @@ 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; } @@ -113,7 +104,7 @@ constexpr bool Mesh< MeshConfig, Device >:: entitiesAvailable() { - return MeshTraitsType::template EntityTraits< Dimension >::storageEnabled; + return EntityTraits< Dimension >::storageEnabled; } template< typename MeshConfig, typename Device > @@ -123,30 +114,20 @@ 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." ); + static_assert( entitiesAvailable< Dimension >(), "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 >& +typename Mesh< MeshConfig, Device >::template EntityType< Dimension > Mesh< MeshConfig, Device >:: -getEntity( const GlobalIndexType& entityIndex ) +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 ); -} - -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 ); + static_assert( entitiesAvailable< Dimension >(), "You try to get entity which is not configured for storage." ); + TNL_ASSERT_LT( entityIndex, getEntitiesCount< Dimension >(), "invalid entity index" ); + return EntityType< Dimension >( *this, entityIndex ); } @@ -164,21 +145,75 @@ getEntitiesCount() const template< typename MeshConfig, typename Device > template< typename Entity > __cuda_callable__ -Entity& +Entity Mesh< MeshConfig, Device >:: -getEntity( const GlobalIndexType& entityIndex ) +getEntity( const GlobalIndexType entityIndex ) const { return getEntity< Entity::getEntityDimension() >( entityIndex ); } + template< typename MeshConfig, typename Device > - template< typename Entity > __cuda_callable__ -const Entity& +const typename Mesh< MeshConfig, Device >::PointType& Mesh< MeshConfig, Device >:: -getEntity( const GlobalIndexType& entityIndex ) const +getPoint( const GlobalIndexType vertexIndex ) const { - return getEntity< Entity::getEntityDimension() >( entityIndex ); + 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 this->template getSubentityStorageNetwork< EntityDimension, SubentityDimension >().getValuesCount( entityIndex ); +} + +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 +{ + return this->template getSubentityStorageNetwork< EntityDimension, SubentityDimension >().getValue( entityIndex, 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 getSuperentityStorageNetwork< EntityDimension, SuperentityDimension >().getValuesCount( 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 +{ + return this->template getSuperentityStorageNetwork< EntityDimension, SuperentityDimension >().getValue( entityIndex, superentityIndex ); } @@ -211,9 +246,6 @@ reorderEntities( const GlobalIndexVector& perm, << ", 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(); } @@ -237,8 +269,6 @@ 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 > diff --git a/src/TNL/Meshes/MeshDetails/Traverser_impl.h b/src/TNL/Meshes/MeshDetails/Traverser_impl.h index 2ce07addf..a24411b0e 100644 --- a/src/TNL/Meshes/MeshDetails/Traverser_impl.h +++ b/src/TNL/Meshes/MeshDetails/Traverser_impl.h @@ -33,8 +33,7 @@ processBoundaryEntities( const MeshPointer& meshPointer, 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 + const auto entity = mesh->template getEntity< MeshEntity::getEntityDimension() >( entityIndex ); EntitiesProcessor::processEntity( *mesh, userData, entity ); }; Pointers::synchronizeSmartPointersOnDevice< DeviceType >(); @@ -62,8 +61,7 @@ processInteriorEntities( const MeshPointer& meshPointer, 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 + const auto entity = mesh->template getEntity< MeshEntity::getEntityDimension() >( entityIndex ); EntitiesProcessor::processEntity( *mesh, userData, entity ); }; Pointers::synchronizeSmartPointersOnDevice< DeviceType >(); @@ -90,8 +88,7 @@ processAllEntities( const MeshPointer& meshPointer, 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 + const auto entity = mesh->template getEntity< MeshEntity::getEntityDimension() >( entityIndex ); EntitiesProcessor::processEntity( *mesh, userData, entity ); }; Pointers::synchronizeSmartPointersOnDevice< DeviceType >(); diff --git a/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h b/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h index ada83b5fb..0c7400d1c 100644 --- a/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h +++ b/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h @@ -66,12 +66,11 @@ class EntityInitializer using InitializerType = Initializer< MeshConfig >; public: - static void initEntity( EntityType& entity, const GlobalIndexType& entityIndex, const SeedType& entitySeed, InitializerType& initializer) + 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 ] ); } }; @@ -84,13 +83,10 @@ class EntityInitializer< MeshConfig, Topologies::Vertex > public: using VertexType = typename MeshTraits< MeshConfig >::VertexType; using GlobalIndexType = typename MeshTraits< MeshConfig >::GlobalIndexType; - using PointType = typename MeshTraits< MeshConfig >::PointType; using InitializerType = Initializer< MeshConfig >; - static void initEntity( VertexType& entity, const GlobalIndexType& entityIndex, const PointType& point, InitializerType& initializer) + static void initEntity( const GlobalIndexType& entityIndex, InitializerType& initializer) { - initializer.setEntityIndex( entity, entityIndex ); - initializer.setVertexPoint( entity, point ); } }; @@ -138,13 +134,12 @@ public: 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 ); superentityInitializer.addSuperentity( subentityIndex, superentityIndex ); } } @@ -198,15 +193,14 @@ public: 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 ); superentityInitializer.addSuperentity( subentityIndex, superentityIndex ); subentityOrientationsArray[ i ] = meshInitializer.template getReferenceOrientation< SubdimensionTag >( subentityIndex ).createOrientation( subentitySeeds[ i ] ); @@ -259,15 +253,14 @@ public: 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 ] ); } @@ -317,13 +310,12 @@ public: 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 ); } } @@ -374,8 +366,7 @@ public: 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++ ) { diff --git a/src/TNL/Meshes/MeshDetails/initializer/Initializer.h b/src/TNL/Meshes/MeshDetails/initializer/Initializer.h index acaeeaff4..03c873425 100644 --- a/src/TNL/Meshes/MeshDetails/initializer/Initializer.h +++ b/src/TNL/Meshes/MeshDetails/initializer/Initializer.h @@ -78,7 +78,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 >; @@ -90,10 +90,6 @@ class Initializer using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; - Initializer() - : mesh( 0 ) - {} - // The points and cellSeeds arrays will be reset when not needed to save memory. void createMesh( PointArrayType& points, CellSeedArrayType& cellSeeds, @@ -101,14 +97,6 @@ class Initializer { 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 ); - } - - template< typename Entity, typename GlobalIndex > - void setEntityIndex( Entity& entity, const GlobalIndex& index ) - { - entity.setIndex( index ); } template< int Dimension > @@ -118,32 +106,21 @@ class Initializer mesh->template setEntitiesCount< Dimension >( entitiesCount ); } - template< int Subdimension, typename EntityType, typename LocalIndex, typename GlobalIndex > + template< int Dimension, int Subdimension, typename LocalIndex, typename GlobalIndex > 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 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. - return mesh->template getSubentityStorageNetwork< EntityType::EntityTopology::dimension, Subdimension >().getValues( entityIndex )[ localIndex ]; + mesh->template getSubentityStorageNetwork< Dimension, Subdimension >().getValues( entityIndex )[ localIndex ] = globalIndex; } - template< typename EntityType, typename GlobalIndex > + template< int Dimension, typename GlobalIndex > auto - getSubvertices( const EntityType& entity, const GlobalIndex& entityIndex ) - -> decltype( this->mesh->template getSubentityStorageNetwork< EntityType::EntityTopology::dimension, 0 >().getValues( 0 ) ) + getSubvertices( const GlobalIndex& entityIndex ) + -> decltype( this->mesh->template getSubentityStorageNetwork< Dimension, 0 >().getValues( 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 getSubentityStorageNetwork< Dimension, 0 >().getValues( entityIndex ); } template< typename EntityTopology, int Superdimension > @@ -153,18 +130,13 @@ class Initializer return mesh->template getSuperentityStorageNetwork< EntityTopology::dimension, Superdimension >(); } - static void - setVertexPoint( typename MeshType::Vertex& vertex, const typename MeshType::PointType& point ) - { - vertex.setPoint( point ); - } - - template< typename SubDimensionTag, typename MeshEntity > - static typename MeshTraitsType::template SubentityTraits< typename MeshEntity::EntityTopology, SubDimensionTag::value >::OrientationArrayType& - subentityOrientationsArray( MeshEntity& entity ) + template< int Dimension, int Subdimension, typename GlobalIndex > + auto + subentityOrientationsArray( const GlobalIndex 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 > @@ -210,7 +182,7 @@ class InitializerLayer< MeshConfig, //std::cout << " Initiating entities with dimension " << DimensionTag::value << " ... " << std::endl; initializer.template setEntitiesCount< DimensionTag::value >( 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 ); @@ -258,7 +230,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 ] ); } @@ -281,7 +253,7 @@ class InitializerLayer< MeshConfig, 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 +261,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 ); } } } @@ -349,7 +321,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 ] ); } @@ -373,7 +345,7 @@ class InitializerLayer< MeshConfig, 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 +353,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 ); } } @@ -454,8 +426,10 @@ class InitializerLayer< MeshConfig, { //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 ); + for( GlobalIndexType i = 0; i < points.getSize(); i++ ) { + EntityInitializerType::initEntity( i, initializer ); + mesh.getPoint( i ) = points[ i ]; + } points.reset(); EntityInitializerType::initSuperentities( initializer, mesh ); diff --git a/src/TNL/Meshes/MeshDetails/initializer/SubentitySeedsCreator.h b/src/TNL/Meshes/MeshDetails/initializer/SubentitySeedsCreator.h index 30cbb31e6..5ed079f34 100644 --- a/src/TNL/Meshes/MeshDetails/initializer/SubentitySeedsCreator.h +++ b/src/TNL/Meshes/MeshDetails/initializer/SubentitySeedsCreator.h @@ -28,18 +28,14 @@ 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 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; @@ -47,7 +43,7 @@ public: 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; } @@ -83,16 +79,12 @@ 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 SubentityTraits = typename MeshTraitsType::template SubentityTraits< EntityTopology, 0 >; - static constexpr LocalIndexType SUBENTITIES_COUNT = EntityType::template getSubentitiesCount< 0 >(); - public: using SubentitySeedArray = typename SubentityTraits::SeedArrayType; diff --git a/src/TNL/Meshes/MeshDetails/traits/MeshSubentityTraits.h b/src/TNL/Meshes/MeshDetails/traits/MeshSubentityTraits.h index c10b56958..1c063ca28 100644 --- a/src/TNL/Meshes/MeshDetails/traits/MeshSubentityTraits.h +++ b/src/TNL/Meshes/MeshDetails/traits/MeshSubentityTraits.h @@ -17,7 +17,6 @@ #pragma once #include -#include #include #include #include @@ -65,6 +64,7 @@ public: // 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 5e535b4a7..c2e292f31 100644 --- a/src/TNL/Meshes/MeshDetails/traits/MeshSuperentityTraits.h +++ b/src/TNL/Meshes/MeshDetails/traits/MeshSuperentityTraits.h @@ -16,7 +16,6 @@ #pragma once -#include #include #include diff --git a/src/TNL/Meshes/MeshEntity.h b/src/TNL/Meshes/MeshEntity.h index b1c8afea5..c87acfe21 100644 --- a/src/TNL/Meshes/MeshEntity.h +++ b/src/TNL/Meshes/MeshEntity.h @@ -16,71 +16,45 @@ #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; 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 +64,48 @@ 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 ); - + template< int Subdimension > __cuda_callable__ - MeshEntity& operator=( const MeshEntity& entity ); + LocalIndexType getSubentitiesCount() const; - 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; - - __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; - - /**** - * Points - */ + template< int Superdimension > __cuda_callable__ - PointType getPoint() const; + LocalIndexType getSuperentitiesCount() const; + template< int Superdimension > __cuda_callable__ - void setPoint( const PointType& point ); + GlobalIndexType getSuperentityIndex( const LocalIndexType localIndex ) 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, diff --git a/src/TNL/Meshes/TypeResolver/MeshTypeResolver.h b/src/TNL/Meshes/TypeResolver/MeshTypeResolver.h index dfdb0ad1e..60c2a95d4 100644 --- a/src/TNL/Meshes/TypeResolver/MeshTypeResolver.h +++ b/src/TNL/Meshes/TypeResolver/MeshTypeResolver.h @@ -110,39 +110,16 @@ protected: 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 + // Overload for enabled local index 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 = typename std::enable_if< BuildConfigTags::MeshLocalIndexTag< ConfigTag, LocalIndex >::enabled >::type > static bool resolveMeshType( const Reader& reader, ProblemSetterArgs&&... problemSetterArgs ); @@ -155,8 +132,7 @@ protected: MeshConfig::worldDimension, typename MeshConfig::RealType, typename MeshConfig::GlobalIndexType, - typename MeshConfig::LocalIndexType, - typename MeshConfig::IdType + typename MeshConfig::LocalIndexType >::enabled >::type, typename = void > static bool resolveTerminate( const Reader& reader, @@ -171,8 +147,7 @@ protected: MeshConfig::worldDimension, typename MeshConfig::RealType, typename MeshConfig::GlobalIndexType, - typename MeshConfig::LocalIndexType, - typename MeshConfig::IdType + typename MeshConfig::LocalIndexType >::enabled >::type > static bool resolveTerminate( const Reader& reader, ProblemSetterArgs&&... problemSetterArgs ); diff --git a/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h b/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h index 5bdd34f9c..7e5eb05da 100644 --- a/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h +++ b/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h @@ -230,15 +230,15 @@ resolveLocalIndex( const Reader& reader, if( reader.getLocalIndexType() == "short int" || reader.getLocalIndexType() == "std::int16_t" || reader.getLocalIndexType() == "std::uint16_t" ) - return resolveId< CellTopology, WorldDimension, Real, GlobalIndex, short int >( reader, std::forward(problemSetterArgs)... ); + return resolveMeshType< CellTopology, WorldDimension, Real, GlobalIndex, short int >( reader, std::forward(problemSetterArgs)... ); if( reader.getLocalIndexType() == "int" || reader.getLocalIndexType() == "std::int32_t" || reader.getLocalIndexType() == "std::uint32_t" ) - return resolveId< CellTopology, WorldDimension, Real, GlobalIndex, int >( reader, std::forward(problemSetterArgs)... ); + return resolveMeshType< CellTopology, WorldDimension, Real, GlobalIndex, int >( reader, std::forward(problemSetterArgs)... ); if( reader.getLocalIndexType() == "long int" || reader.getLocalIndexType() == "std::int64_t" || reader.getLocalIndexType() == "std::uint64_t" ) - return resolveId< CellTopology, WorldDimension, Real, GlobalIndex, long int >( reader, std::forward(problemSetterArgs)... ); + return resolveMeshType< CellTopology, WorldDimension, Real, GlobalIndex, long int >( reader, std::forward(problemSetterArgs)... ); std::cerr << "Unsupported local index type: " << reader.getLocalIndexType() << std::endl; return false; } @@ -256,7 +256,7 @@ template< typename Reader, typename, typename > bool MeshTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveId( const Reader& reader, +resolveMeshType( const Reader& reader, ProblemSetterArgs&&... problemSetterArgs ) { std::cerr << "The mesh local index type " << getType< LocalIndex >() << " is disabled in the build configuration." << std::endl; @@ -276,66 +276,10 @@ template< typename Reader, typename > bool MeshTypeResolver< Reader, ConfigTag, Device, ProblemSetter, ProblemSetterArgs... >:: -resolveId( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) -{ - if( reader.getIdType() == "short int" || - reader.getIdType() == "std::int16_t" || - reader.getIdType() == "std::uint16_t" ) - return resolveMeshType< CellTopology, WorldDimension, Real, GlobalIndex, LocalIndex, short int >( reader, std::forward(problemSetterArgs)... ); - if( reader.getIdType() == "int" || - reader.getIdType() == "std::int32_t" || - reader.getIdType() == "std::uint32_t" ) - return resolveMeshType< CellTopology, WorldDimension, Real, GlobalIndex, LocalIndex, int >( reader, std::forward(problemSetterArgs)... ); - if( reader.getIdType() == "long int" || - reader.getIdType() == "std::int64_t" || - reader.getIdType() == "std::uint64_t" ) - 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 >; + using MeshConfig = typename BuildConfigTags::MeshConfigTemplateTag< ConfigTag >::template MeshConfig< CellTopology, WorldDimension, Real, GlobalIndex, LocalIndex >; return resolveTerminate< MeshConfig >( reader, std::forward(problemSetterArgs)... ); } diff --git a/src/TNL/Meshes/Writers/NetgenWriter.h b/src/TNL/Meshes/Writers/NetgenWriter.h index 7a3a86f92..860034dcd 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/VerticesPerEntity.h b/src/TNL/Meshes/Writers/VerticesPerEntity.h index 50a71eae5..5cefb37c7 100644 --- a/src/TNL/Meshes/Writers/VerticesPerEntity.h +++ b/src/TNL/Meshes/Writers/VerticesPerEntity.h @@ -42,7 +42,7 @@ template< typename Entity, bool _is_mesh_entity = details::has_entity_topology< Entity >::value > struct VerticesPerEntity { - static constexpr int count = Entity::getVerticesCount(); + static constexpr int count = Topologies::Subtopology< typename Entity::EntityTopology, 0 >::count; }; template< typename MeshConfig, typename Device > diff --git a/src/Tools/tnl-grid-to-mesh.cpp b/src/Tools/tnl-grid-to-mesh.cpp index d2d03a29a..921b0ab3c 100644 --- a/src/Tools/tnl-grid-to-mesh.cpp +++ b/src/Tools/tnl-grid-to-mesh.cpp @@ -51,8 +51,7 @@ struct MeshCreator< TNL::Meshes::Grid< 1, Real, Device, Index > > CellTopology::dimension, typename GridType::RealType, typename GridType::GlobalIndexType, - LocalIndexType, - typename GridType::GlobalIndexType >; + LocalIndexType >; using MeshType = TNL::Meshes::Mesh< MeshConfig >; static bool run( const GridType& grid, MeshType& mesh ) @@ -89,8 +88,7 @@ struct MeshCreator< TNL::Meshes::Grid< 2, Real, Device, Index > > CellTopology::dimension, typename GridType::RealType, typename GridType::GlobalIndexType, - LocalIndexType, - typename GridType::GlobalIndexType >; + LocalIndexType >; using MeshType = TNL::Meshes::Mesh< MeshConfig >; static bool run( const GridType& grid, MeshType& mesh ) @@ -129,8 +127,7 @@ struct MeshCreator< TNL::Meshes::Grid< 3, Real, Device, Index > > CellTopology::dimension, typename GridType::RealType, typename GridType::GlobalIndexType, - LocalIndexType, - typename GridType::GlobalIndexType >; + LocalIndexType >; using MeshType = TNL::Meshes::Mesh< MeshConfig >; static bool run( const GridType& grid, MeshType& mesh ) diff --git a/src/Tools/tnl-mesh-converter.cpp b/src/Tools/tnl-mesh-converter.cpp index 9b96671b5..7f8ad1133 100644 --- a/src/Tools/tnl-mesh-converter.cpp +++ b/src/Tools/tnl-mesh-converter.cpp @@ -52,12 +52,9 @@ struct MeshWorldDimensionTag< MeshConverterConfigTag, CellTopology, WorldDimensi 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, double > { enum { enabled = true }; }; template<> struct MeshGlobalIndexTag< MeshConverterConfigTag, int > { enum { enabled = true }; }; 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 }; }; } // namespace BuildConfigTags } // namespace Meshes diff --git a/src/Tools/tnl-view.cpp b/src/Tools/tnl-view.cpp index 202249202..0df3066cb 100644 --- a/src/Tools/tnl-view.cpp +++ b/src/Tools/tnl-view.cpp @@ -50,12 +50,9 @@ struct MeshWorldDimensionTag< TNLViewBuildConfigTag, CellTopology, WorldDimensio 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, double > { enum { enabled = true }; }; template<> struct MeshGlobalIndexTag< TNLViewBuildConfigTag, int > { enum { enabled = true }; }; 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 diff --git a/src/UnitTests/Meshes/CMakeLists.txt b/src/UnitTests/Meshes/CMakeLists.txt index 91bf37215..4147119c8 100644 --- a/src/UnitTests/Meshes/CMakeLists.txt +++ b/src/UnitTests/Meshes/CMakeLists.txt @@ -31,16 +31,19 @@ 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( 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} ) + + +## 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/MeshOrderingTest.h b/src/UnitTests/Meshes/MeshOrderingTest.h index 054e4309e..eb533fa90 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; } @@ -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 ] ); diff --git a/src/UnitTests/Meshes/MeshTest.h b/src/UnitTests/Meshes/MeshTest.h index 5c95221ed..8549480ff 100644 --- a/src/UnitTests/Meshes/MeshTest.h +++ b/src/UnitTests/Meshes/MeshTest.h @@ -129,8 +129,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 +141,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 +234,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 ); diff --git a/src/UnitTests/Meshes/MeshTraverserTest.h b/src/UnitTests/Meshes/MeshTraverserTest.h index b5b45f8e5..5c4274622 100644 --- a/src/UnitTests/Meshes/MeshTraverserTest.h +++ b/src/UnitTests/Meshes/MeshTraverserTest.h @@ -22,7 +22,7 @@ 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, 2, double, int, int > { public: static constexpr bool entityStorage( int dimensions ) { return true; } @@ -33,7 +33,7 @@ public: // 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, 3, double, int, int > { public: static constexpr bool entityStorage( int dimensions ) { return true; } -- GitLab From eeeffb99b5eef2210f9a28d5883f980044dea3c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Wed, 25 Mar 2020 23:45:17 +0100 Subject: [PATCH 21/73] Removed method getIdType from mesh readers --- src/TNL/Meshes/Readers/NetgenReader.h | 7 ------- src/TNL/Meshes/Readers/TNLReader.h | 12 ++---------- src/TNL/Meshes/Readers/VTKReader.h | 7 ------- src/TNL/Meshes/Readers/VTKReader_libvtk.h | 7 ------- src/TNL/Meshes/Readers/VTUReader.h | 7 ------- 5 files changed, 2 insertions(+), 38 deletions(-) diff --git a/src/TNL/Meshes/Readers/NetgenReader.h b/src/TNL/Meshes/Readers/NetgenReader.h index abf56c03e..5186c87db 100644 --- a/src/TNL/Meshes/Readers/NetgenReader.h +++ b/src/TNL/Meshes/Readers/NetgenReader.h @@ -278,13 +278,6 @@ public: return "short int"; } - String - getIdType() const - { - // not stored in the Netgen file - return "int"; - } - protected: String fileName; int meshDimension, worldDimension; diff --git a/src/TNL/Meshes/Readers/TNLReader.h b/src/TNL/Meshes/Readers/TNLReader.h index b7cdc54c9..2a37f5617 100644 --- a/src/TNL/Meshes/Readers/TNLReader.h +++ b/src/TNL/Meshes/Readers/TNLReader.h @@ -39,7 +39,7 @@ 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 = VTK::EntityShape::Line; @@ -69,7 +69,6 @@ public: realType = parsedMeshConfig[ 3 ]; globalIndexType = parsedMeshConfig[ 4 ]; localIndexType = parsedMeshConfig[ 5 ]; - idType = parsedMeshConfig[ 6 ]; if( topology == "MeshEdgeTopology" ) cellShape = VTK::EntityShape::Line; @@ -144,12 +143,6 @@ public: return localIndexType; } - String - getIdType() const - { - return idType; - } - protected: String fileName; String meshType; @@ -159,7 +152,6 @@ protected: String realType; String globalIndexType; String localIndexType; - String idType; void reset() { @@ -167,7 +159,7 @@ protected: meshType = ""; meshDimension = worldDimension = 0; cellShape = VTK::EntityShape::Vertex; - realType = localIndexType = globalIndexType = idType = ""; + realType = localIndexType = globalIndexType = ""; } }; diff --git a/src/TNL/Meshes/Readers/VTKReader.h b/src/TNL/Meshes/Readers/VTKReader.h index 438d0b546..cd9cdd413 100644 --- a/src/TNL/Meshes/Readers/VTKReader.h +++ b/src/TNL/Meshes/Readers/VTKReader.h @@ -336,13 +336,6 @@ public: return "short int"; } - String - getIdType() const - { - // not stored in the VTK file - return "int"; - } - protected: // output of parseHeader std::string dataType; diff --git a/src/TNL/Meshes/Readers/VTKReader_libvtk.h b/src/TNL/Meshes/Readers/VTKReader_libvtk.h index 46945e00f..048f825c3 100644 --- a/src/TNL/Meshes/Readers/VTKReader_libvtk.h +++ b/src/TNL/Meshes/Readers/VTKReader_libvtk.h @@ -175,13 +175,6 @@ public: return "short int"; } - String - getIdType() const - { - // not stored in the VTK file - return "int"; - } - protected: int worldDimension = 0; int meshDimension = 0; diff --git a/src/TNL/Meshes/Readers/VTUReader.h b/src/TNL/Meshes/Readers/VTUReader.h index 7c46694a8..974dff93d 100644 --- a/src/TNL/Meshes/Readers/VTUReader.h +++ b/src/TNL/Meshes/Readers/VTUReader.h @@ -522,13 +522,6 @@ public: return "short int"; } - std::string - getIdType() const - { - // not stored in the VTK file - return getGlobalIndexType(); - } - protected: std::string fileName; -- GitLab From 0e1d04826b13acff41e932d858cac1171e5f2396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Wed, 8 Apr 2020 21:23:48 +0200 Subject: [PATCH 22/73] Optimized default BuildConfigTags for meshes --- src/TNL/Meshes/BuildConfigTags.h | 29 ++++++++++++++++++---------- src/TNL/Solvers/FastBuildConfigTag.h | 9 ++------- src/Tools/tnl-mesh-converter.cpp | 5 ++--- src/Tools/tnl-view.cpp | 5 ++--- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/TNL/Meshes/BuildConfigTags.h b/src/TNL/Meshes/BuildConfigTags.h index 0481f65a0..9539ef5df 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,14 +89,19 @@ 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 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 > diff --git a/src/TNL/Solvers/FastBuildConfigTag.h b/src/TNL/Solvers/FastBuildConfigTag.h index 18e0d80ad..e0f1cc174 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/Tools/tnl-mesh-converter.cpp b/src/Tools/tnl-mesh-converter.cpp index 7f8ad1133..dae0a0af1 100644 --- a/src/Tools/tnl-mesh-converter.cpp +++ b/src/Tools/tnl-mesh-converter.cpp @@ -49,11 +49,10 @@ 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<> 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 }; }; } // namespace BuildConfigTags diff --git a/src/Tools/tnl-view.cpp b/src/Tools/tnl-view.cpp index 0df3066cb..2a17be889 100644 --- a/src/Tools/tnl-view.cpp +++ b/src/Tools/tnl-view.cpp @@ -47,11 +47,10 @@ 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<> 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 }; }; } // namespace BuildConfigTags -- GitLab From f4102b4fa2f3932b511a1321db5000ccb0614a74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Thu, 26 Mar 2020 07:51:48 +0100 Subject: [PATCH 23/73] Optimized access to points in getEntityCenter and getEntityMeasure --- src/TNL/Meshes/Geometry/getEntityCenter.h | 3 +- src/TNL/Meshes/Geometry/getEntityMeasure.h | 42 +++++++++++----------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/TNL/Meshes/Geometry/getEntityCenter.h b/src/TNL/Meshes/Geometry/getEntityCenter.h index cdcf6cefb..6e869f6ec 100644 --- a/src/TNL/Meshes/Geometry/getEntityCenter.h +++ b/src/TNL/Meshes/Geometry/getEntityCenter.h @@ -56,8 +56,7 @@ getEntityCenter( const Mesh< MeshConfig, Device > & mesh, 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 070c28c58..93dc9671d 100644 --- a/src/TNL/Meshes/Geometry/getEntityMeasure.h +++ b/src/TNL/Meshes/Geometry/getEntityMeasure.h @@ -74,9 +74,9 @@ 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 @@ -112,12 +112,12 @@ 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(); + 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 ) ); + using Point = std::decay_t< decltype( v0 ) >; + const Point p1 = v2 - v0; + const Point p2 = v1 - v0; return getTriangleArea( p1, p2 ); } @@ -130,13 +130,13 @@ 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(); + 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 ) ); + using Point = std::decay_t< decltype( v0 ) >; + const Point p1 = v2 - v0; + const Point p2 = v3 - v1; return getTriangleArea( p1, p2 ); } @@ -164,11 +164,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 -- GitLab From 2734b3b607370e73da9194ac80284e03875b0fa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Thu, 26 Mar 2020 08:36:24 +0100 Subject: [PATCH 24/73] Optimized getTriangleArea using vector expressions --- src/TNL/Meshes/Geometry/getEntityMeasure.h | 29 ++++++++++------------ 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/TNL/Meshes/Geometry/getEntityMeasure.h b/src/TNL/Meshes/Geometry/getEntityMeasure.h index 93dc9671d..9950e50b8 100644 --- a/src/TNL/Meshes/Geometry/getEntityMeasure.h +++ b/src/TNL/Meshes/Geometry/getEntityMeasure.h @@ -80,21 +80,24 @@ getEntityMeasure( const Mesh< MeshConfig, Device > & mesh, } // 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() ); } -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 @@ -115,10 +118,7 @@ getEntityMeasure( const Mesh< MeshConfig, Device > & mesh, 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 ) ); - using Point = std::decay_t< decltype( v0 ) >; - const Point p1 = v2 - v0; - const Point p2 = v1 - v0; - return getTriangleArea( p1, p2 ); + return getTriangleArea( v2 - v0, v1 - v0 ); } // Quadrilateral @@ -134,10 +134,7 @@ getEntityMeasure( const Mesh< MeshConfig, Device > & mesh, 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 ) ); - using Point = std::decay_t< decltype( v0 ) >; - const Point p1 = v2 - v0; - const Point p2 = v3 - v1; - return getTriangleArea( p1, p2 ); + return getTriangleArea( v2 - v0, v3 - v1 ); } template< typename VectorExpression > -- GitLab From 49e7bb6a2922b7bb0a9abf324d00003cd0d4d744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Thu, 26 Mar 2020 09:30:20 +0100 Subject: [PATCH 25/73] Optimized getTriangleArea for 3D --- src/TNL/Meshes/Geometry/getEntityMeasure.h | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/TNL/Meshes/Geometry/getEntityMeasure.h b/src/TNL/Meshes/Geometry/getEntityMeasure.h index 9950e50b8..ca50453b1 100644 --- a/src/TNL/Meshes/Geometry/getEntityMeasure.h +++ b/src/TNL/Meshes/Geometry/getEntityMeasure.h @@ -99,14 +99,10 @@ getTriangleArea( const VectorExpression & v1, { 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 0.5 * TNL::sqrt( c1 * c1 + c2 * c2 + c3 * c3 ); } template< typename MeshConfig, typename Device > -- GitLab From 56b2bbb37e4561d3a4a8fb25a944dc84d376cd60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Thu, 26 Mar 2020 17:26:54 +0100 Subject: [PATCH 26/73] Cleaning up mesh initializer --- src/TNL/Meshes/Mesh.h | 1 + .../MeshDetails/MeshLayers/StorageLayer.h | 5 ++ .../initializer/EntityInitializer.h | 18 ------ .../MeshDetails/initializer/Initializer.h | 63 ++++++++----------- .../SuperentityStorageInitializer.h | 3 +- .../traits/MeshSuperentityTraits.h | 1 - 6 files changed, 34 insertions(+), 57 deletions(-) diff --git a/src/TNL/Meshes/Mesh.h b/src/TNL/Meshes/Mesh.h index 488b0b103..f1fbdb386 100644 --- a/src/TNL/Meshes/Mesh.h +++ b/src/TNL/Meshes/Mesh.h @@ -202,6 +202,7 @@ class Mesh protected: // Methods for the mesh initializer + using StorageBaseType::getPoints; using StorageBaseType::setEntitiesCount; using StorageBaseType::getSubentityStorageNetwork; using StorageBaseType::getSuperentityStorageNetwork; diff --git a/src/TNL/Meshes/MeshDetails/MeshLayers/StorageLayer.h b/src/TNL/Meshes/MeshDetails/MeshLayers/StorageLayer.h index ecd1f790e..2cf0bf737 100644 --- a/src/TNL/Meshes/MeshDetails/MeshLayers/StorageLayer.h +++ b/src/TNL/Meshes/MeshDetails/MeshLayers/StorageLayer.h @@ -105,6 +105,11 @@ public: return points; } + typename MeshTraitsType::PointArrayType& getPoints() + { + return points; + } + template< int Dimension > void setEntitiesCount( const typename MeshTraitsType::GlobalIndexType& entitiesCount ) { diff --git a/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h b/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h index 0c7400d1c..a4a403bd0 100644 --- a/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h +++ b/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h @@ -59,8 +59,6 @@ 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 >; @@ -74,22 +72,6 @@ public: } }; -template< typename MeshConfig > -class EntityInitializer< MeshConfig, Topologies::Vertex > - : public EntityInitializerLayer< MeshConfig, - DimensionTag< 0 >, - DimensionTag< MeshTraits< MeshConfig >::meshDimension > > -{ -public: - using VertexType = typename MeshTraits< MeshConfig >::VertexType; - using GlobalIndexType = typename MeshTraits< MeshConfig >::GlobalIndexType; - using InitializerType = Initializer< MeshConfig >; - - static void initEntity( const GlobalIndexType& entityIndex, InitializerType& initializer) - { - } -}; - /**** * Mesh entity initializer layer with specializations diff --git a/src/TNL/Meshes/MeshDetails/initializer/Initializer.h b/src/TNL/Meshes/MeshDetails/initializer/Initializer.h index 03c873425..dfe592e6c 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: @@ -95,8 +95,13 @@ class Initializer CellSeedArrayType& cellSeeds, MeshType& mesh ) { + // copy points + mesh.template setEntitiesCount< 0 >( points.getSize() ); + mesh.getPoints() = points; + points.reset(); + this->mesh = &mesh; - BaseType::initEntities( *this, points, cellSeeds, mesh ); + BaseType::initEntities( *this, cellSeeds, mesh ); } template< int Dimension > @@ -123,11 +128,12 @@ class Initializer return mesh->template getSubentityStorageNetwork< Dimension, 0 >().getValues( entityIndex ); } - template< typename EntityTopology, int Superdimension > - typename MeshTraitsType::template SuperentityTraits< EntityTopology, Superdimension >::StorageNetworkType& + template< int Dimension, int Superdimension > + auto meshSuperentityStorageNetwork() + -> decltype( this->mesh->template getSuperentityStorageNetwork< Dimension, Superdimension >() ) { - return mesh->template getSuperentityStorageNetwork< EntityTopology::dimension, Superdimension >(); + return mesh->template getSuperentityStorageNetwork< Dimension, Superdimension >(); } @@ -168,16 +174,15 @@ 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() ); @@ -185,7 +190,7 @@ class InitializerLayer< MeshConfig, EntityInitializerType::initEntity( i, cellSeeds[ i ], initializer ); cellSeeds.reset(); - BaseType::initEntities( initializer, points, mesh ); + BaseType::initEntities( initializer, mesh ); } using BaseType::findEntitySeedIndex; @@ -212,11 +217,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; @@ -244,7 +248,7 @@ 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 ); @@ -269,12 +273,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; @@ -301,11 +304,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; @@ -335,7 +337,7 @@ 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 ); @@ -363,7 +365,7 @@ class InitializerLayer< MeshConfig, this->seedsIndexedSet.clear(); this->referenceOrientations.clear(); - BaseType::initEntities( initializer, points, mesh ); + BaseType::initEntities( initializer, mesh ); } using BaseType::getReferenceOrientation; @@ -407,11 +409,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 >; @@ -422,24 +423,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( i, initializer ); - mesh.getPoint( i ) = points[ i ]; - } - 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/SuperentityStorageInitializer.h b/src/TNL/Meshes/MeshDetails/initializer/SuperentityStorageInitializer.h index c0327785d..0a8d61dd4 100644 --- a/src/TNL/Meshes/MeshDetails/initializer/SuperentityStorageInitializer.h +++ b/src/TNL/Meshes/MeshDetails/initializer/SuperentityStorageInitializer.h @@ -39,7 +39,6 @@ class SuperentityStorageInitializer 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; @@ -63,7 +62,7 @@ public: "Superentities for some entities are missing. " "This is probably a bug in the mesh initializer." ); - SuperentityStorageNetwork& superentityStorageNetwork = meshInitializer.template meshSuperentityStorageNetwork< EntityTopology, SuperdimensionTag::value >(); + SuperentityStorageNetwork& superentityStorageNetwork = meshInitializer.template meshSuperentityStorageNetwork< SubdimensionTag::value, 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." ); diff --git a/src/TNL/Meshes/MeshDetails/traits/MeshSuperentityTraits.h b/src/TNL/Meshes/MeshDetails/traits/MeshSuperentityTraits.h index c2e292f31..ca60c250e 100644 --- a/src/TNL/Meshes/MeshDetails/traits/MeshSuperentityTraits.h +++ b/src/TNL/Meshes/MeshDetails/traits/MeshSuperentityTraits.h @@ -45,7 +45,6 @@ public: */ // 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 -- GitLab From 2b037e771e71d2b66204026198034cf8841b8bd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Thu, 26 Mar 2020 17:28:13 +0100 Subject: [PATCH 27/73] Refactoring mesh traits: removed Seed, SeedArrayType and StorageArrayType --- src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h | 1 - src/TNL/Meshes/MeshDetails/MeshLayers/StorageLayer.h | 1 - .../Meshes/MeshDetails/initializer/SubentitySeedsCreator.h | 5 +++-- src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h | 1 - src/TNL/Meshes/MeshDetails/traits/MeshSubentityTraits.h | 6 ------ 5 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h b/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h index 079c08cf6..a29a705df 100644 --- a/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h +++ b/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h @@ -171,7 +171,6 @@ public: { using IndexType = typename Mesh::GlobalIndexType; using DeviceType = typename Mesh::DeviceType; - using StorageArrayType = typename Mesh::template EntityTraits< Dimension >::StorageArrayType; if( Dimension == 0 ) permutePoints( mesh, perm, iperm ); diff --git a/src/TNL/Meshes/MeshDetails/MeshLayers/StorageLayer.h b/src/TNL/Meshes/MeshDetails/MeshLayers/StorageLayer.h index 2cf0bf737..19f307a62 100644 --- a/src/TNL/Meshes/MeshDetails/MeshLayers/StorageLayer.h +++ b/src/TNL/Meshes/MeshDetails/MeshLayers/StorageLayer.h @@ -214,7 +214,6 @@ public: 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 >; diff --git a/src/TNL/Meshes/MeshDetails/initializer/SubentitySeedsCreator.h b/src/TNL/Meshes/MeshDetails/initializer/SubentitySeedsCreator.h index 5ed079f34..e849f1827 100644 --- a/src/TNL/Meshes/MeshDetails/initializer/SubentitySeedsCreator.h +++ b/src/TNL/Meshes/MeshDetails/initializer/SubentitySeedsCreator.h @@ -38,7 +38,7 @@ class SubentitySeedsCreator 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 ) { @@ -84,9 +84,10 @@ class SubentitySeedsCreator< MeshConfig, EntityDimensionTag, DimensionTag< 0 > > using EntityTopology = typename EntityTraitsType::EntityTopology; using SubvertexAccessorType = typename MeshTraitsType::template SubentityTraits< EntityTopology, 0 >::SubentityAccessorType; using SubentityTraits = typename MeshTraitsType::template SubentityTraits< EntityTopology, 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 ) { diff --git a/src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h b/src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h index 796cc1b1e..d32395501 100644 --- a/src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h +++ b/src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h @@ -60,7 +60,6 @@ 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 >; diff --git a/src/TNL/Meshes/MeshDetails/traits/MeshSubentityTraits.h b/src/TNL/Meshes/MeshDetails/traits/MeshSubentityTraits.h index 1c063ca28..4e2e37c82 100644 --- a/src/TNL/Meshes/MeshDetails/traits/MeshSubentityTraits.h +++ b/src/TNL/Meshes/MeshDetails/traits/MeshSubentityTraits.h @@ -26,8 +26,6 @@ namespace Meshes { template< typename MeshConfig, typename EntityTopology > class MeshEntityOrientation; -template< typename MeshConfig, typename EntityTopology > -class EntitySeed; template< typename MeshConfig, typename Device, @@ -48,7 +46,6 @@ 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 >; /**** @@ -58,9 +55,6 @@ public: 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 >; -- GitLab From a266aaf4c1568d848da7570218c2d4f4824f6072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Thu, 26 Mar 2020 18:18:55 +0100 Subject: [PATCH 28/73] Cleaned up DummyMesh --- src/TNL/Meshes/DummyMesh.h | 22 ---------------------- src/Tools/tnl-diff.cpp | 7 ------- src/Tools/tnl-init.cpp | 1 - src/Tools/tnl-view.cpp | 1 - 4 files changed, 31 deletions(-) diff --git a/src/TNL/Meshes/DummyMesh.h b/src/TNL/Meshes/DummyMesh.h index 04a49ed01..e0caf6d3c 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/Tools/tnl-diff.cpp b/src/Tools/tnl-diff.cpp index 84d7a0809..b55013c9e 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-init.cpp b/src/Tools/tnl-init.cpp index 220874ebc..1a7769b5c 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-view.cpp b/src/Tools/tnl-view.cpp index 2a17be889..5587a0f31 100644 --- a/src/Tools/tnl-view.cpp +++ b/src/Tools/tnl-view.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include -- GitLab From 98e542567df0e27b61299afe03499ee29cc2ce7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Thu, 26 Mar 2020 20:57:55 +0100 Subject: [PATCH 29/73] Refactoring mesh configuration: removed entityStorage Consequently, we always store the numbers of all entities, even those that don't have any subentity or superentity mappings enabled. --- src/TNL/Meshes/DefaultConfig.h | 37 ++++-------------- src/TNL/Meshes/Mesh.h | 3 -- src/TNL/Meshes/MeshDetails/ConfigValidator.h | 38 +++++++------------ .../MeshLayers/BoundaryTags/ConfigValidator.h | 2 - .../MeshDetails/MeshLayers/StorageLayer.h | 38 ++++++++----------- src/TNL/Meshes/MeshDetails/Mesh_impl.h | 13 ------- .../MeshDetails/initializer/Initializer.h | 27 +------------ .../MeshDetails/traits/MeshEntityTraits.h | 1 - .../MeshDetails/traits/WeakStorageTraits.h | 18 --------- 9 files changed, 37 insertions(+), 140 deletions(-) diff --git a/src/TNL/Meshes/DefaultConfig.h b/src/TNL/Meshes/DefaultConfig.h index 800b92b4f..f411bde3c 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,9 +23,6 @@ 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, @@ -44,30 +39,16 @@ struct DefaultConfig 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; } /**** @@ -77,7 +58,7 @@ struct DefaultConfig template< typename EntityTopology > static constexpr bool subentityOrientationStorage( EntityTopology, int SubentityDimension ) { - return ( SubentityDimension > 0 ); + return SubentityDimension > 0 && SubentityDimension < meshDimension; } /**** @@ -86,8 +67,7 @@ struct DefaultConfig template< typename EntityTopology > static constexpr bool superentityStorage( EntityTopology, int SuperentityDimension ) { - return entityStorage( EntityTopology::dimension ); - //return false; + return true; } /**** @@ -95,18 +75,15 @@ struct DefaultConfig * * 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 ) { 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; } diff --git a/src/TNL/Meshes/Mesh.h b/src/TNL/Meshes/Mesh.h index f1fbdb386..6b0893b0f 100644 --- a/src/TNL/Meshes/Mesh.h +++ b/src/TNL/Meshes/Mesh.h @@ -117,9 +117,6 @@ class Mesh /** * Entities */ - template< int Dimension > - static constexpr bool entitiesAvailable(); - template< int Dimension > __cuda_callable__ GlobalIndexType getEntitiesCount() const; diff --git a/src/TNL/Meshes/MeshDetails/ConfigValidator.h b/src/TNL/Meshes/MeshDetails/ConfigValidator.h index 9f2753838..fa35ba4d0 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/MeshLayers/BoundaryTags/ConfigValidator.h b/src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/ConfigValidator.h index aba84ba80..094e77caa 100644 --- a/src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/ConfigValidator.h +++ b/src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/ConfigValidator.h @@ -24,8 +24,6 @@ class ConfigValidatorBoundaryTagsLayer { 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 ), diff --git a/src/TNL/Meshes/MeshDetails/MeshLayers/StorageLayer.h b/src/TNL/Meshes/MeshDetails/MeshLayers/StorageLayer.h index 19f307a62..e40c7d576 100644 --- a/src/TNL/Meshes/MeshDetails/MeshLayers/StorageLayer.h +++ b/src/TNL/Meshes/MeshDetails/MeshLayers/StorageLayer.h @@ -28,7 +28,7 @@ namespace Meshes { template< typename MeshConfig, typename Device, typename DimensionTag, - bool EntityStorage = WeakEntityStorageTrait< MeshConfig, Device, DimensionTag >::storageEnabled > + bool EntityStorage = (DimensionTag::value <= MeshConfig::meshDimension) > class StorageLayer; @@ -44,6 +44,9 @@ class StorageLayerFamily 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; @@ -113,7 +116,6 @@ public: 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 ); if( Dimension == 0 ) points.setSize( entitiesCount ); @@ -124,8 +126,9 @@ public: 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." ); + static_assert( SubentityTraits< Dimension, Subdimension >::storageEnabled, + "You try to get subentity storage network which is disabled in the mesh configuration." ); using BaseType = SubentityStorageLayerFamily< MeshConfig, Device, typename EntityTraits< Dimension >::EntityTopology >; @@ -137,8 +140,9 @@ public: const typename MeshTraitsType::template SubentityTraits< typename EntityTraits< Dimension >::EntityTopology, Subdimension >::StorageNetworkType& getSubentityStorageNetwork() const { - 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." ); + static_assert( SubentityTraits< Dimension, Subdimension >::storageEnabled, + "You try to get subentity storage network which is disabled in the mesh configuration." ); using BaseType = SubentityStorageLayerFamily< MeshConfig, Device, typename EntityTraits< Dimension >::EntityTopology >; @@ -150,8 +154,9 @@ public: 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." ); + static_assert( SuperentityTraits< Dimension, Superdimension >::storageEnabled, + "You try to get superentity storage network which is disabled in the mesh configuration." ); using BaseType = SuperentityStorageLayerFamily< MeshConfig, Device, typename EntityTraits< Dimension >::EntityTopology >; @@ -163,8 +168,9 @@ public: const typename MeshTraitsType::template SuperentityTraits< typename EntityTraits< Dimension >::EntityTopology, Superdimension >::StorageNetworkType& getSuperentityStorageNetwork() const { - 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." ); + static_assert( SuperentityTraits< Dimension, Superdimension >::storageEnabled, + "You try to get superentity storage network which is disabled in the mesh configuration." ); using BaseType = SuperentityStorageLayerFamily< MeshConfig, Device, typename EntityTraits< Dimension >::EntityTopology >; @@ -176,7 +182,8 @@ public: 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." ); + 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 ); } @@ -185,7 +192,8 @@ public: 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." ); + 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 ); } }; @@ -313,20 +321,6 @@ protected: 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 > diff --git a/src/TNL/Meshes/MeshDetails/Mesh_impl.h b/src/TNL/Meshes/MeshDetails/Mesh_impl.h index 2e5c4e0d3..295c3255d 100644 --- a/src/TNL/Meshes/MeshDetails/Mesh_impl.h +++ b/src/TNL/Meshes/MeshDetails/Mesh_impl.h @@ -98,15 +98,6 @@ getSerializationTypeVirtual() const return this->getSerializationType(); } -template< typename MeshConfig, typename Device > - template< int Dimension > -constexpr bool -Mesh< MeshConfig, Device >:: -entitiesAvailable() -{ - return EntityTraits< Dimension >::storageEnabled; -} - template< typename MeshConfig, typename Device > template< int Dimension > __cuda_callable__ @@ -114,7 +105,6 @@ typename Mesh< MeshConfig, Device >::GlobalIndexType Mesh< MeshConfig, Device >:: getEntitiesCount() const { - static_assert( entitiesAvailable< Dimension >(), "You try to get number of entities which are not configured for storage." ); return StorageBaseType::getEntitiesCount( DimensionTag< Dimension >() ); } @@ -125,7 +115,6 @@ typename Mesh< MeshConfig, Device >::template EntityType< Dimension > Mesh< MeshConfig, Device >:: getEntity( const GlobalIndexType entityIndex ) const { - static_assert( entitiesAvailable< Dimension >(), "You try to get entity which is not configured for storage." ); TNL_ASSERT_LT( entityIndex, getEntitiesCount< Dimension >(), "invalid entity index" ); return EntityType< Dimension >( *this, entityIndex ); } @@ -224,8 +213,6 @@ 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 diff --git a/src/TNL/Meshes/MeshDetails/initializer/Initializer.h b/src/TNL/Meshes/MeshDetails/initializer/Initializer.h index dfe592e6c..ffda4b4f2 100644 --- a/src/TNL/Meshes/MeshDetails/initializer/Initializer.h +++ b/src/TNL/Meshes/MeshDetails/initializer/Initializer.h @@ -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; @@ -155,13 +151,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 > @@ -198,14 +192,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 > @@ -285,14 +277,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 > @@ -379,28 +369,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 >; diff --git a/src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h b/src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h index d32395501..f5948e28b 100644 --- a/src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h +++ b/src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h @@ -64,7 +64,6 @@ public: 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; }; diff --git a/src/TNL/Meshes/MeshDetails/traits/WeakStorageTraits.h b/src/TNL/Meshes/MeshDetails/traits/WeakStorageTraits.h index 154681fdd..1b8f481bb 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, -- GitLab From 57100b46cee7c84744e6f3267d24a3e1c91a722a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Fri, 27 Mar 2020 12:34:44 +0100 Subject: [PATCH 30/73] Fixed Python bindings for Mesh --- src/Python/pytnl/tnl/Mesh.h | 42 +++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/Python/pytnl/tnl/Mesh.h b/src/Python/pytnl/tnl/Mesh.h index 6b40ce58c..21fa015fc 100644 --- a/src/Python/pytnl/tnl/Mesh.h +++ b/src/Python/pytnl/tnl/Mesh.h @@ -13,15 +13,12 @@ namespace py = pybind11; #include -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 ); @@ -31,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 & ) { } @@ -40,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 ); @@ -51,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(); @@ -70,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 & ) { } @@ -85,9 +87,9 @@ void export_MeshEntity( Scope & scope, const char* name ) // 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 > -- GitLab From bd7486831c5036599a2ee4a629f265e2db694c7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Thu, 2 Apr 2020 23:18:06 +0200 Subject: [PATCH 31/73] Added minimal mesh config to tnl-mesh-converter and fixed related bug in mesh entity initializer --- src/TNL/Meshes/MeshBuilder.h | 1 - .../initializer/EntityInitializer.h | 20 ++++++++- src/Tools/tnl-mesh-converter.cpp | 45 +++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/TNL/Meshes/MeshBuilder.h b/src/TNL/Meshes/MeshBuilder.h index cc5ef2def..8dc390633 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/initializer/EntityInitializer.h b/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h index a4a403bd0..cbef106d0 100644 --- a/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h +++ b/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h @@ -46,7 +46,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 >, @@ -72,6 +74,22 @@ public: } }; +template< typename MeshConfig, + typename EntityTopology > +class EntityInitializer< MeshConfig, EntityTopology, false > + : public EntityInitializerLayer< MeshConfig, + DimensionTag< EntityTopology::dimension >, + DimensionTag< MeshTraits< MeshConfig >::meshDimension > > +{ + using MeshTraitsType = MeshTraits< MeshConfig >; + using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; + + using SeedType = EntitySeed< MeshConfig, EntityTopology >; + using InitializerType = Initializer< MeshConfig >; +public: + static void initEntity( const GlobalIndexType& entityIndex, const SeedType& entitySeed, InitializerType& initializer) {} +}; + /**** * Mesh entity initializer layer with specializations diff --git a/src/Tools/tnl-mesh-converter.cpp b/src/Tools/tnl-mesh-converter.cpp index dae0a0af1..719398629 100644 --- a/src/Tools/tnl-mesh-converter.cpp +++ b/src/Tools/tnl-mesh-converter.cpp @@ -55,6 +55,51 @@ template<> struct MeshGlobalIndexTag< MeshConverterConfigTag, int > { enum { ena template<> struct MeshGlobalIndexTag< MeshConverterConfigTag, long int > { enum { enabled = false }; }; template<> struct MeshLocalIndexTag< MeshConverterConfigTag, short int > { 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 boundaryTagsStorage( EntityTopology ) + { + return false; + } + }; +}; + } // namespace BuildConfigTags } // namespace Meshes } // namespace TNL -- GitLab From 01852cf25aade1dfa8dcf83df3a0ec7a76e1e9cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Fri, 3 Apr 2020 18:31:33 +0200 Subject: [PATCH 32/73] Optimized mesh initializer for cases with disabled entity orientations --- src/TNL/Meshes/MeshDetails/initializer/Initializer.h | 3 ++- src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h | 12 +++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/TNL/Meshes/MeshDetails/initializer/Initializer.h b/src/TNL/Meshes/MeshDetails/initializer/Initializer.h index ffda4b4f2..b1509fd51 100644 --- a/src/TNL/Meshes/MeshDetails/initializer/Initializer.h +++ b/src/TNL/Meshes/MeshDetails/initializer/Initializer.h @@ -93,7 +93,7 @@ class Initializer { // copy points mesh.template setEntitiesCount< 0 >( points.getSize() ); - mesh.getPoints() = points; + mesh.getPoints().swap( points ); points.reset(); this->mesh = &mesh; @@ -354,6 +354,7 @@ class InitializerLayer< MeshConfig, EntityInitializerType::initSuperentities( initializer, mesh ); this->seedsIndexedSet.clear(); this->referenceOrientations.clear(); + this->referenceOrientations.shrink_to_fit(); BaseType::initEntities( initializer, mesh ); } diff --git a/src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h b/src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h index f5948e28b..4e1115daf 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" ); @@ -64,7 +74,7 @@ public: using SeedSetType = std::unordered_set< typename SeedIndexedSetType::key_type, typename SeedIndexedSetType::hasher, typename SeedIndexedSetType::key_equal >; using ReferenceOrientationArrayType = std::vector< ReferenceOrientationType >; - static constexpr bool orientationNeeded = 0 < Dimension && Dimension < MeshConfig::meshDimension; + static constexpr bool orientationNeeded = checkOrientationNeeded(); }; } // namespace Meshes -- GitLab From aaec039cc11b0c55955e7fa77a97a784c5b8f6df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Fri, 3 Apr 2020 22:23:20 +0200 Subject: [PATCH 33/73] Moved Mesh_impl.h, MeshEntity_impl.h and Traverser_impl.h from MeshDetails to Meshes and changed suffix to .hpp --- src/TNL/Meshes/Mesh.h | 2 +- .../{MeshDetails/Mesh_impl.h => Mesh.hpp} | 2 +- src/TNL/Meshes/MeshEntity.h | 2 +- .../MeshEntity_impl.h => MeshEntity.hpp} | 2 +- src/TNL/Meshes/Traverser.h | 42 +++++++++---------- .../Traverser_impl.h => Traverser.hpp} | 2 +- 6 files changed, 26 insertions(+), 26 deletions(-) rename src/TNL/Meshes/{MeshDetails/Mesh_impl.h => Mesh.hpp} (99%) rename src/TNL/Meshes/{MeshDetails/MeshEntity_impl.h => MeshEntity.hpp} (98%) rename src/TNL/Meshes/{MeshDetails/Traverser_impl.h => Traverser.hpp} (98%) diff --git a/src/TNL/Meshes/Mesh.h b/src/TNL/Meshes/Mesh.h index 6b0893b0f..d90260763 100644 --- a/src/TNL/Meshes/Mesh.h +++ b/src/TNL/Meshes/Mesh.h @@ -218,4 +218,4 @@ std::ostream& operator<<( std::ostream& str, const Mesh< MeshConfig, Device >& m #include -#include +#include diff --git a/src/TNL/Meshes/MeshDetails/Mesh_impl.h b/src/TNL/Meshes/Mesh.hpp similarity index 99% rename from src/TNL/Meshes/MeshDetails/Mesh_impl.h rename to src/TNL/Meshes/Mesh.hpp index 295c3255d..665474d82 100644 --- a/src/TNL/Meshes/MeshDetails/Mesh_impl.h +++ b/src/TNL/Meshes/Mesh.hpp @@ -1,5 +1,5 @@ /*************************************************************************** - Mesh_impl.h - description + Mesh.hpp - description ------------------- begin : Sep 5, 2015 copyright : (C) 2015 by Tomas Oberhuber et al. diff --git a/src/TNL/Meshes/MeshEntity.h b/src/TNL/Meshes/MeshEntity.h index c87acfe21..e9397a626 100644 --- a/src/TNL/Meshes/MeshEntity.h +++ b/src/TNL/Meshes/MeshEntity.h @@ -116,4 +116,4 @@ std::ostream& operator<<( std::ostream& str, const MeshEntity< MeshConfig, Devic } // namespace Meshes } // namespace TNL -#include +#include diff --git a/src/TNL/Meshes/MeshDetails/MeshEntity_impl.h b/src/TNL/Meshes/MeshEntity.hpp similarity index 98% rename from src/TNL/Meshes/MeshDetails/MeshEntity_impl.h rename to src/TNL/Meshes/MeshEntity.hpp index b14e85427..e754bd9e4 100644 --- a/src/TNL/Meshes/MeshDetails/MeshEntity_impl.h +++ b/src/TNL/Meshes/MeshEntity.hpp @@ -1,5 +1,5 @@ /*************************************************************************** - MeshEntity_impl.h - description + MeshEntity.hpp - description ------------------- begin : Sep 8, 2015 copyright : (C) 2015 by Tomas Oberhuber et al. diff --git a/src/TNL/Meshes/Traverser.h b/src/TNL/Meshes/Traverser.h index f157e3afc..a4c5a25d4 100644 --- a/src/TNL/Meshes/Traverser.h +++ b/src/TNL/Meshes/Traverser.h @@ -22,32 +22,32 @@ 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; }; } // namespace Meshes } // namespace TNL -#include +#include #include #include #include diff --git a/src/TNL/Meshes/MeshDetails/Traverser_impl.h b/src/TNL/Meshes/Traverser.hpp similarity index 98% rename from src/TNL/Meshes/MeshDetails/Traverser_impl.h rename to src/TNL/Meshes/Traverser.hpp index a24411b0e..4a8f06f59 100644 --- a/src/TNL/Meshes/MeshDetails/Traverser_impl.h +++ b/src/TNL/Meshes/Traverser.hpp @@ -1,5 +1,5 @@ /*************************************************************************** - Traverser_impl.h - description + Traverser.hpp - description ------------------- begin : Dec 25, 2016 copyright : (C) 2016 by Tomas Oberhuber et al. -- GitLab From 0f4f0f391eabc59acd698f8aca58d2673a0731e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Fri, 3 Apr 2020 22:33:58 +0200 Subject: [PATCH 34/73] Renamed MeshLayers directory to 'layers' --- src/TNL/Meshes/Mesh.h | 4 ++-- .../{MeshLayers => layers}/BoundaryTags/ConfigValidator.h | 0 .../{MeshLayers => layers}/BoundaryTags/Initializer.h | 0 .../MeshDetails/{MeshLayers => layers}/BoundaryTags/Layer.h | 0 .../{MeshLayers => layers}/BoundaryTags/LayerFamily.h | 0 .../{MeshLayers => layers}/BoundaryTags/Traits.h | 0 .../MeshDetails/{MeshLayers => layers}/StorageLayer.h | 6 +++--- .../{MeshLayers => layers}/SubentityOrientationsLayer.h | 0 .../{MeshLayers => layers}/SubentityStorageLayer.h | 0 .../{MeshLayers => layers}/SuperentityStorageLayer.h | 0 10 files changed, 5 insertions(+), 5 deletions(-) rename src/TNL/Meshes/MeshDetails/{MeshLayers => layers}/BoundaryTags/ConfigValidator.h (100%) rename src/TNL/Meshes/MeshDetails/{MeshLayers => layers}/BoundaryTags/Initializer.h (100%) rename src/TNL/Meshes/MeshDetails/{MeshLayers => layers}/BoundaryTags/Layer.h (100%) rename src/TNL/Meshes/MeshDetails/{MeshLayers => layers}/BoundaryTags/LayerFamily.h (100%) rename src/TNL/Meshes/MeshDetails/{MeshLayers => layers}/BoundaryTags/Traits.h (100%) rename src/TNL/Meshes/MeshDetails/{MeshLayers => layers}/StorageLayer.h (98%) rename src/TNL/Meshes/MeshDetails/{MeshLayers => layers}/SubentityOrientationsLayer.h (100%) rename src/TNL/Meshes/MeshDetails/{MeshLayers => layers}/SubentityStorageLayer.h (100%) rename src/TNL/Meshes/MeshDetails/{MeshLayers => layers}/SuperentityStorageLayer.h (100%) diff --git a/src/TNL/Meshes/Mesh.h b/src/TNL/Meshes/Mesh.h index d90260763..b3a8d8192 100644 --- a/src/TNL/Meshes/Mesh.h +++ b/src/TNL/Meshes/Mesh.h @@ -22,8 +22,8 @@ #include #include #include -#include -#include +#include +#include #include diff --git a/src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/ConfigValidator.h b/src/TNL/Meshes/MeshDetails/layers/BoundaryTags/ConfigValidator.h similarity index 100% rename from src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/ConfigValidator.h rename to src/TNL/Meshes/MeshDetails/layers/BoundaryTags/ConfigValidator.h diff --git a/src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/Initializer.h b/src/TNL/Meshes/MeshDetails/layers/BoundaryTags/Initializer.h similarity index 100% rename from src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/Initializer.h rename to src/TNL/Meshes/MeshDetails/layers/BoundaryTags/Initializer.h diff --git a/src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/Layer.h b/src/TNL/Meshes/MeshDetails/layers/BoundaryTags/Layer.h similarity index 100% rename from src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/Layer.h rename to src/TNL/Meshes/MeshDetails/layers/BoundaryTags/Layer.h diff --git a/src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/LayerFamily.h b/src/TNL/Meshes/MeshDetails/layers/BoundaryTags/LayerFamily.h similarity index 100% rename from src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/LayerFamily.h rename to src/TNL/Meshes/MeshDetails/layers/BoundaryTags/LayerFamily.h diff --git a/src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/Traits.h b/src/TNL/Meshes/MeshDetails/layers/BoundaryTags/Traits.h similarity index 100% rename from src/TNL/Meshes/MeshDetails/MeshLayers/BoundaryTags/Traits.h rename to src/TNL/Meshes/MeshDetails/layers/BoundaryTags/Traits.h diff --git a/src/TNL/Meshes/MeshDetails/MeshLayers/StorageLayer.h b/src/TNL/Meshes/MeshDetails/layers/StorageLayer.h similarity index 98% rename from src/TNL/Meshes/MeshDetails/MeshLayers/StorageLayer.h rename to src/TNL/Meshes/MeshDetails/layers/StorageLayer.h index e40c7d576..f017158cf 100644 --- a/src/TNL/Meshes/MeshDetails/MeshLayers/StorageLayer.h +++ b/src/TNL/Meshes/MeshDetails/layers/StorageLayer.h @@ -18,9 +18,9 @@ #include #include -#include -#include -#include +#include +#include +#include namespace TNL { namespace Meshes { diff --git a/src/TNL/Meshes/MeshDetails/MeshLayers/SubentityOrientationsLayer.h b/src/TNL/Meshes/MeshDetails/layers/SubentityOrientationsLayer.h similarity index 100% rename from src/TNL/Meshes/MeshDetails/MeshLayers/SubentityOrientationsLayer.h rename to src/TNL/Meshes/MeshDetails/layers/SubentityOrientationsLayer.h diff --git a/src/TNL/Meshes/MeshDetails/MeshLayers/SubentityStorageLayer.h b/src/TNL/Meshes/MeshDetails/layers/SubentityStorageLayer.h similarity index 100% rename from src/TNL/Meshes/MeshDetails/MeshLayers/SubentityStorageLayer.h rename to src/TNL/Meshes/MeshDetails/layers/SubentityStorageLayer.h diff --git a/src/TNL/Meshes/MeshDetails/MeshLayers/SuperentityStorageLayer.h b/src/TNL/Meshes/MeshDetails/layers/SuperentityStorageLayer.h similarity index 100% rename from src/TNL/Meshes/MeshDetails/MeshLayers/SuperentityStorageLayer.h rename to src/TNL/Meshes/MeshDetails/layers/SuperentityStorageLayer.h -- GitLab From a45cc411d9b4d32c171b1030dbb1bed68b8eb15e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Wed, 1 Apr 2020 20:21:30 +0200 Subject: [PATCH 35/73] Added new tool: tnl-decompose-mesh --- cmake/FindMETIS.cmake | 23 + src/TNL/Meshes/VTKTraits.h | 38 ++ src/Tools/CMakeLists.txt | 17 + src/Tools/tnl-decompose-mesh.cpp | 756 +++++++++++++++++++++++++++++++ 4 files changed, 834 insertions(+) create mode 100644 cmake/FindMETIS.cmake create mode 100644 src/Tools/tnl-decompose-mesh.cpp diff --git a/cmake/FindMETIS.cmake b/cmake/FindMETIS.cmake new file mode 100644 index 000000000..bfaf1abd8 --- /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/TNL/Meshes/VTKTraits.h b/src/TNL/Meshes/VTKTraits.h index 38f93db98..0db339acf 100644 --- a/src/TNL/Meshes/VTKTraits.h +++ b/src/TNL/Meshes/VTKTraits.h @@ -157,6 +157,44 @@ 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/Tools/CMakeLists.txt b/src/Tools/CMakeLists.txt index fb34e032c..7a4e975e9 100644 --- a/src/Tools/CMakeLists.txt +++ b/src/Tools/CMakeLists.txt @@ -41,6 +41,23 @@ if( tinyxml2_FOUND ) target_link_libraries(tnl-mesh-converter 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 diff --git a/src/Tools/tnl-decompose-mesh.cpp b/src/Tools/tnl-decompose-mesh.cpp new file mode 100644 index 000000000..75cb8e714 --- /dev/null +++ b/src/Tools/tnl-decompose-mesh.cpp @@ -0,0 +1,756 @@ +/*************************************************************************** + 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 boundaryTagsStorage( EntityTopology ) + { + return EntityTopology::dimension >= meshDimension - 1; + } + }; +}; + +} // 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.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 Config::ParameterContainer& parameters ) + { + const String inputFileName = parameters.template getParameter< String >( "input-file" ); + Mesh mesh; + if( ! Meshes::loadMesh( inputFileName, mesh ) ) { + std::cerr << "Failed to load mesh from the file '" << inputFileName << "'." << std::endl; + return false; + } + + // 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]; + + // prepare output file name + const String outputFile = parameters.template getParameter< String >( "output-file" ); + FileName outputFileName( removeFileNameExtension( outputFile ) + ".subdomain", "vtu" ); + outputFileName.setDigitsCount( std::floor(std::log10( nparts - 1 )) + 1 ); + + std::cout << "Writing subdomains..." << std::endl; + for( unsigned p = 0; p < nparts; p++ ) { + outputFileName.setIndex( p ); + std::cout << outputFileName.getFileName() << 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 < seed.getCornerIds().getSize(); 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, 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.getFileName() ); + 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" ); + } + } + + // write a .pvtu file + std::ofstream file( outputFile.getString() ); + // TODO: refactor into a PVTU writer + using HeaderType = std::uint64_t; + using namespace Meshes; + file << "\n"; + file << "\n"; + file << "\n"; + file << "\n"; + file << "\n"; + file << "\n"; + if( ghost_levels > 0 ) { + // the PointData and CellData from the individual files should be added here + file << "\n"; + file << "\n"; + file << "\n"; + file << "\n"; + file << "\n"; + file << "\n"; + file << "\n"; + file << "\n"; + } + + for( unsigned p = 0; p < nparts; p++ ) { + outputFileName.setIndex( p ); + // make sure the source path is relative to the main .pvtu file + // TODO: use proper path library + const auto parts = outputFileName.getFileName().split('/'); + const std::string basename = parts.back(); + file << "\n"; + } + + file << "\n"; + file << "\n"; + + 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 outputFile = parameters.template getParameter< String >( "output-file" ); + if( getFileExtension( outputFile ) != "pvtu" ) { + std::cerr << "Error: the output file must have a '.pvtu' extension." << std::endl; + return EXIT_FAILURE; + } + + return ! Meshes::resolveMeshType< DecomposeMeshConfigTag, Devices::Host, DecomposeMesh >( inputFileName, parameters ); +} -- GitLab From 566521196674c3ea0c30617b07815bb3d472ac47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sat, 4 Apr 2020 21:56:04 +0200 Subject: [PATCH 36/73] Added mesh layer for the dual graph storage --- src/TNL/Meshes/DefaultConfig.h | 16 ++ src/TNL/Meshes/Mesh.h | 9 + src/TNL/Meshes/Mesh.hpp | 46 +++- .../MeshDetails/IndexPermutationApplier.h | 59 +++-- .../MeshDetails/layers/DualGraphLayer.h | 178 +++++++++++++++ .../Meshes/MeshDetails/layers/StorageLayer.h | 9 +- .../Meshes/MeshDetails/traits/MeshTraits.h | 4 + src/Tools/tnl-decompose-mesh.cpp | 5 + src/Tools/tnl-mesh-converter.cpp | 5 + src/UnitTests/Meshes/MeshOrderingTest.h | 6 + src/UnitTests/Meshes/MeshTest.h | 212 +++++++++++++++++- 11 files changed, 515 insertions(+), 34 deletions(-) create mode 100644 src/TNL/Meshes/MeshDetails/layers/DualGraphLayer.h diff --git a/src/TNL/Meshes/DefaultConfig.h b/src/TNL/Meshes/DefaultConfig.h index f411bde3c..076bfc9bd 100644 --- a/src/TNL/Meshes/DefaultConfig.h +++ b/src/TNL/Meshes/DefaultConfig.h @@ -87,6 +87,22 @@ struct DefaultConfig ( 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/Mesh.h b/src/TNL/Meshes/Mesh.h index b3a8d8192..51a9c9e6c 100644 --- a/src/TNL/Meshes/Mesh.h +++ b/src/TNL/Meshes/Mesh.h @@ -165,6 +165,15 @@ class Mesh __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; + /* * The permutations follow the definition used in the Metis library: Let M diff --git a/src/TNL/Meshes/Mesh.hpp b/src/TNL/Meshes/Mesh.hpp index 665474d82..523f2a1b7 100644 --- a/src/TNL/Meshes/Mesh.hpp +++ b/src/TNL/Meshes/Mesh.hpp @@ -29,10 +29,13 @@ 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, *static_cast(this) ); + initializer.createMesh( points, cellSeeds, *mesh ); // init boundary tags - static_cast< BoundaryTags::LayerFamily< MeshConfig, Device, MeshType >* >( static_cast< MeshType* >( this ) )->initLayer(); + static_cast< BoundaryTags::LayerFamily< MeshConfig, Device, MeshType >* >( mesh )->initLayer(); + // init dual graph + mesh->initializeDualGraph( *mesh ); } @@ -205,6 +208,30 @@ getSuperentityIndex( const GlobalIndexType entityIndex, const LocalIndexType sup return this->template getSuperentityStorageNetwork< EntityDimension, SuperentityDimension >().getValue( entityIndex, 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." ); + return this->getDualGraph().getValues( cellIndex )[ neighborIndex ]; +} + template< typename MeshConfig, typename Device > template< int Dimension > @@ -253,9 +280,18 @@ void Mesh< MeshConfig, Device >:: load( File& file ) { - Object::load( file ); - StorageBaseType::load( file ); - BoundaryTagsLayerFamily::load( file ); + // loading via host is necessary for the initialization of the dual graph + 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 ); + BoundaryTagsLayerFamily::load( file ); + this->initializeDualGraph( *this ); + } } template< typename MeshConfig, typename Device > diff --git a/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h b/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h index a29a705df..a4ce7e325 100644 --- a/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h +++ b/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h @@ -120,49 +120,55 @@ private: template< int Superdimension > using SuperentitiesWorker = IndexPermutationApplierSuperentitiesWorker< Superdimension >; -public: - static void permutePoints( 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 GlobalIndexVector& perm, const GlobalIndexVector& iperm ) { - using IndexType = typename Mesh::GlobalIndexType; - using DeviceType = typename Mesh::DeviceType; - using PointArrayType = typename Mesh::MeshTraitsType::PointArrayType; + permuteArray( mesh.getNeighborCounts(), perm ); + auto& graph = mesh.getDualGraph(); + Containers::Multimaps::permuteMultimapKeys( graph, perm ); + Containers::Multimaps::permuteMultimapValues( graph, iperm ); + } - const IndexType pointsCount = mesh.template getEntitiesCount< 0 >(); + template< typename Mesh_, std::enable_if_t< ! Mesh_::Config::dualGraphStorage(), bool > = true > + static void permuteDualGraph( Mesh_& mesh, const GlobalIndexVector& perm, const GlobalIndexVector& iperm ) {} - PointArrayType points; - points.setSize( pointsCount ); +public: + template< typename Array > + static void permuteArray( Array& array, const GlobalIndexVector& perm ) + { + using IndexType = typename Array::IndexType; + using DeviceType = typename Array::DeviceType; + + Array buffer( array.getSize() ); // kernel to copy entities to new array, applying the permutation auto kernel1 = [] __cuda_callable__ ( IndexType i, - const Mesh* mesh, - typename PointArrayType::ValueType* pointsArray, + const typename Array::ValueType* array, + typename Array::ValueType* buffer, const IndexType* perm ) { - pointsArray[ i ] = mesh->getPoint( perm[ i ] ); + buffer[ i ] = array[ perm[ i ] ]; }; // kernel to copy permuted entities back to the mesh auto kernel2 = [] __cuda_callable__ ( IndexType i, - Mesh* mesh, - const typename PointArrayType::ValueType* pointsArray ) + typename Array::ValueType* array, + const typename Array::ValueType* buffer ) { - mesh->getPoint( i ) = pointsArray[ i ]; + array[ i ] = buffer[ i ]; }; - Pointers::DevicePointer< Mesh > meshPointer( mesh ); - Algorithms::ParallelFor< DeviceType >::exec( (IndexType) 0, pointsCount, + Algorithms::ParallelFor< DeviceType >::exec( (IndexType) 0, array.getSize(), kernel1, - &meshPointer.template getData< DeviceType >(), - points.getData(), + array.getData(), + buffer.getData(), perm.getData() ); - Algorithms::ParallelFor< DeviceType >::exec( (IndexType) 0, pointsCount, + Algorithms::ParallelFor< DeviceType >::exec( (IndexType) 0, array.getSize(), kernel2, - &meshPointer.template modifyData< DeviceType >(), - points.getData() ); + array.getData(), + buffer.getData() ); } static void exec( Mesh& mesh, @@ -173,7 +179,7 @@ public: using DeviceType = typename Mesh::DeviceType; if( Dimension == 0 ) - permutePoints( mesh, perm, iperm ); + permuteArray( mesh.getPoints(), perm ); // permute superentities storage Algorithms::TemplateStaticFor< int, 0, Dimension, SubentitiesStorageWorker >::execHost( mesh, perm ); @@ -186,6 +192,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/layers/DualGraphLayer.h b/src/TNL/Meshes/MeshDetails/layers/DualGraphLayer.h new file mode 100644 index 000000000..06ff35e25 --- /dev/null +++ b/src/TNL/Meshes/MeshDetails/layers/DualGraphLayer.h @@ -0,0 +1,178 @@ +/*************************************************************************** + 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 DualGraph::ValuesAllocationVectorType; + + 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.setLike( other.getDualGraph() ); + 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.setKeysRange( cellsCount ); + graph.allocate( neighborCounts ); + + // fill in neighbor indices + for( GlobalIndexType k = 0; k < cellsCount; k++) { + auto adjncy = graph.getValues( k ); + const LocalIndexType nnbrs = findNeighbors( k ); + for( LocalIndexType j = 0; j < nnbrs; j++) + adjncy[ j ] = neighbors[ j ]; + } + } + +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/layers/StorageLayer.h b/src/TNL/Meshes/MeshDetails/layers/StorageLayer.h index f017158cf..b00e8caed 100644 --- a/src/TNL/Meshes/MeshDetails/layers/StorageLayer.h +++ b/src/TNL/Meshes/MeshDetails/layers/StorageLayer.h @@ -21,6 +21,7 @@ #include #include #include +#include namespace TNL { namespace Meshes { @@ -34,7 +35,8 @@ class StorageLayer; template< typename MeshConfig, typename Device > class StorageLayerFamily - : public StorageLayer< MeshConfig, Device, DimensionTag< 0 > > + : public StorageLayer< MeshConfig, Device, DimensionTag< 0 > >, + public DualGraphLayer< MeshConfig, Device > { using MeshTraitsType = MeshTraits< MeshConfig, Device >; using BaseType = StorageLayer< MeshConfig, Device, DimensionTag< 0 > >; @@ -68,6 +70,7 @@ public: { points = layer.getPoints(); BaseType::operator=( layer ); + DualGraphLayer< MeshConfig, Device >::operator=( layer ); return *this; } @@ -76,13 +79,15 @@ public: { 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 ) ); + BaseType::operator==( layer ) && + DualGraphLayer< MeshConfig, Device >::operator==( layer ) ); } void save( File& file ) const diff --git a/src/TNL/Meshes/MeshDetails/traits/MeshTraits.h b/src/TNL/Meshes/MeshDetails/traits/MeshTraits.h index 545edbb23..d88a6abb9 100644 --- a/src/TNL/Meshes/MeshDetails/traits/MeshTraits.h +++ b/src/TNL/Meshes/MeshDetails/traits/MeshTraits.h @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -61,6 +62,9 @@ public: using SuperentityTraits = MeshSuperentityTraits< MeshConfig, DeviceType, EntityTopology, Superdimension >; using DimensionTag = Meshes::DimensionTag< meshDimension >; + + // TODO: write general operator= for different SliceSize and remove the '32' here + using DualGraph = Containers::Multimaps::EllpackIndexMultimap< GlobalIndexType, DeviceType, GlobalIndexType, 32 >; }; } // namespace Meshes diff --git a/src/Tools/tnl-decompose-mesh.cpp b/src/Tools/tnl-decompose-mesh.cpp index 75cb8e714..17d69bcb5 100644 --- a/src/Tools/tnl-decompose-mesh.cpp +++ b/src/Tools/tnl-decompose-mesh.cpp @@ -103,6 +103,11 @@ struct MeshConfigTemplateTag< DecomposeMeshConfigTag > { return EntityTopology::dimension >= meshDimension - 1; } + + static constexpr bool dualGraphStorage() + { + return false; + } }; }; diff --git a/src/Tools/tnl-mesh-converter.cpp b/src/Tools/tnl-mesh-converter.cpp index 719398629..ada236cff 100644 --- a/src/Tools/tnl-mesh-converter.cpp +++ b/src/Tools/tnl-mesh-converter.cpp @@ -97,6 +97,11 @@ struct MeshConfigTemplateTag< MeshConverterConfigTag > { return false; } + + static constexpr bool dualGraphStorage() + { + return false; + } }; }; diff --git a/src/UnitTests/Meshes/MeshOrderingTest.h b/src/UnitTests/Meshes/MeshOrderingTest.h index eb533fa90..d59e9a012 100644 --- a/src/UnitTests/Meshes/MeshOrderingTest.h +++ b/src/UnitTests/Meshes/MeshOrderingTest.h @@ -217,6 +217,12 @@ void testMesh( const Mesh< TestTriangleMeshConfig, Devices::Host >& mesh, // boundary indices are always sorted so we can't test this // EXPECT_EQ( mesh.template getInteriorEntityIndex< 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 diff --git a/src/UnitTests/Meshes/MeshTest.h b/src/UnitTests/Meshes/MeshTest.h index 8549480ff..4fbd0ea86 100644 --- a/src/UnitTests/Meshes/MeshTest.h +++ b/src/UnitTests/Meshes/MeshTest.h @@ -258,9 +258,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 +276,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 +648,39 @@ 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; + ASSERT_EQ( mesh.getDualGraph().getValues( cellIdx ).getSize(), nnbrs ); + std::set< IndexType > neighbors; + for( IndexType n = 0; n < nnbrs; n++ ) + neighbors.insert( mesh.getDualGraph().getValues( cellIdx )[ 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 +846,176 @@ 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; + ASSERT_EQ( mesh.getDualGraph().getValues( cellIdx ).getSize(), nnbrs ); + std::set< IndexType > neighbors; + for( IndexType n = 0; n < nnbrs; n++ ) + neighbors.insert( mesh.getDualGraph().getValues( cellIdx )[ 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; + + ASSERT_EQ( mesh.getDualGraph().getValues( cellIdx ).getSize(), nnbrs ); + std::set< IndexType > neighbors; + for( IndexType n = 0; n < nnbrs; n++ ) + neighbors.insert( mesh.getDualGraph().getValues( cellIdx )[ 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; + + ASSERT_EQ( mesh.getDualGraph().getValues( cellIdx ).getSize(), nnbrs ); + std::set< IndexType > neighbors; + for( IndexType n = 0; n < nnbrs; n++ ) + neighbors.insert( mesh.getDualGraph().getValues( cellIdx )[ 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 ); } -- GitLab From 8708b2e0738e0e9038ac1231b517f2be0a2a31a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Tue, 7 Apr 2020 09:58:24 +0200 Subject: [PATCH 37/73] Optimized the initialization of mesh links from subentities to superentities --- .../initializer/EntityInitializer.h | 101 +++++++++++++---- .../SuperentityStorageInitializer.h | 102 ------------------ 2 files changed, 83 insertions(+), 120 deletions(-) delete mode 100644 src/TNL/Meshes/MeshDetails/initializer/SuperentityStorageInitializer.h diff --git a/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h b/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h index cbef106d0..fc653dc93 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 { @@ -119,32 +118,55 @@ 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 SuperentityStorageNetwork = typename MeshTraits< MeshConfig >::template SuperentityTraits< SubentityTopology, SuperdimensionTag::value >::StorageNetworkType; + using ValuesAllocationVectorType = typename SuperentityStorageNetwork::ValuesAllocationVectorType; 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; + + // counter for superentities of each subentity + const GlobalIndexType subentitiesCount = mesh.template getEntitiesCount< SubdimensionTag::value >(); + ValuesAllocationVectorType superentitiesCounts( subentitiesCount ); + superentitiesCounts.setValue( 0 ); for( GlobalIndexType superentityIndex = 0; superentityIndex < mesh.template getEntitiesCount< SuperdimensionTag::value >(); 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< SuperdimensionTag::value, SubdimensionTag::value >( superentityIndex, i, subentityIndex ); - superentityInitializer.addSuperentity( subentityIndex, superentityIndex ); + superentitiesCounts[ subentityIndex ]++; } } - superentityInitializer.initSuperentities( meshInitializer ); + // allocate superentities storage + SuperentityStorageNetwork& superentityStorageNetwork = meshInitializer.template meshSuperentityStorageNetwork< SubdimensionTag::value, SuperdimensionTag::value >(); + superentityStorageNetwork.allocate( superentitiesCounts ); + superentitiesCounts.setValue( 0 ); + + // initialize superentities storage + for( GlobalIndexType superentityIndex = 0; + superentityIndex < mesh.template getEntitiesCount< SuperdimensionTag::value >(); + 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 ); + superentityStorageNetwork.getValues( subentityIndex ).setValue( superentitiesCounts[ subentityIndex ]++, superentityIndex ); + } + } BaseType::initSuperentities( meshInitializer, mesh ); } @@ -178,36 +200,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 SuperentityStorageNetwork = typename MeshTraits< MeshConfig >::template SuperentityTraits< SubentityTopology, SuperdimensionTag::value >::StorageNetworkType; + using ValuesAllocationVectorType = typename SuperentityStorageNetwork::ValuesAllocationVectorType; 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; + + // counter for superentities of each subentity + const GlobalIndexType subentitiesCount = mesh.template getEntitiesCount< SubdimensionTag::value >(); + ValuesAllocationVectorType superentitiesCounts( subentitiesCount ); + superentitiesCounts.setValue( 0 ); for( GlobalIndexType superentityIndex = 0; superentityIndex < mesh.template getEntitiesCount< SuperdimensionTag::value >(); superentityIndex++ ) { auto subentitySeeds = SubentitySeedsCreatorType::create( meshInitializer.template getSubvertices< SuperdimensionTag::value >( superentityIndex ) ); - auto& subentityOrientationsArray = meshInitializer.template subentityOrientationsArray< SuperdimensionTag::value, SubdimensionTag::value >( superentityIndex ); - for( LocalIndexType i = 0; i < subentitySeeds.getSize(); i++ ) { const GlobalIndexType subentityIndex = meshInitializer.findEntitySeedIndex( subentitySeeds[ i ] ); meshInitializer.template setSubentityIndex< SuperdimensionTag::value, SubdimensionTag::value >( superentityIndex, i, subentityIndex ); - superentityInitializer.addSuperentity( subentityIndex, superentityIndex ); - + superentitiesCounts[ subentityIndex ]++; subentityOrientationsArray[ i ] = meshInitializer.template getReferenceOrientation< SubdimensionTag >( subentityIndex ).createOrientation( subentitySeeds[ i ] ); } } - superentityInitializer.initSuperentities( meshInitializer ); + // allocate superentities storage + SuperentityStorageNetwork& superentityStorageNetwork = meshInitializer.template meshSuperentityStorageNetwork< SubdimensionTag::value, SuperdimensionTag::value >(); + superentityStorageNetwork.allocate( superentitiesCounts ); + superentitiesCounts.setValue( 0 ); + + // initialize superentities storage + for( GlobalIndexType superentityIndex = 0; + superentityIndex < mesh.template getEntitiesCount< SuperdimensionTag::value >(); + 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 ); + superentityStorageNetwork.getValues( subentityIndex ).setValue( superentitiesCounts[ subentityIndex ]++, superentityIndex ); + } + } BaseType::initSuperentities( meshInitializer, mesh ); } @@ -351,31 +394,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 SuperentityStorageNetwork = typename MeshTraits< MeshConfig >::template SuperentityTraits< SubentityTopology, SuperdimensionTag::value >::StorageNetworkType; + using ValuesAllocationVectorType = typename SuperentityStorageNetwork::ValuesAllocationVectorType; 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; + + // counter for superentities of each subentity + const GlobalIndexType subentitiesCount = mesh.template getEntitiesCount< SubdimensionTag::value >(); + ValuesAllocationVectorType superentitiesCounts( subentitiesCount ); + superentitiesCounts.setValue( 0 ); for( GlobalIndexType superentityIndex = 0; superentityIndex < mesh.template getEntitiesCount< SuperdimensionTag::value >(); 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 + SuperentityStorageNetwork& superentityStorageNetwork = meshInitializer.template meshSuperentityStorageNetwork< SubdimensionTag::value, SuperdimensionTag::value >(); + superentityStorageNetwork.allocate( superentitiesCounts ); + superentitiesCounts.setValue( 0 ); + + // initialize superentities storage + for( GlobalIndexType superentityIndex = 0; + superentityIndex < mesh.template getEntitiesCount< SuperdimensionTag::value >(); + 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 ] ); + superentityStorageNetwork.getValues( subentityIndex ).setValue( superentitiesCounts[ subentityIndex ]++, superentityIndex ); + } + } BaseType::initSuperentities( meshInitializer, mesh ); } diff --git a/src/TNL/Meshes/MeshDetails/initializer/SuperentityStorageInitializer.h b/src/TNL/Meshes/MeshDetails/initializer/SuperentityStorageInitializer.h deleted file mode 100644 index 0a8d61dd4..000000000 --- a/src/TNL/Meshes/MeshDetails/initializer/SuperentityStorageInitializer.h +++ /dev/null @@ -1,102 +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 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< SubdimensionTag::value, 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 -- GitLab From ebe8ba387e7288b3d82237f18ea548cc55e0c5ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Tue, 7 Apr 2020 20:27:42 +0200 Subject: [PATCH 38/73] Refactoring: generalized BoundaryTags to EntityTags --- src/TNL/Meshes/DefaultConfig.h | 4 +- src/TNL/Meshes/Mesh.h | 6 +- src/TNL/Meshes/Mesh.hpp | 20 +- .../MeshDetails/layers/BoundaryTags/Layer.h | 255 --------------- .../ConfigValidator.h | 12 +- .../Initializer.h | 34 +- .../MeshDetails/layers/EntityTags/Layer.h | 299 ++++++++++++++++++ .../LayerFamily.h | 86 +++-- .../{BoundaryTags => EntityTags}/Traits.h | 16 +- .../Meshes/MeshDetails/traits/MeshTraits.h | 26 +- src/TNL/Meshes/MeshEntity.h | 7 + src/TNL/Meshes/MeshEntity.hpp | 12 + src/Tools/tnl-decompose-mesh.cpp | 2 +- src/Tools/tnl-mesh-converter.cpp | 2 +- src/UnitTests/Meshes/BoundaryTagsTest.cpp | 2 - src/UnitTests/Meshes/CMakeLists.txt | 8 +- src/UnitTests/Meshes/EntityTagsTest.cpp | 2 + .../{BoundaryTagsTest.h => EntityTagsTest.h} | 64 +++- 18 files changed, 498 insertions(+), 359 deletions(-) delete mode 100644 src/TNL/Meshes/MeshDetails/layers/BoundaryTags/Layer.h rename src/TNL/Meshes/MeshDetails/layers/{BoundaryTags => EntityTags}/ConfigValidator.h (85%) rename src/TNL/Meshes/MeshDetails/layers/{BoundaryTags => EntityTags}/Initializer.h (81%) create mode 100644 src/TNL/Meshes/MeshDetails/layers/EntityTags/Layer.h rename src/TNL/Meshes/MeshDetails/layers/{BoundaryTags => EntityTags}/LayerFamily.h (69%) rename src/TNL/Meshes/MeshDetails/layers/{BoundaryTags => EntityTags}/Traits.h (72%) delete mode 100644 src/UnitTests/Meshes/BoundaryTagsTest.cpp create mode 100644 src/UnitTests/Meshes/EntityTagsTest.cpp rename src/UnitTests/Meshes/{BoundaryTagsTest.h => EntityTagsTest.h} (58%) diff --git a/src/TNL/Meshes/DefaultConfig.h b/src/TNL/Meshes/DefaultConfig.h index 076bfc9bd..f149c433e 100644 --- a/src/TNL/Meshes/DefaultConfig.h +++ b/src/TNL/Meshes/DefaultConfig.h @@ -71,7 +71,7 @@ struct DefaultConfig } /**** - * 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: @@ -80,7 +80,7 @@ struct DefaultConfig * must be stored as subentities of faces */ template< typename EntityTopology > - static constexpr bool boundaryTagsStorage( EntityTopology ) + static constexpr bool entityTagsStorage( EntityTopology ) { using FaceTopology = typename Topologies::Subtopology< CellTopology, meshDimension - 1 >::Topology; return superentityStorage( FaceTopology(), meshDimension ) && diff --git a/src/TNL/Meshes/Mesh.h b/src/TNL/Meshes/Mesh.h index 51a9c9e6c..18f803fdc 100644 --- a/src/TNL/Meshes/Mesh.h +++ b/src/TNL/Meshes/Mesh.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include @@ -68,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; diff --git a/src/TNL/Meshes/Mesh.hpp b/src/TNL/Meshes/Mesh.hpp index 523f2a1b7..089b1c180 100644 --- a/src/TNL/Meshes/Mesh.hpp +++ b/src/TNL/Meshes/Mesh.hpp @@ -33,7 +33,7 @@ init( typename MeshTraitsType::PointArrayType& points, Initializer< typename MeshType::Config > initializer; initializer.createMesh( points, cellSeeds, *mesh ); // init boundary tags - static_cast< BoundaryTags::LayerFamily< MeshConfig, Device, MeshType >* >( mesh )->initLayer(); + static_cast< EntityTags::LayerFamily< MeshConfig, Device, MeshType >* >( mesh )->initLayer(); // init dual graph mesh->initializeDualGraph( *mesh ); } @@ -43,7 +43,7 @@ template< typename MeshConfig, typename Device > Mesh< MeshConfig, Device >:: Mesh( const Mesh& mesh ) : StorageBaseType( mesh ), - BoundaryTagsLayerFamily( mesh ) + EntityTagsLayerFamily( mesh ) { } @@ -52,7 +52,7 @@ template< typename MeshConfig, typename Device > Mesh< MeshConfig, Device >:: Mesh( const Mesh< MeshConfig, Device_ >& mesh ) : StorageBaseType( mesh ), - BoundaryTagsLayerFamily( mesh ) + EntityTagsLayerFamily( mesh ) { } @@ -62,7 +62,7 @@ Mesh< MeshConfig, Device >:: operator=( const Mesh& mesh ) { StorageBaseType::operator=( mesh ); - BoundaryTagsLayerFamily::operator=( mesh ); + EntityTagsLayerFamily::operator=( mesh ); return *this; } @@ -73,7 +73,7 @@ Mesh< MeshConfig, Device >:: operator=( const Mesh< MeshConfig, Device_ >& mesh ) { StorageBaseType::operator=( mesh ); - BoundaryTagsLayerFamily::operator=( mesh ); + EntityTagsLayerFamily::operator=( mesh ); return *this; } @@ -261,7 +261,7 @@ reorderEntities( const GlobalIndexVector& perm, IndexPermutationApplier< Mesh, Dimension >::exec( *this, perm, iperm ); // update boundary tags - static_cast< BoundaryTagsLayerFamily* >( this )->initLayer(); + static_cast< EntityTagsLayerFamily* >( this )->initLayer(); } @@ -272,7 +272,7 @@ save( File& file ) const { Object::save( file ); StorageBaseType::save( file ); - BoundaryTagsLayerFamily::save( file ); + EntityTagsLayerFamily::save( file ); } template< typename MeshConfig, typename Device > @@ -289,7 +289,7 @@ load( File& file ) else { Object::load( file ); StorageBaseType::load( file ); - BoundaryTagsLayerFamily::load( file ); + EntityTagsLayerFamily::load( file ); this->initializeDualGraph( *this ); } } @@ -305,7 +305,7 @@ print( std::ostream& str ) const } else { StorageBaseType::print( str ); - BoundaryTagsLayerFamily::print( str ); + EntityTagsLayerFamily::print( str ); } } @@ -315,7 +315,7 @@ Mesh< MeshConfig, Device >:: operator==( const Mesh& mesh ) const { return StorageBaseType::operator==( mesh ) && - BoundaryTagsLayerFamily::operator==( mesh ); + EntityTagsLayerFamily::operator==( mesh ); } template< typename MeshConfig, typename Device > diff --git a/src/TNL/Meshes/MeshDetails/layers/BoundaryTags/Layer.h b/src/TNL/Meshes/MeshDetails/layers/BoundaryTags/Layer.h deleted file mode 100644 index e31c76dae..000000000 --- a/src/TNL/Meshes/MeshDetails/layers/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/layers/BoundaryTags/ConfigValidator.h b/src/TNL/Meshes/MeshDetails/layers/EntityTags/ConfigValidator.h similarity index 85% rename from src/TNL/Meshes/MeshDetails/layers/BoundaryTags/ConfigValidator.h rename to src/TNL/Meshes/MeshDetails/layers/EntityTags/ConfigValidator.h index 094e77caa..3aa4963c3 100644 --- a/src/TNL/Meshes/MeshDetails/layers/BoundaryTags/ConfigValidator.h +++ b/src/TNL/Meshes/MeshDetails/layers/EntityTags/ConfigValidator.h @@ -15,12 +15,12 @@ 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; @@ -32,7 +32,7 @@ class ConfigValidatorBoundaryTagsLayer template< typename MeshConfig, typename EntityTopology > -class ConfigValidatorBoundaryTagsLayer< MeshConfig, EntityTopology, false > +class ConfigValidatorEntityTagsLayer< MeshConfig, EntityTopology, false > { }; @@ -40,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 > { }; @@ -56,6 +56,6 @@ class ConfigValidator { }; -} // namespace BoundaryTags +} // namespace EntityTags } // namespace Meshes } // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/layers/BoundaryTags/Initializer.h b/src/TNL/Meshes/MeshDetails/layers/EntityTags/Initializer.h similarity index 81% rename from src/TNL/Meshes/MeshDetails/layers/BoundaryTags/Initializer.h rename to src/TNL/Meshes/MeshDetails/layers/EntityTags/Initializer.h index f29fec33e..8275944a6 100644 --- a/src/TNL/Meshes/MeshDetails/layers/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,16 @@ 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 + struct ResetEntityTags { static void exec( Mesh& mesh ) { - mesh.template resetBoundaryTags< Dimension >(); + mesh.template resetEntityTags< Dimension >(); } }; @@ -67,7 +69,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 +82,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 ); } } }; @@ -115,14 +117,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 +133,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 ); } @@ -163,6 +165,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 000000000..a14598d8c --- /dev/null +++ b/src/TNL/Meshes/MeshDetails/layers/EntityTags/Layer.h @@ -0,0 +1,299 @@ +/*************************************************************************** + 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; + 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; + return *this; + } + + + void setEntitiesCount( DimensionTag, const GlobalIndexType& entitiesCount ) + { + tags.setSize( entitiesCount ); + } + + void resetEntityTags( DimensionTag ) + { + tags.setValue( 0 ); + } + + __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 updateBoundaryIndices( 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 and ghost entities may overlap for vertices, so we count all categories separately + const auto tags_view = tags.getConstView(); + auto is_interior = [=] __cuda_callable__ ( GlobalIndexType entityIndex ) -> GlobalIndexType + { + return ! (tags_view[ entityIndex ] & ( EntityTags::BoundaryEntity | EntityTags::GhostEntity )); + }; + auto is_boundary = [=] __cuda_callable__ ( GlobalIndexType entityIndex ) -> GlobalIndexType + { + return tags_view[ entityIndex ] & EntityTags::BoundaryEntity; + }; + auto is_ghost = [=] __cuda_callable__ ( GlobalIndexType entityIndex ) -> GlobalIndexType + { + return tags_view[ entityIndex ] & EntityTags::GhostEntity; + }; + const GlobalIndexType interiorEntities = Algorithms::Reduction< Device >::reduce( (GlobalIndexType) 0, tags.getSize(), std::plus<>{}, is_interior, (GlobalIndexType) 0 ); + 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( interiorEntities ); + boundaryIndices.setSize( boundaryEntities ); + ghostIndices.setSize( ghostEntities ); + + if( ! std::is_same< Device, Devices::Cuda >::value ) { + GlobalIndexType i = 0, b = 0, g = 0; + for( GlobalIndexType e = 0; e < tags.getSize(); e++ ) { + if( ! (tags[ e ] & ( EntityTags::BoundaryEntity | EntityTags::GhostEntity )) ) + interiorIndices[ i++ ] = e; + if( tags[ e ] & EntityTags::BoundaryEntity ) + boundaryIndices[ b++ ] = e; + if( tags[ e ] & EntityTags::GhostEntity ) + ghostIndices[ g++ ] = e; + } + } + // 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, hostGhostIndices; + + hostTags.setLike( tags ); + hostInteriorIndices.setLike( interiorIndices ); + hostBoundaryIndices.setLike( boundaryIndices ); + hostGhostIndices.setLike( ghostIndices ); + + hostTags = tags; + + GlobalIndexType i = 0, b = 0, g = 0; + for( GlobalIndexType e = 0; e < tags.getSize(); e++ ) { + if( ! (hostTags[ e ] & ( EntityTags::BoundaryEntity | EntityTags::GhostEntity )) ) + hostInteriorIndices[ i++ ] = e; + if( hostTags[ e ] & EntityTags::BoundaryEntity ) + hostBoundaryIndices[ b++ ] = e; + if( hostTags[ e ] & EntityTags::GhostEntity ) + hostGhostIndices[ g++ ] = e; + } + + interiorIndices = hostInteriorIndices; + boundaryIndices = hostBoundaryIndices; + ghostIndices = hostGhostIndices; + } + } + + __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 << tags; + } + + void load( File& file ) + { + file >> tags; + updateBoundaryIndices( 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 << "Indices of the ghost entities of dimension " << DimensionTag::value << " are: "; + str << ghostIndices << std::endl; + } + + bool operator==( const Layer& layer ) const + { + TNL_ASSERT( ( tags == layer.tags && interiorIndices == layer.interiorIndices && boundaryIndices == layer.boundaryIndices && ghostIndices == layer.ghostIndices ) || + ( tags != layer.tags && interiorIndices != layer.interiorIndices && boundaryIndices != layer.boundaryIndices && ghostIndices != layer.ghostIndices ), + 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 + << "ghostIndices = " << ghostIndices << std::endl + << "layer.ghostIndices = " << layer.ghostIndices << std::endl; ); + return tags == layer.tags; + } + +private: + EntityTagsArrayType tags; + OrderingArray interiorIndices, boundaryIndices, ghostIndices; + + // 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 resetEntityTags( 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 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 EntityTags +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/layers/BoundaryTags/LayerFamily.h b/src/TNL/Meshes/MeshDetails/layers/EntityTags/LayerFamily.h similarity index 69% rename from src/TNL/Meshes/MeshDetails/layers/BoundaryTags/LayerFamily.h rename to src/TNL/Meshes/MeshDetails/layers/EntityTags/LayerFamily.h index d78ba1c52..e3c532041 100644 --- a/src/TNL/Meshes/MeshDetails/layers/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,9 +28,12 @@ class LayerInheritor using BaseType = LayerInheritor< MeshConfig, Device, typename Dimension::Increment >; protected: using LayerType::setEntitiesCount; - using LayerType::resetBoundaryTags; - using LayerType::setIsBoundaryEntity; + using LayerType::resetEntityTags; + using LayerType::getEntityTag; + using LayerType::addEntityTag; + using LayerType::removeEntityTag; using LayerType::isBoundaryEntity; + using LayerType::isGhostEntity; using LayerType::updateBoundaryIndices; using LayerType::getBoundaryEntitiesCount; using LayerType::getBoundaryEntityIndex; @@ -38,9 +41,12 @@ protected: using LayerType::getInteriorEntityIndex; using BaseType::setEntitiesCount; - using BaseType::resetBoundaryTags; - using BaseType::setIsBoundaryEntity; + using BaseType::resetEntityTags; + using BaseType::getEntityTag; + using BaseType::addEntityTag; + using BaseType::removeEntityTag; using BaseType::isBoundaryEntity; + using BaseType::isGhostEntity; using BaseType::updateBoundaryIndices; using BaseType::getBoundaryEntitiesCount; using BaseType::getBoundaryEntityIndex; @@ -107,9 +113,12 @@ class LayerInheritor< MeshConfig, Device, DimensionTag< MeshConfig::meshDimensio { protected: void setEntitiesCount(); - void resetBoundaryTags(); - void setIsBoundaryEntity(); + void resetEntityTags(); + void getEntityTag(); + void addEntityTag(); + void removeEntityTag(); void isBoundaryEntity(); + void isGhostEntity(); void updateBoundaryIndices(); void getBoundaryEntitiesCount(); void getBoundaryEntityIndex(); @@ -145,6 +154,7 @@ class LayerFamily { using MeshTraitsType = MeshTraits< MeshConfig, Device >; using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; + using TagType = typename MeshTraitsType::EntityTagType; using BaseType = LayerInheritor< MeshConfig, Device, DimensionTag< 0 > >; template< int Dimension > using EntityTraits = typename MeshTraitsType::template EntityTraits< Dimension >; @@ -158,20 +168,51 @@ public: using BaseType::BaseType; using BaseType::operator=; - // getters for boundary tags + template< int Dimension > + __cuda_callable__ + TagType getEntityTag( const GlobalIndexType& entityIndex ) const + { + 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__ + void addEntityTag( const GlobalIndexType& entityIndex, TagType tag ) + { + 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__ + void removeEntityTag( const GlobalIndexType& entityIndex, TagType tag ) + { + static_assert( WeakTrait< Dimension >::entityTagsEnabled, "You try to access entity tags which are not configured for storage." ); + BaseType::removeEntityTag( DimensionTag< Dimension >(), entityIndex, tag ); + } + template< int Dimension > __cuda_callable__ bool isBoundaryEntity( const GlobalIndexType& entityIndex ) const { - static_assert( WeakTrait< Dimension >::boundaryTagsEnabled, "You try to access boundary tags which are not configured for storage." ); + 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__ GlobalIndexType getBoundaryEntitiesCount() const { - static_assert( WeakTrait< Dimension >::boundaryTagsEnabled, "You try to access boundary tags which are not configured for storage." ); + static_assert( WeakTrait< Dimension >::entityTagsEnabled, "You try to access entity tags which are not configured for storage." ); return BaseType::getBoundaryEntitiesCount( DimensionTag< Dimension >() ); } @@ -179,7 +220,7 @@ public: __cuda_callable__ GlobalIndexType getBoundaryEntityIndex( const GlobalIndexType& i ) const { - static_assert( WeakTrait< Dimension >::boundaryTagsEnabled, "You try to access boundary tags which are not configured for storage." ); + static_assert( WeakTrait< Dimension >::entityTagsEnabled, "You try to access entity tags which are not configured for storage." ); return BaseType::getBoundaryEntityIndex( DimensionTag< Dimension >(), i ); } @@ -187,7 +228,7 @@ public: __cuda_callable__ GlobalIndexType getInteriorEntitiesCount() const { - static_assert( WeakTrait< Dimension >::boundaryTagsEnabled, "You try to access boundary tags which are not configured for storage." ); + static_assert( WeakTrait< Dimension >::entityTagsEnabled, "You try to access entity tags which are not configured for storage." ); return BaseType::getInteriorEntitiesCount( DimensionTag< Dimension >() ); } @@ -195,22 +236,21 @@ public: __cuda_callable__ GlobalIndexType getInteriorEntityIndex( const GlobalIndexType& i ) const { - static_assert( WeakTrait< Dimension >::boundaryTagsEnabled, "You try to access boundary tags which are not configured for storage." ); + static_assert( WeakTrait< Dimension >::entityTagsEnabled, "You try to access entity tags which are not configured for storage." ); return BaseType::getInteriorEntityIndex( DimensionTag< Dimension >(), i ); } protected: - // setters for boundary tags template< int Dimension > - void boundaryTagsSetEntitiesCount( const GlobalIndexType& entitiesCount ) + void entityTagsSetEntitiesCount( const GlobalIndexType& entitiesCount ) { BaseType::setEntitiesCount( DimensionTag< Dimension >(), entitiesCount ); } - + template< int Dimension > - void resetBoundaryTags() + void resetEntityTags() { - BaseType::resetBoundaryTags( DimensionTag< Dimension >() ); + BaseType::resetEntityTags( DimensionTag< Dimension >() ); } template< int Dimension > @@ -218,16 +258,8 @@ protected: { BaseType::updateBoundaryIndices( DimensionTag< Dimension >() ); } - - template< int Dimension > - __cuda_callable__ - void setIsBoundaryEntity( const GlobalIndexType& entityIndex, bool isBoundary ) - { - static_assert( WeakTrait< Dimension >::boundaryTagsEnabled, "You try to access boundary tags which are not configured for storage." ); - BaseType::setIsBoundaryEntity( DimensionTag< Dimension >(), entityIndex, isBoundary ); - } }; -} // namespace BoundaryTags +} // namespace EntityTags } // namespace Meshes } // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/layers/BoundaryTags/Traits.h b/src/TNL/Meshes/MeshDetails/layers/EntityTags/Traits.h similarity index 72% rename from src/TNL/Meshes/MeshDetails/layers/BoundaryTags/Traits.h rename to src/TNL/Meshes/MeshDetails/layers/EntityTags/Traits.h index 0d67f0db1..b7c28f155 100644 --- a/src/TNL/Meshes/MeshDetails/layers/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/traits/MeshTraits.h b/src/TNL/Meshes/MeshDetails/traits/MeshTraits.h index d88a6abb9..8bfdbe048 100644 --- a/src/TNL/Meshes/MeshDetails/traits/MeshTraits.h +++ b/src/TNL/Meshes/MeshDetails/traits/MeshTraits.h @@ -39,18 +39,20 @@ 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 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 PointArrayType = Containers::Array< PointType, DeviceType, GlobalIndexType >; - using CellSeedArrayType = Containers::Array< CellSeedType, DeviceType, GlobalIndexType >; + 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 EntityTagType = std::uint8_t; + + 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 >; diff --git a/src/TNL/Meshes/MeshEntity.h b/src/TNL/Meshes/MeshEntity.h index e9397a626..744febe5f 100644 --- a/src/TNL/Meshes/MeshEntity.h +++ b/src/TNL/Meshes/MeshEntity.h @@ -37,6 +37,7 @@ class MeshEntity 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 MeshType::MeshTraitsType::template SubentityTraits< EntityTopology, Subdimension >; @@ -103,6 +104,12 @@ class MeshEntity __cuda_callable__ GlobalIndexType getSuperentityIndex( const LocalIndexType localIndex ) const; + /**** + * Tags + */ + __cuda_callable__ + TagType getTag() const; + protected: const MeshType* meshPointer = nullptr; GlobalIndexType index = 0; diff --git a/src/TNL/Meshes/MeshEntity.hpp b/src/TNL/Meshes/MeshEntity.hpp index e754bd9e4..36ced6134 100644 --- a/src/TNL/Meshes/MeshEntity.hpp +++ b/src/TNL/Meshes/MeshEntity.hpp @@ -163,6 +163,18 @@ getSuperentityIndex( const LocalIndexType localIndex ) const 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 > diff --git a/src/Tools/tnl-decompose-mesh.cpp b/src/Tools/tnl-decompose-mesh.cpp index 17d69bcb5..30981ebf9 100644 --- a/src/Tools/tnl-decompose-mesh.cpp +++ b/src/Tools/tnl-decompose-mesh.cpp @@ -99,7 +99,7 @@ struct MeshConfigTemplateTag< DecomposeMeshConfigTag > } template< typename EntityTopology > - static constexpr bool boundaryTagsStorage( EntityTopology ) + static constexpr bool entityTagsStorage( EntityTopology ) { return EntityTopology::dimension >= meshDimension - 1; } diff --git a/src/Tools/tnl-mesh-converter.cpp b/src/Tools/tnl-mesh-converter.cpp index ada236cff..03c0da691 100644 --- a/src/Tools/tnl-mesh-converter.cpp +++ b/src/Tools/tnl-mesh-converter.cpp @@ -93,7 +93,7 @@ struct MeshConfigTemplateTag< MeshConverterConfigTag > } template< typename EntityTopology > - static constexpr bool boundaryTagsStorage( EntityTopology ) + static constexpr bool entityTagsStorage( EntityTopology ) { return false; } diff --git a/src/UnitTests/Meshes/BoundaryTagsTest.cpp b/src/UnitTests/Meshes/BoundaryTagsTest.cpp deleted file mode 100644 index c83e40752..000000000 --- a/src/UnitTests/Meshes/BoundaryTagsTest.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "BoundaryTagsTest.h" -#include "../main.h" diff --git a/src/UnitTests/Meshes/CMakeLists.txt b/src/UnitTests/Meshes/CMakeLists.txt index 4147119c8..6409a21eb 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,7 +31,7 @@ else() TARGET_LINK_LIBRARIES( MeshOrderingTest ${GTEST_BOTH_LIBRARIES} ) endif() -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} ) diff --git a/src/UnitTests/Meshes/EntityTagsTest.cpp b/src/UnitTests/Meshes/EntityTagsTest.cpp new file mode 100644 index 000000000..592e94a90 --- /dev/null +++ b/src/UnitTests/Meshes/EntityTagsTest.cpp @@ -0,0 +1,2 @@ +#include "EntityTagsTest.h" +#include "../main.h" diff --git a/src/UnitTests/Meshes/BoundaryTagsTest.h b/src/UnitTests/Meshes/EntityTagsTest.h similarity index 58% rename from src/UnitTests/Meshes/BoundaryTagsTest.h rename to src/UnitTests/Meshes/EntityTagsTest.h index 6bc07adab..5cce83784 100644 --- a/src/UnitTests/Meshes/BoundaryTagsTest.h +++ b/src/UnitTests/Meshes/EntityTagsTest.h @@ -11,7 +11,7 @@ #include #include -namespace BoundaryTagsTest { +namespace EntityTagsTest { using namespace TNL; using namespace TNL::Meshes; @@ -57,26 +57,26 @@ TEST( MeshTest, RegularMeshOfQuadrilateralsTest ) * 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 ) ); + 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 ); - } + 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 ) ); @@ -96,6 +96,22 @@ TEST( MeshTest, RegularMeshOfQuadrilateralsTest ) EXPECT_EQ( mesh.template getInteriorEntityIndex< 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}; @@ -111,8 +127,24 @@ TEST( MeshTest, RegularMeshOfQuadrilateralsTest ) EXPECT_FALSE( mesh.template isBoundaryEntity< 1 >( interiorFaces[ i ] ) ); EXPECT_EQ( mesh.template getInteriorEntityIndex< 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 BoundaryTagsTest +} // namespace EntityTagsTest #endif -- GitLab From b724b16ce6619d9d8e4ff0a954a9ed91faf71065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sat, 11 Apr 2020 13:38:25 +0200 Subject: [PATCH 39/73] Mesh: renamed GlobalIndexVector to GlobalIndexArray --- .../Multimaps/MultimapPermutationApplier.h | 16 +++++------ src/TNL/Meshes/Mesh.h | 6 ++-- src/TNL/Meshes/Mesh.hpp | 21 ++++++++------ .../MeshDetails/IndexPermutationApplier.h | 28 +++++++++---------- src/UnitTests/Meshes/MeshOrderingTest.h | 26 ++++++++--------- 5 files changed, 51 insertions(+), 46 deletions(-) diff --git a/src/TNL/Containers/Multimaps/MultimapPermutationApplier.h b/src/TNL/Containers/Multimaps/MultimapPermutationApplier.h index 953339305..a50d7b354 100644 --- a/src/TNL/Containers/Multimaps/MultimapPermutationApplier.h +++ b/src/TNL/Containers/Multimaps/MultimapPermutationApplier.h @@ -18,10 +18,10 @@ namespace Containers { namespace Multimaps { template< typename Multimap, - typename PermutationVector > -void permuteMultimapKeys( Multimap& multimap, const PermutationVector& perm ) + typename PermutationArray > +void permuteMultimapKeys( Multimap& multimap, const PermutationArray& perm ) { - static_assert( std::is_same< typename Multimap::DeviceType, typename PermutationVector::DeviceType >::value, + static_assert( std::is_same< typename Multimap::DeviceType, typename PermutationArray::DeviceType >::value, "The multimap and permutation vector must be stored on the same device." ); using IndexType = typename Multimap::IndexType; using DeviceType = typename Multimap::DeviceType; @@ -38,7 +38,7 @@ void permuteMultimapKeys( Multimap& multimap, const PermutationVector& perm ) ( IndexType i, const Multimap* multimap, Multimap* multimapCopy, - const typename PermutationVector::RealType* perm ) + const typename PermutationArray::ValueType* perm ) { const auto srcValues = multimap->getValues( perm[ i ] ); auto destValues = multimapCopy->getValues( i ); @@ -59,10 +59,10 @@ void permuteMultimapKeys( Multimap& multimap, const PermutationVector& perm ) } template< typename Multimap, - typename PermutationVector > -void permuteMultimapValues( Multimap& multimap, const PermutationVector& iperm ) + typename PermutationArray > +void permuteMultimapValues( Multimap& multimap, const PermutationArray& iperm ) { - static_assert( std::is_same< typename Multimap::DeviceType, typename PermutationVector::DeviceType >::value, + static_assert( std::is_same< typename Multimap::DeviceType, typename PermutationArray::DeviceType >::value, "The multimap and permutation vector must be stored on the same device." ); using IndexType = typename Multimap::IndexType; using DeviceType = typename Multimap::DeviceType; @@ -71,7 +71,7 @@ void permuteMultimapValues( Multimap& multimap, const PermutationVector& iperm ) auto kernel = [] __cuda_callable__ ( IndexType i, Multimap* multimap, - const typename PermutationVector::RealType* iperm ) + const typename PermutationArray::ValueType* iperm ) { auto values = multimap->getValues( i ); for( typename Multimap::LocalIndexType v = 0; v < values.getSize(); v++ ) diff --git a/src/TNL/Meshes/Mesh.h b/src/TNL/Meshes/Mesh.h index 18f803fdc..754ed61d5 100644 --- a/src/TNL/Meshes/Mesh.h +++ b/src/TNL/Meshes/Mesh.h @@ -81,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 >; @@ -182,8 +182,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; diff --git a/src/TNL/Meshes/Mesh.hpp b/src/TNL/Meshes/Mesh.hpp index 089b1c180..87cbe0c86 100644 --- a/src/TNL/Meshes/Mesh.hpp +++ b/src/TNL/Meshes/Mesh.hpp @@ -237,8 +237,8 @@ template< typename MeshConfig, typename Device > template< int Dimension > void Mesh< MeshConfig, Device >:: -reorderEntities( const GlobalIndexVector& perm, - const GlobalIndexVector& iperm ) +reorderEntities( const GlobalIndexArray& perm, + const GlobalIndexArray& iperm ) { const GlobalIndexType entitiesCount = getEntitiesCount< Dimension >(); @@ -248,16 +248,21 @@ reorderEntities( const GlobalIndexVector& perm, "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 ) +#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 ) == 0 && max( iperm ) == entitiesCount - 1, - std::cerr << "Given array is not a permutation: min = " << min( iperm ) - << ", max = " << max( iperm ) + 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 ); // update boundary tags diff --git a/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h b/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h index a4ce7e325..c7cd7ceeb 100644 --- a/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h +++ b/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h @@ -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,7 +30,7 @@ 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 ); @@ -40,7 +40,7 @@ private: template< int Subdimension > struct _SubentitiesStorageWorker< Subdimension, false > { - static void exec( Mesh& mesh, const GlobalIndexVector& iperm ) {} + static void exec( Mesh& mesh, const GlobalIndexArray& iperm ) {} }; @@ -51,7 +51,7 @@ 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 ); @@ -61,7 +61,7 @@ private: template< int Superdimension > struct _SuperentitiesStorageWorker< Superdimension, false > { - static void exec( Mesh& mesh, const GlobalIndexVector& iperm ) {} + static void exec( Mesh& mesh, const GlobalIndexArray& iperm ) {} }; @@ -72,7 +72,7 @@ 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 ); @@ -82,7 +82,7 @@ private: template< int Subdimension > struct IndexPermutationApplierSubentitiesWorker< Subdimension, false > { - static void exec( Mesh& mesh, const GlobalIndexVector& iperm ) {} + static void exec( Mesh& mesh, const GlobalIndexArray& iperm ) {} }; @@ -93,7 +93,7 @@ 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 ); @@ -103,7 +103,7 @@ private: template< int Superdimension > struct IndexPermutationApplierSuperentitiesWorker< Superdimension, false > { - static void exec( Mesh& mesh, const GlobalIndexVector& iperm ) {} + static void exec( Mesh& mesh, const GlobalIndexArray& iperm ) {} }; @@ -121,7 +121,7 @@ private: using SuperentitiesWorker = IndexPermutationApplierSuperentitiesWorker< Superdimension >; template< typename Mesh_, std::enable_if_t< Mesh_::Config::dualGraphStorage(), bool > = true > - static void permuteDualGraph( Mesh_& mesh, const GlobalIndexVector& perm, const GlobalIndexVector& iperm ) + static void permuteDualGraph( Mesh_& mesh, const GlobalIndexArray& perm, const GlobalIndexArray& iperm ) { permuteArray( mesh.getNeighborCounts(), perm ); auto& graph = mesh.getDualGraph(); @@ -130,11 +130,11 @@ private: } template< typename Mesh_, std::enable_if_t< ! Mesh_::Config::dualGraphStorage(), bool > = true > - static void permuteDualGraph( Mesh_& mesh, const GlobalIndexVector& perm, const GlobalIndexVector& iperm ) {} + static void permuteDualGraph( Mesh_& mesh, const GlobalIndexArray& perm, const GlobalIndexArray& iperm ) {} public: template< typename Array > - static void permuteArray( Array& array, const GlobalIndexVector& perm ) + static void permuteArray( Array& array, const GlobalIndexArray& perm ) { using IndexType = typename Array::IndexType; using DeviceType = typename Array::DeviceType; @@ -172,8 +172,8 @@ public: } static void exec( Mesh& mesh, - const GlobalIndexVector& perm, - const GlobalIndexVector& iperm ) + const GlobalIndexArray& perm, + const GlobalIndexArray& iperm ) { using IndexType = typename Mesh::GlobalIndexType; using DeviceType = typename Mesh::DeviceType; diff --git a/src/UnitTests/Meshes/MeshOrderingTest.h b/src/UnitTests/Meshes/MeshOrderingTest.h index d59e9a012..93b747fd2 100644 --- a/src/UnitTests/Meshes/MeshOrderingTest.h +++ b/src/UnitTests/Meshes/MeshOrderingTest.h @@ -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; @@ -253,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 ); @@ -288,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; @@ -309,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 ); -- GitLab From 7075606774d37bfa6710699e46a762be2cbc229c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Wed, 8 Apr 2020 19:35:28 +0200 Subject: [PATCH 40/73] Removed VTKReader_libvtk.h and MeshReaderTest.h --- src/TNL/Meshes/Readers/VTKReader_libvtk.h | 323 ---------------------- src/UnitTests/Meshes/CMakeLists.txt | 32 --- src/UnitTests/Meshes/MeshReaderTest.cpp | 1 - src/UnitTests/Meshes/MeshReaderTest.cu | 1 - src/UnitTests/Meshes/MeshReaderTest.h | 196 ------------- 5 files changed, 553 deletions(-) delete mode 100644 src/TNL/Meshes/Readers/VTKReader_libvtk.h delete mode 100644 src/UnitTests/Meshes/MeshReaderTest.cpp delete mode 100644 src/UnitTests/Meshes/MeshReaderTest.cu delete mode 100644 src/UnitTests/Meshes/MeshReaderTest.h diff --git a/src/TNL/Meshes/Readers/VTKReader_libvtk.h b/src/TNL/Meshes/Readers/VTKReader_libvtk.h deleted file mode 100644 index 048f825c3..000000000 --- a/src/TNL/Meshes/Readers/VTKReader_libvtk.h +++ /dev/null @@ -1,323 +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; - } - - VTK::EntityShape - getCellShape() const - { - return this->entityTypes.at( this->meshDimension ); - } - -// int -// getVerticesInCell() const -// { -// return this->verticesInEntities.at( this->getMeshDimension() ); -// } - -// VTK::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"; - } - -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, VTK::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 VTK::EntityShape type = (VTK::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 " << VTK::getShapeName( this->entityTypes[ dimension ] ) << " and " << VTK::getShapeName( 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/UnitTests/Meshes/CMakeLists.txt b/src/UnitTests/Meshes/CMakeLists.txt index 6409a21eb..75fab220e 100644 --- a/src/UnitTests/Meshes/CMakeLists.txt +++ b/src/UnitTests/Meshes/CMakeLists.txt @@ -44,35 +44,3 @@ ADD_TEST( MeshOrderingTest ${EXECUTABLE_OUTPUT_PATH}/MeshOrderingTest${CMAKE_EXE #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} ) - - - -## -## 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() diff --git a/src/UnitTests/Meshes/MeshReaderTest.cpp b/src/UnitTests/Meshes/MeshReaderTest.cpp deleted file mode 100644 index ac1b0314a..000000000 --- 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 ac1b0314a..000000000 --- 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 17bc6a317..000000000 --- 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; -} -- GitLab From 39dc72b92c48f6198de7a4a329876281d2dd5fa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Thu, 9 Apr 2020 00:00:24 +0200 Subject: [PATCH 41/73] Refactoring mesh type resolver using generic lambdas instead of variadic templates --- src/Python/pytnl/tnl/Mesh.cpp | 2 +- .../DistributedMeshes/loadDistributedMesh.h | 106 ++++++ src/TNL/Meshes/Readers/NetgenReader.h | 14 +- src/TNL/Meshes/Readers/TNLReader.h | 14 +- src/TNL/Meshes/Readers/VTKReader.h | 14 +- src/TNL/Meshes/Readers/VTUReader.h | 17 +- .../Meshes/TypeResolver/GridTypeResolver.h | 135 ++++---- .../TypeResolver/GridTypeResolver_impl.h | 210 +++++------- .../Meshes/TypeResolver/MeshTypeResolver.h | 243 +++++++------ .../TypeResolver/MeshTypeResolver_impl.h | 324 ++++++++---------- src/TNL/Meshes/TypeResolver/TypeResolver.h | 64 +++- .../Meshes/TypeResolver/TypeResolver_impl.h | 283 ++++----------- .../Solvers/PDE/TimeDependentPDESolver_impl.h | 15 +- .../Solvers/PDE/TimeIndependentPDESolver.h | 2 + .../PDE/TimeIndependentPDESolver_impl.h | 18 +- src/TNL/Solvers/SolverInitiator_impl.h | 6 +- src/Tools/tnl-decompose-mesh.cpp | 16 +- src/Tools/tnl-grid-to-mesh.cpp | 62 ++-- src/Tools/tnl-mesh-converter.cpp | 78 ++--- src/Tools/tnl-view.cpp | 10 +- src/Tools/tnl-view.h | 119 +++---- 21 files changed, 863 insertions(+), 889 deletions(-) create mode 100644 src/TNL/Meshes/DistributedMeshes/loadDistributedMesh.h diff --git a/src/Python/pytnl/tnl/Mesh.cpp b/src/Python/pytnl/tnl/Mesh.cpp index c267aeade..3cd6227a7 100644 --- a/src/Python/pytnl/tnl/Mesh.cpp +++ b/src/Python/pytnl/tnl/Mesh.cpp @@ -13,7 +13,7 @@ void export_Meshes( py::module & m ) using Reader = TNL::Meshes::Readers::VTKReader; py::class_< Reader >( m, "VTKReader" ) - .def(py::init<>()) + .def(py::init()) .def("readMesh", &Reader::template readMesh< MeshOfEdges >) .def("readMesh", &Reader::template readMesh< MeshOfTriangles >) .def("readMesh", &Reader::template readMesh< MeshOfTetrahedrons >) diff --git a/src/TNL/Meshes/DistributedMeshes/loadDistributedMesh.h b/src/TNL/Meshes/DistributedMeshes/loadDistributedMesh.h new file mode 100644 index 000000000..230d5a7b5 --- /dev/null +++ b/src/TNL/Meshes/DistributedMeshes/loadDistributedMesh.h @@ -0,0 +1,106 @@ +/*************************************************************************** + 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 + +namespace TNL { +namespace Meshes { + +template< typename CommunicatorType, + typename MeshConfig, + typename Device > +bool +loadDistributedMesh( const String& fileName, + Mesh< MeshConfig, Device >& mesh, + DistributedMeshes::DistributedMesh< Mesh< MeshConfig, Device > >& distributedMesh ) +{ + std::cerr << "Distributed Mesh is not supported yet, only Distributed Grid is supported."; + return false; +} + +template< typename Problem, + 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 ) +{ + 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( const String& fileName, + Grid< Dimension, Real, Device, Index >& mesh, + DistributedMeshes::DistributedMesh< Grid< Dimension, Real, Device, Index > > &distributedMesh ) +{ + 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 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/Readers/NetgenReader.h b/src/TNL/Meshes/Readers/NetgenReader.h index 5186c87db..8bc40d657 100644 --- a/src/TNL/Meshes/Readers/NetgenReader.h +++ b/src/TNL/Meshes/Readers/NetgenReader.h @@ -30,10 +30,15 @@ namespace Readers { class NetgenReader { public: - bool detectMesh( const String& fileName ) + NetgenReader() = delete; + + NetgenReader( const String& fileName ) + : fileName( fileName ) + {} + + bool detectMesh() { this->reset(); - this->fileName = fileName; std::ifstream inputFile( fileName.getString() ); if( ! inputFile ) @@ -146,13 +151,15 @@ public: } template< typename MeshType > - static bool readMesh( const String& fileName, MeshType& mesh ) + bool readMesh( MeshType& mesh ) { typedef typename MeshType::PointType PointType; typedef MeshBuilder< MeshType > MeshBuilder; const int dimension = PointType::getSize(); + // TODO: check that detectMesh has been called + // TODO: reuse inputFile from the detectMesh method std::ifstream inputFile( fileName.getString() ); if( ! inputFile ) { @@ -285,7 +292,6 @@ protected: void reset() { - fileName = ""; meshDimension = worldDimension = 0; cellShape = VTK::EntityShape::Vertex; } diff --git a/src/TNL/Meshes/Readers/TNLReader.h b/src/TNL/Meshes/Readers/TNLReader.h index 2a37f5617..ab5d0c704 100644 --- a/src/TNL/Meshes/Readers/TNLReader.h +++ b/src/TNL/Meshes/Readers/TNLReader.h @@ -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 ); @@ -94,8 +99,8 @@ public: } template< typename MeshType > - static bool - readMesh( const String& fileName, MeshType& mesh ) + bool + readMesh( MeshType& mesh ) { mesh.load( fileName ); return true; @@ -155,7 +160,6 @@ protected: void reset() { - fileName = ""; meshType = ""; meshDimension = worldDimension = 0; cellShape = VTK::EntityShape::Vertex; diff --git a/src/TNL/Meshes/Readers/VTKReader.h b/src/TNL/Meshes/Readers/VTKReader.h index cd9cdd413..efb339dc6 100644 --- a/src/TNL/Meshes/Readers/VTKReader.h +++ b/src/TNL/Meshes/Readers/VTKReader.h @@ -26,10 +26,15 @@ namespace Readers { class VTKReader { public: - bool detectMesh( const String& fileName ) + VTKReader() = delete; + + VTKReader( const String& fileName ) + : fileName( fileName ) + {} + + bool detectMesh() { this->reset(); - this->fileName = fileName; std::ifstream inputFile( fileName.getString() ); if( ! inputFile ) { @@ -149,7 +154,7 @@ public: } template< typename MeshType > - bool readMesh( const String& fileName, MeshType& mesh ) + bool readMesh( MeshType& mesh ) { using MeshBuilder = MeshBuilder< MeshType >; using IndexType = typename MeshType::GlobalIndexType; @@ -159,6 +164,8 @@ public: const VTK::EntityShape cellType = VTK::TopologyToEntityShape< typename MeshType::template EntityTraits< MeshType::getMeshDimension() >::EntityTopology >::shape; MeshBuilder meshBuilder; + // TODO: check that detectMesh has been called + // TODO: reuse inputFile from the detectMesh method std::ifstream inputFile( fileName.getString() ); if( ! inputFile ) { std::cerr << "Failed to open the file " << fileName << "." << std::endl; @@ -348,7 +355,6 @@ protected: void reset() { - fileName = ""; meshDimension = worldDimension = 0; cellShape = VTK::EntityShape::Vertex; realType = ""; diff --git a/src/TNL/Meshes/Readers/VTUReader.h b/src/TNL/Meshes/Readers/VTUReader.h index 974dff93d..3b583b8d7 100644 --- a/src/TNL/Meshes/Readers/VTUReader.h +++ b/src/TNL/Meshes/Readers/VTUReader.h @@ -361,11 +361,16 @@ class VTUReader #endif public: - bool detectMesh( const std::string& fileName ) + VTUReader() = delete; + + VTUReader( const std::string& fileName ) + : fileName( fileName ) + {} + + bool detectMesh() { #ifdef HAVE_TINYXML2 this->reset(); - this->fileName = fileName; using namespace tinyxml2; @@ -420,11 +425,9 @@ public: } template< typename MeshType > - bool readMesh( const std::string& fileName, MeshType& mesh ) + bool readMesh( MeshType& mesh ) { - // reading is actually done in detectMesh, but the mesh type resolver discards the first reader instance - if( ! detectMesh( fileName ) ) - return false; + // TODO: check that detectMesh has been called // check that the cell shape mathes const VTK::EntityShape meshCellShape = VTK::TopologyToEntityShape< typename MeshType::template EntityTraits< MeshType::getMeshDimension() >::EntityTopology >::shape; @@ -540,7 +543,7 @@ protected: void reset() { - fileName = fileType = ""; + fileType = ""; NumberOfPoints = NumberOfCells = 0; meshDimension = worldDimension = 0; cellShape = VTK::EntityShape::Vertex; diff --git a/src/TNL/Meshes/TypeResolver/GridTypeResolver.h b/src/TNL/Meshes/TypeResolver/GridTypeResolver.h index 498ae0f58..3a23a8028 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 20f40c268..cdd397038 100644 --- a/src/TNL/Meshes/TypeResolver/GridTypeResolver_impl.h +++ b/src/TNL/Meshes/TypeResolver/GridTypeResolver_impl.h @@ -17,184 +17,164 @@ #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)... ); + return resolveIndex< MeshDimension, double >( reader, std::forward(functor) ); if( reader.getRealType() == "long-double" ) - return resolveIndex< MeshDimension, long double >( reader, std::forward(problemSetterArgs)... ); + 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)... ); + return resolveGridType< MeshDimension, Real, short int >( reader, std::forward(functor) ); if( reader.getGlobalIndexType() == "int" ) - return resolveGridType< MeshDimension, Real, int >( reader, std::forward(problemSetterArgs)... ); + return resolveGridType< MeshDimension, Real, int >( reader, std::forward(functor) ); if( reader.getGlobalIndexType() == "long int" ) - return resolveGridType< MeshDimension, Real, long int >( reader, std::forward(problemSetterArgs)... ); + 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 60c2a95d4..a8b1ecb4a 100644 --- a/src/TNL/Meshes/TypeResolver/MeshTypeResolver.h +++ b/src/TNL/Meshes/TypeResolver/MeshTypeResolver.h @@ -17,140 +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 resolveMeshType( 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 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 - >::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 - >::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 7e5eb05da..0062f45bd 100644 --- a/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h +++ b/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h @@ -20,101 +20,91 @@ 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 ) { switch( reader.getCellShape() ) { case VTK::EntityShape::Line: - return resolveWorldDimension< Topologies::Edge >( reader, std::forward(problemSetterArgs)... ); + return resolveWorldDimension< Topologies::Edge >( reader, std::forward(functor) ); case VTK::EntityShape::Triangle: - return resolveWorldDimension< Topologies::Triangle >( reader, std::forward(problemSetterArgs)... ); + return resolveWorldDimension< Topologies::Triangle >( reader, std::forward(functor) ); case VTK::EntityShape::Quad: - return resolveWorldDimension< Topologies::Quadrilateral >( reader, std::forward(problemSetterArgs)... ); + return resolveWorldDimension< Topologies::Quadrilateral >( reader, std::forward(functor) ); case VTK::EntityShape::Tetra: - return resolveWorldDimension< Topologies::Tetrahedron >( reader, std::forward(problemSetterArgs)... ); + return resolveWorldDimension< Topologies::Tetrahedron >( reader, std::forward(functor) ); case VTK::EntityShape::Hexahedron: - return resolveWorldDimension< Topologies::Hexahedron >( reader, std::forward(problemSetterArgs)... ); + return resolveWorldDimension< Topologies::Hexahedron >( reader, std::forward(functor) ); default: 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 @@ -122,197 +112,179 @@ 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)... ); + return resolveGlobalIndex< CellTopology, WorldDimension, double >( reader, std::forward(functor) ); if( reader.getRealType() == "long double" ) - return resolveGlobalIndex< CellTopology, WorldDimension, long double >( reader, std::forward(problemSetterArgs)... ); + 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" || reader.getGlobalIndexType() == "std::int16_t" || reader.getGlobalIndexType() == "std::uint16_t" ) - return resolveLocalIndex< CellTopology, WorldDimension, Real, short int >( reader, std::forward(problemSetterArgs)... ); + 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(problemSetterArgs)... ); + return resolveLocalIndex< CellTopology, WorldDimension, Real, int >( reader, std::forward(functor) ); if( reader.getGlobalIndexType() == "long int" || reader.getGlobalIndexType() == "std::int64_t" || reader.getGlobalIndexType() == "std::uint64_t" ) - return resolveLocalIndex< CellTopology, WorldDimension, Real, long int >( reader, std::forward(problemSetterArgs)... ); + 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" || reader.getLocalIndexType() == "std::int16_t" || reader.getLocalIndexType() == "std::uint16_t" ) - return resolveMeshType< CellTopology, WorldDimension, Real, GlobalIndex, short int >( reader, std::forward(problemSetterArgs)... ); + 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(problemSetterArgs)... ); + return resolveMeshType< CellTopology, WorldDimension, Real, GlobalIndex, int >( reader, std::forward(functor) ); if( 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(problemSetterArgs)... ); + 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... >:: -resolveMeshType( 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... >:: -resolveMeshType( const Reader& reader, - ProblemSetterArgs&&... problemSetterArgs ) +MeshTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: +resolveMeshType( Reader& reader, Functor&& functor ) { using MeshConfig = typename BuildConfigTags::MeshConfigTemplateTag< ConfigTag >::template MeshConfig< CellTopology, WorldDimension, Real, GlobalIndex, LocalIndex >; - return resolveTerminate< MeshConfig >( reader, std::forward(problemSetterArgs)... ); + 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 a04391af6..58a10ea26 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,66 @@ 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( const String& fileName, Functor&& functor ); + +/* + * 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( const String& fileName, Functor&& functor ); + +/* + * 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( const String& fileName, + Mesh< MeshConfig, Device >& mesh ); + +template< typename MeshConfig > +bool +loadMesh( const String& fileName, + Mesh< MeshConfig, Devices::Cuda >& mesh ); + +template< int Dimension, + typename Real, + typename Device, + typename Index > +bool +loadMesh( const String& fileName, + Grid< Dimension, Real, Device, Index >& grid ); } // namespace Meshes } // namespace TNL diff --git a/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h b/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h index a0f87b58a..93af4191d 100644 --- a/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h +++ b/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h @@ -10,112 +10,73 @@ #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()); -} 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( const String& fileName, Functor&& functor ) { - 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; + if( fileName.endsWith( ".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( fileName.endsWith( ".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_ ) ) + Readers::NetgenReader reader( fileName ); + if( ! reader.detectMesh() ) return false; 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 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( fileName.endsWith( ".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 ); + if( ! reader.detectMesh() ) return false; 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 VTK reader." << std::endl; return false; } } - else if( ends_with( fileName, ".vtu" ) ) { - // FIXME: The XML VTK files don't store local index and id types. + else if( fileName.endsWith( ".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; - if( ! reader.detectMesh( fileName_ ) ) + Readers::VTUReader reader( fileName ); + if( ! reader.detectMesh() ) return false; 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 VTK reader." << std::endl; return false; @@ -127,38 +88,53 @@ bool resolveMeshType( const String& fileName_, } } -// TODO: reorganize -template< typename CommunicatorType, - typename MeshConfig, +template< typename ConfigTag, + typename Device, + typename Functor > +bool resolveAndLoadMesh( const String& fileName, Functor&& functor ) +{ + auto wrapper = [&]( auto& reader, auto&& mesh ) -> bool + { + using MeshType = std::decay_t< decltype(mesh) >; + std::cout << "Loading a mesh from the file " << fileName << " ..." << std::endl; + if( ! reader.readMesh( mesh ) ) { + std::cerr << "Failed to load the mesh from the file " << fileName << ". The mesh type is " + << getType< MeshType >() << std::endl; + return false; + } + return functor( reader, std::forward(mesh) ); + }; + return resolveMeshType< ConfigTag, Device >( fileName, wrapper ); +} + +template< typename MeshConfig, typename Device > bool loadMesh( const String& fileName, - Mesh< MeshConfig, Device >& mesh, - DistributedMeshes::DistributedMesh< Mesh< MeshConfig, Device > >& distributedMesh ) + Mesh< MeshConfig, Device >& mesh ) { - 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() ); + std::cout << "Loading a mesh from the file " << fileName << " ..." << std::endl; bool status = true; - if( ends_with( fileName_, ".tnl" ) ) + if( fileName.endsWith( ".tnl" ) ) mesh.load( fileName ); - else if( ends_with( fileName_, ".ng" ) ) { - Readers::NetgenReader reader; - status = reader.readMesh( fileName, mesh ); + else if( fileName.endsWith( ".ng" ) ) { + Readers::NetgenReader reader( fileName ); + status = reader.detectMesh(); + if( status ) + status = reader.readMesh( mesh ); } - else if( ends_with( fileName_, ".vtk" ) ) { - Readers::VTKReader reader; - status = reader.readMesh( fileName, mesh ); + else if( fileName.endsWith( ".vtk" ) ) { + Readers::VTKReader reader( fileName ); + status = reader.detectMesh(); + if( status ) + status = reader.readMesh( mesh ); } - else if( ends_with( fileName_, ".vtu" ) ) { - Readers::VTUReader reader; - status = reader.readMesh( fileName, mesh ); + else if( fileName.endsWith( ".vtu" ) ) { + Readers::VTUReader reader( fileName ); + status = reader.detectMesh(); + if( status ) + status = reader.readMesh( mesh ); } else { std::cerr << "File '" << fileName << "' has unknown extension. Supported extensions are '.tnl', '.vtk', '.vtu' and '.ng'." << std::endl; @@ -167,150 +143,43 @@ loadMesh( const String& fileName, if( ! status ) { - 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 " + std::cerr << "Failed to load the mesh from the file " << fileName << ". The mesh type is " << getType< decltype(mesh) >() << std::endl; return false; } return true; } -template< typename Problem, - 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 ) -{ - using CommunicatorType = typename Problem::CommunicatorType; - if( CommunicatorType::isDistributed() ) - { - std::cerr << "Distributed Mesh is not supported yet, only Distributed Grid is supported."; - 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 ) + Mesh< MeshConfig, Devices::Cuda >& mesh ) { - 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( fileName, hostMesh ) ) 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 ) + Grid< Dimension, Real, Device, Index >& grid ) { - - 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/Solvers/PDE/TimeDependentPDESolver_impl.h b/src/TNL/Solvers/PDE/TimeDependentPDESolver_impl.h index 89369c886..3e56dcd25 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 { @@ -60,10 +61,16 @@ setup( const Config::ParameterContainer& parameters, // const String& meshFile = parameters.getParameter< String >( "mesh" ); 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 >( meshFile, *this->meshPointer, distributedMesh ) ) + return false; + if( ! Meshes::decomposeMesh< Problem >( parameters, prefix, *this->meshPointer, distributedMesh, *problem ) ) + return false; + } + else { + if( ! Meshes::loadMesh( meshFile, *this->meshPointer ) ) + return false; + } problem->setMesh( this->meshPointer ); diff --git a/src/TNL/Solvers/PDE/TimeIndependentPDESolver.h b/src/TNL/Solvers/PDE/TimeIndependentPDESolver.h index 8b5b8f52e..189584e79 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 81ada508d..28adf8419 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 { @@ -50,11 +52,17 @@ setup( const Config::ParameterContainer& parameters, // const String& meshFile = parameters.getParameter< String >( "mesh" ); 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 >( meshFile, *this->meshPointer, distributedMesh ) ) + return false; + if( ! Meshes::decomposeMesh< Problem >( parameters, prefix, *this->meshPointer, distributedMesh, *problem ) ) + return false; + } + else { + if( ! Meshes::loadMesh( meshFile, *this->meshPointer ) ) + return false; + } + problem->setMesh( this->meshPointer ); /**** diff --git a/src/TNL/Solvers/SolverInitiator_impl.h b/src/TNL/Solvers/SolverInitiator_impl.h index e54a8fe30..561d47cf4 100644 --- a/src/TNL/Solvers/SolverInitiator_impl.h +++ b/src/TNL/Solvers/SolverInitiator_impl.h @@ -225,7 +225,11 @@ 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 ); + auto wrapper = [&]( const auto& reader, auto&& mesh ) { + using MeshType = std::decay_t< decltype(mesh) >; + return ProblemSetterWrapper< MeshType >::run( parameters ); + }; + return Meshes::resolveMeshType< ConfigTag, Device >( meshFileName, wrapper ); } }; diff --git a/src/Tools/tnl-decompose-mesh.cpp b/src/Tools/tnl-decompose-mesh.cpp index 30981ebf9..90c59af22 100644 --- a/src/Tools/tnl-decompose-mesh.cpp +++ b/src/Tools/tnl-decompose-mesh.cpp @@ -285,15 +285,8 @@ struct DecomposeMesh using IndexArray = Containers::Array< Index, Devices::Sequential, Index >; using MetisIndexArray = Containers::Array< idx_t, Devices::Sequential, idx_t >; - static bool run( const Config::ParameterContainer& parameters ) + static bool run( const Mesh& mesh, const Config::ParameterContainer& parameters ) { - const String inputFileName = parameters.template getParameter< String >( "input-file" ); - Mesh mesh; - if( ! Meshes::loadMesh( inputFileName, mesh ) ) { - std::cerr << "Failed to load mesh from the file '" << inputFileName << "'." << std::endl; - return false; - } - // 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. " @@ -757,5 +750,10 @@ int main( int argc, char* argv[] ) return EXIT_FAILURE; } - return ! Meshes::resolveMeshType< DecomposeMeshConfigTag, Devices::Host, DecomposeMesh >( inputFileName, parameters ); + 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 >( inputFileName, wrapper ); } diff --git a/src/Tools/tnl-grid-to-mesh.cpp b/src/Tools/tnl-grid-to-mesh.cpp index 921b0ab3c..11f707b9d 100644 --- a/src/Tools/tnl-grid-to-mesh.cpp +++ b/src/Tools/tnl-grid-to-mesh.cpp @@ -162,40 +162,36 @@ struct MeshCreator< TNL::Meshes::Grid< 3, Real, Device, Index > > }; template< typename Grid > -struct GridConverter +bool convertGrid( Grid& grid, const TNL::String& fileName, const TNL::String& outputFileName ) { - static bool run( const TNL::String& fileName, const TNL::String& outputFileName ) - { - using MeshCreator = MeshCreator< Grid >; - using Mesh = typename MeshCreator::MeshType; - - Grid grid; - Mesh mesh; + using MeshCreator = MeshCreator< Grid >; + using Mesh = typename MeshCreator::MeshType; - 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; - } + TNL::Meshes::Readers::TNLReader reader( fileName ); + if( ! reader.readMesh( grid ) ) { + std::cerr << "Failed to load grid from file '" << fileName << "'." << std::endl; + return false; + } - try - { - mesh.save( outputFileName ); - } - catch(...) - { - std::cerr << "Failed to save the mesh to file '" << outputFileName << "'." << std::endl; - return false; - } + if( ! MeshCreator::run( grid, mesh ) ) { + std::cerr << "Unable to build mesh from grid." << std::endl; + return false; + } - return true; + try + { + mesh.save( outputFileName ); } -}; + catch(...) + { + std::cerr << "Failed to save the mesh to file '" << outputFileName << "'." << std::endl; + return false; + } + + return true; +} int main( int argc, char* argv[] ) @@ -210,9 +206,9 @@ main( int argc, char* argv[] ) String fileName( argv[ 1 ] ); String outputFileName( argv[ 2 ] ); - return ! Meshes::resolveMeshType< GridToMeshConfigTag, Devices::Host, GridConverter > - ( fileName, - fileName, // passed to GridConverter::run - outputFileName - ); + auto wrapper = [&] ( const auto& reader, auto&& grid ) + { + return convertGrid( grid, fileName, outputFileName ); + }; + return ! Meshes::resolveMeshType< GridToMeshConfigTag, Devices::Host >( fileName, wrapper ); } diff --git a/src/Tools/tnl-mesh-converter.cpp b/src/Tools/tnl-mesh-converter.cpp index 03c0da691..2dedccae3 100644 --- a/src/Tools/tnl-mesh-converter.cpp +++ b/src/Tools/tnl-mesh-converter.cpp @@ -111,50 +111,41 @@ struct MeshConfigTemplateTag< MeshConverterConfigTag > 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; - if( ! Meshes::loadMesh( inputFileName, mesh ) ) { - 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::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 ); + 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 ) { @@ -182,10 +173,9 @@ int main( int argc, char* argv[] ) 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 - ); + auto wrapper = [&] ( auto& reader, auto&& mesh ) -> bool + { + return convertMesh( mesh, inputFileName, outputFileName, outputFormat ); + }; + return ! Meshes::resolveAndLoadMesh< MeshConverterConfigTag, Devices::Host >( inputFileName, wrapper ); } diff --git a/src/Tools/tnl-view.cpp b/src/Tools/tnl-view.cpp index 5587a0f31..0f048e78f 100644 --- a/src/Tools/tnl-view.cpp +++ b/src/Tools/tnl-view.cpp @@ -85,8 +85,10 @@ 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 ); + 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 >( meshFile, wrapper ); } diff --git a/src/Tools/tnl-view.h b/src/Tools/tnl-view.h index 5fdf53e37..afa66201d 100644 --- a/src/Tools/tnl-view.h +++ b/src/Tools/tnl-view.h @@ -20,7 +20,6 @@ #include #include -#include #include using namespace TNL; @@ -313,76 +312,68 @@ bool setValueType( const MeshPointer& meshPointer, } template< typename Mesh > -struct FilesProcessor +bool processFiles( const Config::ParameterContainer& parameters ) { - static bool run( const Config::ParameterContainer& parameters ) + int verbose = parameters.getParameter< int >( "verbose"); + String meshFile = parameters.getParameter< String >( "mesh" ); + + typedef Pointers::SharedPointer< Mesh > MeshPointer; + MeshPointer meshPointer; + if( ! Meshes::loadMesh( meshFile, *meshPointer ) ) + 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 = getOutputFileName( inputFiles[ i ], outputFormat ); - 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 ... "; + + 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; +} -- GitLab From c3d90b910a418464487acef6aa42cd016510cf91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Thu, 16 Apr 2020 23:08:31 +0200 Subject: [PATCH 42/73] Added more type aliases for matching in MeshTypeResolver and GridTypeResolver --- .../Meshes/TypeResolver/GridTypeResolver_impl.h | 16 ++++++++++++---- .../Meshes/TypeResolver/MeshTypeResolver_impl.h | 12 ++++++++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/TNL/Meshes/TypeResolver/GridTypeResolver_impl.h b/src/TNL/Meshes/TypeResolver/GridTypeResolver_impl.h index cdd397038..817d2b23f 100644 --- a/src/TNL/Meshes/TypeResolver/GridTypeResolver_impl.h +++ b/src/TNL/Meshes/TypeResolver/GridTypeResolver_impl.h @@ -76,7 +76,7 @@ resolveReal( Reader& reader, Functor&& functor ) return resolveIndex< MeshDimension, float >( reader, std::forward(functor) ); if( reader.getRealType() == "double" ) return resolveIndex< MeshDimension, double >( reader, std::forward(functor) ); - if( reader.getRealType() == "long-double" ) + 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; @@ -108,11 +108,19 @@ bool GridTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: resolveIndex( Reader& reader, Functor&& functor ) { - if( reader.getGlobalIndexType() == "short int" ) + 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" ) + 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 int" ) + 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; diff --git a/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h b/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h index 0062f45bd..c4a29fadc 100644 --- a/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h +++ b/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h @@ -161,7 +161,8 @@ bool MeshTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: resolveGlobalIndex( Reader& reader, Functor&& functor ) { - if( reader.getGlobalIndexType() == "short int" || + 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) ); @@ -169,7 +170,8 @@ resolveGlobalIndex( Reader& reader, Functor&& functor ) reader.getGlobalIndexType() == "std::int32_t" || reader.getGlobalIndexType() == "std::uint32_t" ) return resolveLocalIndex< CellTopology, WorldDimension, Real, int >( reader, std::forward(functor) ); - if( reader.getGlobalIndexType() == "long int" || + 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) ); @@ -207,7 +209,8 @@ bool MeshTypeResolver< ConfigTag, Device >::detail< Reader, Functor >:: resolveLocalIndex( Reader& reader, Functor&& functor ) { - if( reader.getLocalIndexType() == "short int" || + 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) ); @@ -215,7 +218,8 @@ resolveLocalIndex( Reader& reader, Functor&& functor ) 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 int" || + 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) ); -- GitLab From 43733a7b868c7127bfa818e6102644eec04a86aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Thu, 9 Apr 2020 18:47:54 +0200 Subject: [PATCH 43/73] Optimized data reuse in mesh readers --- src/TNL/Meshes/Readers/NetgenReader.h | 8 +- src/TNL/Meshes/Readers/TNLReader.h | 6 +- src/TNL/Meshes/Readers/VTKReader.h | 277 ++++++++---------- src/TNL/Meshes/Readers/VTUReader.h | 7 +- .../Meshes/TypeResolver/TypeResolver_impl.h | 12 +- 5 files changed, 148 insertions(+), 162 deletions(-) diff --git a/src/TNL/Meshes/Readers/NetgenReader.h b/src/TNL/Meshes/Readers/NetgenReader.h index 8bc40d657..0d1be8d33 100644 --- a/src/TNL/Meshes/Readers/NetgenReader.h +++ b/src/TNL/Meshes/Readers/NetgenReader.h @@ -147,18 +147,22 @@ public: return false; } + meshDetected = true; return true; } template< typename MeshType > bool readMesh( MeshType& mesh ) { + // check that detectMesh has been called + if( ! meshDetected ) + detectMesh(); + typedef typename MeshType::PointType PointType; typedef MeshBuilder< MeshType > MeshBuilder; const int dimension = PointType::getSize(); - // TODO: check that detectMesh has been called // TODO: reuse inputFile from the detectMesh method std::ifstream inputFile( fileName.getString() ); if( ! inputFile ) @@ -287,11 +291,13 @@ public: protected: String fileName; + bool meshDetected = false; int meshDimension, worldDimension; VTK::EntityShape cellShape = VTK::EntityShape::Vertex; void reset() { + meshDetected = false; meshDimension = worldDimension = 0; cellShape = VTK::EntityShape::Vertex; } diff --git a/src/TNL/Meshes/Readers/TNLReader.h b/src/TNL/Meshes/Readers/TNLReader.h index ab5d0c704..9d5b1d3d2 100644 --- a/src/TNL/Meshes/Readers/TNLReader.h +++ b/src/TNL/Meshes/Readers/TNLReader.h @@ -129,7 +129,7 @@ public: { return cellShape; } - + String getRealType() const { @@ -141,13 +141,13 @@ public: { return globalIndexType; } - + String getLocalIndexType() const { return localIndexType; } - + protected: String fileName; String meshType; diff --git a/src/TNL/Meshes/Readers/VTKReader.h b/src/TNL/Meshes/Readers/VTKReader.h index efb339dc6..8c0faa106 100644 --- a/src/TNL/Meshes/Readers/VTKReader.h +++ b/src/TNL/Meshes/Readers/VTKReader.h @@ -12,7 +12,6 @@ #include #include -#include #include #include @@ -44,6 +43,7 @@ public: 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; @@ -67,14 +67,12 @@ public: iss.clear(); iss.str( line ); iss >> aux; - long int numberOfVertices; - iss >> numberOfVertices; + iss >> NumberOfPoints; iss >> realType; // 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; @@ -88,14 +86,13 @@ public: double aux; iss >> aux; if( ! iss ) { - std::cerr << "VTKReader: unable to read " << i << "th component of the vertex number " << verticesRead << "." << std::endl; + std::cerr << "VTKReader: unable to read " << i << "th component of the vertex number " << pointIndex << "." << std::endl; return false; } if( aux != 0.0 ) worldDimension = std::max( worldDimension, i + 1 ); + pointsArray.push_back( aux ); } - - verticesRead++; } // skip to the CELL_TYPES section @@ -107,15 +104,14 @@ public: 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, VTK::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; + std::cerr << "VTKReader: unable to read enough cell types, the file may be invalid or corrupted." << std::endl; + this->reset(); return false; } getline( inputFile, line ); @@ -125,177 +121,148 @@ public: iss.clear(); iss.str( line ); iss >> typeId; - const VTK::EntityShape type = (VTK::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 " << VTK::getShapeName( entityTypes[ dimension ] ) << " and " << VTK::getShapeName( type ) << ". " - << "The type of all entities with the same dimension must be the same." << std::endl; - this->reset(); - return false; - } + typesArray.push_back( typeId ); + } - entitiesRead++; + // 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]; } - // set meshDimension and cellShape - meshDimension = 0; - for( auto it : entityTypes ) - if( it.first > meshDimension ) { - meshDimension = it.first; - cellShape = it.second; + // set meshDimension + meshDimension = 3; + if( entitiesCounts[3] == 0 ) { + meshDimension--; + if( entitiesCounts[2] == 0 ) { + meshDimension--; + if( entitiesCounts[1] == 0 ) + meshDimension--; } - - return true; - } - - template< typename MeshType > - bool readMesh( MeshType& mesh ) - { - using MeshBuilder = MeshBuilder< MeshType >; - using IndexType = typename MeshType::GlobalIndexType; - using PointType = typename MeshType::PointType; - using CellSeedType = typename MeshBuilder::CellSeedType; - - const VTK::EntityShape cellType = VTK::TopologyToEntityShape< typename MeshType::template EntityTraits< MeshType::getMeshDimension() >::EntityTopology >::shape; - MeshBuilder meshBuilder; - - // TODO: check that detectMesh has been called - // TODO: reuse inputFile from the detectMesh method - std::ifstream inputFile( fileName.getString() ); - if( ! inputFile ) { - std::cerr << "Failed to open the file " << fileName << "." << std::endl; - return false; } - 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; + if( meshDimension == 0 ) { + std::cerr << "Mesh dimension cannot be 0. Are there any entities at all?" << std::endl; + this->reset(); return false; } - // TODO: implement binary parsing - if( dataType == "BINARY" ) { - throw Exceptions::NotImplementedError("VTKReader: parsing of BINARY data is not implemented yet."); + // 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 ); } - 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; + // set number of cells + NumberOfCells = cellTypes.size(); + if( NumberOfCells == 0 || NumberOfCells != entitiesCounts[meshDimension] ) { + std::cerr << "VTKReader: invalid number of cells (" << NumberOfCells << "). Counts of entities for each dimension (0,1,2,3) are: "; + std::cerr << entitiesCounts[0] << ", " << entitiesCounts[1] << ", " << entitiesCounts[2] << ", " << entitiesCounts[3] << std::endl; + this->reset(); return false; } - 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 ); - } - - // 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< VTK::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; + // validate cell types + cellShape = (VTK::EntityShape) cellTypes[0]; + for( auto c : cellTypes ) + if( (VTK::EntityShape) c != cellShape ) { + std::cerr << "Mixed unstructured meshes are not supported. There are cells with type " + << VTK::getShapeName(cellShape) + " and " + VTK::getShapeName((VTK::EntityShape) c) << "." << std::endl; + this->reset(); return false; } - getline( inputFile, line ); - - // get entity type - int typeId; - iss.clear(); - iss.str( line ); - iss >> typeId; - entityTypes[ entityIndex ] = (VTK::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; + this->reset(); return false; } 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; + std::cerr << "VTKReader: unable to read enough cells, the file may be invalid or corrupted."; + std::cerr << " (entityIndex = " << entityIndex << ")" << std::endl; + this->reset(); return false; } 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 ) + if( ! iss ) { + std::cerr << "VTKReader: unable to read enough cells, the file may be invalid or corrupted."; + std::cerr << " (entityIndex = " << entityIndex << ", subvertex = " << v << ")" << std::endl; + std::cerr << line << std::endl; + this->reset(); return false; - seed.setCornerId( v, vid ); + } + connectivityArray.push_back( vid ); } + offsetsArray.push_back( connectivityArray.size() ); } } - // no cells found - if( cellIndex == 0 ) - return false; + // set cell types + std::swap( cellTypes, typesArray ); + + meshDetected = true; + return true; + } + + template< typename MeshType > + bool readMesh( MeshType& mesh ) + { + // check that detectMesh has been called + if( ! meshDetected ) + detectMesh(); + + using MeshBuilder = MeshBuilder< MeshType >; + using PointType = typename MeshType::PointType; + using CellSeedType = typename MeshBuilder::CellSeedType; + + MeshBuilder meshBuilder; + meshBuilder.setPointsCount( NumberOfPoints ); + meshBuilder.setCellsCount( NumberOfCells ); + + // assign points + PointType p; + std::size_t i = 0; + for( auto c : pointsArray ) { + int dim = i++ % 3; + if( dim >= PointType::getSize() ) + continue; + p[dim] = c; + if( dim == PointType::getSize() - 1 ) + meshBuilder.setPoint( (i - 1) / 3, p ); + } + + // assign cells + std::size_t offsetStart = 0; + for( std::size_t i = 0; i < NumberOfCells; i++ ) { + CellSeedType& seed = meshBuilder.getCellSeed( i ); + const std::size_t offsetEnd = offsetsArray[ i ]; + for( std::size_t o = offsetStart; o < offsetEnd; o++ ) + seed.setCornerId( o - offsetStart, connectivityArray[ o ] ); + offsetStart = offsetEnd; + } + + // reset arrays since they are not needed anymore + pointsArray = {}; + connectivityArray = offsetsArray = {}; + typesArray = {}; return meshBuilder.build( mesh ); } @@ -349,15 +316,29 @@ protected: std::string dataset; String fileName; + bool meshDetected = false; + + std::size_t NumberOfPoints, NumberOfCells; int meshDimension, worldDimension; VTK::EntityShape cellShape = VTK::EntityShape::Vertex; std::string realType; + // arrays holding the data from the VTK file + std::vector< double > pointsArray; + std::vector< std::int64_t > connectivityArray, offsetsArray; + std::vector< std::uint8_t > typesArray; + void reset() { + meshDetected = false; + NumberOfPoints = NumberOfCells = 0; meshDimension = worldDimension = 0; cellShape = VTK::EntityShape::Vertex; realType = ""; + // reset arrays + pointsArray = {}; + connectivityArray = offsetsArray = {}; + typesArray = {}; } bool parseHeader( std::istream& str ) diff --git a/src/TNL/Meshes/Readers/VTUReader.h b/src/TNL/Meshes/Readers/VTUReader.h index 3b583b8d7..af8a54a60 100644 --- a/src/TNL/Meshes/Readers/VTUReader.h +++ b/src/TNL/Meshes/Readers/VTUReader.h @@ -417,6 +417,7 @@ public: // TODO: generalize the reader for other XML VTK formats throw XMLVTKError( "parsing the " + fileType + " files is not implemented (yet)" ); + meshDetected = true; return true; #else throw std::runtime_error("The program was compiled without XML parsing. Make sure that TinyXML-2 is " @@ -427,7 +428,9 @@ public: template< typename MeshType > bool readMesh( MeshType& mesh ) { - // TODO: check that detectMesh has been called + // check that detectMesh has been called + if( ! meshDetected ) + detectMesh(); // check that the cell shape mathes const VTK::EntityShape meshCellShape = VTK::TopologyToEntityShape< typename MeshType::template EntityTraits< MeshType::getMeshDimension() >::EntityTopology >::shape; @@ -527,6 +530,7 @@ public: protected: std::string fileName; + bool meshDetected = false; // VTK file type std::string fileType; @@ -543,6 +547,7 @@ protected: void reset() { + meshDetected = false; fileType = ""; NumberOfPoints = NumberOfCells = 0; meshDimension = worldDimension = 0; diff --git a/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h b/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h index 93af4191d..a6fee63bf 100644 --- a/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h +++ b/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h @@ -120,21 +120,15 @@ loadMesh( const String& fileName, mesh.load( fileName ); else if( fileName.endsWith( ".ng" ) ) { Readers::NetgenReader reader( fileName ); - status = reader.detectMesh(); - if( status ) - status = reader.readMesh( mesh ); + status = reader.readMesh( mesh ); } else if( fileName.endsWith( ".vtk" ) ) { Readers::VTKReader reader( fileName ); - status = reader.detectMesh(); - if( status ) - status = reader.readMesh( mesh ); + status = reader.readMesh( mesh ); } else if( fileName.endsWith( ".vtu" ) ) { Readers::VTUReader reader( fileName ); - status = reader.detectMesh(); - if( status ) - status = reader.readMesh( mesh ); + status = reader.readMesh( mesh ); } else { std::cerr << "File '" << fileName << "' has unknown extension. Supported extensions are '.tnl', '.vtk', '.vtu' and '.ng'." << std::endl; -- GitLab From 597022eb2fdb63221f50bdd5088046c378bfff9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Fri, 10 Apr 2020 16:54:14 +0200 Subject: [PATCH 44/73] Refactoring mesh readers: created base class MeshReader TNLReader does not fit, but it will be probably removed anyway... --- src/Python/pytnl/tnl/Mesh.cpp | 18 +- src/TNL/Meshes/Readers/MeshReader.h | 220 ++++++++++++ src/TNL/Meshes/Readers/NetgenReader.h | 334 +++++------------- src/TNL/Meshes/Readers/TNLReader.h | 19 +- src/TNL/Meshes/Readers/VTKReader.h | 232 ++++-------- src/TNL/Meshes/Readers/VTUReader.h | 246 +++---------- .../Meshes/TypeResolver/TypeResolver_impl.h | 67 ++-- src/Tools/tnl-grid-to-mesh.cpp | 9 +- 8 files changed, 486 insertions(+), 659 deletions(-) create mode 100644 src/TNL/Meshes/Readers/MeshReader.h diff --git a/src/Python/pytnl/tnl/Mesh.cpp b/src/Python/pytnl/tnl/Mesh.cpp index 3cd6227a7..4c8184304 100644 --- a/src/Python/pytnl/tnl/Mesh.cpp +++ b/src/Python/pytnl/tnl/Mesh.cpp @@ -14,17 +14,17 @@ void export_Meshes( py::module & m ) 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("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/TNL/Meshes/Readers/MeshReader.h b/src/TNL/Meshes/Readers/MeshReader.h new file mode 100644 index 000000000..19115ca92 --- /dev/null +++ b/src/TNL/Meshes/Readers/MeshReader.h @@ -0,0 +1,220 @@ +/*************************************************************************** + 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() = delete; + + MeshReader( const std::string& fileName ) + : fileName( fileName ) + {} + + virtual ~MeshReader() {} + + /** + * \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( ! meshDetected ) + 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 "Meshes::Mesh"; + } + + 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; + + // indicator that detectMesh has been successfully called + bool meshDetected = false; + + // 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() + { + meshDetected = false; + 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 0d1be8d33..a501e5ba9 100644 --- a/src/TNL/Meshes/Readers/NetgenReader.h +++ b/src/TNL/Meshes/Readers/NetgenReader.h @@ -10,296 +10,150 @@ /*** * 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: NetgenReader() = delete; - NetgenReader( const String& fileName ) - : fileName( fileName ) + NetgenReader( const std::string& fileName ) + : MeshReader( fileName ) {} - bool detectMesh() + virtual void detectMesh() override { - this->reset(); + 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 + "'." ); std::string line; std::istringstream iss; - /**** - * Skip whitespaces - */ + // skip whitespace 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; + throw MeshReaderError( "NetgenReader", "unexpected error when reading the file '" + fileName + "'." ); - /**** - * Read the first vertex and compute number of components - */ - if( ! inputFile ) - return false; + // read number of points 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; + 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." ); } - 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 = VTK::EntityShape::Line; - else if( meshDimension == 2 ) { - if( verticesInCell == 3 ) - cellShape = VTK::EntityShape::Triangle; - else if( verticesInCell == 4 ) - cellShape = VTK::EntityShape::Quad; - } - else if( meshDimension == 3 ) { - if( verticesInCell == 4 ) - cellShape = VTK::EntityShape::Tetra; - else if( verticesInCell == 8 ) - cellShape = VTK::EntityShape::Hexahedron; - } - if( cellShape == VTK::EntityShape::Vertex ) { - std::cerr << "Unknown cell topology: mesh dimension is " << meshDimension << ", number of vertices in cells is " << verticesInCell << "." << std::endl; - return false; - } - - meshDetected = true; - return true; - } - - template< typename MeshType > - bool readMesh( MeshType& mesh ) - { - // check that detectMesh has been called - if( ! meshDetected ) - detectMesh(); - - typedef typename MeshType::PointType PointType; - typedef MeshBuilder< MeshType > MeshBuilder; - - const int dimension = PointType::getSize(); - - // TODO: reuse inputFile from the detectMesh method - std::ifstream inputFile( fileName.getString() ); - if( ! inputFile ) - { - std::cerr << "I am not able to open the file " << fileName << "." << std::endl; - return false; - } - - MeshBuilder meshBuilder; - std::string line; - std::istringstream iss; - - /**** - * Skip white spaces - */ - inputFile >> std::ws; - - /**** - * Read the number of vertices - */ - if( ! inputFile ) - return false; - 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++ ) - { 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"; - } - int getMeshDimension() const - { - return this->meshDimension; - } - - int - getWorldDimension() const - { - return worldDimension; - } + // set cell types + typesArray.resize( NumberOfCells, (std::uint8_t) cellShape ); - VTK::EntityShape - getCellShape() const - { - return cellShape; - } + // 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 - getRealType() const - { - // not stored in the Netgen file - return "float"; - } - - String - getGlobalIndexType() const - { - // not stored in the Netgen file - return "int"; - } - - String - getLocalIndexType() const - { - // not stored in the Netgen file - return "short int"; - } - -protected: - String fileName; - bool meshDetected = false; - int meshDimension, worldDimension; - VTK::EntityShape cellShape = VTK::EntityShape::Vertex; - - void reset() - { - meshDetected = false; - meshDimension = worldDimension = 0; - cellShape = VTK::EntityShape::Vertex; + meshDetected = true; } }; diff --git a/src/TNL/Meshes/Readers/TNLReader.h b/src/TNL/Meshes/Readers/TNLReader.h index 9d5b1d3d2..1258a051b 100644 --- a/src/TNL/Meshes/Readers/TNLReader.h +++ b/src/TNL/Meshes/Readers/TNLReader.h @@ -59,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 ] << ", "; @@ -75,15 +75,15 @@ public: globalIndexType = parsedMeshConfig[ 4 ]; localIndexType = parsedMeshConfig[ 5 ]; - if( topology == "MeshEdgeTopology" ) + if( topology == "TNL::Meshes::Topologies::Edge" ) cellShape = VTK::EntityShape::Line; - else if( topology == "MeshTriangleTopology" ) + else if( topology == "TNL::Meshes::Topologies::Triangle" ) cellShape = VTK::EntityShape::Triangle; - else if( topology == "MeshQuadrilateralTopology" ) + else if( topology == "TNL::Meshes::Topologies::Quadrilateral" ) cellShape = VTK::EntityShape::Quad; - else if( topology == "MeshTetrahedronTopology" ) + else if( topology == "TNL::Meshes::Topologies::Tetrahedron" ) cellShape = VTK::EntityShape::Tetra; - else if( topology == "MeshHexahedronTopology" ) + else if( topology == "TNL::Meshes::Topologies::Hexahedron" ) cellShape = VTK::EntityShape::Hexahedron; else { std::cerr << "Detected topology '" << topology << "' is not supported." << std::endl; @@ -99,11 +99,10 @@ public: } template< typename MeshType > - bool - readMesh( MeshType& mesh ) + void + loadMesh( MeshType& mesh ) { mesh.load( fileName ); - return true; } String diff --git a/src/TNL/Meshes/Readers/VTKReader.h b/src/TNL/Meshes/Readers/VTKReader.h index 8c0faa106..e58471565 100644 --- a/src/TNL/Meshes/Readers/VTKReader.h +++ b/src/TNL/Meshes/Readers/VTKReader.h @@ -8,14 +8,15 @@ /* See Copyright Notice in tnl/Copyright */ +// Implemented by: Jakub Klinkovský + #pragma once #include #include #include -#include -#include +#include #include namespace TNL { @@ -23,59 +24,63 @@ namespace Meshes { namespace Readers { class VTKReader +: public MeshReader { public: VTKReader() = delete; - VTKReader( const String& fileName ) - : fileName( fileName ) + VTKReader( const std::string& fileName ) + : MeshReader( fileName ) {} - bool detectMesh() + virtual void detectMesh() override { - this->reset(); + 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; iss >> NumberOfPoints; - iss >> realType; + 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 worldDimension = 0; 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 ); @@ -86,8 +91,8 @@ public: double aux; iss >> aux; if( ! iss ) { - std::cerr << "VTKReader: unable to read " << i << "th component of the vertex number " << pointIndex << "." << 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 ); @@ -97,8 +102,8 @@ public: // 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(); @@ -110,9 +115,8 @@ public: // read entity types for( std::size_t entityIndex = 0; entityIndex < NumberOfEntities; entityIndex++ ) { if( ! inputFile ) { - std::cerr << "VTKReader: unable to read enough cell types, the file may be invalid or corrupted." << std::endl; - this->reset(); - return false; + reset(); + throw MeshReaderError( "VTKReader", "unable to read enough cell types, the file may be invalid or corrupted." ); } getline( inputFile, line ); @@ -143,9 +147,8 @@ public: } if( meshDimension == 0 ) { - std::cerr << "Mesh dimension cannot be 0. Are there any entities at all?" << std::endl; - this->reset(); - return false; + reset(); + throw MeshReaderError( "VTKReader", "Mesh dimension cannot be 0. Are there any entities at all?" ); } // filter out cell shapes @@ -159,37 +162,36 @@ public: // set number of cells NumberOfCells = cellTypes.size(); if( NumberOfCells == 0 || NumberOfCells != entitiesCounts[meshDimension] ) { - std::cerr << "VTKReader: invalid number of cells (" << NumberOfCells << "). Counts of entities for each dimension (0,1,2,3) are: "; - std::cerr << entitiesCounts[0] << ", " << entitiesCounts[1] << ", " << entitiesCounts[2] << ", " << entitiesCounts[3] << std::endl; - this->reset(); - return false; + 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 ); } // validate cell types cellShape = (VTK::EntityShape) cellTypes[0]; for( auto c : cellTypes ) if( (VTK::EntityShape) c != cellShape ) { - std::cerr << "Mixed unstructured meshes are not supported. There are cells with type " - << VTK::getShapeName(cellShape) + " and " + VTK::getShapeName((VTK::EntityShape) c) << "." << std::endl; - this->reset(); - return false; + 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 ); } // 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; - this->reset(); - return false; + reset(); + throw MeshReaderError( "VTKReader", "unable to find the CELLS section, the file may be invalid or corrupted." ); } getline( inputFile, line ); // read entities for( std::size_t entityIndex = 0; entityIndex < NumberOfEntities; entityIndex++ ) { if( ! inputFile ) { - std::cerr << "VTKReader: unable to read enough cells, the file may be invalid or corrupted."; - std::cerr << " (entityIndex = " << entityIndex << ")" << std::endl; - this->reset(); - 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 ); @@ -203,11 +205,9 @@ public: std::size_t vid; iss >> vid; if( ! iss ) { - std::cerr << "VTKReader: unable to read enough cells, the file may be invalid or corrupted."; - std::cerr << " (entityIndex = " << entityIndex << ", subvertex = " << v << ")" << std::endl; - std::cerr << line << std::endl; - this->reset(); - return false; + 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 ); } @@ -218,96 +218,20 @@ public: // set cell types std::swap( cellTypes, typesArray ); - meshDetected = true; - return true; - } - - template< typename MeshType > - bool readMesh( MeshType& mesh ) - { - // check that detectMesh has been called - if( ! meshDetected ) - detectMesh(); - - using MeshBuilder = MeshBuilder< MeshType >; - using PointType = typename MeshType::PointType; - using CellSeedType = typename MeshBuilder::CellSeedType; - - MeshBuilder meshBuilder; - meshBuilder.setPointsCount( NumberOfPoints ); - meshBuilder.setCellsCount( NumberOfCells ); - - // assign points - PointType p; - std::size_t i = 0; - for( auto c : pointsArray ) { - int dim = i++ % 3; - if( dim >= PointType::getSize() ) - continue; - p[dim] = c; - if( dim == PointType::getSize() - 1 ) - meshBuilder.setPoint( (i - 1) / 3, p ); - } - - // assign cells - std::size_t offsetStart = 0; - for( std::size_t i = 0; i < NumberOfCells; i++ ) { - CellSeedType& seed = meshBuilder.getCellSeed( i ); - const std::size_t offsetEnd = offsetsArray[ i ]; - for( std::size_t o = offsetStart; o < offsetEnd; o++ ) - seed.setCornerId( o - offsetStart, connectivityArray[ o ] ); - offsetStart = offsetEnd; - } - - // reset arrays since they are not needed anymore - pointsArray = {}; - connectivityArray = offsetsArray = {}; - typesArray = {}; + // 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); - return meshBuilder.build( mesh ); - } - - String - getMeshType() const - { - return "Meshes::Mesh"; - } - - int getMeshDimension() const - { - return this->meshDimension; - } - - int - getWorldDimension() const - { - return worldDimension; - } - - VTK::EntityShape - getCellShape() const - { - return cellShape; - } - - String - getRealType() const - { - return realType.c_str(); - } - - String - getGlobalIndexType() const - { - // not stored in the VTK file - return "int"; + meshDetected = true; } - String - getLocalIndexType() const + virtual void reset() override { - // not stored in the VTK file - return "short int"; + resetBase(); + dataType = ""; + dataset = ""; } protected: @@ -315,32 +239,6 @@ protected: std::string dataType; std::string dataset; - String fileName; - bool meshDetected = false; - - std::size_t NumberOfPoints, NumberOfCells; - int meshDimension, worldDimension; - VTK::EntityShape cellShape = VTK::EntityShape::Vertex; - std::string realType; - - // arrays holding the data from the VTK file - std::vector< double > pointsArray; - std::vector< std::int64_t > connectivityArray, offsetsArray; - std::vector< std::uint8_t > typesArray; - - void reset() - { - meshDetected = false; - NumberOfPoints = NumberOfCells = 0; - meshDimension = worldDimension = 0; - cellShape = VTK::EntityShape::Vertex; - realType = ""; - // reset arrays - pointsArray = {}; - connectivityArray = offsetsArray = {}; - typesArray = {}; - } - bool parseHeader( std::istream& str ) { std::string line; diff --git a/src/TNL/Meshes/Readers/VTUReader.h b/src/TNL/Meshes/Readers/VTUReader.h index af8a54a60..2b2565c33 100644 --- a/src/TNL/Meshes/Readers/VTUReader.h +++ b/src/TNL/Meshes/Readers/VTUReader.h @@ -8,14 +8,14 @@ /* See Copyright Notice in tnl/Copyright */ +// Implemented by: Jakub Klinkovský + #pragma once #include #include -#include -#include -#include +#include #include #include @@ -31,14 +31,6 @@ namespace TNL { namespace Meshes { namespace Readers { -struct XMLVTKError - : public std::runtime_error -{ - XMLVTKError( std::string msg ) - : std::runtime_error( "XMLVTKReader error: " + msg ) - {} -}; - static const std::map< std::string, std::string > VTKDataTypes { {"Int8", "std::int8_t"}, {"UInt8", "std::uint8_t"}, @@ -53,25 +45,15 @@ static const std::map< std::string, std::string > VTKDataTypes { }; class VTUReader +: public MeshReader { - 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 > >; - #ifdef HAVE_TINYXML2 static void verifyElement( const tinyxml2::XMLElement* elem, const std::string name ) { if( ! elem ) - throw XMLVTKError( "tag <" + name + "> not found" ); + throw MeshReaderError( "VTUReader", "tag <" + name + "> not found" ); if( elem->Name() != name ) - throw XMLVTKError( "invalid XML format - expected a <" + name + "> element, got <" + elem->Name() + ">" ); + throw MeshReaderError( "VTUReader", "invalid XML format - expected a <" + name + "> element, got <" + elem->Name() + ">" ); } static const tinyxml2::XMLElement* @@ -82,9 +64,9 @@ class VTUReader if( ! childName.empty() ) verifyElement( elem, childName ); else if( ! elem ) - throw XMLVTKError( "element " + parentName + " does not contain any child" ); + throw MeshReaderError( "VTUReader", "element " + parentName + " does not contain any child" ); if( elem->NextSibling() ) - throw XMLVTKError( "<" + childName + "> is not the only element in <" + parentName + ">" ); + throw MeshReaderError( "VTUReader", "<" + childName + "> is not the only element in <" + parentName + ">" ); return elem; } @@ -97,7 +79,7 @@ class VTUReader return attribute; if( ! defaultValue.empty() ) return defaultValue; - throw XMLVTKError( "element <" + std::string(elem->Name()) + "> does not have the attribute '" + name + "'" ); + throw MeshReaderError( "VTUReader", "element <" + std::string(elem->Name()) + "> does not have the attribute '" + name + "'" ); } static std::int64_t @@ -106,7 +88,7 @@ class VTUReader std::int64_t value; tinyxml2::XMLError status = elem->QueryInt64Attribute( name.c_str(), &value ); if( status != tinyxml2::XML_SUCCESS ) - throw XMLVTKError( "element <" + std::string(elem->Name()) + "> does not have the attribute '" + name + "' or it could not be converted to int64_t" ); + throw MeshReaderError( "VTUReader", "element <" + std::string(elem->Name()) + "> does not have the attribute '" + name + "' or it could not be converted to int64_t" ); return value; } @@ -127,16 +109,16 @@ class VTUReader // verify type const std::string type = getAttributeString( elem, "type" ); if( VTKDataTypes.count( type ) == 0 ) - throw XMLVTKError( "unsupported DataArray type: " + type ); + throw MeshReaderError( "VTUReader", "unsupported DataArray type: " + type ); // verify format const std::string format = getAttributeString( elem, "format" ); if( format != "ascii" && format != "binary" ) - throw XMLVTKError( "unsupported DataArray format: " + format ); + throw MeshReaderError( "VTUReader", "unsupported DataArray 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 XMLVTKError( "unsupported NumberOfComponents in DataArray: " + NumberOfComponents ); + throw MeshReaderError( "VTUReader", "unsupported NumberOfComponents in DataArray: " + NumberOfComponents ); } static const tinyxml2::XMLElement* @@ -150,29 +132,29 @@ class VTUReader try { arrayName = getAttributeString( child, "Name" ); } - catch( const XMLVTKError& ) {} + catch( const MeshReaderError& ) {} if( arrayName == name ) { if( found == nullptr ) found = child; else - throw XMLVTKError( "the <" + std::string(parent->Name()) + "> tag contains multiple tags with the Name=\"" + name + "\" attribute" ); + throw MeshReaderError( "VTUReader", "the <" + std::string(parent->Name()) + "> tag contains multiple tags with the Name=\"" + name + "\" attribute" ); } child = child->NextSiblingElement( "DataArray" ); } if( found == nullptr ) - throw XMLVTKError( "the <" + std::string(parent->Name()) + "> tag does not contain any tag with the Name=\"" + name + "\" attribute" ); + throw MeshReaderError( "VTUReader", "the <" + std::string(parent->Name()) + "> tag does not contain any tag with the Name=\"" + name + "\" attribute" ); verifyDataArray( found ); return found; } template< typename HeaderType > - std::size_t - readBlockSize( const char* block ) const + 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 XMLVTKError( "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)" ); + throw MeshReaderError( "VTUReader", "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; } @@ -202,12 +184,12 @@ class VTUReader vector[i] = decoded_data.second.get()[i]; return vector; #else - throw XMLVTKError( "The ZLIB compression is not available in this build. Make sure that ZLIB is " - "installed and recompile the program with -DHAVE_ZLIB." ); + throw MeshReaderError( "VTUReader", "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 XMLVTKError( "unsupported compressor type: " + compressor + " (only vtkZLibDataCompressor is supported)" ); + throw MeshReaderError( "VTUReader", "unsupported compressor type: " + compressor + " (only vtkZLibDataCompressor is supported)" ); } template< typename T > @@ -222,7 +204,7 @@ class VTUReader 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 XMLVTKError( "unsupported header type: " + headerType ); + else throw MeshReaderError( "VTUReader", "unsupported header type: " + headerType ); } VariantVector @@ -231,12 +213,12 @@ class VTUReader verifyElement( elem, "DataArray" ); const char* block = elem->GetText(); if( ! block ) - throw XMLVTKError( "the DataArray with Name=\"" + arrayName + "\" does not contain any data" ); + throw MeshReaderError( "VTUReader", "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 XMLVTKError( "reading ASCII arrays is not implemented yet" ); + throw MeshReaderError( "VTUReader", "reading ASCII arrays is not implemented yet" ); } else if( format == "binary" ) { if( type == "Int8" ) return readBinaryBlock< std::int8_t >( block ); @@ -249,10 +231,10 @@ class VTUReader 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 XMLVTKError( "unsupported DataArray type: " + type ); + else throw MeshReaderError( "VTUReader", "unsupported DataArray type: " + type ); } else - throw XMLVTKError( "unsupported DataArray format: " + format ); + throw MeshReaderError( "VTUReader", "unsupported DataArray format: " + format ); } void readUnstructuredGrid( const tinyxml2::XMLElement* elem ) @@ -261,7 +243,7 @@ class VTUReader const XMLElement* piece = getChildSafe( elem, "Piece" ); if( piece->NextSiblingElement( "Piece" ) ) // ambiguity - throw error, we don't know which piece to parse (or all of them?) - throw XMLVTKError( "the serial UnstructuredGrid file contains more than one element" ); + throw MeshReaderError( "VTUReader", "the serial UnstructuredGrid file contains more than one element" ); NumberOfPoints = getAttributeInteger( piece, "NumberOfPoints" ); NumberOfCells = getAttributeInteger( piece, "NumberOfCells" ); @@ -271,7 +253,7 @@ class VTUReader verifyDataArray( pointsData ); const std::string pointsDataName = getAttributeString( pointsData, "Name" ); if( pointsDataName != "Points" ) - throw XMLVTKError( "the tag does not contain a with Name=\"Points\" attribute" ); + throw MeshReaderError( "VTUReader", "the tag does not contain a with Name=\"Points\" attribute" ); // verify cells const XMLElement* cells = getChildSafe( piece, "Cells" ); @@ -291,19 +273,19 @@ class VTUReader // connectivity and offsets must have the same type if( connectivityType != offsetsType ) - throw XMLVTKError( "the \"connectivity\" and \"offsets\" array do not have the same type (" + 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 XMLVTKError( "unsupported data type for the Name=\"types\" array" ); + 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 XMLVTKError( "invalid size of the Points data array (" + std::to_string(array.size()) - + " vs " + std::to_string(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; @@ -321,14 +303,14 @@ class VTUReader visit( [this](auto&& array) { // check array size if( array.size() != NumberOfCells ) - throw XMLVTKError( "size of the types data array does not match the NumberOfCells attribute" ); + 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 XMLVTKError( "Mixed unstructured meshes are not supported. There are cells with type " - + VTK::getShapeName(cellShape) + " and " + VTK::getShapeName((VTK::EntityShape) c) + "." ); + throw MeshReaderError( "VTUReader", "Mixed unstructured meshes are not supported. There are cells with type " + + VTK::getShapeName(cellShape) + " and " + VTK::getShapeName((VTK::EntityShape) c) + "." ); }, typesArray ); @@ -336,10 +318,10 @@ class VTUReader std::size_t max_offset = 0; visit( [this, &max_offset](auto&& array) mutable { if( array.size() != NumberOfCells ) - throw XMLVTKError( "size of the offsets data array does not match the NumberOfCells attribute" ); + 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 XMLVTKError( "the offsets array is not monotonically increasing" ); + throw MeshReaderError( "VTUReader", "the offsets array is not monotonically increasing" ); max_offset = c; } }, @@ -348,10 +330,10 @@ class VTUReader // validate connectivity visit( [this, max_offset](auto&& array) { if( array.size() != max_offset ) - throw XMLVTKError( "size of the connectivity data array does not match the offsets array" ); + 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 XMLVTKError( "connectivity index " + std::to_string(c) + " is out of range" ); + throw MeshReaderError( "VTUReader", "connectivity index " + std::to_string(c) + " is out of range" ); } }, connectivityArray @@ -364,10 +346,10 @@ public: VTUReader() = delete; VTUReader( const std::string& fileName ) - : fileName( fileName ) + : MeshReader( fileName ) {} - bool detectMesh() + virtual void detectMesh() override { #ifdef HAVE_TINYXML2 this->reset(); @@ -380,25 +362,25 @@ public: // load and verify XML status = dom.LoadFile( fileName.c_str() ); if( status != XML_SUCCESS ) - throw XMLVTKError( "VTUReader: failed to parse the file as an XML document." ); + throw MeshReaderError( "VTUReader", "VTUReader: failed to parse the file as an XML document." ); // verify root element const XMLElement* elem = dom.FirstChildElement(); verifyElement( elem, "VTKFile" ); if( elem->NextSibling() ) - throw XMLVTKError( " is not the only element in the file" ); + throw MeshReaderError( "VTUReader", " 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 XMLVTKError( "incompatible byte_order: " + byteOrder + " (the system is " + systemByteOrder + " and the conversion " - "from BigEndian to LittleEndian or vice versa is not implemented yet)" ); + throw MeshReaderError( "VTUReader", "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 XMLVTKError( "invalid header_type: " + headerType ); + throw MeshReaderError( "VTUReader", "invalid header_type: " + headerType ); headerType = VTKDataTypes.at( headerType ); // verify compressor @@ -406,7 +388,7 @@ public: if( compressor == "" ) compressor = ""; if( compressor != "" && compressor != "vtkZLibDataCompressor" ) - throw XMLVTKError( "unsupported compressor type: " + compressor + " (only vtkZLibDataCompressor is supported)" ); + throw MeshReaderError( "VTUReader", "unsupported compressor type: " + compressor + " (only vtkZLibDataCompressor is supported)" ); // verify file type fileType = getAttributeString( elem, "type" ); @@ -415,147 +397,27 @@ public: readUnstructuredGrid( elem ); else // TODO: generalize the reader for other XML VTK formats - throw XMLVTKError( "parsing the " + fileType + " files is not implemented (yet)" ); + throw MeshReaderError( "VTUReader", "parsing the " + fileType + " files is not implemented (yet)" ); meshDetected = true; - return true; #else 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."); #endif } - template< typename MeshType > - bool readMesh( MeshType& mesh ) - { - // check that detectMesh has been called - if( ! meshDetected ) - 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 XMLVTKError( "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( [this, &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 = {}; - - return meshBuilder.build( mesh ); - } - - std::string - getMeshType() const - { - return "Meshes::Mesh"; - } - - int getMeshDimension() const + virtual void reset() override { - return this->meshDimension; - } - - int - getWorldDimension() const - { - return worldDimension; - } - - VTK::EntityShape - getCellShape() const - { - return cellShape; - } - - std::string - getRealType() const - { - return pointsType.c_str(); - } - - std::string - getGlobalIndexType() const - { - return connectivityType; - } - - std::string - getLocalIndexType() const - { - // not stored in the VTK file - return "short int"; + fileType = ""; + byteOrder = compressor = headerType = ""; } protected: - std::string fileName; - bool meshDetected = false; - // VTK file type std::string fileType; - std::size_t NumberOfPoints, NumberOfCells; - int meshDimension, worldDimension; - VTK::EntityShape cellShape = VTK::EntityShape::Vertex; + // header attributes std::string byteOrder, compressor, headerType; - - // arrays holding the s from the VTK file - VariantVector pointsArray, connectivityArray, offsetsArray, typesArray; - // string representation of each array's value type - std::string pointsType, connectivityType, offsetsType, typesType; - - void reset() - { - meshDetected = false; - fileType = ""; - NumberOfPoints = NumberOfCells = 0; - meshDimension = worldDimension = 0; - cellShape = VTK::EntityShape::Vertex; - byteOrder = compressor = headerType = ""; - pointsArray = connectivityArray = offsetsArray = typesArray = {}; - pointsType = connectivityType = offsetsType = typesType = ""; - } }; } // namespace Readers diff --git a/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h b/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h index a6fee63bf..a3b0e3a90 100644 --- a/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h +++ b/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h @@ -27,6 +27,7 @@ template< typename ConfigTag, bool resolveMeshType( const String& fileName, Functor&& functor ) { std::cout << "Detecting mesh from file " << fileName << " ..." << std::endl; + // 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( fileName.endsWith( ".tnl" ) ) { Readers::TNLReader reader( fileName ); if( ! reader.detectMesh() ) @@ -45,10 +46,9 @@ bool resolveMeshType( const String& fileName, Functor&& functor ) // 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( fileName ); - if( ! reader.detectMesh() ) - return false; + reader.detectMesh(); if( reader.getMeshType() == "Meshes::Mesh" ) - return MeshTypeResolver< ConfigTag, Device >::run( reader, functor ); + 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; @@ -59,10 +59,9 @@ bool resolveMeshType( const String& fileName, Functor&& functor ) // 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( fileName ); - if( ! reader.detectMesh() ) - return false; + reader.detectMesh(); if( reader.getMeshType() == "Meshes::Mesh" ) - return MeshTypeResolver< ConfigTag, Device >::run( reader, functor ); + 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; @@ -73,10 +72,9 @@ bool resolveMeshType( const String& fileName, Functor&& functor ) // 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 ); - if( ! reader.detectMesh() ) - return false; + reader.detectMesh(); if( reader.getMeshType() == "Meshes::Mesh" ) - return MeshTypeResolver< ConfigTag, Device >::run( reader, functor ); + 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; @@ -97,9 +95,11 @@ bool resolveAndLoadMesh( const String& fileName, Functor&& functor ) { using MeshType = std::decay_t< decltype(mesh) >; std::cout << "Loading a mesh from the file " << fileName << " ..." << std::endl; - if( ! reader.readMesh( mesh ) ) { - std::cerr << "Failed to load the mesh from the file " << fileName << ". The mesh type is " - << getType< MeshType >() << 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) ); @@ -114,33 +114,32 @@ loadMesh( const String& fileName, Mesh< MeshConfig, Device >& mesh ) { std::cout << "Loading a mesh from the file " << fileName << " ..." << std::endl; - bool status = true; - if( fileName.endsWith( ".tnl" ) ) - mesh.load( fileName ); - else if( fileName.endsWith( ".ng" ) ) { - Readers::NetgenReader reader( fileName ); - status = reader.readMesh( mesh ); - } - else if( fileName.endsWith( ".vtk" ) ) { - Readers::VTKReader reader( fileName ); - status = reader.readMesh( mesh ); - } - else if( fileName.endsWith( ".vtu" ) ) { - Readers::VTUReader reader( fileName ); - status = reader.readMesh( mesh ); + try { + if( fileName.endsWith( ".tnl" ) ) + mesh.load( fileName ); + else if( fileName.endsWith( ".ng" ) ) { + Readers::NetgenReader reader( fileName ); + reader.loadMesh( mesh ); + } + else if( fileName.endsWith( ".vtk" ) ) { + Readers::VTKReader reader( fileName ); + reader.loadMesh( mesh ); + } + else if( fileName.endsWith( ".vtu" ) ) { + Readers::VTUReader reader( fileName ); + reader.loadMesh( mesh ); + } + else { + std::cerr << "File '" << fileName << "' has unknown extension. Supported extensions are '.tnl', '.vtk', '.vtu' and '.ng'." << std::endl; + return false; + } } - else { - std::cerr << "File '" << fileName << "' has unknown extension. Supported extensions are '.tnl', '.vtk', '.vtu' and '.ng'." << std::endl; + 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; } - if( ! status ) - { - std::cerr << "Failed to load the mesh from the file " << fileName << ". The mesh type is " - << getType< decltype(mesh) >() << std::endl; - return false; - } return true; } diff --git a/src/Tools/tnl-grid-to-mesh.cpp b/src/Tools/tnl-grid-to-mesh.cpp index 11f707b9d..b976a238b 100644 --- a/src/Tools/tnl-grid-to-mesh.cpp +++ b/src/Tools/tnl-grid-to-mesh.cpp @@ -167,14 +167,9 @@ bool convertGrid( Grid& grid, const TNL::String& fileName, const TNL::String& ou using MeshCreator = MeshCreator< Grid >; using Mesh = typename MeshCreator::MeshType; - Mesh mesh; - - TNL::Meshes::Readers::TNLReader reader( fileName ); - if( ! reader.readMesh( grid ) ) { - std::cerr << "Failed to load grid from file '" << fileName << "'." << std::endl; - return false; - } + grid.load( fileName ); + Mesh mesh; if( ! MeshCreator::run( grid, mesh ) ) { std::cerr << "Unable to build mesh from grid." << std::endl; return false; -- GitLab From 273ce6d67ab2f467dfbb10bdc0ceaa5b906168dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Fri, 10 Apr 2020 23:51:39 +0200 Subject: [PATCH 45/73] Refactored general functionality from VTUReader into XMLVTK base class and added PVTUReader --- src/TNL/Meshes/Readers/MeshReader.h | 19 +- src/TNL/Meshes/Readers/NetgenReader.h | 5 +- src/TNL/Meshes/Readers/PVTUReader.h | 241 ++++++++++++++++ src/TNL/Meshes/Readers/VTKReader.h | 5 +- src/TNL/Meshes/Readers/VTUReader.h | 299 ++------------------ src/TNL/Meshes/Readers/XMLVTK.h | 377 ++++++++++++++++++++++++++ 6 files changed, 656 insertions(+), 290 deletions(-) create mode 100644 src/TNL/Meshes/Readers/PVTUReader.h create mode 100644 src/TNL/Meshes/Readers/XMLVTK.h diff --git a/src/TNL/Meshes/Readers/MeshReader.h b/src/TNL/Meshes/Readers/MeshReader.h index 19115ca92..abf550510 100644 --- a/src/TNL/Meshes/Readers/MeshReader.h +++ b/src/TNL/Meshes/Readers/MeshReader.h @@ -45,7 +45,7 @@ public: std::vector< float >, std::vector< double > >; - MeshReader() = delete; + MeshReader() = default; MeshReader( const std::string& fileName ) : fileName( fileName ) @@ -53,6 +53,12 @@ public: virtual ~MeshReader() {} + void setFileName( const std::string& fileName ) + { + reset(); + this->fileName = fileName; + } + /** * \brief This method resets the reader to an empty state. * @@ -85,7 +91,7 @@ public: void loadMesh( MeshType& mesh ) { // check that detectMesh has been called - if( ! meshDetected ) + if( meshType == "" ) detectMesh(); // check that the cell shape mathes @@ -146,7 +152,7 @@ public: std::string getMeshType() const { - return "Meshes::Mesh"; + return meshType; } int @@ -190,8 +196,9 @@ protected: // input file name std::string fileName; - // indicator that detectMesh has been successfully called - bool meshDetected = false; + // 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; @@ -206,7 +213,7 @@ protected: void resetBase() { - meshDetected = false; + meshType = ""; NumberOfPoints = NumberOfCells = 0; meshDimension = worldDimension = 0; cellShape = VTK::EntityShape::Vertex; diff --git a/src/TNL/Meshes/Readers/NetgenReader.h b/src/TNL/Meshes/Readers/NetgenReader.h index a501e5ba9..3cd69a1ab 100644 --- a/src/TNL/Meshes/Readers/NetgenReader.h +++ b/src/TNL/Meshes/Readers/NetgenReader.h @@ -30,7 +30,7 @@ class NetgenReader : public MeshReader { public: - NetgenReader() = delete; + NetgenReader() = default; NetgenReader( const std::string& fileName ) : MeshReader( fileName ) @@ -153,7 +153,8 @@ public: this->offsetsArray = std::move(offsetsArray); this->typesArray = std::move(typesArray); - meshDetected = true; + // 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 000000000..5cc98bf08 --- /dev/null +++ b/src/TNL/Meshes/Readers/PVTUReader.h @@ -0,0 +1,241 @@ +/*************************************************************************** + 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 + +namespace TNL { +namespace Meshes { +namespace Readers { + +class PVTUReader +: public XMLVTK +{ + std::string + getSourcePath( std::string source ) + { + // TODO: use proper path library + std::vector< String > parts = String( fileName ).split( '/' ); + parts.pop_back(); + parts.push_back( source ); + std::string result; + for( auto p : parts ) + if( result.empty() ) + result += p; + else + result += "/" + p; + return result; + } + +#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 ); + } + + // 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/VTKReader.h b/src/TNL/Meshes/Readers/VTKReader.h index e58471565..58a196a85 100644 --- a/src/TNL/Meshes/Readers/VTKReader.h +++ b/src/TNL/Meshes/Readers/VTKReader.h @@ -27,7 +27,7 @@ class VTKReader : public MeshReader { public: - VTKReader() = delete; + VTKReader() = default; VTKReader( const std::string& fileName ) : MeshReader( fileName ) @@ -224,7 +224,8 @@ public: this->offsetsArray = std::move(offsetsArray); this->typesArray = std::move(typesArray); - meshDetected = true; + // indicate success by setting the mesh type + meshType = "Meshes::Mesh"; } virtual void reset() override diff --git a/src/TNL/Meshes/Readers/VTUReader.h b/src/TNL/Meshes/Readers/VTUReader.h index 2b2565c33..f9c3902d0 100644 --- a/src/TNL/Meshes/Readers/VTUReader.h +++ b/src/TNL/Meshes/Readers/VTUReader.h @@ -12,235 +12,20 @@ #pragma once -#include -#include - -#include -#include -#include - -#ifdef HAVE_ZLIB - #include -#endif - -#ifdef HAVE_TINYXML2 - #include -#endif +#include 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 VTUReader -: public MeshReader +: public XMLVTK { #ifdef HAVE_TINYXML2 - static void verifyElement( const tinyxml2::XMLElement* elem, const std::string name ) - { - if( ! elem ) - throw MeshReaderError( "VTUReader", "tag <" + name + "> not found" ); - if( elem->Name() != name ) - throw MeshReaderError( "VTUReader", "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( "VTUReader", "element " + parentName + " does not contain any child" ); - if( elem->NextSibling() ) - throw MeshReaderError( "VTUReader", "<" + 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( "VTUReader", "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( "VTUReader", "element <" + std::string(elem->Name()) + "> does not have the attribute '" + name + "' or it could not be converted to int64_t" ); - 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 ) - { - verifyElement( elem, "DataArray" ); - // verify Name - getAttributeString( elem, "Name" ); - // verify type - const std::string type = getAttributeString( elem, "type" ); - if( VTKDataTypes.count( type ) == 0 ) - throw MeshReaderError( "VTUReader", "unsupported DataArray type: " + type ); - // verify format - const std::string format = getAttributeString( elem, "format" ); - if( format != "ascii" && format != "binary" ) - throw MeshReaderError( "VTUReader", "unsupported DataArray 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( "VTUReader", "unsupported NumberOfComponents in DataArray: " + 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( "VTUReader", "the <" + std::string(parent->Name()) + "> tag contains multiple tags with the Name=\"" + name + "\" attribute" ); - } - child = child->NextSiblingElement( "DataArray" ); - } - if( found == nullptr ) - throw MeshReaderError( "VTUReader", "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( "VTUReader", "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( "VTUReader", "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( "VTUReader", "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( "VTUReader", "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( "VTUReader", "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( "VTUReader", "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( "VTUReader", "unsupported DataArray type: " + type ); - } - else - throw MeshReaderError( "VTUReader", "unsupported DataArray format: " + format ); - } - - void readUnstructuredGrid( const tinyxml2::XMLElement* elem ) + void readUnstructuredGrid() { using namespace tinyxml2; - const XMLElement* piece = getChildSafe( elem, "Piece" ); + 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" ); @@ -338,86 +123,40 @@ class VTUReader }, connectivityArray ); - } #endif public: - VTUReader() = delete; + VTUReader() = default; VTUReader( const std::string& fileName ) - : MeshReader( fileName ) + : XMLVTK( fileName ) {} virtual void detectMesh() override { #ifdef HAVE_TINYXML2 - this->reset(); - - using namespace tinyxml2; - - XMLDocument dom; - XMLError status; - - // load and verify XML - status = dom.LoadFile( fileName.c_str() ); - if( status != XML_SUCCESS ) - throw MeshReaderError( "VTUReader", "VTUReader: 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( "VTUReader", " 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( "VTUReader", "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( "VTUReader", "invalid header_type: " + headerType ); - headerType = VTKDataTypes.at( headerType ); - - // verify compressor - compressor = getAttributeString( elem, "compressor", "" ); - if( compressor == "" ) - compressor = ""; - if( compressor != "" && compressor != "vtkZLibDataCompressor" ) - throw MeshReaderError( "VTUReader", "unsupported compressor type: " + compressor + " (only vtkZLibDataCompressor is supported)" ); + reset(); + try { + openVTKFile(); + } + catch( const MeshReaderError& ) { + reset(); + throw; + } // verify file type - fileType = getAttributeString( elem, "type" ); - elem = verifyHasOnlyOneChild( elem, fileType ); if( fileType == "UnstructuredGrid" ) - readUnstructuredGrid( elem ); + readUnstructuredGrid(); else - // TODO: generalize the reader for other XML VTK formats - throw MeshReaderError( "VTUReader", "parsing the " + fileType + " files is not implemented (yet)" ); + throw MeshReaderError( "VTUReader", "the reader cannot read data of the type " + fileType + ". Use a different reader if possible." ); - meshDetected = true; + // indicate success by setting the mesh type + meshType = "Meshes::Mesh"; #else - 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."); + throw_no_tinyxml(); #endif } - - virtual void reset() override - { - fileType = ""; - byteOrder = compressor = headerType = ""; - } - -protected: - // VTK file type - std::string fileType; - - // header attributes - std::string byteOrder, compressor, headerType; }; } // namespace Readers diff --git a/src/TNL/Meshes/Readers/XMLVTK.h b/src/TNL/Meshes/Readers/XMLVTK.h new file mode 100644 index 000000000..fb8e1eb40 --- /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 -- GitLab From a98f773fe35d0a0541f5256f360ffd48ab2ac20b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Fri, 10 Apr 2020 23:53:33 +0200 Subject: [PATCH 46/73] First working version of DistributedMesh and DistributedMeshSynchronizer --- .../DistributedMeshes/DistributedMesh.h | 209 +++++++++++++- .../DistributedMeshSynchronizer.h | 271 +++++++++++++++++- .../DistributedMeshes/GlobalIndexStorage.h | 43 +++ .../DistributedMeshes/loadDistributedMesh.h | 74 ++++- 4 files changed, 573 insertions(+), 24 deletions(-) create mode 100644 src/TNL/Meshes/DistributedMeshes/GlobalIndexStorage.h diff --git a/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h b/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h index a6e7c01f9..d664449c1 100644 --- a/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h +++ b/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h @@ -1,38 +1,221 @@ /*************************************************************************** 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 namespace TNL { -namespace Meshes { +namespace Meshes { namespace DistributedMeshes { -template< typename MeshType > +template< typename Mesh > class DistributedMesh +: protected GlobalIndexStorage< Mesh, 0 >, + protected GlobalIndexStorage< Mesh, Mesh::getMeshDimension() > { 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 ) + { + localMesh = other.getLocalMesh(); + group = other.getCommunicationGroup(); + return *this; + } + + /** + * 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; + } + + MeshType& getLocalMesh() + { + return localMesh; + } + + void setGhostLevels( int levels ) + { + ghostLevels = levels; + } + + int getGhostLevels() const + { + return ghostLevels; + } - bool IsDistributed(void) + template< int Dimension > + const GlobalIndexArray& + getGlobalIndices() const { - return false; - }; + static_assert( Dimension == 0 || Dimension == MeshType::getMeshDimension(), + "Global index array for this dimension is not implemented yet." ); + return GlobalIndexStorage< MeshType, Dimension >::getGlobalIndices(); + } + + template< int Dimension > + GlobalIndexArray& + getGlobalIndices() + { + static_assert( Dimension == 0 || Dimension == MeshType::getMeshDimension(), + "Global index array for this dimension is not implemented yet." ); + return GlobalIndexStorage< MeshType, 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; + } + + void + printInfo( std::ostream& str ) const + { + const GlobalIndexType pointsCount = localMesh.template getEntitiesCount< 0 >(); + const GlobalIndexType cellsCount = localMesh.template getEntitiesCount< Mesh::getMeshDimension() >(); + + // TODO: the mesh should explicitly store ghost counts (or offsets) - useful for efficient iteration + GlobalIndexType ghostPoints = 0; + for( GlobalIndexType i = 0; i < pointsCount; i++ ) + if( localMesh.template isGhostEntity< 0 >( i ) ) + ghostPoints++; + GlobalIndexType ghostCells = 0; + for( GlobalIndexType i = 0; i < cellsCount; i++ ) + if( localMesh.template isGhostEntity< Mesh::getMeshDimension() >( i ) ) + ghostCells++; + + 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" + << "\tPoints count:\t" << pointsCount << "\n" + << "\tGhost levels:\t" << getGhostLevels() << "\n" + << "\tGhost cells:\t" << ghostCells << "\n" + << "\tGhost points:\t" << ghostPoints << "\n"; + const GlobalIndexType globalPointIndices = getGlobalIndices< 0 >().getSize(); + const GlobalIndexType globalCellIndices = getGlobalIndices< Mesh::getMeshDimension() >().getSize(); + if( getGhostLevels() > 0 ) { + if( globalPointIndices != pointsCount ) + 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() != pointsCount ) + 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 b1f9d51ef..ef771fa71 100644 --- a/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h +++ b/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h @@ -1,15 +1,19 @@ /*************************************************************************** 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 { @@ -21,16 +25,267 @@ template< typename MeshFunction, typename Mesh = typename MeshFunction::MeshType > class DistributedMeshSynchronizer { - public: +public: + // TODO: generalize + static constexpr int EntityDimension = MeshFunction::getEntitiesDimension(); + static_assert( EntityDimension == 0 || EntityDimension == MeshFunction::getMeshDimension(), + "Synchronization for entities of the specified dimension is not implemented yet." ); + + using RealType = typename MeshFunction::RealType; + using DeviceType = typename MeshFunction::DeviceType; + using IndexType = typename MeshFunction::IndexType; + using CommunicatorType = Communicators::MpiCommunicator; + + DistributedMeshSynchronizer() = default; - // FIXME: clang does not like this (incomplete type error) -// typedef typename MeshFunctionType::DistributedMeshType DistributedMeshType; + void initialize( const Mesh& mesh ) + { + static_assert( std::is_same< typename Mesh::CommunicatorType, CommunicatorType >::value, + "Mesh::CommunnicatorType does not match" ); + localEntitiesCount = mesh.getLocalMesh().template getEntitiesCount< EntityDimension >(); - template< typename DistributedMeshType > - void setDistributedGrid( DistributedMeshType *distributedGrid ) + // 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 IndexType ownStart = mesh.template getGlobalIndices< EntityDimension >()[ 0 ]; + Containers::Array< IndexType, DeviceType > offsets( nproc ); { - throw Exceptions::NotImplementedError("Distributed version of this mesh type is not implemented."); + Containers::Array< IndexType, DeviceType > sendbuf( nproc ); + sendbuf.setValue( ownStart ); + CommunicatorType::Alltoall( sendbuf.getData(), 1, + offsets.getData(), 1, + group ); + } + + auto getOwner = [&] ( IndexType idx ) + { + // TODO: is there a more efficient way? + for( int i = 0; i < nproc - 1; i++ ) + if( offsets[ i ] <= idx && idx < offsets[ i + 1 ] ) + return i; + return nproc - 1; + }; + + // TODO: initialization of the distributed mesh should set the ranges for + // local and ghost entities so we can iterate just over the ghost + // entities + // FIXME: the local vertices are not contiguous !!! + // count local ghost entities for each rank + Containers::Array< IndexType, DeviceType > localGhostCounts( nproc ); + localGhostCounts.setValue( 0 ); + IndexType firstGhost = 0; + bool ghost_found = false; + for( IndexType local_idx = 0; local_idx < localEntitiesCount; local_idx++ ) { + const IndexType global_idx = mesh.template getGlobalIndices< EntityDimension >()[ local_idx ]; + const bool isNeighbor = mesh.getLocalMesh().template isGhostEntity< EntityDimension >( local_idx ); + if( ! isNeighbor ) { + // TODO: this is just for testing/debugging + if( ghost_found ) + std::cerr << "ERROR: global indices of local entities are not contiguous (local index " << local_idx << " is after a ghost entity has been found)" << std::endl; + ++firstGhost; + } + else { + const int owner = getOwner( global_idx ); + if( owner != rank ) { + ++localGhostCounts[ owner ]; + ghost_found = true; + } + } + } + + // exchange the ghost counts + ghostEntitiesCounts.setDimensions( nproc, nproc ); + { + Matrices::DenseMatrix< IndexType, 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.getElement( i ) ); + CommunicatorType::Alltoall( &sendbuf(0, 0), nproc, + &ghostEntitiesCounts(0, 0), nproc, + group ); + } + + // allocate ghost offsets + ghostOffsets.setSize( nproc ); + + // 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 and send buffers + ghostNeighbors.setSize( ghostNeighborOffsets[ nproc ] ); + sendBuffers.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 + for( int i = 0; i < nproc; i++ ) { + if( ghostEntitiesCounts( rank, i ) > 0 ) { + requests.push_back( CommunicatorType::ISend( + mesh.template getGlobalIndices< EntityDimension >().getData() + firstGhost, + ghostEntitiesCounts( rank, i ), + i, 0, group ) ); + // update ghost offsets + ghostOffsets[ i ] = firstGhost; + firstGhost += ghostEntitiesCounts( rank, i ); + } + else + ghostOffsets[ i ] = 0; + } + + // 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; + } + +#if 0 + CommunicatorType::Barrier(); + for( int i = 0; i < nproc; i++ ) { + if( i == rank ) { + std::cout << "rank = " << rank << "\n"; +// std::cout << "offsets = " << offsets << std::endl; + std::cout << "local ghost counts = " << localGhostCounts << "\n"; + std::cout << "ghost entities counts matrix:\n" << ghostEntitiesCounts; + std::cout << "global indices = " << mesh.template getGlobalIndices< EntityDimension >() << "\n"; + std::cout << "ghost offsets = " << ghostOffsets << "\n"; + std::cout << "ghost neighbors = " << ghostNeighbors << "\n"; + std::cout.flush(); + } + CommunicatorType::Barrier(); } +#endif + } + + void synchronize( MeshFunction& function ) + { + TNL_ASSERT_EQ( function.getData().getSize(), localEntitiesCount, + "The mesh function does not have the expected size." ); + + // 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 ); + + // 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( + function.getData().getData() + ghostOffsets[ j ], + ghostEntitiesCounts( rank, j ), + j, 0, group ) ); + } + } + + for( int i = 0; i < nproc; i++ ) { + if( ghostEntitiesCounts( i, rank ) > 0 ) { + const IndexType offset = ghostNeighborOffsets[ i ]; + // copy data to send buffers + for( IndexType k = 0; k < ghostEntitiesCounts( i, rank ); k++ ) + sendBuffers[ offset + k ] = function.getData()[ ghostNeighbors[ offset + k ] ]; + + // issue async send operation + requests.push_back( CommunicatorType::ISend( + sendBuffers.getData() + ghostNeighborOffsets[ i ], + ghostEntitiesCounts( i, rank ), + i, 0, group ) ); + } + } + + // wait for all communications to finish + CommunicatorType::WaitAll( requests.data(), requests.size() ); + } + +protected: + // count of local entities (including ghosts) - used only for asserts in the + // synchronize method + IndexType localEntitiesCount = 0; + + // GOTCHA (see above) + int gpu_id = 0; + + // communication group taken from the distributed mesh + typename CommunicatorType::CommunicationGroup group; + + /** + * 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< IndexType, Devices::Host, int > ghostEntitiesCounts; + + /** + * Ghost offsets: the i-th value is the local index of the first ghost + * entity owned by the i-th rank. All ghost entities owned by the i-th + * rank are assumed to be indexed contiguously in the local mesh. + */ + Containers::Array< IndexType, DeviceType, IndexType > 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< IndexType, 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< IndexType, DeviceType, IndexType > ghostNeighbors; + + /** + * Send buffers: array for buffering the mesh function values which will be + * sent to other ranks. 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< RealType, DeviceType, IndexType > sendBuffers; }; } // namespace DistributedMeshes diff --git a/src/TNL/Meshes/DistributedMeshes/GlobalIndexStorage.h b/src/TNL/Meshes/DistributedMeshes/GlobalIndexStorage.h new file mode 100644 index 000000000..7412b79b2 --- /dev/null +++ b/src/TNL/Meshes/DistributedMeshes/GlobalIndexStorage.h @@ -0,0 +1,43 @@ +/*************************************************************************** + DistributedMesh.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 + +namespace TNL { +namespace Meshes { +namespace DistributedMeshes { + +template< typename Mesh, int Dimension > +class GlobalIndexStorage +{ +public: + using GlobalIndexArray = typename Mesh::GlobalIndexArray; + + const GlobalIndexArray& + getGlobalIndices() const + { + return globalIndices; + } + + GlobalIndexArray& + getGlobalIndices() + { + return globalIndices; + } + +protected: + GlobalIndexArray globalIndices; +}; + +} // namespace DistributedMeshes +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/DistributedMeshes/loadDistributedMesh.h b/src/TNL/Meshes/DistributedMeshes/loadDistributedMesh.h index 230d5a7b5..e54048a7c 100644 --- a/src/TNL/Meshes/DistributedMeshes/loadDistributedMesh.h +++ b/src/TNL/Meshes/DistributedMeshes/loadDistributedMesh.h @@ -11,11 +11,70 @@ #pragma once #include +#include +#include #include namespace TNL { namespace Meshes { +template< typename ConfigTag, + typename Device, + typename Functor > +bool +resolveDistributedMeshType( const String& fileName, + Functor&& functor ) +{ + 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) } ); + }; + std::cout << "Detecting distributed mesh from file " << fileName << " ..." << std::endl; + if( fileName.endsWith( ".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 { + std::cerr << "File '" << fileName << "' has unknown extension. Supported extensions are '.pvtu'." << std::endl; + return false; + } +} + +template< typename ConfigTag, + typename Device, + typename Functor > +bool +resolveAndLoadDistributedMesh( const String& fileName, + Functor&& functor ) +{ + 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 >( fileName, wrapper ); +} + template< typename CommunicatorType, typename MeshConfig, typename Device > @@ -24,8 +83,18 @@ loadDistributedMesh( const String& fileName, Mesh< MeshConfig, Device >& mesh, DistributedMeshes::DistributedMesh< Mesh< MeshConfig, Device > >& distributedMesh ) { - std::cerr << "Distributed Mesh is not supported yet, only Distributed Grid is supported."; - return false; + // TODO: simplify interface, pass only the distributed mesh + TNL_ASSERT_EQ( &mesh, &distributedMesh.getLocalMesh(), "mesh is not local mesh of the distributed mesh" ); + + if( fileName.endsWith( ".pvtu" ) ) { + Readers::PVTUReader reader( fileName ); + reader.loadMesh( distributedMesh ); + return true; + } + else { + std::cerr << "The file has an unsupported extension: " << fileName << ". Only .pvtu files can be loaded into the distributed mesh." << std::endl; + return false; + } } template< typename Problem, @@ -101,6 +170,5 @@ decomposeMesh( const Config::ParameterContainer& parameters, return true; } - } // namespace Meshes } // namespace TNL -- GitLab From 902807279f1316ae8f9db9e0e3e5c45b765cfcc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sat, 18 Apr 2020 15:01:39 +0200 Subject: [PATCH 47/73] Refactored PVTUWriter from tnl-decompose-mesh into its own class --- CMakeLists.txt | 4 + src/TNL/Meshes/Writers/PVTUWriter.h | 107 +++++++++++ src/TNL/Meshes/Writers/PVTUWriter.hpp | 255 ++++++++++++++++++++++++++ src/Tools/tnl-decompose-mesh.cpp | 72 ++------ 4 files changed, 384 insertions(+), 54 deletions(-) create mode 100644 src/TNL/Meshes/Writers/PVTUWriter.h create mode 100644 src/TNL/Meshes/Writers/PVTUWriter.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index fa92f1f85..7d2749253 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/src/TNL/Meshes/Writers/PVTUWriter.h b/src/TNL/Meshes/Writers/PVTUWriter.h new file mode 100644 index 000000000..8ef4d2b7b --- /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 000000000..71e19da1d --- /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/Tools/tnl-decompose-mesh.cpp b/src/Tools/tnl-decompose-mesh.cpp index 90c59af22..0231b66ab 100644 --- a/src/Tools/tnl-decompose-mesh.cpp +++ b/src/Tools/tnl-decompose-mesh.cpp @@ -10,10 +10,10 @@ // Implemented by: Jakub Klinkovský -#include #include #include #include +#include #include @@ -549,15 +549,24 @@ struct DecomposeMesh for( unsigned p = 1; p < nparts; p++ ) points_offsets[p] = points_offsets[p-1] + points_counts[p-1]; - // prepare output file name - const String outputFile = parameters.template getParameter< String >( "output-file" ); - FileName outputFileName( removeFileNameExtension( outputFile ) + ".subdomain", "vtu" ); - outputFileName.setDigitsCount( std::floor(std::log10( nparts - 1 )) + 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++ ) { - outputFileName.setIndex( p ); - std::cout << outputFileName.getFileName() << std::endl; + 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. @@ -673,7 +682,7 @@ struct DecomposeMesh // write the subdomain using Writer = Meshes::Writers::VTUWriter< Mesh >; - std::ofstream file( outputFileName.getFileName() ); + std::ofstream file( outputFileName ); Writer writer( file ); writer.template writeEntities< Mesh::getMeshDimension() >( subdomain ); if( ghost_levels > 0 ) { @@ -684,51 +693,6 @@ struct DecomposeMesh } } - // write a .pvtu file - std::ofstream file( outputFile.getString() ); - // TODO: refactor into a PVTU writer - using HeaderType = std::uint64_t; - using namespace Meshes; - file << "\n"; - file << "\n"; - file << "\n"; - file << "\n"; - file << "\n"; - file << "\n"; - if( ghost_levels > 0 ) { - // the PointData and CellData from the individual files should be added here - file << "\n"; - file << "\n"; - file << "\n"; - file << "\n"; - file << "\n"; - file << "\n"; - file << "\n"; - file << "\n"; - } - - for( unsigned p = 0; p < nparts; p++ ) { - outputFileName.setIndex( p ); - // make sure the source path is relative to the main .pvtu file - // TODO: use proper path library - const auto parts = outputFileName.getFileName().split('/'); - const std::string basename = parts.back(); - file << "\n"; - } - - file << "\n"; - file << "\n"; - return true; } }; @@ -745,7 +709,7 @@ int main( int argc, char* argv[] ) const String inputFileName = parameters.getParameter< String >( "input-file" ); const String outputFile = parameters.template getParameter< String >( "output-file" ); - if( getFileExtension( outputFile ) != "pvtu" ) { + if( ! outputFile.endsWith( ".pvtu" ) ) { std::cerr << "Error: the output file must have a '.pvtu' extension." << std::endl; return EXIT_FAILURE; } -- GitLab From cd1e5102aab33befc3bbc5b20e86a5811565f578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sat, 18 Apr 2020 15:48:37 +0200 Subject: [PATCH 48/73] PVTUReader: use std::experimental::filesystem library --- src/TNL/Meshes/Readers/PVTUReader.h | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/TNL/Meshes/Readers/PVTUReader.h b/src/TNL/Meshes/Readers/PVTUReader.h index 5cc98bf08..f33bb948d 100644 --- a/src/TNL/Meshes/Readers/PVTUReader.h +++ b/src/TNL/Meshes/Readers/PVTUReader.h @@ -12,6 +12,8 @@ #pragma once +#include + #include #include #include @@ -26,17 +28,8 @@ class PVTUReader std::string getSourcePath( std::string source ) { - // TODO: use proper path library - std::vector< String > parts = String( fileName ).split( '/' ); - parts.pop_back(); - parts.push_back( source ); - std::string result; - for( auto p : parts ) - if( result.empty() ) - result += p; - else - result += "/" + p; - return result; + namespace fs = std::experimental::filesystem; + return fs::path(fileName).parent_path() / source; } #ifdef HAVE_TINYXML2 -- GitLab From fae134f67476cac53ded3f15b372a799966be14e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sun, 19 Apr 2020 09:54:46 +0200 Subject: [PATCH 49/73] Added support for manual override of the automatically detected mesh file format --- .../DistributedMeshes/loadDistributedMesh.h | 70 ++++++++++++----- src/TNL/Meshes/TypeResolver/TypeResolver.h | 25 ++++-- .../Meshes/TypeResolver/TypeResolver_impl.h | 78 ++++++++++++++----- .../Solvers/PDE/TimeDependentPDESolver_impl.h | 5 +- .../PDE/TimeIndependentPDESolver_impl.h | 5 +- src/TNL/Solvers/SolverConfig_impl.h | 3 +- src/TNL/Solvers/SolverInitiator_impl.h | 3 +- src/Tools/tnl-decompose-mesh.cpp | 4 +- src/Tools/tnl-grid-to-mesh.cpp | 2 +- src/Tools/tnl-mesh-converter.cpp | 10 ++- src/Tools/tnl-view.cpp | 4 +- src/Tools/tnl-view.h | 5 +- 12 files changed, 153 insertions(+), 61 deletions(-) diff --git a/src/TNL/Meshes/DistributedMeshes/loadDistributedMesh.h b/src/TNL/Meshes/DistributedMeshes/loadDistributedMesh.h index e54048a7c..135e3c15a 100644 --- a/src/TNL/Meshes/DistributedMeshes/loadDistributedMesh.h +++ b/src/TNL/Meshes/DistributedMeshes/loadDistributedMesh.h @@ -10,6 +10,8 @@ #pragma once +#include + #include #include #include @@ -22,17 +24,29 @@ template< typename ConfigTag, typename Device, typename Functor > bool -resolveDistributedMeshType( const String& fileName, - Functor&& functor ) +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) } ); }; - std::cout << "Detecting distributed mesh from file " << fileName << " ..." << std::endl; - if( fileName.endsWith( ".pvtu" ) ) { + + 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. @@ -47,7 +61,11 @@ resolveDistributedMeshType( const String& fileName, } } else { - std::cerr << "File '" << fileName << "' has unknown extension. Supported extensions are '.pvtu'." << 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 'pvtu'." << std::endl; return false; } } @@ -56,8 +74,9 @@ template< typename ConfigTag, typename Device, typename Functor > bool -resolveAndLoadDistributedMesh( const String& fileName, - Functor&& functor ) +resolveAndLoadDistributedMesh( Functor&& functor, + const std::string& fileName, + const std::string& fileFormat = "auto" ) { auto wrapper = [&]( Readers::MeshReader& reader, auto&& mesh ) -> bool { @@ -72,27 +91,41 @@ resolveAndLoadDistributedMesh( const String& fileName, } return functor( reader, std::forward(mesh) ); }; - return resolveDistributedMeshType< ConfigTag, Device >( fileName, wrapper ); + return resolveDistributedMeshType< ConfigTag, Device >( wrapper, fileName, fileFormat ); } template< typename CommunicatorType, typename MeshConfig, typename Device > bool -loadDistributedMesh( const String& fileName, - Mesh< MeshConfig, Device >& mesh, - DistributedMeshes::DistributedMesh< Mesh< MeshConfig, Device > >& distributedMesh ) +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" ); - if( fileName.endsWith( ".pvtu" ) ) { + 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 { - std::cerr << "The file has an unsupported extension: " << fileName << ". Only .pvtu files can be loaded into the distributed mesh." << 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 'pvtu'." << std::endl; return false; } } @@ -102,7 +135,7 @@ template< typename Problem, typename Device > bool decomposeMesh( const Config::ParameterContainer& parameters, - const String& prefix, + const std::string& prefix, Mesh< MeshConfig, Device >& mesh, DistributedMeshes::DistributedMesh< Mesh< MeshConfig, Device > >& distributedMesh, Problem& problem ) @@ -118,9 +151,10 @@ template< typename CommunicatorType, typename Device, typename Index > bool -loadDistributedMesh( const String& fileName, - Grid< Dimension, Real, Device, Index >& mesh, - DistributedMeshes::DistributedMesh< Grid< Dimension, Real, Device, Index > > &distributedMesh ) +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; @@ -149,7 +183,7 @@ template< typename Problem, typename Index > bool decomposeMesh( const Config::ParameterContainer& parameters, - const String& prefix, + const std::string& prefix, Grid< Dimension, Real, Device, Index >& mesh, DistributedMeshes::DistributedMesh< Grid< Dimension, Real, Device, Index > > &distributedMesh, Problem& problem ) diff --git a/src/TNL/Meshes/TypeResolver/TypeResolver.h b/src/TNL/Meshes/TypeResolver/TypeResolver.h index 58a10ea26..a4002eaae 100644 --- a/src/TNL/Meshes/TypeResolver/TypeResolver.h +++ b/src/TNL/Meshes/TypeResolver/TypeResolver.h @@ -33,7 +33,10 @@ namespace Meshes { template< typename ConfigTag, typename Device, typename Functor > -bool resolveMeshType( const String& fileName, Functor&& 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 @@ -51,7 +54,10 @@ bool resolveMeshType( const String& fileName, Functor&& functor ); template< typename ConfigTag, typename Device, typename Functor > -bool resolveAndLoadMesh( const String& fileName, Functor&& 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 @@ -64,21 +70,24 @@ bool resolveAndLoadMesh( const String& fileName, Functor&& functor ); template< typename MeshConfig, typename Device > bool -loadMesh( const String& fileName, - Mesh< MeshConfig, Device >& mesh ); +loadMesh( Mesh< MeshConfig, Device >& mesh, + const std::string& fileName, + const std::string& fileFormat = "auto" ); template< typename MeshConfig > bool -loadMesh( const String& fileName, - Mesh< MeshConfig, Devices::Cuda >& mesh ); +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( const String& fileName, - Grid< Dimension, Real, Device, Index >& grid ); +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 a3b0e3a90..45ba4ca18 100644 --- a/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h +++ b/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h @@ -10,6 +10,8 @@ #pragma once +#include + #include #include #include @@ -24,11 +26,24 @@ namespace Meshes { template< typename ConfigTag, typename Device, typename Functor > -bool resolveMeshType( const String& fileName, Functor&& functor ) +bool +resolveMeshType( Functor&& functor, + const std::string& fileName, + const std::string& fileFormat ) { 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( fileName.endsWith( ".tnl" ) ) { + if( format == "tnl" ) { Readers::TNLReader reader( fileName ); if( ! reader.detectMesh() ) return false; @@ -41,7 +56,7 @@ bool resolveMeshType( const String& fileName, Functor&& functor ) return false; } } - else if( fileName.endsWith( ".ng" ) ) { + 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. @@ -54,7 +69,7 @@ bool resolveMeshType( const String& fileName, Functor&& functor ) return false; } } - else if( fileName.endsWith( ".vtk" ) ) { + 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. @@ -67,7 +82,7 @@ bool resolveMeshType( const String& fileName, Functor&& functor ) return false; } } - else if( fileName.endsWith( ".vtu" ) ) { + 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. @@ -81,7 +96,11 @@ bool resolveMeshType( const String& fileName, Functor&& functor ) } } else { - std::cerr << "File '" << fileName << "' has unknown extension. Supported extensions are '.tnl', '.vtk', '.vtu' 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; } } @@ -89,7 +108,10 @@ bool resolveMeshType( const String& fileName, Functor&& functor ) template< typename ConfigTag, typename Device, typename Functor > -bool resolveAndLoadMesh( const String& fileName, Functor&& functor ) +bool +resolveAndLoadMesh( Functor&& functor, + const std::string& fileName, + const std::string& fileFormat ) { auto wrapper = [&]( auto& reader, auto&& mesh ) -> bool { @@ -104,34 +126,48 @@ bool resolveAndLoadMesh( const String& fileName, Functor&& functor ) } return functor( reader, std::forward(mesh) ); }; - return resolveMeshType< ConfigTag, Device >( fileName, wrapper ); + return resolveMeshType< ConfigTag, Device >( wrapper, fileName, fileFormat ); } template< typename MeshConfig, typename Device > bool -loadMesh( const String& fileName, - Mesh< MeshConfig, Device >& mesh ) +loadMesh( Mesh< MeshConfig, Device >& mesh, + const std::string& fileName, + const std::string& fileFormat ) { 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( fileName.endsWith( ".tnl" ) ) + if( format == "tnl" ) mesh.load( fileName ); - else if( fileName.endsWith( ".ng" ) ) { + else if( format == "ng" ) { Readers::NetgenReader reader( fileName ); reader.loadMesh( mesh ); } - else if( fileName.endsWith( ".vtk" ) ) { + else if( format == "vtk" ) { Readers::VTKReader reader( fileName ); reader.loadMesh( mesh ); } - else if( fileName.endsWith( ".vtu" ) ) { + else if( format == "vtu" ) { Readers::VTUReader reader( fileName ); reader.loadMesh( mesh ); } else { - std::cerr << "File '" << fileName << "' has unknown extension. Supported extensions are '.tnl', '.vtk', '.vtu' 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; } } @@ -145,11 +181,12 @@ loadMesh( const String& fileName, template< typename MeshConfig > bool -loadMesh( const String& fileName, - Mesh< MeshConfig, Devices::Cuda >& mesh ) +loadMesh( Mesh< MeshConfig, Devices::Cuda >& mesh, + const std::string& fileName, + const std::string& fileFormat ) { Mesh< MeshConfig, Devices::Host > hostMesh; - if( ! loadMesh( fileName, hostMesh ) ) + if( ! loadMesh( hostMesh, fileName, fileFormat ) ) return false; mesh = hostMesh; return true; @@ -161,8 +198,9 @@ template< int Dimension, typename Device, typename Index > bool -loadMesh( const String& fileName, - Grid< Dimension, Real, Device, Index >& grid ) +loadMesh( Grid< Dimension, Real, Device, Index >& grid, + const std::string& fileName, + const std::string& fileFormat ) { std::cout << "Loading a grid from the file " << fileName << "..." << std::endl; try { diff --git a/src/TNL/Solvers/PDE/TimeDependentPDESolver_impl.h b/src/TNL/Solvers/PDE/TimeDependentPDESolver_impl.h index 3e56dcd25..46ffa6fea 100644 --- a/src/TNL/Solvers/PDE/TimeDependentPDESolver_impl.h +++ b/src/TNL/Solvers/PDE/TimeDependentPDESolver_impl.h @@ -60,15 +60,16 @@ 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( Problem::CommunicatorType::isDistributed() ) { - if( ! Meshes::loadDistributedMesh< typename Problem::CommunicatorType >( meshFile, *this->meshPointer, distributedMesh ) ) + 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( meshFile, *this->meshPointer ) ) + if( ! Meshes::loadMesh( *this->meshPointer, meshFile, meshFileFormat ) ) return false; } diff --git a/src/TNL/Solvers/PDE/TimeIndependentPDESolver_impl.h b/src/TNL/Solvers/PDE/TimeIndependentPDESolver_impl.h index 28adf8419..455682e2b 100644 --- a/src/TNL/Solvers/PDE/TimeIndependentPDESolver_impl.h +++ b/src/TNL/Solvers/PDE/TimeIndependentPDESolver_impl.h @@ -51,15 +51,16 @@ 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( Problem::CommunicatorType::isDistributed() ) { - if( ! Meshes::loadDistributedMesh< typename Problem::CommunicatorType >( meshFile, *this->meshPointer, distributedMesh ) ) + 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( meshFile, *this->meshPointer ) ) + if( ! Meshes::loadMesh( *this->meshPointer, meshFile, meshFileFormat ) ) return false; } diff --git a/src/TNL/Solvers/SolverConfig_impl.h b/src/TNL/Solvers/SolverConfig_impl.h index 3c21a7b23..16d106a90 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 561d47cf4..16e0fd222 100644 --- a/src/TNL/Solvers/SolverInitiator_impl.h +++ b/src/TNL/Solvers/SolverInitiator_impl.h @@ -225,11 +225,12 @@ class SolverInitiatorMeshResolver< ProblemSetter, Real, Device, Index, ConfigTag static bool run( const Config::ParameterContainer& parameters ) { const String& meshFileName = parameters.getParameter< String >( "mesh" ); + 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 >( meshFileName, wrapper ); + return Meshes::resolveMeshType< ConfigTag, Device >( wrapper, meshFileName, meshFileFormat ); } }; diff --git a/src/Tools/tnl-decompose-mesh.cpp b/src/Tools/tnl-decompose-mesh.cpp index 0231b66ab..57ddb1740 100644 --- a/src/Tools/tnl-decompose-mesh.cpp +++ b/src/Tools/tnl-decompose-mesh.cpp @@ -120,6 +120,7 @@ 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 ); @@ -708,6 +709,7 @@ int main( int argc, char* argv[] ) 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; @@ -719,5 +721,5 @@ int main( int argc, char* argv[] ) using MeshType = std::decay_t< decltype(mesh) >; return DecomposeMesh< MeshType >::run( std::forward(mesh), parameters ); }; - return ! Meshes::resolveAndLoadMesh< DecomposeMeshConfigTag, Devices::Host >( inputFileName, wrapper ); + return ! Meshes::resolveAndLoadMesh< DecomposeMeshConfigTag, Devices::Host >( wrapper, inputFileName, inputFileFormat ); } diff --git a/src/Tools/tnl-grid-to-mesh.cpp b/src/Tools/tnl-grid-to-mesh.cpp index b976a238b..b3bd9ea93 100644 --- a/src/Tools/tnl-grid-to-mesh.cpp +++ b/src/Tools/tnl-grid-to-mesh.cpp @@ -205,5 +205,5 @@ main( int argc, char* argv[] ) { return convertGrid( grid, fileName, outputFileName ); }; - return ! Meshes::resolveMeshType< GridToMeshConfigTag, Devices::Host >( fileName, wrapper ); + return ! Meshes::resolveMeshType< GridToMeshConfigTag, Devices::Host >( wrapper, fileName ); } diff --git a/src/Tools/tnl-mesh-converter.cpp b/src/Tools/tnl-mesh-converter.cpp index 2dedccae3..8049e2a23 100644 --- a/src/Tools/tnl-mesh-converter.cpp +++ b/src/Tools/tnl-mesh-converter.cpp @@ -151,8 +151,9 @@ 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" ); @@ -170,12 +171,13 @@ int main( int argc, char* argv[] ) 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" ); + const String outputFileFormat = parameters.getParameter< String >( "output-file-format" ); auto wrapper = [&] ( auto& reader, auto&& mesh ) -> bool { - return convertMesh( mesh, inputFileName, outputFileName, outputFormat ); + return convertMesh( mesh, inputFileName, outputFileName, outputFileFormat ); }; - return ! Meshes::resolveAndLoadMesh< MeshConverterConfigTag, Devices::Host >( inputFileName, wrapper ); + return ! Meshes::resolveAndLoadMesh< MeshConverterConfigTag, Devices::Host >( wrapper, inputFileName, inputFileFormat ); } diff --git a/src/Tools/tnl-view.cpp b/src/Tools/tnl-view.cpp index 0f048e78f..97cfa7a97 100644 --- a/src/Tools/tnl-view.cpp +++ b/src/Tools/tnl-view.cpp @@ -60,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 ); @@ -85,10 +86,11 @@ int main( int argc, char* argv[] ) return EXIT_FAILURE; const String meshFile = parameters.getParameter< String >( "mesh" ); + 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 >( meshFile, wrapper ); + 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 afa66201d..bfda4f960 100644 --- a/src/Tools/tnl-view.h +++ b/src/Tools/tnl-view.h @@ -315,11 +315,12 @@ template< typename Mesh > bool processFiles( const Config::ParameterContainer& parameters ) { int verbose = parameters.getParameter< int >( "verbose"); - String meshFile = parameters.getParameter< String >( "mesh" ); + 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( meshFile, *meshPointer ) ) + if( ! Meshes::loadMesh( *meshPointer, meshFile, meshFileFormat ) ) return false; bool checkOutputFile = parameters.getParameter< bool >( "check-output-file" ); -- GitLab From 99cf6c69213e0a2bdf094e7fd21598c498c3e771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sun, 10 May 2020 10:33:20 +0200 Subject: [PATCH 50/73] Replaced multimaps in Mesh with segments-based binary matrices --- src/TNL/Matrices/MatrixPermutationApplier.h | 97 ++++++++++++++++ src/TNL/Meshes/Mesh.h | 4 +- src/TNL/Meshes/Mesh.hpp | 32 ++++-- .../MeshDetails/IndexPermutationApplier.h | 23 ++-- .../initializer/EntityInitializer.h | 104 +++++++++++------- .../MeshDetails/initializer/Initializer.h | 59 +++++++--- .../initializer/SubentitySeedsCreator.h | 8 +- .../MeshDetails/layers/DualGraphLayer.h | 11 +- .../Meshes/MeshDetails/layers/StorageLayer.h | 76 ++++++++++--- .../layers/SubentityStorageLayer.h | 67 ++++++----- .../layers/SuperentityStorageLayer.h | 91 +++++++++------ .../MeshDetails/traits/MeshSubentityTraits.h | 8 -- .../traits/MeshSuperentityTraits.h | 7 -- .../Meshes/MeshDetails/traits/MeshTraits.h | 21 +++- src/UnitTests/Meshes/MeshTest.h | 18 +-- 15 files changed, 429 insertions(+), 197 deletions(-) create mode 100644 src/TNL/Matrices/MatrixPermutationApplier.h diff --git a/src/TNL/Matrices/MatrixPermutationApplier.h b/src/TNL/Matrices/MatrixPermutationApplier.h new file mode 100644 index 000000000..91cfbe54f --- /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/Mesh.h b/src/TNL/Meshes/Mesh.h index 754ed61d5..f783781f5 100644 --- a/src/TNL/Meshes/Mesh.h +++ b/src/TNL/Meshes/Mesh.h @@ -210,8 +210,8 @@ class Mesh // Methods for the mesh initializer using StorageBaseType::getPoints; using StorageBaseType::setEntitiesCount; - using StorageBaseType::getSubentityStorageNetwork; - using StorageBaseType::getSuperentityStorageNetwork; + using StorageBaseType::getSubentitiesMatrix; + using StorageBaseType::getSuperentitiesMatrix; friend Initializer< MeshConfig >; diff --git a/src/TNL/Meshes/Mesh.hpp b/src/TNL/Meshes/Mesh.hpp index 87cbe0c86..e712af13a 100644 --- a/src/TNL/Meshes/Mesh.hpp +++ b/src/TNL/Meshes/Mesh.hpp @@ -175,7 +175,7 @@ constexpr typename Mesh< MeshConfig, Device >::LocalIndexType Mesh< MeshConfig, Device >:: getSubentitiesCount( const GlobalIndexType entityIndex ) const { - return this->template getSubentityStorageNetwork< EntityDimension, SubentityDimension >().getValuesCount( entityIndex ); + return StorageBaseType::template getSubentitiesCount< EntityDimension, SubentityDimension >(); } template< typename MeshConfig, typename Device > @@ -185,7 +185,9 @@ typename Mesh< MeshConfig, Device >::GlobalIndexType Mesh< MeshConfig, Device >:: getSubentityIndex( const GlobalIndexType entityIndex, const LocalIndexType subentityIndex ) const { - return this->template getSubentityStorageNetwork< EntityDimension, SubentityDimension >().getValue( entityIndex, subentityIndex ); + 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 > @@ -195,7 +197,7 @@ typename Mesh< MeshConfig, Device >::LocalIndexType Mesh< MeshConfig, Device >:: getSuperentitiesCount( const GlobalIndexType entityIndex ) const { - return this->template getSuperentityStorageNetwork< EntityDimension, SuperentityDimension >().getValuesCount( entityIndex ); + return this->template getSuperentitiesCountsArray< EntityDimension, SuperentityDimension >()[ entityIndex ]; } template< typename MeshConfig, typename Device > @@ -205,7 +207,9 @@ typename Mesh< MeshConfig, Device >::GlobalIndexType Mesh< MeshConfig, Device >:: getSuperentityIndex( const GlobalIndexType entityIndex, const LocalIndexType superentityIndex ) const { - return this->template getSuperentityStorageNetwork< EntityDimension, SuperentityDimension >().getValue( entityIndex, superentityIndex ); + 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 > @@ -229,7 +233,9 @@ getCellNeighborIndex( const GlobalIndexType cellIndex, const LocalIndexType neig "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." ); - return this->getDualGraph().getValues( cellIndex )[ neighborIndex ]; + 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 ); } @@ -275,9 +281,17 @@ void Mesh< MeshConfig, Device >:: save( File& file ) const { - Object::save( file ); - StorageBaseType::save( file ); - EntityTagsLayerFamily::save( file ); + // 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 > @@ -285,7 +299,7 @@ void Mesh< MeshConfig, Device >:: load( File& file ) { - // loading via host is necessary for the initialization of the dual graph + // 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 ); diff --git a/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h b/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h index c7cd7ceeb..f875fac28 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 { @@ -32,8 +32,8 @@ private: { 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 ); } }; @@ -53,8 +53,9 @@ private: { 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 ); } }; @@ -74,8 +75,8 @@ private: { 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 ); } }; @@ -95,8 +96,8 @@ private: { 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 ); } }; @@ -125,8 +126,8 @@ private: { permuteArray( mesh.getNeighborCounts(), perm ); auto& graph = mesh.getDualGraph(); - Containers::Multimaps::permuteMultimapKeys( graph, perm ); - Containers::Multimaps::permuteMultimapValues( graph, iperm ); + Matrices::permuteMatrixRows( graph, perm ); + Matrices::permuteMatrixColumns( graph, iperm ); } template< typename Mesh_, std::enable_if_t< ! Mesh_::Config::dualGraphStorage(), bool > = true > diff --git a/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h b/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h index fc653dc93..0ee9f2e36 100644 --- a/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h +++ b/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h @@ -65,7 +65,12 @@ class EntityInitializer using InitializerType = Initializer< MeshConfig >; public: - static void initEntity( 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 ) { // 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++ ) @@ -86,7 +91,8 @@ class EntityInitializer< MeshConfig, EntityTopology, false > using SeedType = EntitySeed< MeshConfig, EntityTopology >; using InitializerType = Initializer< MeshConfig >; public: - static void initEntity( const GlobalIndexType& entityIndex, const SeedType& entitySeed, InitializerType& initializer) {} + static void initSubvertexMatrix( const GlobalIndexType entitiesCount, InitializerType& initializer ) {} + static void initEntity( const GlobalIndexType entityIndex, const SeedType& entitySeed, InitializerType& initializer ) {} }; @@ -123,22 +129,24 @@ class EntityInitializerLayer< MeshConfig, using SuperentityTraitsType = typename MeshTraits< MeshConfig >::template EntityTraits< SuperdimensionTag::value >; using SuperentityTopology = typename SuperentityTraitsType::EntityTopology; using SubentitySeedsCreatorType = SubentitySeedsCreator< MeshConfig, SuperdimensionTag, SubdimensionTag >; - using SuperentityStorageNetwork = typename MeshTraits< MeshConfig >::template SuperentityTraits< SubentityTopology, SuperdimensionTag::value >::StorageNetworkType; - using ValuesAllocationVectorType = typename SuperentityStorageNetwork::ValuesAllocationVectorType; + 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; - // counter for superentities of each subentity const GlobalIndexType subentitiesCount = mesh.template getEntitiesCount< SubdimensionTag::value >(); - ValuesAllocationVectorType superentitiesCounts( subentitiesCount ); + 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 < mesh.template getEntitiesCount< SuperdimensionTag::value >(); - superentityIndex++ ) + 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++ ) @@ -150,21 +158,21 @@ public: } // allocate superentities storage - SuperentityStorageNetwork& superentityStorageNetwork = meshInitializer.template meshSuperentityStorageNetwork< SubdimensionTag::value, SuperdimensionTag::value >(); - superentityStorageNetwork.allocate( superentitiesCounts ); + 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 < mesh.template getEntitiesCount< SuperdimensionTag::value >(); - superentityIndex++ ) + 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 ); - superentityStorageNetwork.getValues( subentityIndex ).setValue( superentitiesCounts[ subentityIndex ]++, superentityIndex ); + auto row = matrix.getRow( subentityIndex ); + row.setElement( superentitiesCounts[ subentityIndex ]++, superentityIndex, true ); } } @@ -205,22 +213,24 @@ class EntityInitializerLayer< MeshConfig, using SuperentityTraitsType = typename MeshTraits< MeshConfig >::template EntityTraits< SuperdimensionTag::value >; using SuperentityTopology = typename SuperentityTraitsType::EntityTopology; using SubentitySeedsCreatorType = SubentitySeedsCreator< MeshConfig, SuperdimensionTag, SubdimensionTag >; - using SuperentityStorageNetwork = typename MeshTraits< MeshConfig >::template SuperentityTraits< SubentityTopology, SuperdimensionTag::value >::StorageNetworkType; - using ValuesAllocationVectorType = typename SuperentityStorageNetwork::ValuesAllocationVectorType; + 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; - // counter for superentities of each subentity const GlobalIndexType subentitiesCount = mesh.template getEntitiesCount< SubdimensionTag::value >(); - ValuesAllocationVectorType superentitiesCounts( subentitiesCount ); + 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 < mesh.template getEntitiesCount< SuperdimensionTag::value >(); - superentityIndex++ ) + 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 ); @@ -234,21 +244,21 @@ public: } // allocate superentities storage - SuperentityStorageNetwork& superentityStorageNetwork = meshInitializer.template meshSuperentityStorageNetwork< SubdimensionTag::value, SuperdimensionTag::value >(); - superentityStorageNetwork.allocate( superentitiesCounts ); + 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 < mesh.template getEntitiesCount< SuperdimensionTag::value >(); - superentityIndex++ ) + 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 ); - superentityStorageNetwork.getValues( subentityIndex ).setValue( superentitiesCounts[ subentityIndex ]++, superentityIndex ); + auto row = matrix.getRow( subentityIndex ); + row.setElement( superentitiesCounts[ subentityIndex ]++, superentityIndex, true ); } } @@ -292,6 +302,12 @@ 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++ ) @@ -349,6 +365,12 @@ 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++ ) @@ -399,22 +421,22 @@ class EntityInitializerLayer< MeshConfig, using SuperentityTraitsType = typename MeshTraits< MeshConfig >::template EntityTraits< SuperdimensionTag::value >; using SuperentityTopology = typename SuperentityTraitsType::EntityTopology; using SubentitySeedsCreatorType = SubentitySeedsCreator< MeshConfig, SuperdimensionTag, SubdimensionTag >; - using SuperentityStorageNetwork = typename MeshTraits< MeshConfig >::template SuperentityTraits< SubentityTopology, SuperdimensionTag::value >::StorageNetworkType; - using ValuesAllocationVectorType = typename SuperentityStorageNetwork::ValuesAllocationVectorType; + 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; - // counter for superentities of each subentity const GlobalIndexType subentitiesCount = mesh.template getEntitiesCount< SubdimensionTag::value >(); - ValuesAllocationVectorType superentitiesCounts( subentitiesCount ); + 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 < mesh.template getEntitiesCount< SuperdimensionTag::value >(); - superentityIndex++ ) + 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++ ) @@ -425,20 +447,20 @@ public: } // allocate superentities storage - SuperentityStorageNetwork& superentityStorageNetwork = meshInitializer.template meshSuperentityStorageNetwork< SubdimensionTag::value, SuperdimensionTag::value >(); - superentityStorageNetwork.allocate( superentitiesCounts ); + 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 < mesh.template getEntitiesCount< SuperdimensionTag::value >(); - superentityIndex++ ) + 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 ] ); - superentityStorageNetwork.getValues( subentityIndex ).setValue( superentitiesCounts[ subentityIndex ]++, superentityIndex ); + auto row = matrix.getRow( subentityIndex ); + row.setElement( superentitiesCounts[ subentityIndex ]++, superentityIndex, true ); } } diff --git a/src/TNL/Meshes/MeshDetails/initializer/Initializer.h b/src/TNL/Meshes/MeshDetails/initializer/Initializer.h index b1509fd51..754e82a2c 100644 --- a/src/TNL/Meshes/MeshDetails/initializer/Initializer.h +++ b/src/TNL/Meshes/MeshDetails/initializer/Initializer.h @@ -84,6 +84,7 @@ class Initializer using PointArrayType = typename MeshTraitsType::PointArrayType; using CellSeedArrayType = typename MeshTraitsType::CellSeedArrayType; using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; + using LocalIndexType = typename MeshTraitsType::LocalIndexType; // The points and cellSeeds arrays will be reset when not needed to save memory. @@ -100,42 +101,63 @@ class Initializer BaseType::initEntities( *this, cellSeeds, mesh ); } + template< int Dimension, int Subdimension > + void initSubentityMatrix( const GlobalIndexType entitiesCount, GlobalIndexType subentitiesCount = 0 ) + { + 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 Dimension, int Subdimension, typename LocalIndex, typename GlobalIndex > + template< int Dimension, int Subdimension > void - setSubentityIndex( const GlobalIndex& entityIndex, const LocalIndex& localIndex, const GlobalIndex& globalIndex ) + setSubentityIndex( const GlobalIndexType entityIndex, const LocalIndexType localIndex, const GlobalIndexType globalIndex ) { - mesh->template getSubentityStorageNetwork< Dimension, Subdimension >().getValues( entityIndex )[ localIndex ] = globalIndex; + mesh->template getSubentitiesMatrix< Dimension, Subdimension >().getRow( entityIndex ).setElement( localIndex, globalIndex, true ); } - template< int Dimension, typename GlobalIndex > + template< int Dimension > + auto + getSubvertices( const GlobalIndexType entityIndex ) + -> decltype( this->mesh->template getSubentitiesMatrix< Dimension, 0 >().getRow( 0 ) ) + { + return mesh->template getSubentitiesMatrix< Dimension, 0 >().getRow( entityIndex ); + } + + template< int Dimension, int Superdimension > auto - getSubvertices( const GlobalIndex& entityIndex ) - -> decltype( this->mesh->template getSubentityStorageNetwork< Dimension, 0 >().getValues( 0 ) ) + getSuperentitiesCountsArray() + -> decltype( this->mesh->template getSuperentitiesCountsArray< Dimension, Superdimension >() ) { - // 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< Dimension, 0 >().getValues( entityIndex ); + return mesh->template getSuperentitiesCountsArray< Dimension, Superdimension >(); } template< int Dimension, int Superdimension > auto - meshSuperentityStorageNetwork() - -> decltype( this->mesh->template getSuperentityStorageNetwork< Dimension, Superdimension >() ) + getSuperentitiesMatrix() + -> decltype( this->mesh->template getSuperentitiesMatrix< Dimension, Superdimension >() ) { - return mesh->template getSuperentityStorageNetwork< Dimension, Superdimension >(); + return mesh->template getSuperentitiesMatrix< Dimension, Superdimension >(); } - template< int Dimension, int Subdimension, typename GlobalIndex > + template< int Dimension, int Subdimension > auto - subentityOrientationsArray( const GlobalIndex entityIndex ) + subentityOrientationsArray( const GlobalIndexType entityIndex ) -> decltype( this->mesh->template subentityOrientationsArray< Dimension, Subdimension >( entityIndex ) ) { return mesh->template subentityOrientationsArray< Dimension, Subdimension >( entityIndex ); @@ -143,7 +165,7 @@ class Initializer 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 ); } @@ -180,6 +202,7 @@ class InitializerLayer< MeshConfig, { //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( i, cellSeeds[ i ], initializer ); cellSeeds.reset(); @@ -245,6 +268,7 @@ class InitializerLayer< MeshConfig, //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++ ) @@ -332,6 +356,7 @@ class InitializerLayer< MeshConfig, //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 >; diff --git a/src/TNL/Meshes/MeshDetails/initializer/SubentitySeedsCreator.h b/src/TNL/Meshes/MeshDetails/initializer/SubentitySeedsCreator.h index e849f1827..1e2edb332 100644 --- a/src/TNL/Meshes/MeshDetails/initializer/SubentitySeedsCreator.h +++ b/src/TNL/Meshes/MeshDetails/initializer/SubentitySeedsCreator.h @@ -31,7 +31,7 @@ class SubentitySeedsCreator using LocalIndexType = typename MeshTraitsType::LocalIndexType; using EntityTraitsType = typename MeshTraitsType::template EntityTraits< EntityDimensionTag::value >; 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 SubentityTopology = typename SubentityTraits::SubentityTopology; @@ -68,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 ) ); } }; }; @@ -82,7 +82,7 @@ class SubentitySeedsCreator< MeshConfig, EntityDimensionTag, DimensionTag< 0 > > using LocalIndexType = typename MeshTraitsType::LocalIndexType; using EntityTraitsType = typename MeshTraitsType::template EntityTraits< EntityDimensionTag::value >; 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 >; using SubentityTopology = typename SubentityTraits::SubentityTopology; @@ -93,7 +93,7 @@ public: { 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/layers/DualGraphLayer.h b/src/TNL/Meshes/MeshDetails/layers/DualGraphLayer.h index 06ff35e25..ae775319f 100644 --- a/src/TNL/Meshes/MeshDetails/layers/DualGraphLayer.h +++ b/src/TNL/Meshes/MeshDetails/layers/DualGraphLayer.h @@ -27,7 +27,7 @@ public: using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; using LocalIndexType = typename MeshTraitsType::LocalIndexType; using DualGraph = typename MeshTraitsType::DualGraph; - using NeighborCountsArray = typename DualGraph::ValuesAllocationVectorType; + using NeighborCountsArray = typename MeshTraitsType::NeighborCountsArray; DualGraphLayer() = default; @@ -45,7 +45,6 @@ public: DualGraphLayer& operator=( const DualGraphLayer< MeshConfig, Device_ >& other ) { neighborCounts = other.getNeighborCounts(); - graph.setLike( other.getDualGraph() ); graph = other.getDualGraph(); return *this; } @@ -146,15 +145,15 @@ public: neighborCounts[ k ] = findNeighbors( k ); // allocate adjacency matrix - graph.setKeysRange( cellsCount ); - graph.allocate( neighborCounts ); + graph.setDimensions( cellsCount, cellsCount ); + graph.setRowCapacities( neighborCounts ); // fill in neighbor indices for( GlobalIndexType k = 0; k < cellsCount; k++) { - auto adjncy = graph.getValues( k ); + auto row = graph.getRow( k ); const LocalIndexType nnbrs = findNeighbors( k ); for( LocalIndexType j = 0; j < nnbrs; j++) - adjncy[ j ] = neighbors[ j ]; + row.setElement( j, neighbors[ j ], true ); } } diff --git a/src/TNL/Meshes/MeshDetails/layers/StorageLayer.h b/src/TNL/Meshes/MeshDetails/layers/StorageLayer.h index b00e8caed..f92729532 100644 --- a/src/TNL/Meshes/MeshDetails/layers/StorageLayer.h +++ b/src/TNL/Meshes/MeshDetails/layers/StorageLayer.h @@ -128,58 +128,100 @@ public: template< int Dimension, int Subdimension > __cuda_callable__ - typename MeshTraitsType::template SubentityTraits< typename EntityTraits< Dimension >::EntityTopology, Subdimension >::StorageNetworkType& - getSubentityStorageNetwork() + 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 subentity storage network which is disabled in the mesh configuration." ); + "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 getSubentityStorageNetwork< Subdimension >(); + return BaseType::template getSubentitiesCount< Subdimension >(); } template< int Dimension, int Subdimension > __cuda_callable__ - const typename MeshTraitsType::template SubentityTraits< typename EntityTraits< Dimension >::EntityTopology, Subdimension >::StorageNetworkType& - getSubentityStorageNetwork() const + typename MeshTraitsType::SubentityMatrixType& + getSubentitiesMatrix() { static_assert( Dimension > Subdimension, "Invalid combination of Dimension and Subdimension." ); static_assert( SubentityTraits< Dimension, Subdimension >::storageEnabled, - "You try to get subentity storage network which is disabled in the mesh configuration." ); + "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 getSubentityStorageNetwork< Subdimension >(); + 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::template SuperentityTraits< typename EntityTraits< Dimension >::EntityTopology, Superdimension >::StorageNetworkType& - getSuperentityStorageNetwork() + typename MeshTraitsType::SuperentityMatrixType& + getSuperentitiesMatrix() { static_assert( Dimension < Superdimension, "Invalid combination of Dimension and Superdimension." ); static_assert( SuperentityTraits< Dimension, Superdimension >::storageEnabled, - "You try to get superentity storage network which is disabled in the mesh configuration." ); + "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 getSuperentityStorageNetwork< Superdimension >(); + return BaseType::template getSuperentitiesMatrix< Superdimension >(); } template< int Dimension, int Superdimension > __cuda_callable__ - const typename MeshTraitsType::template SuperentityTraits< typename EntityTraits< Dimension >::EntityTopology, Superdimension >::StorageNetworkType& - getSuperentityStorageNetwork() const + 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 superentity storage network which is disabled in the mesh configuration." ); + "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 getSuperentityStorageNetwork< Superdimension >(); + return BaseType::template getSuperentitiesMatrix< Superdimension >(); } template< int Dimension, int Subdimension > @@ -314,9 +356,7 @@ protected: void setEntitiesCount( DimensionTag, const GlobalIndexType& entitiesCount ) { this->entitiesCount = entitiesCount; - SubentityStorageBaseType::setEntitiesCount( entitiesCount ); SubentityOrientationsBaseType::setEntitiesCount( entitiesCount ); - SuperentityStorageBaseType::setEntitiesCount( entitiesCount ); } GlobalIndexType entitiesCount = 0; diff --git a/src/TNL/Meshes/MeshDetails/layers/SubentityStorageLayer.h b/src/TNL/Meshes/MeshDetails/layers/SubentityStorageLayer.h index e64f944c7..ed84e2b82 100644 --- a/src/TNL/Meshes/MeshDetails/layers/SubentityStorageLayer.h +++ b/src/TNL/Meshes/MeshDetails/layers/SubentityStorageLayer.h @@ -48,20 +48,29 @@ public: protected: template< int Subdimension > __cuda_callable__ - typename MeshTraitsType::template SubentityTraits< EntityTopology, Subdimension >::StorageNetworkType& - getSubentityStorageNetwork() + 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__ - const typename MeshTraitsType::template SubentityTraits< EntityTopology, Subdimension >::StorageNetworkType& - getSubentityStorageNetwork() const + typename MeshTraitsType::SubentityMatrixType& + getSubentitiesMatrix() { static_assert( EntityTopology::dimension > Subdimension, "Invalid combination of Dimension and Subdimension." ); - return BaseType::getSubentityStorageNetwork( DimensionTag< 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 >() ); } }; @@ -78,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; @@ -100,8 +109,7 @@ protected: SubentityStorageLayer& operator=( const SubentityStorageLayer& other ) { BaseType::operator=( other ); - storageNetwork.setLike( other.storageNetwork ); - storageNetwork = other.storageNetwork; + matrix = other.matrix; return *this; } @@ -109,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; } @@ -118,51 +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; + using BaseType::getSubentitiesMatrix; __cuda_callable__ - StorageNetworkType& getSubentityStorageNetwork( SubdimensionTag ) + SubentityMatrixType& getSubentitiesMatrix( SubdimensionTag ) { - return this->storageNetwork; + return matrix; } __cuda_callable__ - const StorageNetworkType& getSubentityStorageNetwork( SubdimensionTag ) const + const SubentityMatrixType& getSubentitiesMatrix( SubdimensionTag ) const { - return this->storageNetwork; + 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_ > @@ -209,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 @@ -221,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/layers/SuperentityStorageLayer.h b/src/TNL/Meshes/MeshDetails/layers/SuperentityStorageLayer.h index 397f111b4..339090de5 100644 --- a/src/TNL/Meshes/MeshDetails/layers/SuperentityStorageLayer.h +++ b/src/TNL/Meshes/MeshDetails/layers/SuperentityStorageLayer.h @@ -54,20 +54,38 @@ public: protected: template< int Superdimension > __cuda_callable__ - typename MeshTraitsType::template SuperentityTraits< EntityTopology, Superdimension >::StorageNetworkType& - getSuperentityStorageNetwork() + 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::template SuperentityTraits< EntityTopology, Superdimension >::StorageNetworkType& - getSuperentityStorageNetwork() const + const typename MeshTraitsType::NeighborCountsArray& + getSuperentitiesCountsArray() const { 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__ + 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 >() ); } }; @@ -79,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 ) @@ -102,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; } @@ -111,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; } @@ -120,50 +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 { - BaseType::setEntitiesCount( entitiesCount ); - this->storageNetwork.setKeysRange( entitiesCount ); + return superentitiesCounts; } - using BaseType::getSuperentityStorageNetwork; + using BaseType::getSuperentitiesMatrix; __cuda_callable__ - StorageNetworkType& getSuperentityStorageNetwork( SuperdimensionTag ) + SuperentityMatrixType& getSuperentitiesMatrix( SuperdimensionTag ) { - return this->storageNetwork; + return matrix; } __cuda_callable__ - const StorageNetworkType& getSuperentityStorageNetwork( SuperdimensionTag ) const + 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_ > @@ -193,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_ > @@ -202,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 {} @@ -214,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/MeshSubentityTraits.h b/src/TNL/Meshes/MeshDetails/traits/MeshSubentityTraits.h index 4e2e37c82..570671931 100644 --- a/src/TNL/Meshes/MeshDetails/traits/MeshSubentityTraits.h +++ b/src/TNL/Meshes/MeshDetails/traits/MeshSubentityTraits.h @@ -19,7 +19,6 @@ #include #include #include -#include namespace TNL { namespace Meshes { @@ -48,13 +47,6 @@ public: using SubentityType = typename MeshEntityTraits< MeshConfig, Device, Dimension >::EntityType; 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; - // orientation and its accessor using OrientationArrayType = Containers::StaticArray< count, Orientation >; using IdPermutationArrayType = Containers::StaticArray< count, LocalIndexType >; diff --git a/src/TNL/Meshes/MeshDetails/traits/MeshSuperentityTraits.h b/src/TNL/Meshes/MeshDetails/traits/MeshSuperentityTraits.h index ca60c250e..4b5e38819 100644 --- a/src/TNL/Meshes/MeshDetails/traits/MeshSuperentityTraits.h +++ b/src/TNL/Meshes/MeshDetails/traits/MeshSuperentityTraits.h @@ -17,7 +17,6 @@ #pragma once #include -#include namespace TNL { namespace Meshes { @@ -39,12 +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 >; }; } // namespace Meshes diff --git a/src/TNL/Meshes/MeshDetails/traits/MeshTraits.h b/src/TNL/Meshes/MeshDetails/traits/MeshTraits.h index 8bfdbe048..1bad55de4 100644 --- a/src/TNL/Meshes/MeshDetails/traits/MeshTraits.h +++ b/src/TNL/Meshes/MeshDetails/traits/MeshTraits.h @@ -18,7 +18,9 @@ #include #include -#include +#include +#include +#include #include #include @@ -31,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 @@ -50,6 +58,7 @@ public: using CellSeedType = EntitySeed< MeshConfig, CellTopology >; using EntityTagType = std::uint8_t; + 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 >; @@ -65,8 +74,14 @@ public: using DimensionTag = Meshes::DimensionTag< meshDimension >; - // TODO: write general operator= for different SliceSize and remove the '32' here - using DualGraph = Containers::Multimaps::EllpackIndexMultimap< GlobalIndexType, DeviceType, GlobalIndexType, 32 >; + // 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/UnitTests/Meshes/MeshTest.h b/src/UnitTests/Meshes/MeshTest.h index 4fbd0ea86..db291c0eb 100644 --- a/src/UnitTests/Meshes/MeshTest.h +++ b/src/UnitTests/Meshes/MeshTest.h @@ -659,10 +659,11 @@ TEST( MeshTest, RegularMeshOfQuadrilateralsTest ) --nnbrs; if( j == 0 || j == ySize - 1 ) --nnbrs; - ASSERT_EQ( mesh.getDualGraph().getValues( cellIdx ).getSize(), nnbrs ); + + EXPECT_EQ( mesh.getCellNeighborsCount( cellIdx ), nnbrs ); std::set< IndexType > neighbors; for( IndexType n = 0; n < nnbrs; n++ ) - neighbors.insert( mesh.getDualGraph().getValues( cellIdx )[ 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 ); @@ -860,10 +861,11 @@ TEST( MeshTest, RegularMeshOfHexahedronsTest ) --nnbrs; if( k == 0 || k == zSize - 1 ) --nnbrs; - ASSERT_EQ( mesh.getDualGraph().getValues( cellIdx ).getSize(), nnbrs ); + + EXPECT_EQ( mesh.getCellNeighborsCount( cellIdx ), nnbrs ); std::set< IndexType > neighbors; for( IndexType n = 0; n < nnbrs; n++ ) - neighbors.insert( mesh.getDualGraph().getValues( cellIdx )[ 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 ); @@ -906,10 +908,10 @@ TEST( MeshTest, RegularMeshOfHexahedronsTest ) if( (j == 0 || j == ySize - 1) && (k == 0 || k == zSize - 1) ) ++nnbrs; - ASSERT_EQ( mesh.getDualGraph().getValues( cellIdx ).getSize(), nnbrs ); + EXPECT_EQ( mesh.getCellNeighborsCount( cellIdx ), nnbrs ); std::set< IndexType > neighbors; for( IndexType n = 0; n < nnbrs; n++ ) - neighbors.insert( mesh.getDualGraph().getValues( cellIdx )[ 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 ); @@ -967,10 +969,10 @@ TEST( MeshTest, RegularMeshOfHexahedronsTest ) if( (i == 0 || i == xSize - 1) && (j == 0 || j == ySize - 1) && (k == 0 || k == zSize - 1) ) --nnbrs; - ASSERT_EQ( mesh.getDualGraph().getValues( cellIdx ).getSize(), nnbrs ); + EXPECT_EQ( mesh.getCellNeighborsCount( cellIdx ), nnbrs ); std::set< IndexType > neighbors; for( IndexType n = 0; n < nnbrs; n++ ) - neighbors.insert( mesh.getDualGraph().getValues( cellIdx )[ 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 ); -- GitLab From 5a51c574a3e4efa8c8c735f9637eaa61754f2d05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Tue, 19 May 2020 11:05:03 +0200 Subject: [PATCH 51/73] Optimized geometry functions - avoiding double-precision constants --- src/TNL/Meshes/Geometry/getEntityMeasure.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/TNL/Meshes/Geometry/getEntityMeasure.h b/src/TNL/Meshes/Geometry/getEntityMeasure.h index ca50453b1..47394f293 100644 --- a/src/TNL/Meshes/Geometry/getEntityMeasure.h +++ b/src/TNL/Meshes/Geometry/getEntityMeasure.h @@ -87,7 +87,8 @@ 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 VectorExpression, @@ -102,7 +103,7 @@ getTriangleArea( const VectorExpression & v1, 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 0.5 * TNL::sqrt( c1 * c1 + c2 * c2 + c3 * c3 ); + return Real( 0.5 ) * TNL::sqrt( c1 * c1 + c2 * c2 + c3 * c3 ); } template< typename MeshConfig, typename Device > @@ -148,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 > -- GitLab From b4c848d7f8c95d1d8d80832019fd62f91e01f032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Wed, 17 Jun 2020 14:05:08 +0200 Subject: [PATCH 52/73] Added tests for DistributedMesh and DistributedMeshSynchronizer --- .../DistributedMeshes/DistributedMesh.h | 37 +- .../DistributedMeshSynchronizer.h | 73 +- .../layers/EntityTags/Initializer.h | 6 +- .../MeshDetails/layers/EntityTags/Layer.h | 77 +- .../layers/EntityTags/LayerFamily.h | 54 +- src/TNL/Meshes/Readers/PVTUReader.h | 4 + src/TNL/Meshes/Traverser.h | 10 + src/TNL/Meshes/Traverser.hpp | 55 ++ .../Meshes/DistributedMeshes/CMakeLists.txt | 21 +- .../DistributedMeshes/DistributedMeshTest.cpp | 2 + .../DistributedMeshes/DistributedMeshTest.cu | 2 + .../DistributedMeshes/DistributedMeshTest.h | 706 ++++++++++++++++++ 12 files changed, 950 insertions(+), 97 deletions(-) create mode 100644 src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.cpp create mode 100644 src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.cu create mode 100644 src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.h diff --git a/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h b/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h index d664449c1..37f867a6b 100644 --- a/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h +++ b/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h @@ -55,11 +55,32 @@ public: template< typename Mesh_ > DistributedMesh& operator=( const Mesh_& other ) { + getGlobalIndices< 0 >() = other.template getGlobalIndices< 0 >(); + getGlobalIndices< Mesh::getMeshDimension() >() = other.template getGlobalIndices< Mesh::getMeshDimension() >(); localMesh = other.getLocalMesh(); group = other.getCommunicationGroup(); + ghostLevels = other.getGhostLevels(); + vtkPointGhostTypesArray = other.vtkPointGhostTypes(); + vtkCellGhostTypesArray = other.vtkCellGhostTypes(); return *this; } + bool operator==( const DistributedMesh& other ) const + { + return ( getGlobalIndices< 0 >() == other.template getGlobalIndices< 0 >() && + getGlobalIndices< Mesh::getMeshDimension() >() == other.template getGlobalIndices< Mesh::getMeshDimension() >() && + 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 */ @@ -160,16 +181,6 @@ public: const GlobalIndexType pointsCount = localMesh.template getEntitiesCount< 0 >(); const GlobalIndexType cellsCount = localMesh.template getEntitiesCount< Mesh::getMeshDimension() >(); - // TODO: the mesh should explicitly store ghost counts (or offsets) - useful for efficient iteration - GlobalIndexType ghostPoints = 0; - for( GlobalIndexType i = 0; i < pointsCount; i++ ) - if( localMesh.template isGhostEntity< 0 >( i ) ) - ghostPoints++; - GlobalIndexType ghostCells = 0; - for( GlobalIndexType i = 0; i < cellsCount; i++ ) - if( localMesh.template isGhostEntity< Mesh::getMeshDimension() >( i ) ) - ghostCells++; - CommunicatorType::Barrier(); for( int i = 0; i < CommunicatorType::GetSize(); i++ ) { if( i == CommunicatorType::GetRank() ) { @@ -179,8 +190,10 @@ public: << "\tCells count:\t" << cellsCount << "\n" << "\tPoints count:\t" << pointsCount << "\n" << "\tGhost levels:\t" << getGhostLevels() << "\n" - << "\tGhost cells:\t" << ghostCells << "\n" - << "\tGhost points:\t" << ghostPoints << "\n"; + << "\tGhost cells count:\t" << localMesh.template getGhostEntitiesCount< Mesh::getMeshDimension() >() << "\n" + << "\tGhost points count:\t" << localMesh.template getGhostEntitiesCount< 0 >() << "\n" + << "\tBoundary cells count:\t" << localMesh.template getBoundaryEntitiesCount< Mesh::getMeshDimension() >() << "\n" + << "\tBoundary points count:\t" << localMesh.template getBoundaryEntitiesCount< 0 >() << "\n"; const GlobalIndexType globalPointIndices = getGlobalIndices< 0 >().getSize(); const GlobalIndexType globalCellIndices = getGlobalIndices< Mesh::getMeshDimension() >().getSize(); if( getGhostLevels() > 0 ) { diff --git a/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h b/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h index ef771fa71..ed5d51446 100644 --- a/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h +++ b/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h @@ -38,10 +38,15 @@ public: DistributedMeshSynchronizer() = default; - void initialize( const Mesh& mesh ) + template< typename DistributedMesh > + void initialize( const DistributedMesh& mesh ) { - static_assert( std::is_same< typename Mesh::CommunicatorType, CommunicatorType >::value, - "Mesh::CommunnicatorType does not match" ); + static_assert( std::is_same< typename DistributedMesh::MeshType, typename MeshFunction::MeshType >::value, + "The type of the local mesh does not match the type of the mesh function's mesh." ); + static_assert( std::is_same< typename DistributedMesh::CommunicatorType, CommunicatorType >::value, + "DistributedMesh::CommunnicatorType does not match" ); + if( mesh.getGhostLevels() <= 0 ) + throw std::logic_error( "There are no ghost levels on the distributed mesh." ); localEntitiesCount = mesh.getLocalMesh().template getEntitiesCount< EntityDimension >(); // GOTCHA: https://devblogs.nvidia.com/cuda-pro-tip-always-set-current-device-avoid-multithreading-bugs/ @@ -56,10 +61,10 @@ public: // exchange the global index offsets so that each rank can determine the // owner of every entity by its global index - const IndexType ownStart = mesh.template getGlobalIndices< EntityDimension >()[ 0 ]; - Containers::Array< IndexType, DeviceType > offsets( nproc ); + const IndexType ownStart = mesh.template getGlobalIndices< EntityDimension >().getElement( 0 ); + Containers::Array< IndexType, Devices::Host, int > offsets( nproc ); { - Containers::Array< IndexType, DeviceType > sendbuf( nproc ); + Containers::Array< IndexType, Devices::Host, int > sendbuf( nproc ); sendbuf.setValue( ownStart ); CommunicatorType::Alltoall( sendbuf.getData(), 1, offsets.getData(), 1, @@ -75,31 +80,27 @@ public: return nproc - 1; }; - // TODO: initialization of the distributed mesh should set the ranges for - // local and ghost entities so we can iterate just over the ghost - // entities - // FIXME: the local vertices are not contiguous !!! // count local ghost entities for each rank - Containers::Array< IndexType, DeviceType > localGhostCounts( nproc ); + Containers::Array< IndexType, Devices::Host, int > localGhostCounts( nproc ); localGhostCounts.setValue( 0 ); - IndexType firstGhost = 0; - bool ghost_found = false; - for( IndexType local_idx = 0; local_idx < localEntitiesCount; local_idx++ ) { - const IndexType global_idx = mesh.template getGlobalIndices< EntityDimension >()[ local_idx ]; - const bool isNeighbor = mesh.getLocalMesh().template isGhostEntity< EntityDimension >( local_idx ); - if( ! isNeighbor ) { - // TODO: this is just for testing/debugging - if( ghost_found ) - std::cerr << "ERROR: global indices of local entities are not contiguous (local index " << local_idx << " is after a ghost entity has been found)" << std::endl; - ++firstGhost; - } - else { - const int owner = getOwner( global_idx ); - if( owner != rank ) { - ++localGhostCounts[ owner ]; - ghost_found = true; - } - } + Containers::Array< IndexType, Devices::Host, IndexType > hostGlobalIndices; + hostGlobalIndices = mesh.template getGlobalIndices< EntityDimension >(); + IndexType prev_global_idx = 0; + for( IndexType local_idx = mesh.getLocalMesh().template getGhostEntitiesOffset< EntityDimension >(); + local_idx < localEntitiesCount; + 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 IndexType 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 = getOwner( 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 @@ -135,6 +136,7 @@ public: std::vector< typename CommunicatorType::Request > requests; // send our ghost indices to the neighboring ranks + IndexType firstGhost = mesh.getLocalMesh().template getGhostEntitiesOffset< EntityDimension >(); for( int i = 0; i < nproc; i++ ) { if( ghostEntitiesCounts( rank, i ) > 0 ) { requests.push_back( CommunicatorType::ISend( @@ -211,12 +213,19 @@ public: } } + auto sendBuffersView = sendBuffers.getView(); + const auto ghostNeighborsView = ghostNeighbors.getConstView(); + const auto functionDataView = function.getData().getConstView(); + auto copy_kernel = [sendBuffersView, functionDataView, ghostNeighborsView] __cuda_callable__ ( IndexType k, IndexType offset ) mutable + { + sendBuffersView[ offset + k ] = functionDataView[ ghostNeighborsView[ offset + k ] ]; + }; + for( int i = 0; i < nproc; i++ ) { if( ghostEntitiesCounts( i, rank ) > 0 ) { const IndexType offset = ghostNeighborOffsets[ i ]; // copy data to send buffers - for( IndexType k = 0; k < ghostEntitiesCounts( i, rank ); k++ ) - sendBuffers[ offset + k ] = function.getData()[ ghostNeighbors[ offset + k ] ]; + Algorithms::ParallelFor< DeviceType >::exec( (IndexType) 0, ghostEntitiesCounts( i, rank ), copy_kernel, offset ); // issue async send operation requests.push_back( CommunicatorType::ISend( @@ -258,7 +267,7 @@ protected: * entity owned by the i-th rank. All ghost entities owned by the i-th * rank are assumed to be indexed contiguously in the local mesh. */ - Containers::Array< IndexType, DeviceType, IndexType > ghostOffsets; + Containers::Array< IndexType, Devices::Host, int > ghostOffsets; /** * Ghost neighbor offsets: array of size nproc + 1 where the i-th value is diff --git a/src/TNL/Meshes/MeshDetails/layers/EntityTags/Initializer.h b/src/TNL/Meshes/MeshDetails/layers/EntityTags/Initializer.h index 8275944a6..3409bd149 100644 --- a/src/TNL/Meshes/MeshDetails/layers/EntityTags/Initializer.h +++ b/src/TNL/Meshes/MeshDetails/layers/EntityTags/Initializer.h @@ -103,11 +103,11 @@ protected: }; template< int Dimension > - struct UpdateBoundaryIndices + struct UpdateEntityTagsLayer { static void exec( Mesh& mesh ) { - mesh.template updateBoundaryIndices< Dimension >(); + mesh.template updateEntityTagsLayer< Dimension >(); } }; @@ -148,7 +148,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 ); } }; diff --git a/src/TNL/Meshes/MeshDetails/layers/EntityTags/Layer.h b/src/TNL/Meshes/MeshDetails/layers/EntityTags/Layer.h index a14598d8c..449b7a2d0 100644 --- a/src/TNL/Meshes/MeshDetails/layers/EntityTags/Layer.h +++ b/src/TNL/Meshes/MeshDetails/layers/EntityTags/Layer.h @@ -57,6 +57,7 @@ public: tags = other.tags; boundaryIndices = other.boundaryIndices; interiorIndices = other.interiorIndices; + ghostsOffset = other.ghostsOffset; return *this; } @@ -69,6 +70,7 @@ public: tags = other.tags; boundaryIndices = other.boundaryIndices; interiorIndices = other.interiorIndices; + ghostsOffset = other.ghostsOffset; return *this; } @@ -76,6 +78,7 @@ public: void setEntitiesCount( DimensionTag, const GlobalIndexType& entitiesCount ) { tags.setSize( entitiesCount ); + ghostsOffset = entitiesCount; } void resetEntityTags( DimensionTag ) @@ -113,41 +116,36 @@ public: return tags[ entityIndex ] & EntityTags::GhostEntity; } - void updateBoundaryIndices( DimensionTag ) + 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 and ghost entities may overlap for vertices, so we count all categories separately + // NOTE: boundary/interior entities may overlap with ghost entities, so we count all categories separately const auto tags_view = tags.getConstView(); - auto is_interior = [=] __cuda_callable__ ( GlobalIndexType entityIndex ) -> GlobalIndexType - { - return ! (tags_view[ entityIndex ] & ( EntityTags::BoundaryEntity | EntityTags::GhostEntity )); - }; auto is_boundary = [=] __cuda_callable__ ( GlobalIndexType entityIndex ) -> GlobalIndexType { - return tags_view[ entityIndex ] & EntityTags::BoundaryEntity; + return bool(tags_view[ entityIndex ] & EntityTags::BoundaryEntity); }; auto is_ghost = [=] __cuda_callable__ ( GlobalIndexType entityIndex ) -> GlobalIndexType { - return tags_view[ entityIndex ] & EntityTags::GhostEntity; + return bool(tags_view[ entityIndex ] & EntityTags::GhostEntity); }; - const GlobalIndexType interiorEntities = Algorithms::Reduction< Device >::reduce( (GlobalIndexType) 0, tags.getSize(), std::plus<>{}, is_interior, (GlobalIndexType) 0 ); 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( interiorEntities ); + interiorIndices.setSize( tags.getSize() - boundaryEntities ); boundaryIndices.setSize( boundaryEntities ); - ghostIndices.setSize( ghostEntities ); + ghostsOffset = tags.getSize() - ghostEntities; if( ! std::is_same< Device, Devices::Cuda >::value ) { - GlobalIndexType i = 0, b = 0, g = 0; + GlobalIndexType i = 0, b = 0; for( GlobalIndexType e = 0; e < tags.getSize(); e++ ) { - if( ! (tags[ e ] & ( EntityTags::BoundaryEntity | EntityTags::GhostEntity )) ) - interiorIndices[ i++ ] = e; if( tags[ e ] & EntityTags::BoundaryEntity ) boundaryIndices[ b++ ] = e; - if( tags[ e ] & EntityTags::GhostEntity ) - ghostIndices[ g++ ] = 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 @@ -156,28 +154,26 @@ public: using OrderingHostArray = typename OrderingArray::template Self< typename OrderingArray::ValueType, Devices::Host >; EntityTagsHostArray hostTags; - OrderingHostArray hostBoundaryIndices, hostInteriorIndices, hostGhostIndices; + OrderingHostArray hostBoundaryIndices, hostInteriorIndices; hostTags.setLike( tags ); hostInteriorIndices.setLike( interiorIndices ); hostBoundaryIndices.setLike( boundaryIndices ); - hostGhostIndices.setLike( ghostIndices ); hostTags = tags; - GlobalIndexType i = 0, b = 0, g = 0; + GlobalIndexType i = 0, b = 0; for( GlobalIndexType e = 0; e < tags.getSize(); e++ ) { - if( ! (hostTags[ e ] & ( EntityTags::BoundaryEntity | EntityTags::GhostEntity )) ) - hostInteriorIndices[ i++ ] = e; if( hostTags[ e ] & EntityTags::BoundaryEntity ) hostBoundaryIndices[ b++ ] = e; - if( hostTags[ e ] & EntityTags::GhostEntity ) - hostGhostIndices[ g++ ] = 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; - ghostIndices = hostGhostIndices; } } @@ -205,6 +201,18 @@ public: return interiorIndices[ i ]; } + __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; @@ -213,7 +221,7 @@ public: void load( File& file ) { file >> tags; - updateBoundaryIndices( DimensionTag() ); + updateEntityTagsLayer( DimensionTag() ); } void print( std::ostream& str ) const @@ -224,14 +232,14 @@ public: str << interiorIndices << std::endl; str << "Indices of the boundary entities of dimension " << DimensionTag::value << " are: "; str << boundaryIndices << std::endl; - str << "Indices of the ghost entities of dimension " << DimensionTag::value << " are: "; - str << ghostIndices << 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 && ghostIndices == layer.ghostIndices ) || - ( tags != layer.tags && interiorIndices != layer.interiorIndices && boundaryIndices != layer.boundaryIndices && ghostIndices != layer.ghostIndices ), + 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 @@ -239,14 +247,15 @@ public: << "layer.interiorIndices = " << layer.interiorIndices << std::endl << "boundaryIndices = " << boundaryIndices << std::endl << "layer.boundaryIndices = " << layer.boundaryIndices << std::endl - << "ghostIndices = " << ghostIndices << std::endl - << "layer.ghostIndices = " << layer.ghostIndices << std::endl; ); + << "ghostsOffset = " << ghostsOffset << std::endl + << "layer.ghostsOffset = " << layer.ghostsOffset << std::endl; ); return tags == layer.tags; } private: EntityTagsArrayType tags; - OrderingArray interiorIndices, boundaryIndices, ghostIndices; + OrderingArray interiorIndices, boundaryIndices; + GlobalIndexType ghostsOffset = 0; // friend class is needed for templated assignment operators template< typename MeshConfig_, typename Device_, typename DimensionTag_, bool TagStorage_ > @@ -277,11 +286,13 @@ protected: void removeEntityTag( DimensionTag, const GlobalIndexType&, TagType ) const {} void isBoundaryEntity( DimensionTag, const GlobalIndexType& ) const {} void isGhostEntity( DimensionTag, const GlobalIndexType& ) const {} - void updateBoundaryIndices( DimensionTag ) {} + void updateEntityTagsLayer( DimensionTag ) {} void getBoundaryEntitiesCount( DimensionTag ) const {} void getBoundaryEntityIndex( DimensionTag, const GlobalIndexType& i ) const {} void getInteriorEntitiesCount( DimensionTag ) const {} void getInteriorEntityIndex( DimensionTag, const GlobalIndexType& i ) const {} + void getGhostEntitiesCount() const; + void getGhostEntitiesOffset() const; void save( File& file ) const {} void load( File& file ) {} diff --git a/src/TNL/Meshes/MeshDetails/layers/EntityTags/LayerFamily.h b/src/TNL/Meshes/MeshDetails/layers/EntityTags/LayerFamily.h index e3c532041..0b40debfc 100644 --- a/src/TNL/Meshes/MeshDetails/layers/EntityTags/LayerFamily.h +++ b/src/TNL/Meshes/MeshDetails/layers/EntityTags/LayerFamily.h @@ -34,11 +34,13 @@ protected: using LayerType::removeEntityTag; using LayerType::isBoundaryEntity; using LayerType::isGhostEntity; - using LayerType::updateBoundaryIndices; + using LayerType::updateEntityTagsLayer; using LayerType::getBoundaryEntitiesCount; using LayerType::getBoundaryEntityIndex; using LayerType::getInteriorEntitiesCount; using LayerType::getInteriorEntityIndex; + using LayerType::getGhostEntitiesCount; + using LayerType::getGhostEntitiesOffset; using BaseType::setEntitiesCount; using BaseType::resetEntityTags; @@ -47,11 +49,13 @@ protected: using BaseType::removeEntityTag; using BaseType::isBoundaryEntity; using BaseType::isGhostEntity; - using BaseType::updateBoundaryIndices; + using BaseType::updateEntityTagsLayer; using BaseType::getBoundaryEntitiesCount; using BaseType::getBoundaryEntityIndex; using BaseType::getInteriorEntitiesCount; using BaseType::getInteriorEntityIndex; + using BaseType::getGhostEntitiesCount; + using BaseType::getGhostEntitiesOffset; LayerInheritor() = default; @@ -114,16 +118,18 @@ class LayerInheritor< MeshConfig, Device, DimensionTag< MeshConfig::meshDimensio protected: void setEntitiesCount(); void resetEntityTags(); - void getEntityTag(); + void getEntityTag() const; void addEntityTag(); void removeEntityTag(); - void isBoundaryEntity(); - void isGhostEntity(); - void updateBoundaryIndices(); - void getBoundaryEntitiesCount(); - void getBoundaryEntityIndex(); - void getInteriorEntitiesCount(); - void getInteriorEntityIndex(); + void isBoundaryEntity() const; + void isGhostEntity() const; + void updateEntityTagsLayer(); + void getBoundaryEntitiesCount() const; + void getBoundaryEntityIndex() const; + void getInteriorEntitiesCount() const; + void getInteriorEntityIndex() const; + void getGhostEntitiesCount() const; + void getGhostEntitiesOffset() const; LayerInheritor() = default; explicit LayerInheritor( const LayerInheritor& other ) {} @@ -240,6 +246,28 @@ public: return BaseType::getInteriorEntityIndex( DimensionTag< Dimension >(), i ); } + template< int Dimension > + __cuda_callable__ + GlobalIndexType getGhostEntitiesCount() const + { + 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__ + GlobalIndexType getGhostEntitiesOffset() const + { + 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 ) @@ -252,12 +280,6 @@ protected: { BaseType::resetEntityTags( DimensionTag< Dimension >() ); } - - template< int Dimension > - void updateBoundaryIndices() - { - BaseType::updateBoundaryIndices( DimensionTag< Dimension >() ); - } }; } // namespace EntityTags diff --git a/src/TNL/Meshes/Readers/PVTUReader.h b/src/TNL/Meshes/Readers/PVTUReader.h index f33bb948d..666aa4f45 100644 --- a/src/TNL/Meshes/Readers/PVTUReader.h +++ b/src/TNL/Meshes/Readers/PVTUReader.h @@ -178,6 +178,10 @@ public: 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() >(); diff --git a/src/TNL/Meshes/Traverser.h b/src/TNL/Meshes/Traverser.h index a4c5a25d4..c0c2103c1 100644 --- a/src/TNL/Meshes/Traverser.h +++ b/src/TNL/Meshes/Traverser.h @@ -42,6 +42,16 @@ public: 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 diff --git a/src/TNL/Meshes/Traverser.hpp b/src/TNL/Meshes/Traverser.hpp index 4a8f06f59..4d61457f8 100644 --- a/src/TNL/Meshes/Traverser.hpp +++ b/src/TNL/Meshes/Traverser.hpp @@ -99,5 +99,60 @@ processAllEntities( const MeshPointer& meshPointer, 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 auto ghostsOffset = meshPointer->template getGhostEntitiesOffset< MeshEntity::getEntityDimension() >(); + const auto 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 auto 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/UnitTests/Meshes/DistributedMeshes/CMakeLists.txt b/src/UnitTests/Meshes/DistributedMeshes/CMakeLists.txt index bd294bf36..dad8a3c7f 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/DistributedMeshTest.cpp b/src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.cpp new file mode 100644 index 000000000..e2fd04487 --- /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 000000000..e2fd04487 --- /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 000000000..a266ec7b5 --- /dev/null +++ b/src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.h @@ -0,0 +1,706 @@ +#pragma once + +#ifdef HAVE_GTEST +#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 ); + } + + 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 ); +} + +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< MeshFunction >; + + // 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 ); +#ifdef HAVE_CUDA + testSynchronizerOnDevice< Devices::Cuda, typename Mesh::Cell >( mesh ); + testSynchronizerOnDevice< Devices::Cuda, typename Mesh::Vertex >( 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 ); + 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 -- GitLab From 7325ec0ad9ec396661685fbf9a8bacd64f452e18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Wed, 17 Jun 2020 14:38:10 +0200 Subject: [PATCH 53/73] Small fixes in unit tests --- src/UnitTests/Meshes/MeshTraverserTest.h | 8 ++------ src/UnitTests/main_mpi.h | 4 ++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/UnitTests/Meshes/MeshTraverserTest.h b/src/UnitTests/Meshes/MeshTraverserTest.h index 5c4274622..a8a1d6d9a 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 > +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 > +class TestHexahedronMeshConfig : public DefaultConfig< Topologies::Hexahedron > { public: static constexpr bool entityStorage( int dimensions ) { return true; } diff --git a/src/UnitTests/main_mpi.h b/src/UnitTests/main_mpi.h index 9fe75c850..0f8f4b059 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 -- GitLab From d10273a5a9e47b092b537549dba0b1c031ac6753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sat, 20 Jun 2020 06:44:29 +0200 Subject: [PATCH 54/73] Fixed ordering of points in tnl-decompose-mesh --- src/Tools/tnl-decompose-mesh.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Tools/tnl-decompose-mesh.cpp b/src/Tools/tnl-decompose-mesh.cpp index 57ddb1740..b1fb30d06 100644 --- a/src/Tools/tnl-decompose-mesh.cpp +++ b/src/Tools/tnl-decompose-mesh.cpp @@ -584,7 +584,7 @@ struct DecomposeMesh if( cell_global_indices.count( cell.getIndex() ) != 0 ) return false; CellSeed seed; - for( Index v = 0; v < seed.getCornerIds().getSize(); v++ ) { + 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()} ); @@ -596,6 +596,22 @@ struct DecomposeMesh 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++ ) { -- GitLab From b740ae77435d23b77986255af96de7f6aefa81a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sat, 20 Jun 2020 06:49:34 +0200 Subject: [PATCH 55/73] Added tnl-game-of-life --- src/Tools/CMakeLists.txt | 10 +- src/Tools/tnl-game-of-life.cpp | 379 +++++++++++++++++++++++++++++++++ 2 files changed, 388 insertions(+), 1 deletion(-) create mode 100644 src/Tools/tnl-game-of-life.cpp diff --git a/src/Tools/CMakeLists.txt b/src/Tools/CMakeLists.txt index 7a4e975e9..34dfc5e3e 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 ) @@ -33,12 +34,19 @@ if( ZLIB_FOUND ) 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 ) @@ -71,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 @@ -82,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-game-of-life.cpp b/src/Tools/tnl-game-of-life.cpp new file mode 100644 index 000000000..b6df2d3f6 --- /dev/null +++ b/src/Tools/tnl-game-of-life.cpp @@ -0,0 +1,379 @@ +/*************************************************************************** + 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 ); + + // 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; + }; + + // test synchronizer +// using MeshFunction = Functions::MeshFunction< LocalMesh, Mesh::getMeshDimension(), Real >; + using Synchronizer = Meshes::DistributedMeshes::DistributedMeshSynchronizer< MyMeshFunction, Mesh >; + Synchronizer sync; + sync.initialize( mesh ); + + MyMeshFunction f_in( localMesh ), f_out( localMesh ); + f_in.getData().setValue( 0 ); + + const Index entitiesCount = localMesh.template getEntitiesCount< MyMeshFunction::getEntitiesDimension() >(); + 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 ); + + bool all_done = false; + Index iteration = 0; + do { + iteration++; + if( CommunicatorType::GetRank() == 0 ) + std::cout << "Computing iteration " << iteration << "..." << std::endl; + + // traverse the mesh + for( Index i = 0; i < entitiesCount; i++ ) { + // end on the first ghost entity + // TODO: the mesh should allow iteration over local entities only + if( localMesh.template isGhostEntity< MyMeshFunction::getEntitiesDimension() >( i ) ) + break; + // sum values of the function on the neighbor entities + Real sum = 0; + for( Index n = 0; n < localMesh.getCellNeighborsCount( i ); n++ ) { + const Index neighbor = localMesh.getCellNeighborIndex( i, n ); + sum += f_in.getData()[ neighbor ]; + } + const bool live = f_in.getData()[ i ]; + + // Conway's rules for square grid + if( live ) { + // any live cell with less than two live neighbors dies + if( sum < 2 ) + f_out.getData()[ i ] = 0; + // any live cell with two or three live neighbors survives + else if( sum < 4 ) + f_out.getData()[ i ] = 1; + // any live cell with more than three live neighbors dies + else + f_out.getData()[ i ] = 0; + } + else { + // any dead cell with exactly three live neighbors becomes a live cell + if( sum == 3 ) + f_out.getData()[ i ] = 1; + // any other dead cell remains dead + else + f_out.getData()[ i ] = 0; + } + } + + // 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 ); +} -- GitLab From fe5e13515268294a8c44e8c2a3b09b83a571ae83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sat, 20 Jun 2020 07:13:32 +0200 Subject: [PATCH 56/73] Improved parsing of CLI arguments in tnl-grid-to-mesh --- src/Tools/tnl-grid-to-mesh.cpp | 109 ++++++++++++++++++++++----------- 1 file changed, 73 insertions(+), 36 deletions(-) diff --git a/src/Tools/tnl-grid-to-mesh.cpp b/src/Tools/tnl-grid-to-mesh.cpp index b3bd9ea93..9ff091975 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,23 +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 >; - using MeshType = TNL::Meshes::Mesh< MeshConfig >; + 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 ); @@ -80,23 +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 >; - using MeshType = TNL::Meshes::Mesh< MeshConfig >; + 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 ); @@ -119,23 +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 >; - using MeshType = TNL::Meshes::Mesh< MeshConfig >; + 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 ); @@ -162,7 +177,7 @@ struct MeshCreator< TNL::Meshes::Grid< 3, Real, Device, Index > > }; template< typename Grid > -bool convertGrid( Grid& grid, const TNL::String& fileName, const TNL::String& outputFileName ) +bool convertGrid( Grid& grid, const String& fileName, const String& outputFileName, const String& outputFormat ) { using MeshCreator = MeshCreator< Grid >; using Mesh = typename MeshCreator::MeshType; @@ -175,35 +190,57 @@ bool convertGrid( Grid& grid, const TNL::String& fileName, const TNL::String& ou return false; } - try - { - mesh.save( outputFileName ); + if( outputFormat == "vtk" ) { + using VTKWriter = Meshes::Writers::VTKWriter< Mesh >; + std::ofstream file( outputFileName.getString() ); + VTKWriter writer( file ); + writer.template writeEntities< Mesh::getMeshDimension() >( mesh ); } - catch(...) - { - std::cerr << "Failed to save the mesh to file '" << outputFileName << "'." << std::endl; - return false; + 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 ); } 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" ); auto wrapper = [&] ( const auto& reader, auto&& grid ) { - return convertGrid( grid, fileName, outputFileName ); + return convertGrid( grid, inputFileName, outputFileName, outputFileFormat ); }; - return ! Meshes::resolveMeshType< GridToMeshConfigTag, Devices::Host >( wrapper, fileName ); + return ! Meshes::resolveMeshType< GridToMeshConfigTag, Devices::Host >( wrapper, inputFileName ); } -- GitLab From 2e8dfa1e34d70f1b7096426ab796640c38cd9fb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sun, 21 Jun 2020 17:31:27 +0200 Subject: [PATCH 57/73] Cleanup: removed multimaps classes as they were superseded with sparse binary matrices --- .../Multimaps/EllpackIndexMultimap.h | 116 ------- .../Multimaps/EllpackIndexMultimap.hpp | 321 ------------------ .../Multimaps/EllpackIndexMultimapValues.h | 121 ------- .../Multimaps/EllpackIndexMultimapValues.hpp | 293 ---------------- .../Multimaps/MultimapPermutationApplier.h | 90 ----- .../Multimaps/StaticEllpackIndexMultimap.h | 113 ------ .../Multimaps/StaticEllpackIndexMultimap.hpp | 277 --------------- .../StaticEllpackIndexMultimapValues.h | 108 ------ .../StaticEllpackIndexMultimapValues.hpp | 285 ---------------- src/UnitTests/Containers/CMakeLists.txt | 1 - .../Containers/Multimaps/CMakeLists.txt | 10 - .../Containers/Multimaps/MultimapTest.cpp | 154 --------- .../Multimaps/StaticMultimapTest.cpp | 102 ------ 13 files changed, 1991 deletions(-) delete mode 100644 src/TNL/Containers/Multimaps/EllpackIndexMultimap.h delete mode 100644 src/TNL/Containers/Multimaps/EllpackIndexMultimap.hpp delete mode 100644 src/TNL/Containers/Multimaps/EllpackIndexMultimapValues.h delete mode 100644 src/TNL/Containers/Multimaps/EllpackIndexMultimapValues.hpp delete mode 100644 src/TNL/Containers/Multimaps/MultimapPermutationApplier.h delete mode 100644 src/TNL/Containers/Multimaps/StaticEllpackIndexMultimap.h delete mode 100644 src/TNL/Containers/Multimaps/StaticEllpackIndexMultimap.hpp delete mode 100644 src/TNL/Containers/Multimaps/StaticEllpackIndexMultimapValues.h delete mode 100644 src/TNL/Containers/Multimaps/StaticEllpackIndexMultimapValues.hpp delete mode 100644 src/UnitTests/Containers/Multimaps/CMakeLists.txt delete mode 100644 src/UnitTests/Containers/Multimaps/MultimapTest.cpp delete mode 100644 src/UnitTests/Containers/Multimaps/StaticMultimapTest.cpp diff --git a/src/TNL/Containers/Multimaps/EllpackIndexMultimap.h b/src/TNL/Containers/Multimaps/EllpackIndexMultimap.h deleted file mode 100644 index 8684ba3b1..000000000 --- a/src/TNL/Containers/Multimaps/EllpackIndexMultimap.h +++ /dev/null @@ -1,116 +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; - - __cuda_callable__ - LocalIndexType getValuesCount( const IndexType& inputIndex ) const; - - __cuda_callable__ - IndexType getValue( const IndexType& inputIndex, const LocalIndexType& portIndex ) 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 0872d8501..000000000 --- a/src/TNL/Containers/Multimaps/EllpackIndexMultimap.hpp +++ /dev/null @@ -1,321 +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 > -__cuda_callable__ -LocalIndex -EllpackIndexMultimap< Index, Device, LocalIndex, SliceSize >:: -getValuesCount( const IndexType& inputIndex ) const -{ - return valuesCounts[ inputIndex ]; -} - -template< typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -__cuda_callable__ -Index -EllpackIndexMultimap< Index, Device, LocalIndex, SliceSize >:: -getValue( const IndexType& inputIndex, const LocalIndexType& portIndex ) 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 this->values[ offset + portIndex * SliceSize ]; -} - -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 9be47980d..000000000 --- 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 6b98927fb..000000000 --- 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 a50d7b354..000000000 --- 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 PermutationArray > -void permuteMultimapKeys( Multimap& multimap, const PermutationArray& perm ) -{ - static_assert( std::is_same< typename Multimap::DeviceType, typename PermutationArray::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 PermutationArray::ValueType* 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 PermutationArray > -void permuteMultimapValues( Multimap& multimap, const PermutationArray& iperm ) -{ - static_assert( std::is_same< typename Multimap::DeviceType, typename PermutationArray::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 PermutationArray::ValueType* 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 c8f2aebaa..000000000 --- a/src/TNL/Containers/Multimaps/StaticEllpackIndexMultimap.h +++ /dev/null @@ -1,113 +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; - - __cuda_callable__ - constexpr LocalIndexType getValuesCount( const IndexType& inputIndex ) const; - - __cuda_callable__ - IndexType getValue( const IndexType& inputIndex, const LocalIndexType& portIndex ) 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 8654ac0a7..000000000 --- a/src/TNL/Containers/Multimaps/StaticEllpackIndexMultimap.hpp +++ /dev/null @@ -1,277 +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 > -__cuda_callable__ -constexpr LocalIndex -StaticEllpackIndexMultimap< ValuesCount, Index, Device, LocalIndex, SliceSize >:: -getValuesCount( const IndexType& inputIndex ) const -{ - return ValuesCount; -} - -template< int ValuesCount, - typename Index, - typename Device, - typename LocalIndex, - int SliceSize > -__cuda_callable__ -Index -StaticEllpackIndexMultimap< ValuesCount, Index, Device, LocalIndex, SliceSize >:: -getValue( const IndexType& inputIndex, const LocalIndexType& portIndex ) 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 this->values[ offset + portIndex * SliceSize ]; -} - -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 efae4f051..000000000 --- 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 2f3bd4df9..000000000 --- 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/UnitTests/Containers/CMakeLists.txt b/src/UnitTests/Containers/CMakeLists.txt index 21be3ded1..ae3c1b0e7 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 0cf3ae746..000000000 --- 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 0ba26092d..000000000 --- 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 86d5c0cb4..000000000 --- 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" -- GitLab From 24de3988b89922d3c7c692c9d119e6a78c4fc9ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sun, 21 Jun 2020 18:34:09 +0200 Subject: [PATCH 58/73] Mesh::print works with CUDA meshes too --- src/TNL/Meshes/Mesh.hpp | 10 ++-------- src/UnitTests/Meshes/MeshTest.h | 1 + 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/TNL/Meshes/Mesh.hpp b/src/TNL/Meshes/Mesh.hpp index e712af13a..3a18f0278 100644 --- a/src/TNL/Meshes/Mesh.hpp +++ b/src/TNL/Meshes/Mesh.hpp @@ -318,14 +318,8 @@ 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 ); - EntityTagsLayerFamily::print( str ); - } + StorageBaseType::print( str ); + EntityTagsLayerFamily::print( str ); } template< typename MeshConfig, typename Device > diff --git a/src/UnitTests/Meshes/MeshTest.h b/src/UnitTests/Meshes/MeshTest.h index db291c0eb..e1ac5af08 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 ); -- GitLab From e6dc0832a8791870f88cbbba9ae74d52b8d1b98c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sun, 21 Jun 2020 22:45:37 +0200 Subject: [PATCH 59/73] Improved logging methods writeProlog and printInfo in Mesh and DistributedMesh --- src/TNL/Meshes/DistributedMeshes/DistributedMesh.h | 12 ++++++------ src/TNL/Meshes/Mesh.hpp | 10 ++++++---- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h b/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h index 37f867a6b..cdd786d52 100644 --- a/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h +++ b/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h @@ -178,7 +178,7 @@ public: void printInfo( std::ostream& str ) const { - const GlobalIndexType pointsCount = localMesh.template getEntitiesCount< 0 >(); + const GlobalIndexType verticesCount = localMesh.template getEntitiesCount< 0 >(); const GlobalIndexType cellsCount = localMesh.template getEntitiesCount< Mesh::getMeshDimension() >(); CommunicatorType::Barrier(); @@ -188,20 +188,20 @@ public: << "\tMesh dimension:\t" << getMeshDimension() << "\n" << "\tCell topology:\t" << getType( typename Cell::EntityTopology{} ) << "\n" << "\tCells count:\t" << cellsCount << "\n" - << "\tPoints count:\t" << pointsCount << "\n" + << "\tvertices count:\t" << verticesCount << "\n" << "\tGhost levels:\t" << getGhostLevels() << "\n" << "\tGhost cells count:\t" << localMesh.template getGhostEntitiesCount< Mesh::getMeshDimension() >() << "\n" - << "\tGhost points count:\t" << localMesh.template getGhostEntitiesCount< 0 >() << "\n" + << "\tGhost vertices count:\t" << localMesh.template getGhostEntitiesCount< 0 >() << "\n" << "\tBoundary cells count:\t" << localMesh.template getBoundaryEntitiesCount< Mesh::getMeshDimension() >() << "\n" - << "\tBoundary points count:\t" << localMesh.template getBoundaryEntitiesCount< 0 >() << "\n"; + << "\tBoundary vertices count:\t" << localMesh.template getBoundaryEntitiesCount< 0 >() << "\n"; const GlobalIndexType globalPointIndices = getGlobalIndices< 0 >().getSize(); const GlobalIndexType globalCellIndices = getGlobalIndices< Mesh::getMeshDimension() >().getSize(); if( getGhostLevels() > 0 ) { - if( globalPointIndices != pointsCount ) + 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() != pointsCount ) + 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"; diff --git a/src/TNL/Meshes/Mesh.hpp b/src/TNL/Meshes/Mesh.hpp index 3a18f0278..403f5111c 100644 --- a/src/TNL/Meshes/Mesh.hpp +++ b/src/TNL/Meshes/Mesh.hpp @@ -345,11 +345,13 @@ Mesh< MeshConfig, Device >:: writeProlog( Logger& logger ) const { logger.writeParameter( "Dimension:", getMeshDimension() ); - logger.writeParameter( "Number of cells:", getEntitiesCount< getMeshDimension() >() ); + logger.writeParameter( "Cell topology:", getType( typename Cell::EntityTopology{} ) ); + logger.writeParameter( "Cells count:", getEntitiesCount< getMeshDimension() >() ); if( getMeshDimension() > 1 ) - logger.writeParameter( "Number of faces:", getEntitiesCount< getMeshDimension() - 1 >() ); - logger.writeParameter( "Number of vertices:", getEntitiesCount< 0 >() ); - // TODO: more parameters? + 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 >() ); } -- GitLab From 53bd736d345f4ff9c88733511c6c19415d0e17b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Wed, 24 Jun 2020 11:08:14 +0200 Subject: [PATCH 60/73] Simplified tests for mesh traversers --- src/UnitTests/Meshes/MeshTraverserTest.h | 166 +++++++++-------------- 1 file changed, 64 insertions(+), 102 deletions(-) diff --git a/src/UnitTests/Meshes/MeshTraverserTest.h b/src/UnitTests/Meshes/MeshTraverserTest.h index a8a1d6d9a..b4846a141 100644 --- a/src/UnitTests/Meshes/MeshTraverserTest.h +++ b/src/UnitTests/Meshes/MeshTraverserTest.h @@ -49,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 ); @@ -124,11 +126,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 >() ); @@ -155,75 +152,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 } @@ -289,12 +274,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 >() ); @@ -329,97 +308,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 } -- GitLab From b633d0d1eba50ede9f1abff38551e291df4ff688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Wed, 24 Jun 2020 14:25:22 +0200 Subject: [PATCH 61/73] Added iteration methods to Mesh: forAll, forBoundary, forInterior, forLocal, forGhost --- .../DistributedMeshes/DistributedMesh.h | 4 +- src/TNL/Meshes/Mesh.h | 56 +++++++++++++++++ src/TNL/Meshes/Mesh.hpp | 62 +++++++++++++++++++ .../MeshDetails/layers/EntityTags/Layer.h | 26 ++------ .../layers/EntityTags/LayerFamily.h | 42 +++---------- src/TNL/Meshes/Traverser.hpp | 22 ++++--- src/Tools/tnl-game-of-life.cpp | 37 ++++++----- .../DistributedMeshes/DistributedMeshTest.h | 31 ++++++++++ src/UnitTests/Meshes/EntityTagsTest.h | 16 ++--- src/UnitTests/Meshes/MeshOrderingTest.h | 8 +-- src/UnitTests/Meshes/MeshTraverserTest.h | 21 +++++++ 11 files changed, 232 insertions(+), 93 deletions(-) diff --git a/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h b/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h index cdd786d52..053f1d289 100644 --- a/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h +++ b/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h @@ -192,8 +192,8 @@ public: << "\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 getBoundaryEntitiesCount< Mesh::getMeshDimension() >() << "\n" - << "\tBoundary vertices count:\t" << localMesh.template getBoundaryEntitiesCount< 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 ) { diff --git a/src/TNL/Meshes/Mesh.h b/src/TNL/Meshes/Mesh.h index f783781f5..f37f3ba67 100644 --- a/src/TNL/Meshes/Mesh.h +++ b/src/TNL/Meshes/Mesh.h @@ -175,6 +175,62 @@ class Mesh 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; + + /* * The permutations follow the definition used in the Metis library: Let M * be the original mesh and M' the permuted mesh. Then entity with index i diff --git a/src/TNL/Meshes/Mesh.hpp b/src/TNL/Meshes/Mesh.hpp index 403f5111c..39c9bda31 100644 --- a/src/TNL/Meshes/Mesh.hpp +++ b/src/TNL/Meshes/Mesh.hpp @@ -239,6 +239,68 @@ getCellNeighborIndex( const GlobalIndexType cellIndex, const LocalIndexType neig } +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 diff --git a/src/TNL/Meshes/MeshDetails/layers/EntityTags/Layer.h b/src/TNL/Meshes/MeshDetails/layers/EntityTags/Layer.h index 449b7a2d0..54c74e3e6 100644 --- a/src/TNL/Meshes/MeshDetails/layers/EntityTags/Layer.h +++ b/src/TNL/Meshes/MeshDetails/layers/EntityTags/Layer.h @@ -178,27 +178,15 @@ public: } __cuda_callable__ - GlobalIndexType getBoundaryEntitiesCount( DimensionTag ) const + auto getBoundaryIndices( DimensionTag ) const { - return boundaryIndices.getSize(); + return boundaryIndices.getConstView(); } __cuda_callable__ - GlobalIndexType getBoundaryEntityIndex( DimensionTag, const GlobalIndexType& i ) const + auto getInteriorIndices( DimensionTag ) 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 ]; + return interiorIndices.getConstView(); } __cuda_callable__ @@ -287,10 +275,8 @@ protected: void isBoundaryEntity( DimensionTag, const GlobalIndexType& ) const {} void isGhostEntity( DimensionTag, const GlobalIndexType& ) const {} void updateEntityTagsLayer( DimensionTag ) {} - void getBoundaryEntitiesCount( DimensionTag ) const {} - void getBoundaryEntityIndex( DimensionTag, const GlobalIndexType& i ) const {} - void getInteriorEntitiesCount( DimensionTag ) const {} - void getInteriorEntityIndex( DimensionTag, const GlobalIndexType& i ) const {} + void getBoundaryIndices( DimensionTag ) const {} + void getInteriorIndices( DimensionTag ) const {} void getGhostEntitiesCount() const; void getGhostEntitiesOffset() const; diff --git a/src/TNL/Meshes/MeshDetails/layers/EntityTags/LayerFamily.h b/src/TNL/Meshes/MeshDetails/layers/EntityTags/LayerFamily.h index 0b40debfc..8dcd35753 100644 --- a/src/TNL/Meshes/MeshDetails/layers/EntityTags/LayerFamily.h +++ b/src/TNL/Meshes/MeshDetails/layers/EntityTags/LayerFamily.h @@ -35,10 +35,8 @@ protected: using LayerType::isBoundaryEntity; using LayerType::isGhostEntity; using LayerType::updateEntityTagsLayer; - using LayerType::getBoundaryEntitiesCount; - using LayerType::getBoundaryEntityIndex; - using LayerType::getInteriorEntitiesCount; - using LayerType::getInteriorEntityIndex; + using LayerType::getBoundaryIndices; + using LayerType::getInteriorIndices; using LayerType::getGhostEntitiesCount; using LayerType::getGhostEntitiesOffset; @@ -50,10 +48,8 @@ protected: using BaseType::isBoundaryEntity; using BaseType::isGhostEntity; using BaseType::updateEntityTagsLayer; - using BaseType::getBoundaryEntitiesCount; - using BaseType::getBoundaryEntityIndex; - using BaseType::getInteriorEntitiesCount; - using BaseType::getInteriorEntityIndex; + using BaseType::getBoundaryIndices; + using BaseType::getInteriorIndices; using BaseType::getGhostEntitiesCount; using BaseType::getGhostEntitiesOffset; @@ -124,10 +120,8 @@ protected: void isBoundaryEntity() const; void isGhostEntity() const; void updateEntityTagsLayer(); - void getBoundaryEntitiesCount() const; - void getBoundaryEntityIndex() const; - void getInteriorEntitiesCount() const; - void getInteriorEntityIndex() const; + void getBoundaryIndices() const; + void getInteriorIndices() const; void getGhostEntitiesCount() const; void getGhostEntitiesOffset() const; @@ -216,34 +210,18 @@ public: template< int Dimension > __cuda_callable__ - GlobalIndexType getBoundaryEntitiesCount() const + auto getBoundaryIndices() const { static_assert( WeakTrait< Dimension >::entityTagsEnabled, "You try to access entity tags which are not configured for storage." ); - return BaseType::getBoundaryEntitiesCount( DimensionTag< Dimension >() ); + return BaseType::getBoundaryIndices( DimensionTag< Dimension >() ); } template< int Dimension > __cuda_callable__ - GlobalIndexType getBoundaryEntityIndex( const GlobalIndexType& i ) const + auto getInteriorIndices() const { static_assert( WeakTrait< Dimension >::entityTagsEnabled, "You try to access entity tags which are not configured for storage." ); - return BaseType::getBoundaryEntityIndex( DimensionTag< Dimension >(), i ); - } - - template< int Dimension > - __cuda_callable__ - GlobalIndexType getInteriorEntitiesCount() const - { - static_assert( WeakTrait< Dimension >::entityTagsEnabled, "You try to access entity tags which are not configured for storage." ); - return BaseType::getInteriorEntitiesCount( DimensionTag< Dimension >() ); - } - - template< int Dimension > - __cuda_callable__ - GlobalIndexType getInteriorEntityIndex( const GlobalIndexType& i ) const - { - static_assert( WeakTrait< Dimension >::entityTagsEnabled, "You try to access entity tags which are not configured for storage." ); - return BaseType::getInteriorEntityIndex( DimensionTag< Dimension >(), i ); + return BaseType::getInteriorIndices( DimensionTag< Dimension >() ); } template< int Dimension > diff --git a/src/TNL/Meshes/Traverser.hpp b/src/TNL/Meshes/Traverser.hpp index 4d61457f8..9f99b1e74 100644 --- a/src/TNL/Meshes/Traverser.hpp +++ b/src/TNL/Meshes/Traverser.hpp @@ -26,13 +26,14 @@ Traverser< Mesh, MeshEntity, EntitiesDimension >:: processBoundaryEntities( const MeshPointer& meshPointer, UserData userData ) const { - const GlobalIndexType entitiesCount = meshPointer->template getBoundaryEntitiesCount< MeshEntity::getEntityDimension() >(); - auto kernel = [] __cuda_callable__ + 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 = mesh->template getBoundaryEntityIndex< MeshEntity::getEntityDimension() >( i ); + const GlobalIndexType entityIndex = boundaryIndices[ i ]; const auto entity = mesh->template getEntity< MeshEntity::getEntityDimension() >( entityIndex ); EntitiesProcessor::processEntity( *mesh, userData, entity ); }; @@ -54,13 +55,14 @@ Traverser< Mesh, MeshEntity, EntitiesDimension >:: processInteriorEntities( const MeshPointer& meshPointer, UserData userData ) const { - const auto entitiesCount = meshPointer->template getInteriorEntitiesCount< MeshEntity::getEntityDimension() >(); - auto kernel = [] __cuda_callable__ + 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 = mesh->template getInteriorEntityIndex< MeshEntity::getEntityDimension() >( i ); + const GlobalIndexType entityIndex = interiorIndices[ i ]; const auto entity = mesh->template getEntity< MeshEntity::getEntityDimension() >( entityIndex ); EntitiesProcessor::processEntity( *mesh, userData, entity ); }; @@ -82,7 +84,7 @@ Traverser< Mesh, MeshEntity, EntitiesDimension >:: processAllEntities( const MeshPointer& meshPointer, UserData userData ) const { - const auto entitiesCount = meshPointer->template getEntitiesCount< MeshEntity::getEntityDimension() >(); + const GlobalIndexType entitiesCount = meshPointer->template getEntitiesCount< MeshEntity::getEntityDimension() >(); auto kernel = [] __cuda_callable__ ( const GlobalIndexType entityIndex, const Mesh* mesh, @@ -109,8 +111,8 @@ Traverser< Mesh, MeshEntity, EntitiesDimension >:: processGhostEntities( const MeshPointer& meshPointer, UserData userData ) const { - const auto ghostsOffset = meshPointer->template getGhostEntitiesOffset< MeshEntity::getEntityDimension() >(); - const auto entitiesCount = meshPointer->template getEntitiesCount< MeshEntity::getEntityDimension() >(); + 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, @@ -137,7 +139,7 @@ Traverser< Mesh, MeshEntity, EntitiesDimension >:: processLocalEntities( const MeshPointer& meshPointer, UserData userData ) const { - const auto ghostsOffset = meshPointer->template getGhostEntitiesOffset< MeshEntity::getEntityDimension() >(); + const GlobalIndexType ghostsOffset = meshPointer->template getGhostEntitiesOffset< MeshEntity::getEntityDimension() >(); auto kernel = [] __cuda_callable__ ( const GlobalIndexType entityIndex, const Mesh* mesh, diff --git a/src/Tools/tnl-game-of-life.cpp b/src/Tools/tnl-game-of-life.cpp index b6df2d3f6..e7c488844 100644 --- a/src/Tools/tnl-game-of-life.cpp +++ b/src/Tools/tnl-game-of-life.cpp @@ -176,7 +176,6 @@ bool runGameOfLife( const Mesh& mesh ) MyMeshFunction f_in( localMesh ), f_out( localMesh ); f_in.getData().setValue( 0 ); - const Index entitiesCount = localMesh.template getEntitiesCount< MyMeshFunction::getEntitiesDimension() >(); const Index pointsCount = localMesh.template getEntitiesCount< 0 >(); const Index cellsCount = localMesh.template getEntitiesCount< Mesh::getMeshDimension() >(); @@ -284,6 +283,12 @@ bool runGameOfLife( const Mesh& mesh ) // 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 { @@ -291,41 +296,39 @@ bool runGameOfLife( const Mesh& mesh ) if( CommunicatorType::GetRank() == 0 ) std::cout << "Computing iteration " << iteration << "..." << std::endl; - // traverse the mesh - for( Index i = 0; i < entitiesCount; i++ ) { - // end on the first ghost entity - // TODO: the mesh should allow iteration over local entities only - if( localMesh.template isGhostEntity< MyMeshFunction::getEntitiesDimension() >( i ) ) - break; + // 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 < localMesh.getCellNeighborsCount( i ); n++ ) { - const Index neighbor = localMesh.getCellNeighborIndex( i, n ); - sum += f_in.getData()[ neighbor ]; + 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.getData()[ i ]; + 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.getData()[ i ] = 0; + f_out_view[ i ] = 0; // any live cell with two or three live neighbors survives else if( sum < 4 ) - f_out.getData()[ i ] = 1; + f_out_view[ i ] = 1; // any live cell with more than three live neighbors dies else - f_out.getData()[ i ] = 0; + f_out_view[ i ] = 0; } else { // any dead cell with exactly three live neighbors becomes a live cell if( sum == 3 ) - f_out.getData()[ i ] = 1; + f_out_view[ i ] = 1; // any other dead cell remains dead else - f_out.getData()[ i ] = 0; + f_out_view[ i ] = 0; } - } + }; + localMesh.template forLocal< MyMeshFunction::getEntitiesDimension() >( kernel ); // synchronize sync.synchronize( f_out ); diff --git a/src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.h b/src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.h index a266ec7b5..c11352658 100644 --- a/src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.h +++ b/src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.h @@ -470,6 +470,37 @@ void testTraverserOnDevice( const MeshType& mesh, 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 > diff --git a/src/UnitTests/Meshes/EntityTagsTest.h b/src/UnitTests/Meshes/EntityTagsTest.h index 5cce83784..dc14541ea 100644 --- a/src/UnitTests/Meshes/EntityTagsTest.h +++ b/src/UnitTests/Meshes/EntityTagsTest.h @@ -84,16 +84,16 @@ TEST( MeshTest, RegularMeshOfQuadrilateralsTest ) std::vector< IndexType > interiorCells = {4, 7}; // Test boundary cells - EXPECT_EQ( mesh.template getBoundaryEntitiesCount< 2 >(), (int) boundaryCells.size() ); + 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 getBoundaryEntityIndex< 2 >( i ), boundaryCells[ i ] ); + EXPECT_EQ( mesh.template getBoundaryIndices< 2 >()[ i ], boundaryCells[ i ] ); } // Test interior cells - EXPECT_EQ( mesh.template getInteriorEntitiesCount< 2 >(), (int) interiorCells.size() ); + 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 getInteriorEntityIndex< 2 >( i ), interiorCells[ i ] ); + EXPECT_EQ( mesh.template getInteriorIndices< 2 >()[ i ], interiorCells[ i ] ); } // Test setting other tags @@ -116,16 +116,16 @@ TEST( MeshTest, RegularMeshOfQuadrilateralsTest ) 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() ); + 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 getBoundaryEntityIndex< 1 >( i ), boundaryFaces[ i ] ); + EXPECT_EQ( mesh.template getBoundaryIndices< 1 >()[ i ], 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 >( interiorFaces[ i ] ) ); - EXPECT_EQ( mesh.template getInteriorEntityIndex< 1 >( i ), interiorFaces[ i ] ); + EXPECT_EQ( mesh.template getInteriorIndices< 1 >()[ i ], interiorFaces[ i ] ); } // Test setting other tags diff --git a/src/UnitTests/Meshes/MeshOrderingTest.h b/src/UnitTests/Meshes/MeshOrderingTest.h index 93b747fd2..634f94454 100644 --- a/src/UnitTests/Meshes/MeshOrderingTest.h +++ b/src/UnitTests/Meshes/MeshOrderingTest.h @@ -204,18 +204,18 @@ 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 diff --git a/src/UnitTests/Meshes/MeshTraverserTest.h b/src/UnitTests/Meshes/MeshTraverserTest.h index b4846a141..785bc40e7 100644 --- a/src/UnitTests/Meshes/MeshTraverserTest.h +++ b/src/UnitTests/Meshes/MeshTraverserTest.h @@ -74,6 +74,27 @@ void testTraverser( 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 ) -- GitLab From 698766e067361959a11919410ea1cee00ce09acc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Wed, 24 Jun 2020 14:25:52 +0200 Subject: [PATCH 62/73] Cleanup: removed getDistributedMesh() from Mesh --- src/TNL/Meshes/Mesh.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/TNL/Meshes/Mesh.h b/src/TNL/Meshes/Mesh.h index f37f3ba67..966afa807 100644 --- a/src/TNL/Meshes/Mesh.h +++ b/src/TNL/Meshes/Mesh.h @@ -257,11 +257,6 @@ class Mesh void writeProlog( Logger& logger ) const; - DistributedMeshes::DistributedMesh< Mesh >* getDistributedMesh(void) const - { - return nullptr; - } - protected: // Methods for the mesh initializer using StorageBaseType::getPoints; -- GitLab From c975952091ad46d4c2812546c061bcf84389337e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sun, 12 Jul 2020 19:18:12 +0200 Subject: [PATCH 63/73] Implemented GlobalIndexStorage for the remaining entities --- .../DistributedMeshes/DistributedMesh.h | 17 ++--- .../DistributedMeshes/GlobalIndexStorage.h | 73 ++++++++++++++++++- 2 files changed, 76 insertions(+), 14 deletions(-) diff --git a/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h b/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h index 053f1d289..2cb44aaf4 100644 --- a/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h +++ b/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h @@ -22,8 +22,7 @@ namespace DistributedMeshes { template< typename Mesh > class DistributedMesh -: protected GlobalIndexStorage< Mesh, 0 >, - protected GlobalIndexStorage< Mesh, Mesh::getMeshDimension() > +: protected GlobalIndexStorageFamily< Mesh > { public: using MeshType = Mesh; @@ -55,8 +54,7 @@ public: template< typename Mesh_ > DistributedMesh& operator=( const Mesh_& other ) { - getGlobalIndices< 0 >() = other.template getGlobalIndices< 0 >(); - getGlobalIndices< Mesh::getMeshDimension() >() = other.template getGlobalIndices< Mesh::getMeshDimension() >(); + GlobalIndexStorageFamily< Mesh >::operator=( other ); localMesh = other.getLocalMesh(); group = other.getCommunicationGroup(); ghostLevels = other.getGhostLevels(); @@ -67,8 +65,7 @@ public: bool operator==( const DistributedMesh& other ) const { - return ( getGlobalIndices< 0 >() == other.template getGlobalIndices< 0 >() && - getGlobalIndices< Mesh::getMeshDimension() >() == other.template getGlobalIndices< Mesh::getMeshDimension() >() && + return ( GlobalIndexStorageFamily< Mesh, DeviceType >::operator==( other ) && localMesh == other.getLocalMesh() && group == other.getCommunicationGroup() && ghostLevels == other.getGhostLevels() && @@ -137,18 +134,14 @@ public: const GlobalIndexArray& getGlobalIndices() const { - static_assert( Dimension == 0 || Dimension == MeshType::getMeshDimension(), - "Global index array for this dimension is not implemented yet." ); - return GlobalIndexStorage< MeshType, Dimension >::getGlobalIndices(); + return GlobalIndexStorage< MeshType, DeviceType, Dimension >::getGlobalIndices(); } template< int Dimension > GlobalIndexArray& getGlobalIndices() { - static_assert( Dimension == 0 || Dimension == MeshType::getMeshDimension(), - "Global index array for this dimension is not implemented yet." ); - return GlobalIndexStorage< MeshType, Dimension >::getGlobalIndices(); + return GlobalIndexStorage< MeshType, DeviceType, Dimension >::getGlobalIndices(); } VTKTypesArrayType& diff --git a/src/TNL/Meshes/DistributedMeshes/GlobalIndexStorage.h b/src/TNL/Meshes/DistributedMeshes/GlobalIndexStorage.h index 7412b79b2..cba45a9e7 100644 --- a/src/TNL/Meshes/DistributedMeshes/GlobalIndexStorage.h +++ b/src/TNL/Meshes/DistributedMeshes/GlobalIndexStorage.h @@ -1,5 +1,5 @@ /*************************************************************************** - DistributedMesh.h - description + GlobalIndexStorage.h - description ------------------- begin : April 11, 2020 copyright : (C) 2020 by Tomas Oberhuber et al. @@ -12,16 +12,36 @@ #pragma once +#include + namespace TNL { namespace Meshes { namespace DistributedMeshes { -template< typename Mesh, int Dimension > +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 { @@ -38,6 +58,55 @@ 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 -- GitLab From d6bd51b7420db48186d283155bea5ed311c31373 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Tue, 14 Jul 2020 15:20:43 +0200 Subject: [PATCH 64/73] Refactoring distributed mesh and grid synchronizers: using DistributedMesh as the primary template parameter --- .../hamilton-jacobi/tnlDirectEikonalProblem.h | 2 +- .../hamilton-jacobi/tnlFastSweepingMethod.h | 4 +- .../DistributedGridSynchronizer.h | 71 +++++---- .../DistributedMeshSynchronizer.h | 141 ++++++++---------- src/TNL/Problems/HeatEquationProblem.h | 2 +- src/Tools/tnl-game-of-life.cpp | 11 +- .../CutDistributedMeshFunctionTest.cpp | 12 +- .../DistributedMeshes/DistributedGridIOTest.h | 2 +- .../DistributedGridIO_MPIIOTest.h | 2 +- .../DistributedGridTest_1D.cpp | 2 +- .../DistributedGridTest_2D.cpp | 2 +- .../DistributedGridTest_3D.cpp | 2 +- .../DistributedMeshes/DistributedMeshTest.h | 2 +- .../DistributedVectorFieldIO_MPIIOTestBase.h | 2 +- 14 files changed, 121 insertions(+), 136 deletions(-) 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 fff0e35dd..26d96d121 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 7023c09a9..2f933cbec 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/Meshes/DistributedMeshes/DistributedGridSynchronizer.h b/src/TNL/Meshes/DistributedMeshes/DistributedGridSynchronizer.h index e5d467615..5a1150240 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/DistributedMeshSynchronizer.h b/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h index ed5d51446..6a2510054 100644 --- a/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h +++ b/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h @@ -12,39 +12,26 @@ #pragma once +#include #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 DistributedMesh, + int EntityDimension = DistributedMesh::getMeshDimension() > class DistributedMeshSynchronizer { public: - // TODO: generalize - static constexpr int EntityDimension = MeshFunction::getEntitiesDimension(); - static_assert( EntityDimension == 0 || EntityDimension == MeshFunction::getMeshDimension(), - "Synchronization for entities of the specified dimension is not implemented yet." ); - - using RealType = typename MeshFunction::RealType; - using DeviceType = typename MeshFunction::DeviceType; - using IndexType = typename MeshFunction::IndexType; - using CommunicatorType = Communicators::MpiCommunicator; + using DeviceType = typename DistributedMesh::DeviceType; + using GlobalIndexType = typename DistributedMesh::GlobalIndexType; + using CommunicatorType = typename DistributedMesh::CommunicatorType; DistributedMeshSynchronizer() = default; - template< typename DistributedMesh > void initialize( const DistributedMesh& mesh ) { - static_assert( std::is_same< typename DistributedMesh::MeshType, typename MeshFunction::MeshType >::value, - "The type of the local mesh does not match the type of the mesh function's mesh." ); - static_assert( std::is_same< typename DistributedMesh::CommunicatorType, CommunicatorType >::value, - "DistributedMesh::CommunnicatorType does not match" ); if( mesh.getGhostLevels() <= 0 ) throw std::logic_error( "There are no ghost levels on the distributed mesh." ); localEntitiesCount = mesh.getLocalMesh().template getEntitiesCount< EntityDimension >(); @@ -61,19 +48,18 @@ public: // exchange the global index offsets so that each rank can determine the // owner of every entity by its global index - const IndexType ownStart = mesh.template getGlobalIndices< EntityDimension >().getElement( 0 ); - Containers::Array< IndexType, Devices::Host, int > offsets( nproc ); + const GlobalIndexType ownStart = mesh.template getGlobalIndices< EntityDimension >().getElement( 0 ); + Containers::Array< GlobalIndexType, Devices::Host, int > offsets( nproc ); { - Containers::Array< IndexType, Devices::Host, int > sendbuf( nproc ); + Containers::Array< GlobalIndexType, Devices::Host, int > sendbuf( nproc ); sendbuf.setValue( ownStart ); CommunicatorType::Alltoall( sendbuf.getData(), 1, offsets.getData(), 1, group ); } - auto getOwner = [&] ( IndexType idx ) + auto getOwner = [&] ( GlobalIndexType idx ) { - // TODO: is there a more efficient way? for( int i = 0; i < nproc - 1; i++ ) if( offsets[ i ] <= idx && idx < offsets[ i + 1 ] ) return i; @@ -81,32 +67,34 @@ public: }; // count local ghost entities for each rank - Containers::Array< IndexType, Devices::Host, int > localGhostCounts( nproc ); - localGhostCounts.setValue( 0 ); - Containers::Array< IndexType, Devices::Host, IndexType > hostGlobalIndices; - hostGlobalIndices = mesh.template getGlobalIndices< EntityDimension >(); - IndexType prev_global_idx = 0; - for( IndexType local_idx = mesh.getLocalMesh().template getGhostEntitiesOffset< EntityDimension >(); - local_idx < localEntitiesCount; - local_idx++ ) + Containers::Array< GlobalIndexType, Devices::Host, int > localGhostCounts( nproc ); { - 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 IndexType 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 = getOwner( 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 ]; + 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 = getOwner( 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< IndexType, Devices::Host, int > sendbuf; + 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++ ) @@ -126,17 +114,15 @@ public: for( int i = 0; i < nproc; i++ ) ghostNeighborOffsets[ i + 1 ] = ghostNeighborOffsets[ i ] + ghostEntitiesCounts( i, rank ); - // allocate ghost neighbors and send buffers + // allocate ghost neighbors ghostNeighbors.setSize( ghostNeighborOffsets[ nproc ] ); - sendBuffers.setSize( ghostNeighborOffsets[ nproc ] ); - // send indices of ghost entities - set them as ghost neighbors on the - // target rank + // 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 - IndexType firstGhost = mesh.getLocalMesh().template getGhostEntitiesOffset< EntityDimension >(); + GlobalIndexType firstGhost = mesh.getLocalMesh().template getGhostEntitiesOffset< EntityDimension >(); for( int i = 0; i < nproc; i++ ) { if( ghostEntitiesCounts( rank, i ) > 0 ) { requests.push_back( CommunicatorType::ISend( @@ -167,29 +153,18 @@ public: // convert received ghost indices from global to local ghostNeighbors -= ownStart; } - -#if 0 - CommunicatorType::Barrier(); - for( int i = 0; i < nproc; i++ ) { - if( i == rank ) { - std::cout << "rank = " << rank << "\n"; -// std::cout << "offsets = " << offsets << std::endl; - std::cout << "local ghost counts = " << localGhostCounts << "\n"; - std::cout << "ghost entities counts matrix:\n" << ghostEntitiesCounts; - std::cout << "global indices = " << mesh.template getGlobalIndices< EntityDimension >() << "\n"; - std::cout << "ghost offsets = " << ghostOffsets << "\n"; - std::cout << "ghost neighbors = " << ghostNeighbors << "\n"; - std::cout.flush(); - } - CommunicatorType::Barrier(); - } -#endif } + template< typename MeshFunction > 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." ); TNL_ASSERT_EQ( function.getData().getSize(), localEntitiesCount, "The mesh function does not have the expected size." ); + using RealType = typename MeshFunction::RealType; // GOTCHA: https://devblogs.nvidia.com/cuda-pro-tip-always-set-current-device-avoid-multithreading-bugs/ #ifdef HAVE_CUDA @@ -200,6 +175,9 @@ public: 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( ghostNeighborOffsets[ nproc ] * sizeof(RealType) ); + // buffer for asynchronous communication requests std::vector< typename CommunicatorType::Request > requests; @@ -213,23 +191,24 @@ public: } } - auto sendBuffersView = sendBuffers.getView(); + Containers::ArrayView< RealType, DeviceType, GlobalIndexType > sendBuffersView; + sendBuffersView.bind( reinterpret_cast( sendBuffers.getData() ), ghostNeighborOffsets[ nproc ] ); const auto ghostNeighborsView = ghostNeighbors.getConstView(); const auto functionDataView = function.getData().getConstView(); - auto copy_kernel = [sendBuffersView, functionDataView, ghostNeighborsView] __cuda_callable__ ( IndexType k, IndexType offset ) mutable + auto copy_kernel = [sendBuffersView, functionDataView, ghostNeighborsView] __cuda_callable__ ( GlobalIndexType k, GlobalIndexType offset ) mutable { sendBuffersView[ offset + k ] = functionDataView[ ghostNeighborsView[ offset + k ] ]; }; for( int i = 0; i < nproc; i++ ) { if( ghostEntitiesCounts( i, rank ) > 0 ) { - const IndexType offset = ghostNeighborOffsets[ i ]; + const GlobalIndexType offset = ghostNeighborOffsets[ i ]; // copy data to send buffers - Algorithms::ParallelFor< DeviceType >::exec( (IndexType) 0, ghostEntitiesCounts( i, rank ), copy_kernel, offset ); + Algorithms::ParallelFor< DeviceType >::exec( (GlobalIndexType) 0, ghostEntitiesCounts( i, rank ), copy_kernel, offset ); // issue async send operation requests.push_back( CommunicatorType::ISend( - sendBuffers.getData() + ghostNeighborOffsets[ i ], + sendBuffersView.getData() + ghostNeighborOffsets[ i ], ghostEntitiesCounts( i, rank ), i, 0, group ) ); } @@ -242,7 +221,7 @@ public: protected: // count of local entities (including ghosts) - used only for asserts in the // synchronize method - IndexType localEntitiesCount = 0; + GlobalIndexType localEntitiesCount = 0; // GOTCHA (see above) int gpu_id = 0; @@ -260,14 +239,14 @@ protected: * - 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< IndexType, Devices::Host, int > ghostEntitiesCounts; + Matrices::DenseMatrix< GlobalIndexType, Devices::Host, int > ghostEntitiesCounts; /** * Ghost offsets: the i-th value is the local index of the first ghost * entity owned by the i-th rank. All ghost entities owned by the i-th * rank are assumed to be indexed contiguously in the local mesh. */ - Containers::Array< IndexType, Devices::Host, int > ghostOffsets; + Containers::Array< GlobalIndexType, Devices::Host, int > ghostOffsets; /** * Ghost neighbor offsets: array of size nproc + 1 where the i-th value is @@ -275,7 +254,7 @@ protected: * value is the size of the ghostNeighbors and sendBuffers arrays (see * below). */ - Containers::Array< IndexType, Devices::Host, int > ghostNeighborOffsets; + Containers::Array< GlobalIndexType, Devices::Host, int > ghostNeighborOffsets; /** * Ghost neighbor indices: array containing local indices of the entities @@ -286,15 +265,17 @@ protected: * ghost neighbor indices cannot be made contiguous in general so we need the * send buffers. */ - Containers::Vector< IndexType, DeviceType, IndexType > ghostNeighbors; + Containers::Vector< GlobalIndexType, DeviceType, GlobalIndexType > ghostNeighbors; /** * Send buffers: array for buffering the mesh function values which will be - * sent to other ranks. 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). + * 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< RealType, DeviceType, IndexType > sendBuffers; + Containers::Array< std::uint8_t, DeviceType, GlobalIndexType > sendBuffers; }; } // namespace DistributedMeshes diff --git a/src/TNL/Problems/HeatEquationProblem.h b/src/TNL/Problems/HeatEquationProblem.h index da261f039..342abe1ba 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/Tools/tnl-game-of-life.cpp b/src/Tools/tnl-game-of-life.cpp index e7c488844..748f0a4bf 100644 --- a/src/Tools/tnl-game-of-life.cpp +++ b/src/Tools/tnl-game-of-life.cpp @@ -133,6 +133,11 @@ bool runGameOfLife( const Mesh& mesh ) // 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; @@ -167,12 +172,6 @@ bool runGameOfLife( const Mesh& mesh ) VectorType data; }; - // test synchronizer -// using MeshFunction = Functions::MeshFunction< LocalMesh, Mesh::getMeshDimension(), Real >; - using Synchronizer = Meshes::DistributedMeshes::DistributedMeshSynchronizer< MyMeshFunction, Mesh >; - Synchronizer sync; - sync.initialize( mesh ); - MyMeshFunction f_in( localMesh ), f_out( localMesh ); f_in.getData().setValue( 0 ); diff --git a/src/UnitTests/Meshes/DistributedMeshes/CutDistributedMeshFunctionTest.cpp b/src/UnitTests/Meshes/DistributedMeshes/CutDistributedMeshFunctionTest.cpp index 12e706768..4d5bb4baf 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 9eaf27995..6b7c489af 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 9cd77cf92..5bbad8f03 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 707a99d83..7cb44ef22 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 b69a85b15..71370cae2 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 8b0ac5aec..765341c1e 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.h b/src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.h index c11352658..0c0039c77 100644 --- a/src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.h +++ b/src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.h @@ -578,7 +578,7 @@ void testSynchronizerOnDevice( const MeshType& mesh ) using DeviceMesh = DistributedMesh< LocalMesh >; using IndexType = typename MeshType::GlobalIndexType; using MeshFunction = Functions::MeshFunction< LocalMesh, EntityType::getEntityDimension(), IndexType >; - using Synchronizer = DistributedMeshes::DistributedMeshSynchronizer< MeshFunction >; + using Synchronizer = DistributedMeshes::DistributedMeshSynchronizer< DeviceMesh, EntityType::getEntityDimension() >; // initialize DeviceMesh deviceMesh; diff --git a/src/UnitTests/Meshes/DistributedMeshes/DistributedVectorFieldIO_MPIIOTestBase.h b/src/UnitTests/Meshes/DistributedMeshes/DistributedVectorFieldIO_MPIIOTestBase.h index 3b82492e0..f35ec8e08 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 Date: Wed, 15 Jul 2020 15:58:09 +0200 Subject: [PATCH 65/73] Refactoring DistributedMeshSynchronizer: removed localentitiesCount --- .../DistributedMeshSynchronizer.h | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h b/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h index 6a2510054..50252f79a 100644 --- a/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h +++ b/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h @@ -34,7 +34,8 @@ public: { if( mesh.getGhostLevels() <= 0 ) throw std::logic_error( "There are no ghost levels on the distributed mesh." ); - localEntitiesCount = mesh.getLocalMesh().template getEntitiesCount< EntityDimension >(); + 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 @@ -99,14 +100,14 @@ public: // 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.getElement( i ) ); + sendbuf.setElement( j, i, localGhostCounts[ i ] ); CommunicatorType::Alltoall( &sendbuf(0, 0), nproc, &ghostEntitiesCounts(0, 0), nproc, group ); } // allocate ghost offsets - ghostOffsets.setSize( nproc ); + ghostOffsets.setSize( nproc + 1 ); // set ghost neighbor offsets ghostNeighborOffsets.setSize( nproc + 1 ); @@ -122,20 +123,21 @@ public: std::vector< typename CommunicatorType::Request > requests; // send our ghost indices to the neighboring ranks - GlobalIndexType firstGhost = mesh.getLocalMesh().template getGhostEntitiesOffset< EntityDimension >(); + 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() + firstGhost, + mesh.template getGlobalIndices< EntityDimension >().getData() + ghostOffset, ghostEntitiesCounts( rank, i ), i, 0, group ) ); - // update ghost offsets - ghostOffsets[ i ] = firstGhost; - firstGhost += ghostEntitiesCounts( rank, i ); + ghostOffset += ghostEntitiesCounts( rank, i ); } - else - ghostOffsets[ i ] = 0; + // 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++ ) { @@ -162,7 +164,7 @@ public: "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." ); - TNL_ASSERT_EQ( function.getData().getSize(), localEntitiesCount, + TNL_ASSERT_EQ( function.getData().getSize(), ghostOffsets[ ghostOffsets.getSize() - 1 ], "The mesh function does not have the expected size." ); using RealType = typename MeshFunction::RealType; @@ -219,10 +221,6 @@ public: } protected: - // count of local entities (including ghosts) - used only for asserts in the - // synchronize method - GlobalIndexType localEntitiesCount = 0; - // GOTCHA (see above) int gpu_id = 0; @@ -242,9 +240,10 @@ protected: Matrices::DenseMatrix< GlobalIndexType, Devices::Host, int > ghostEntitiesCounts; /** - * Ghost offsets: the i-th value is the local index of the first ghost - * entity owned by the i-th rank. All ghost entities owned by the i-th - * rank are assumed to be indexed contiguously in the local mesh. + * 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; -- GitLab From 84d33d3dea85b896776d2d0c539b85c1ac6497cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Fri, 17 Jul 2020 11:27:07 +0200 Subject: [PATCH 66/73] Refactoring entity tags layer: added getEntityTagsView, removed resetEntityTags, optimized Mesh::reorderEntities --- src/TNL/Meshes/Mesh.hpp | 7 +++-- .../MeshDetails/IndexPermutationApplier.h | 23 +++++++------- .../layers/EntityTags/Initializer.h | 25 +++++++++++++-- .../MeshDetails/layers/EntityTags/Layer.h | 15 +++++++-- .../layers/EntityTags/LayerFamily.h | 31 +++++++++++++------ 5 files changed, 74 insertions(+), 27 deletions(-) diff --git a/src/TNL/Meshes/Mesh.hpp b/src/TNL/Meshes/Mesh.hpp index 39c9bda31..278ef215d 100644 --- a/src/TNL/Meshes/Mesh.hpp +++ b/src/TNL/Meshes/Mesh.hpp @@ -333,8 +333,11 @@ reorderEntities( const GlobalIndexArray& perm, #endif IndexPermutationApplier< Mesh, Dimension >::exec( *this, perm, iperm ); - // update boundary tags - static_cast< EntityTagsLayerFamily* >( this )->initLayer(); + // permute entity tags + auto tags = this->template getEntityTagsView< Dimension >(); + IndexPermutationApplier< Mesh, Dimension >::permuteArray( tags, perm ); + // update the entity tags layer + this->template updateEntityTagsLayer< Dimension >(); } diff --git a/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h b/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h index f875fac28..71e9fab0e 100644 --- a/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h +++ b/src/TNL/Meshes/MeshDetails/IndexPermutationApplier.h @@ -134,29 +134,30 @@ private: static void permuteDualGraph( Mesh_& mesh, const GlobalIndexArray& perm, const GlobalIndexArray& iperm ) {} public: - template< typename Array > - static void permuteArray( Array& array, const GlobalIndexArray& perm ) + template< typename ArrayOrView > + static void permuteArray( ArrayOrView& array, const GlobalIndexArray& perm ) { - using IndexType = typename Array::IndexType; - using DeviceType = typename Array::DeviceType; + using ValueType = typename ArrayOrView::ValueType; + using IndexType = typename ArrayOrView::IndexType; + using DeviceType = typename ArrayOrView::DeviceType; - Array buffer( array.getSize() ); + Containers::Array< ValueType, DeviceType, IndexType > buffer( array.getSize() ); - // kernel to copy entities to new array, applying the permutation + // kernel to copy values to new array, applying the permutation auto kernel1 = [] __cuda_callable__ ( IndexType i, - const typename Array::ValueType* array, - typename Array::ValueType* buffer, + const ValueType* array, + ValueType* buffer, const IndexType* perm ) { 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, - typename Array::ValueType* array, - const typename Array::ValueType* buffer ) + ValueType* array, + const ValueType* buffer ) { array[ i ] = buffer[ i ]; }; diff --git a/src/TNL/Meshes/MeshDetails/layers/EntityTags/Initializer.h b/src/TNL/Meshes/MeshDetails/layers/EntityTags/Initializer.h index 3409bd149..95e29182e 100644 --- a/src/TNL/Meshes/MeshDetails/layers/EntityTags/Initializer.h +++ b/src/TNL/Meshes/MeshDetails/layers/EntityTags/Initializer.h @@ -57,11 +57,32 @@ protected: }; template< int Dimension > - struct ResetEntityTags + 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 resetEntityTags< Dimension >(); + Worker< enabled >::exec( mesh ); } }; diff --git a/src/TNL/Meshes/MeshDetails/layers/EntityTags/Layer.h b/src/TNL/Meshes/MeshDetails/layers/EntityTags/Layer.h index 54c74e3e6..dab80fc7e 100644 --- a/src/TNL/Meshes/MeshDetails/layers/EntityTags/Layer.h +++ b/src/TNL/Meshes/MeshDetails/layers/EntityTags/Layer.h @@ -81,9 +81,18 @@ public: ghostsOffset = entitiesCount; } - void resetEntityTags( DimensionTag ) + __cuda_callable__ + typename EntityTagsArrayType::ViewType + getEntityTagsView( DimensionTag ) + { + return tags.getView(); + } + + __cuda_callable__ + typename EntityTagsArrayType::ConstViewType + getEntityTagsView( DimensionTag ) const { - tags.setValue( 0 ); + return tags.getConstView(); } __cuda_callable__ @@ -268,7 +277,7 @@ protected: Layer& operator=( const Layer< MeshConfig, Device_, DimensionTag >& other ) { return *this; } void setEntitiesCount( DimensionTag, const GlobalIndexType& entitiesCount ) {} - void resetEntityTags( DimensionTag ) {} + void getEntityTagsView( DimensionTag ) {} void getEntityTag( DimensionTag, const GlobalIndexType& ) const {} void addEntityTag( DimensionTag, const GlobalIndexType&, TagType ) const {} void removeEntityTag( DimensionTag, const GlobalIndexType&, TagType ) const {} diff --git a/src/TNL/Meshes/MeshDetails/layers/EntityTags/LayerFamily.h b/src/TNL/Meshes/MeshDetails/layers/EntityTags/LayerFamily.h index 8dcd35753..2914850a7 100644 --- a/src/TNL/Meshes/MeshDetails/layers/EntityTags/LayerFamily.h +++ b/src/TNL/Meshes/MeshDetails/layers/EntityTags/LayerFamily.h @@ -28,7 +28,7 @@ class LayerInheritor using BaseType = LayerInheritor< MeshConfig, Device, typename Dimension::Increment >; protected: using LayerType::setEntitiesCount; - using LayerType::resetEntityTags; + using LayerType::getEntityTagsView; using LayerType::getEntityTag; using LayerType::addEntityTag; using LayerType::removeEntityTag; @@ -41,7 +41,7 @@ protected: using LayerType::getGhostEntitiesOffset; using BaseType::setEntitiesCount; - using BaseType::resetEntityTags; + using BaseType::getEntityTagsView; using BaseType::getEntityTag; using BaseType::addEntityTag; using BaseType::removeEntityTag; @@ -113,7 +113,7 @@ class LayerInheritor< MeshConfig, Device, DimensionTag< MeshConfig::meshDimensio { protected: void setEntitiesCount(); - void resetEntityTags(); + void getEntityTagsView(); void getEntityTag() const; void addEntityTag(); void removeEntityTag(); @@ -154,6 +154,7 @@ 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 > @@ -168,6 +169,24 @@ public: using BaseType::BaseType; using BaseType::operator=; + template< int Dimension > + __cuda_callable__ + typename EntityTagsArrayType::ViewType + getEntityTagsView() + { + 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__ + typename EntityTagsArrayType::ConstViewType + getEntityTagsView() const + { + 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__ TagType getEntityTag( const GlobalIndexType& entityIndex ) const @@ -252,12 +271,6 @@ protected: { BaseType::setEntitiesCount( DimensionTag< Dimension >(), entitiesCount ); } - - template< int Dimension > - void resetEntityTags() - { - BaseType::resetEntityTags( DimensionTag< Dimension >() ); - } }; } // namespace EntityTags -- GitLab From 3c66284201170a74367b0adb68af18823f9d4fb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Fri, 17 Jul 2020 13:20:34 +0200 Subject: [PATCH 67/73] Added wrapper method reorderEntities to DistributedMesh --- .../Meshes/DistributedMeshes/DistributedMesh.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h b/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h index 2cb44aaf4..2ea6d8539 100644 --- a/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h +++ b/src/TNL/Meshes/DistributedMeshes/DistributedMesh.h @@ -15,6 +15,7 @@ #include #include #include +#include namespace TNL { namespace Meshes { @@ -168,6 +169,21 @@ public: 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 { -- GitLab From 2a13c0dc5b778bbeb1762fb5467b6919314bd10e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Fri, 17 Jul 2020 13:24:16 +0200 Subject: [PATCH 68/73] Added function distributeSubentities and tests for DistributedMeshSynchronizer on faces Closes #73. --- .../DistributedMeshSynchronizer.h | 137 ++++++++++++ .../DistributedMeshes/distributeSubentities.h | 195 ++++++++++++++++++ src/TNL/Meshes/Mesh.h | 2 - .../DistributedMeshes/DistributedMeshTest.h | 14 ++ 4 files changed, 346 insertions(+), 2 deletions(-) create mode 100644 src/TNL/Meshes/DistributedMeshes/distributeSubentities.h diff --git a/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h b/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h index 50252f79a..3a3fe7432 100644 --- a/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h +++ b/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h @@ -220,6 +220,143 @@ public: 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) + // returns: a tuple of the received rankOffsets, rowPointers and columnIndices arrays + template< typename SparsePattern > + auto + synchronizeSparse( const SparsePattern& pattern ) + { + 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_rowPointers, send_columnIndices, recv_rowPointers, recv_columnIndices; + + // sending part + { + // 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 ); + + // 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(); + rowPtr++; + } + } + + // 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 ); + + // set row pointers + GlobalIndexType rowPtr = 0; + recv_rowPointers[ rowPtr ] = 0; + for( int i = 0; i < nproc; i++ ) { + if( i == rank ) + continue; + 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++; + } + } + + // 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 send 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 ); + } + + // public const accessors for the communication pattern matrix and index arrays which were + // created in the `initialize` method + 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; diff --git a/src/TNL/Meshes/DistributedMeshes/distributeSubentities.h b/src/TNL/Meshes/DistributedMeshes/distributeSubentities.h new file mode 100644 index 000000000..0b989533a --- /dev/null +++ b/src/TNL/Meshes/DistributedMeshes/distributeSubentities.h @@ -0,0 +1,195 @@ +/*************************************************************************** + 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 + +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() ); + + // exchange the global vertex index offsets so that each rank can determine the + // owner of every vertex by its global index + const GlobalIndexType ownVertexStart = mesh.template getGlobalIndices< 0 >().getElement( 0 ); + Containers::Array< GlobalIndexType, Devices::Host, int > vertexOffsets( nproc ); + { + Containers::Array< GlobalIndexType, Devices::Host, int > sendbuf( nproc ); + sendbuf.setValue( ownVertexStart ); + CommunicatorType::Alltoall( sendbuf.getData(), 1, + vertexOffsets.getData(), 1, + mesh.getCommunicationGroup() ); + } + + auto getVertexOwner = [&] ( GlobalIndexType local_idx ) -> int + { + const GlobalIndexType global_idx = mesh.template getGlobalIndices< 0 >()[ local_idx ]; + for( int i = 0; i < nproc - 1; i++ ) + if( vertexOffsets[ i ] <= global_idx && global_idx < vertexOffsets[ i + 1 ] ) + return i; + return nproc - 1; + }; + + auto getEntityOwner = [&] ( GlobalIndexType local_idx ) -> int + { + auto entity = mesh.getLocalMesh().template getEntity< Dimension >( local_idx ); + int owner = 0; + for( LocalIndexType v = 0; v < entity.template getSubentitiesCount< 0 >(); v++ ) { + const GlobalIndexType gv = entity.template getSubentityIndex< 0 >( v ); + owner = TNL::max( 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 cell data to prepare the communication pattern + DistributedMeshSynchronizer< DistributedMesh > synchronizer; + synchronizer.initialize( mesh ); + + // 7. exchange local indices for ghost entities + const auto sparseResult = synchronizer.synchronizeSparse( localMesh.template getSubentitiesMatrix< DistributedMesh::getMeshDimension(), Dimension >() ); + const auto& rankOffsets = std::get< 0 >( sparseResult ); + const auto& rowPointers = std::get< 1 >( sparseResult ); + const auto& columnIndices = std::get< 2 >( sparseResult ); + + // 8. set the global indices of our ghost entities + for( int i = 0; i < nproc; i++ ) { + if( i == rank ) + continue; + for( GlobalIndexType cell = synchronizer.getGhostOffsets()[ i ]; cell < synchronizer.getGhostOffsets()[ i + 1 ]; cell++ ) { + for( LocalIndexType e = 0; e < mesh.getLocalMesh().template getSubentitiesCount< DistributedMesh::getMeshDimension(), Dimension >( cell ); e++ ) { + const GlobalIndexType entityIndex = mesh.getLocalMesh().template getSubentityIndex< DistributedMesh::getMeshDimension(), Dimension >( cell, e ); + const int owner = getEntityOwner( entityIndex ); + // pick the right owner as we might have received an index from multiple ranks + if( owner == i ) { + const GlobalIndexType ghostOffset = cell - synchronizer.getGhostOffsets()[ owner ]; + // global index = owner's local index + owner's offset + const GlobalIndexType globalEntityIndex = columnIndices[ rowPointers[ rankOffsets[ owner ] + ghostOffset ] + e ] + globalOffsets[ owner ]; + mesh.template getGlobalIndices< Dimension >()[ entityIndex ] = globalEntityIndex; + } + } + } + } + + // 9. 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/Mesh.h b/src/TNL/Meshes/Mesh.h index 966afa807..e432d8ea6 100644 --- a/src/TNL/Meshes/Mesh.h +++ b/src/TNL/Meshes/Mesh.h @@ -261,8 +261,6 @@ class Mesh // Methods for the mesh initializer using StorageBaseType::getPoints; using StorageBaseType::setEntitiesCount; - using StorageBaseType::getSubentitiesMatrix; - using StorageBaseType::getSuperentitiesMatrix; friend Initializer< MeshConfig >; diff --git a/src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.h b/src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.h index 0c0039c77..641d3af39 100644 --- a/src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.h +++ b/src/UnitTests/Meshes/DistributedMeshes/DistributedMeshTest.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -263,6 +264,11 @@ struct GridDistributor< TNL::Meshes::Grid< 2, Real, Device, Index > > // 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 ) @@ -607,9 +613,13 @@ 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 } @@ -721,6 +731,10 @@ TEST( DistributedMeshTest, PVTUWriterReader ) 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 -- GitLab From 26a2fd6f9afe62326a3d79e6bb11ba90f9148a0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Fri, 17 Jul 2020 13:30:26 +0200 Subject: [PATCH 69/73] Changed default type for LocalIndex to short int --- src/TNL/Meshes/DefaultConfig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TNL/Meshes/DefaultConfig.h b/src/TNL/Meshes/DefaultConfig.h index f149c433e..e4eaba905 100644 --- a/src/TNL/Meshes/DefaultConfig.h +++ b/src/TNL/Meshes/DefaultConfig.h @@ -28,7 +28,7 @@ template< typename Cell, int WorldDimension = Cell::dimension, typename Real = double, typename GlobalIndex = int, - typename LocalIndex = GlobalIndex > + typename LocalIndex = short int > struct DefaultConfig { using CellTopology = Cell; -- GitLab From 59b54f1548baebd8f50e129164585f55fe6ad3dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Fri, 24 Jul 2020 23:34:29 +0200 Subject: [PATCH 70/73] Generalized DistributedMeshSynchronizer::synchronizeSparse - row capacities have to be exchanged in general --- .../DistributedMeshSynchronizer.h | 50 ++++++++++++++++--- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h b/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h index 3a3fe7432..9ed393e31 100644 --- a/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h +++ b/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h @@ -223,10 +223,13 @@ public: // 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 ) + synchronizeSparse( const SparsePattern& pattern, bool assumeConsistentRowCapacities = false ) { TNL_ASSERT_EQ( pattern.getRows(), ghostOffsets[ ghostOffsets.getSize() - 1 ], "invalid sparse pattern matrix" ); @@ -237,7 +240,7 @@ public: 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_rowPointers, send_columnIndices, recv_rowPointers, recv_columnIndices; + Containers::Array< GlobalIndexType, Devices::Host, GlobalIndexType > send_rowCapacities, send_rowPointers, send_columnIndices, recv_rowPointers, recv_columnIndices; // sending part { @@ -248,6 +251,8 @@ public: // 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; @@ -259,8 +264,17 @@ public: 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 @@ -302,26 +316,48 @@ public: // 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; - 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++; + 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 send operation + // 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 ] ], -- GitLab From 81eaf23e9281b7d3da97fa2c213475a90ffe2247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Fri, 24 Jul 2020 23:45:37 +0200 Subject: [PATCH 71/73] Refactoring DistributedMeshSynchronizer: make getEntityOwner public --- .../DistributedMeshSynchronizer.h | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h b/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h index 9ed393e31..88300d5b8 100644 --- a/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h +++ b/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h @@ -50,23 +50,15 @@ public: // 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 ); - Containers::Array< GlobalIndexType, Devices::Host, int > offsets( nproc ); + globalOffsets.setSize( nproc ); { Containers::Array< GlobalIndexType, Devices::Host, int > sendbuf( nproc ); sendbuf.setValue( ownStart ); CommunicatorType::Alltoall( sendbuf.getData(), 1, - offsets.getData(), 1, + globalOffsets.getData(), 1, group ); } - auto getOwner = [&] ( GlobalIndexType idx ) - { - for( int i = 0; i < nproc - 1; i++ ) - if( offsets[ i ] <= idx && idx < offsets[ i + 1 ] ) - return i; - return nproc - 1; - }; - // count local ghost entities for each rank Containers::Array< GlobalIndexType, Devices::Host, int > localGhostCounts( nproc ); { @@ -84,7 +76,7 @@ public: 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 = getOwner( 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 ]; @@ -371,8 +363,23 @@ public: 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; @@ -400,6 +407,13 @@ protected: // 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 -- GitLab From 34e9dac018fd17b8ddcc83325afa32e6c58e2ca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Fri, 24 Jul 2020 23:53:37 +0200 Subject: [PATCH 72/73] Fixed distributeSubentities --- .../DistributedMeshes/distributeSubentities.h | 112 +++++++++++------- 1 file changed, 70 insertions(+), 42 deletions(-) diff --git a/src/TNL/Meshes/DistributedMeshes/distributeSubentities.h b/src/TNL/Meshes/DistributedMeshes/distributeSubentities.h index 0b989533a..2135ffcee 100644 --- a/src/TNL/Meshes/DistributedMeshes/distributeSubentities.h +++ b/src/TNL/Meshes/DistributedMeshes/distributeSubentities.h @@ -13,6 +13,7 @@ #pragma once #include // std::iota +#include #include #include @@ -43,34 +44,62 @@ distributeSubentities( DistributedMesh& mesh ) const int rank = CommunicatorType::GetRank( mesh.getCommunicationGroup() ); const int nproc = CommunicatorType::GetSize( mesh.getCommunicationGroup() ); - // exchange the global vertex index offsets so that each rank can determine the - // owner of every vertex by its global index - const GlobalIndexType ownVertexStart = mesh.template getGlobalIndices< 0 >().getElement( 0 ); - Containers::Array< GlobalIndexType, Devices::Host, int > vertexOffsets( nproc ); - { - Containers::Array< GlobalIndexType, Devices::Host, int > sendbuf( nproc ); - sendbuf.setValue( ownVertexStart ); - CommunicatorType::Alltoall( sendbuf.getData(), 1, - vertexOffsets.getData(), 1, - 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 ]; - for( int i = 0; i < nproc - 1; i++ ) - if( vertexOffsets[ i ] <= global_idx && global_idx < vertexOffsets[ i + 1 ] ) - return i; - return nproc - 1; + 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 = 0; - for( LocalIndexType v = 0; v < entity.template getSubentitiesCount< 0 >(); v++ ) { - const GlobalIndexType gv = entity.template getSubentityIndex< 0 >( v ); - owner = TNL::max( owner, getVertexOwner( gv ) ); + 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; }; @@ -134,36 +163,35 @@ distributeSubentities( DistributedMesh& mesh ) mesh.template getGlobalIndices< Dimension >()[ i ] = globalOffsets[ rank ] + i; }); - // 6. exchange cell data to prepare the communication pattern - DistributedMeshSynchronizer< DistributedMesh > synchronizer; - synchronizer.initialize( mesh ); - - // 7. exchange local indices for ghost entities - const auto sparseResult = synchronizer.synchronizeSparse( localMesh.template getSubentitiesMatrix< DistributedMesh::getMeshDimension(), Dimension >() ); + // 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 ); - // 8. set the global indices of our ghost entities - for( int i = 0; i < nproc; i++ ) { - if( i == rank ) - continue; - for( GlobalIndexType cell = synchronizer.getGhostOffsets()[ i ]; cell < synchronizer.getGhostOffsets()[ i + 1 ]; cell++ ) { - for( LocalIndexType e = 0; e < mesh.getLocalMesh().template getSubentitiesCount< DistributedMesh::getMeshDimension(), Dimension >( cell ); e++ ) { - const GlobalIndexType entityIndex = mesh.getLocalMesh().template getSubentityIndex< DistributedMesh::getMeshDimension(), Dimension >( cell, e ); - const int owner = getEntityOwner( entityIndex ); - // pick the right owner as we might have received an index from multiple ranks - if( owner == i ) { - const GlobalIndexType ghostOffset = cell - synchronizer.getGhostOffsets()[ owner ]; - // global index = owner's local index + owner's offset - const GlobalIndexType globalEntityIndex = columnIndices[ rowPointers[ rankOffsets[ owner ] + ghostOffset ] + e ] + globalOffsets[ owner ]; - mesh.template getGlobalIndices< Dimension >()[ entityIndex ] = globalEntityIndex; - } + // 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; } } - } + }); - // 9. reorder the entities to make sure that global indices are sorted + // 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 >() ); -- GitLab From 3ccb6528a007eeab8f91a9274b18e8e81c6daa06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Wed, 29 Jul 2020 12:47:08 +0200 Subject: [PATCH 73/73] Generalized DistributedMeshSynchronizer to allow synchronization of plain arrays with multiple values per element --- .../DistributedMeshSynchronizer.h | 55 ++++++++++++++----- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h b/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h index 88300d5b8..724510bf4 100644 --- a/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h +++ b/src/TNL/Meshes/DistributedMeshes/DistributedMeshSynchronizer.h @@ -19,6 +19,16 @@ namespace TNL { namespace Meshes { namespace DistributedMeshes { +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 @@ -149,16 +159,32 @@ public: } } - template< typename MeshFunction > + 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." ); - TNL_ASSERT_EQ( function.getData().getSize(), ghostOffsets[ ghostOffsets.getSize() - 1 ], - "The mesh function does not have the expected size." ); - using RealType = typename MeshFunction::RealType; + + 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 @@ -170,7 +196,7 @@ public: const int nproc = CommunicatorType::GetSize( group ); // allocate send buffers (setSize does nothing if the array size is already correct) - sendBuffers.setSize( ghostNeighborOffsets[ nproc ] * sizeof(RealType) ); + sendBuffers.setSize( valuesPerElement * ghostNeighborOffsets[ nproc ] * sizeof(ValueType) ); // buffer for asynchronous communication requests std::vector< typename CommunicatorType::Request > requests; @@ -179,19 +205,20 @@ public: for( int j = 0; j < nproc; j++ ) { if( ghostEntitiesCounts( rank, j ) > 0 ) { requests.push_back( CommunicatorType::IRecv( - function.getData().getData() + ghostOffsets[ j ], - ghostEntitiesCounts( rank, j ), + array.getData() + valuesPerElement * ghostOffsets[ j ], + valuesPerElement * ghostEntitiesCounts( rank, j ), j, 0, group ) ); } } - Containers::ArrayView< RealType, DeviceType, GlobalIndexType > sendBuffersView; - sendBuffersView.bind( reinterpret_cast( sendBuffers.getData() ), ghostNeighborOffsets[ nproc ] ); + Containers::ArrayView< ValueType, DeviceType, GlobalIndexType > sendBuffersView; + sendBuffersView.bind( reinterpret_cast( sendBuffers.getData() ), valuesPerElement * ghostNeighborOffsets[ nproc ] ); const auto ghostNeighborsView = ghostNeighbors.getConstView(); - const auto functionDataView = function.getData().getConstView(); - auto copy_kernel = [sendBuffersView, functionDataView, ghostNeighborsView] __cuda_callable__ ( GlobalIndexType k, GlobalIndexType offset ) mutable + const auto arrayView = array.getConstView(); + auto copy_kernel = [sendBuffersView, arrayView, ghostNeighborsView, valuesPerElement] __cuda_callable__ ( GlobalIndexType k, GlobalIndexType offset ) mutable { - sendBuffersView[ offset + k ] = functionDataView[ ghostNeighborsView[ offset + k ] ]; + for( int i = 0; i < valuesPerElement; i++ ) + sendBuffersView[ i + valuesPerElement * (offset + k) ] = arrayView[ i + valuesPerElement * ghostNeighborsView[ offset + k ] ]; }; for( int i = 0; i < nproc; i++ ) { @@ -202,8 +229,8 @@ public: // issue async send operation requests.push_back( CommunicatorType::ISend( - sendBuffersView.getData() + ghostNeighborOffsets[ i ], - ghostEntitiesCounts( i, rank ), + sendBuffersView.getData() + valuesPerElement * ghostNeighborOffsets[ i ], + valuesPerElement * ghostEntitiesCounts( i, rank ), i, 0, group ) ); } } -- GitLab