diff --git a/src/Python/pytnl/tnl_mpi/DistributedMeshWriters.cpp b/src/Python/pytnl/tnl_mpi/DistributedMeshWriters.cpp index 17bf57c128dabcb28793fa0bf831150929624abd..62bef5b26db192d9c48117d220d0e1b08d1d0401 100644 --- a/src/Python/pytnl/tnl_mpi/DistributedMeshWriters.cpp +++ b/src/Python/pytnl/tnl_mpi/DistributedMeshWriters.cpp @@ -79,7 +79,7 @@ void export_DistributedMeshWriter( py::module & m, const char* name ) py::arg("array"), py::arg("name"), py::arg("numberOfComponents") = 1) // NOTE: only the overload intended for sequential writing is exported, because we don't // have type casters for MPI_Comm (ideally, it would be compatible with the mpi4py objects) - .def("addPiece", static_cast< std::string (Writer::*)(const TNL::String&, unsigned) >( &Writer::addPiece ), + .def("addPiece", static_cast< std::string (Writer::*)(const std::string&, unsigned) >( &Writer::addPiece ), py::arg("mainFileName"), py::arg("subdomainIndex")) ; } diff --git a/src/TNL/Matrices/SparseMatrix.h b/src/TNL/Matrices/SparseMatrix.h index 1300a40d37caadff9a93dc27d4d22e54085430d8..0082e52f5b53a1d0bb56ad7b6443bb306e2d0ce5 100644 --- a/src/TNL/Matrices/SparseMatrix.h +++ b/src/TNL/Matrices/SparseMatrix.h @@ -365,6 +365,17 @@ class SparseMatrix : public Matrix< Real, Device, Index, RealAllocator > virtual void setDimensions( const IndexType rows, const IndexType columns ) override; + /** + * \brief Set number of columns of this matrix. + * + * Unlike \ref setDimensions, the storage is not reset in this operation. + * It is the user's responsibility to update the column indices stored in + * the matrix to be consistent with the new number of columns. + * + * \param columns is the number of matrix columns. + */ + virtual void setColumnsWithoutReset( const IndexType columns ); + /** * \brief Set the number of matrix rows and columns by the given matrix. * diff --git a/src/TNL/Matrices/SparseMatrix.hpp b/src/TNL/Matrices/SparseMatrix.hpp index 4ae9990b567a40ea03cf14789fc59f65fec26784..a2ffcfcb6e3cff03c8a45a4fd785cd3b2d16f937 100644 --- a/src/TNL/Matrices/SparseMatrix.hpp +++ b/src/TNL/Matrices/SparseMatrix.hpp @@ -216,6 +216,22 @@ setDimensions( const IndexType rows, this->view = this->getView(); } +template< typename Real, + typename Device, + typename Index, + typename MatrixType, + template< typename, typename, typename > class Segments, + typename ComputeReal, + typename RealAllocator, + typename IndexAllocator > +void +SparseMatrix< Real, Device, Index, MatrixType, Segments, ComputeReal, RealAllocator, IndexAllocator >:: +setColumnsWithoutReset( const IndexType columns ) +{ + BaseType::setDimensions( this->getRows(), columns ); + this->view = this->getView(); +} + template< typename Real, typename Device, typename Index, diff --git a/src/TNL/Meshes/EntityShapeGroup.h b/src/TNL/Meshes/EntityShapeGroup.h index f9938068dc08d87cccfb2c5903eae278f8b4652b..8bca05fe367ef1ffabd77434677768b99e6e3ffc 100644 --- a/src/TNL/Meshes/EntityShapeGroup.h +++ b/src/TNL/Meshes/EntityShapeGroup.h @@ -13,7 +13,7 @@ struct EntityShapeGroup template < EntityShape GeneralShape, int index > struct EntityShapeGroupElement -{ +{ }; template <> @@ -34,6 +34,49 @@ struct EntityShapeGroupElement< EntityShape::Polygon, 1 > static constexpr EntityShape shape = EntityShape::Quad; }; + +template <> +struct EntityShapeGroup< EntityShape::Polyhedron > +{ + static constexpr int size = 6; +}; + +template <> +struct EntityShapeGroupElement< EntityShape::Polyhedron, 0 > +{ + static constexpr EntityShape shape = EntityShape::Tetra; +}; + +template <> +struct EntityShapeGroupElement< EntityShape::Polyhedron, 1 > +{ + static constexpr EntityShape shape = EntityShape::Hexahedron; +}; + +template <> +struct EntityShapeGroupElement< EntityShape::Polyhedron, 2 > +{ + static constexpr EntityShape shape = EntityShape::Wedge; +}; + +template <> +struct EntityShapeGroupElement< EntityShape::Polyhedron, 3 > +{ + static constexpr EntityShape shape = EntityShape::Pyramid; +}; + +template <> +struct EntityShapeGroupElement< EntityShape::Polyhedron, 4 > +{ + static constexpr EntityShape shape = EntityShape::PentagonalPrism; +}; + +template <> +struct EntityShapeGroupElement< EntityShape::Polyhedron, 5 > +{ + static constexpr EntityShape shape = EntityShape::HexagonalPrism; +}; + } // namespace VTK } // namespace Meshes -} // namespace TNL \ No newline at end of file +} // namespace TNL diff --git a/src/TNL/Meshes/Geometry/EntityRefiner.h b/src/TNL/Meshes/Geometry/EntityRefiner.h new file mode 100644 index 0000000000000000000000000000000000000000..081a35e5992e470946261f5e1e140320c5b6dbe5 --- /dev/null +++ b/src/TNL/Meshes/Geometry/EntityRefiner.h @@ -0,0 +1,239 @@ +#pragma once + +#include <TNL/Meshes/MeshEntity.h> +#include <TNL/Meshes/Topologies/Triangle.h> +#include <TNL/Meshes/Topologies/Quadrangle.h> +#include <TNL/Meshes/Topologies/Tetrahedron.h> +#include <TNL/Meshes/Topologies/Hexahedron.h> + +namespace TNL { +namespace Meshes { + +enum class EntityRefinerVersion +{ + EdgeBisection +}; + +template< typename MeshConfig, + typename Topology, + EntityRefinerVersion EntityRefinerVersion_ = EntityRefinerVersion::EdgeBisection > +struct EntityRefiner; + +template< typename MeshConfig > +struct EntityRefiner< MeshConfig, Topologies::Triangle, EntityRefinerVersion::EdgeBisection > +{ + using Device = Devices::Host; + using Topology = Topologies::Triangle; + using MeshEntityType = MeshEntity< MeshConfig, Device, Topology >; + using GlobalIndexType = typename MeshConfig::GlobalIndexType; + + // returns: number of *new* points, number of *all* refined entities + static std::pair< GlobalIndexType, GlobalIndexType > getExtraPointsAndEntitiesCount( const MeshEntityType& entity ) + { + return { 3, 4 }; + } + + template< typename AddPointFunctor, + typename AddCellFunctor > + static void decompose( const MeshEntityType& entity, + AddPointFunctor&& addPoint, + AddCellFunctor&& addCell ) + { + const auto v0 = entity.template getSubentityIndex< 0 >( 0 ); + const auto v1 = entity.template getSubentityIndex< 0 >( 1 ); + const auto v2 = entity.template getSubentityIndex< 0 >( 2 ); + + const auto& mesh = entity.getMesh(); + const auto v0_p = mesh.getPoint( v0 ); + const auto v1_p = mesh.getPoint( v1 ); + const auto v2_p = mesh.getPoint( v2 ); + + // add new points: midpoints of triangle edges + const auto w0 = addPoint( 0.5 * ( v1_p + v2_p ) ); + const auto w1 = addPoint( 0.5 * ( v0_p + v2_p ) ); + const auto w2 = addPoint( 0.5 * ( v0_p + v1_p ) ); + + // add refined triangles + addCell( v0, w1, w2 ); + addCell( v1, w0, w2 ); + addCell( v2, w0, w1 ); + addCell( w0, w1, w2 ); + } +}; + +template< typename MeshConfig > +struct EntityRefiner< MeshConfig, Topologies::Quadrangle, EntityRefinerVersion::EdgeBisection > +{ + using Device = Devices::Host; + using Topology = Topologies::Quadrangle; + using MeshEntityType = MeshEntity< MeshConfig, Device, Topology >; + using GlobalIndexType = typename MeshConfig::GlobalIndexType; + + // returns: number of *new* points, number of *all* refined entities + static std::pair< GlobalIndexType, GlobalIndexType > getExtraPointsAndEntitiesCount( const MeshEntityType& entity ) + { + return { 5, 4 }; + } + + template< typename AddPointFunctor, + typename AddCellFunctor > + static void decompose( const MeshEntityType& entity, + AddPointFunctor&& addPoint, + AddCellFunctor&& addCell ) + { + const auto v0 = entity.template getSubentityIndex< 0 >( 0 ); + const auto v1 = entity.template getSubentityIndex< 0 >( 1 ); + const auto v2 = entity.template getSubentityIndex< 0 >( 2 ); + const auto v3 = entity.template getSubentityIndex< 0 >( 3 ); + + const auto& mesh = entity.getMesh(); + const auto v0_p = mesh.getPoint( v0 ); + const auto v1_p = mesh.getPoint( v1 ); + const auto v2_p = mesh.getPoint( v2 ); + const auto v3_p = mesh.getPoint( v3 ); + + // add new points + const auto w0 = addPoint( 0.5 * ( v0_p + v1_p ) ); + const auto w1 = addPoint( 0.5 * ( v1_p + v2_p ) ); + const auto w2 = addPoint( 0.5 * ( v2_p + v3_p ) ); + const auto w3 = addPoint( 0.5 * ( v3_p + v0_p ) ); + const auto c = addPoint( 0.25 * ( v0_p + v1_p + v2_p + v3_p ) ); + + // add refined quadrangles + addCell( v0, w0, c, w3 ); + addCell( w0, v1, w1, c ); + addCell( c, w1, v2, w2 ); + addCell( w3, c, w2, v3 ); + } +}; + +template< typename MeshConfig > +struct EntityRefiner< MeshConfig, Topologies::Tetrahedron, EntityRefinerVersion::EdgeBisection > +{ + using Device = Devices::Host; + using Topology = Topologies::Tetrahedron; + using MeshEntityType = MeshEntity< MeshConfig, Device, Topology >; + using GlobalIndexType = typename MeshConfig::GlobalIndexType; + + // returns: number of *new* points, number of *all* refined entities + static std::pair< GlobalIndexType, GlobalIndexType > getExtraPointsAndEntitiesCount( const MeshEntityType& entity ) + { + return { 6, 8 }; + } + + template< typename AddPointFunctor, + typename AddCellFunctor > + static void decompose( const MeshEntityType& entity, + AddPointFunctor&& addPoint, + AddCellFunctor&& addCell ) + { + const auto v0 = entity.template getSubentityIndex< 0 >( 0 ); + const auto v1 = entity.template getSubentityIndex< 0 >( 1 ); + const auto v2 = entity.template getSubentityIndex< 0 >( 2 ); + const auto v3 = entity.template getSubentityIndex< 0 >( 3 ); + + const auto& mesh = entity.getMesh(); + const auto v0_p = mesh.getPoint( v0 ); + const auto v1_p = mesh.getPoint( v1 ); + const auto v2_p = mesh.getPoint( v2 ); + const auto v3_p = mesh.getPoint( v3 ); + + // add new points: midpoints of triangle edges + const auto w0 = addPoint( 0.5 * ( v1_p + v2_p ) ); + const auto w1 = addPoint( 0.5 * ( v0_p + v2_p ) ); + const auto w2 = addPoint( 0.5 * ( v0_p + v1_p ) ); + const auto w3 = addPoint( 0.5 * ( v0_p + v3_p ) ); + const auto w4 = addPoint( 0.5 * ( v1_p + v3_p ) ); + const auto w5 = addPoint( 0.5 * ( v2_p + v3_p ) ); + + // add refined tetrahedrons + addCell( v0, w1, w2, w3 ); + addCell( v1, w0, w2, w4 ); + addCell( v2, w0, w1, w5 ); + addCell( v3, w3, w4, w5 ); + + addCell( w5, w0, w1, w2 ); + addCell( w5, w1, w2, w3 ); + addCell( w5, w0, w2, w4 ); + addCell( w5, w3, w4, w2 ); + } +}; + +template< typename MeshConfig > +struct EntityRefiner< MeshConfig, Topologies::Hexahedron, EntityRefinerVersion::EdgeBisection > +{ + using Device = Devices::Host; + using Topology = Topologies::Hexahedron; + using MeshEntityType = MeshEntity< MeshConfig, Device, Topology >; + using GlobalIndexType = typename MeshConfig::GlobalIndexType; + + // returns: number of *new* points, number of *all* refined entities + static std::pair< GlobalIndexType, GlobalIndexType > getExtraPointsAndEntitiesCount( const MeshEntityType& entity ) + { + return { 19, 8 }; + } + + template< typename AddPointFunctor, + typename AddCellFunctor > + static void decompose( const MeshEntityType& entity, + AddPointFunctor&& addPoint, + AddCellFunctor&& addCell ) + { + const auto v0 = entity.template getSubentityIndex< 0 >( 0 ); + const auto v1 = entity.template getSubentityIndex< 0 >( 1 ); + const auto v2 = entity.template getSubentityIndex< 0 >( 2 ); + const auto v3 = entity.template getSubentityIndex< 0 >( 3 ); + const auto v4 = entity.template getSubentityIndex< 0 >( 4 ); + const auto v5 = entity.template getSubentityIndex< 0 >( 5 ); + const auto v6 = entity.template getSubentityIndex< 0 >( 6 ); + const auto v7 = entity.template getSubentityIndex< 0 >( 7 ); + + const auto& mesh = entity.getMesh(); + const auto v0_p = mesh.getPoint( v0 ); + const auto v1_p = mesh.getPoint( v1 ); + const auto v2_p = mesh.getPoint( v2 ); + const auto v3_p = mesh.getPoint( v3 ); + const auto v4_p = mesh.getPoint( v4 ); + const auto v5_p = mesh.getPoint( v5 ); + const auto v6_p = mesh.getPoint( v6 ); + const auto v7_p = mesh.getPoint( v7 ); + + // add new points: centers of bottom edges + const auto b0 = addPoint( 0.5 * ( v0_p + v1_p ) ); + const auto b1 = addPoint( 0.5 * ( v1_p + v2_p ) ); + const auto b2 = addPoint( 0.5 * ( v2_p + v3_p ) ); + const auto b3 = addPoint( 0.5 * ( v3_p + v0_p ) ); + // add new points: centers of upper edges + const auto u0 = addPoint( 0.5 * ( v4_p + v5_p ) ); + const auto u1 = addPoint( 0.5 * ( v5_p + v6_p ) ); + const auto u2 = addPoint( 0.5 * ( v6_p + v7_p ) ); + const auto u3 = addPoint( 0.5 * ( v7_p + v4_p ) ); + // add new points: centers of middle (vertical) edges + const auto m0 = addPoint( 0.5 * ( v0_p + v4_p ) ); + const auto m1 = addPoint( 0.5 * ( v1_p + v5_p ) ); + const auto m2 = addPoint( 0.5 * ( v2_p + v6_p ) ); + const auto m3 = addPoint( 0.5 * ( v3_p + v7_p ) ); + // add new points: centers of faces + const auto f0 = addPoint( 0.25 * ( v0_p + v1_p + v2_p + v3_p ) ); + const auto f1 = addPoint( 0.25 * ( v0_p + v1_p + v5_p + v4_p ) ); + const auto f2 = addPoint( 0.25 * ( v1_p + v2_p + v6_p + v5_p ) ); + const auto f3 = addPoint( 0.25 * ( v2_p + v3_p + v7_p + v6_p ) ); + const auto f4 = addPoint( 0.25 * ( v3_p + v0_p + v4_p + v7_p ) ); + const auto f5 = addPoint( 0.25 * ( v4_p + v5_p + v6_p + v7_p ) ); + // add new points: center of the cell + const auto cc = addPoint( 0.125 * ( v0_p + v1_p + v2_p + v3_p + v4_p + v5_p + v6_p + v7_p) ); + + // add refined hexahedrons + addCell( v0, b0, f0, b3, m0, f1, cc, f4 ); + addCell( b0, v1, b1, f0, f1, m1, f2, cc ); + addCell( f0, b1, v2, b2, cc, f2, m2, f3 ); + addCell( b3, f0, b2, v3, f4, cc, f3, m3 ); + addCell( m0, f1, cc, f4, v4, u0, f5, u3 ); + addCell( f1, m1, f2, cc, u0, v5, u1, f5 ); + addCell( cc, f2, m2, f3, f5, u1, v6, u2 ); + addCell( f4, cc, f3, m3, u3, f5, u2, v7 ); + } +}; + +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/Geometry/getRefinedMesh.h b/src/TNL/Meshes/Geometry/getRefinedMesh.h new file mode 100644 index 0000000000000000000000000000000000000000..a378b671dfe5ac781147b73b1fb30e0efd88edd0 --- /dev/null +++ b/src/TNL/Meshes/Geometry/getRefinedMesh.h @@ -0,0 +1,110 @@ +#pragma once + +#include <TNL/Meshes/Mesh.h> +#include <TNL/Meshes/MeshEntity.h> +#include <TNL/Meshes/MeshBuilder.h> +#include <TNL/Meshes/Geometry/EntityRefiner.h> +#include <TNL/Algorithms/ParallelFor.h> +#include <TNL/Algorithms/scan.h> + +namespace TNL { +namespace Meshes { + +// TODO: refactor to avoid duplicate points altogether - first split edges, then faces, then cells +template< EntityRefinerVersion RefinerVersion, + typename MeshConfig, + std::enable_if_t< std::is_same< typename MeshConfig::CellTopology, Topologies::Triangle >::value + || std::is_same< typename MeshConfig::CellTopology, Topologies::Quadrangle >::value + || std::is_same< typename MeshConfig::CellTopology, Topologies::Tetrahedron >::value + || std::is_same< typename MeshConfig::CellTopology, Topologies::Hexahedron >::value, bool > = true > +auto // returns MeshBuilder +refineMesh( const Mesh< MeshConfig, Devices::Host >& inMesh ) +{ + using namespace TNL; + using namespace TNL::Containers; + using namespace TNL::Algorithms; + + using Mesh = Mesh< MeshConfig, Devices::Host >; + using MeshBuilder = MeshBuilder< Mesh >; + using GlobalIndexType = typename Mesh::GlobalIndexType; + using PointType = typename Mesh::PointType; + using EntityRefiner = EntityRefiner< MeshConfig, typename MeshConfig::CellTopology, RefinerVersion >; + constexpr int CellDimension = Mesh::getMeshDimension(); + + MeshBuilder meshBuilder; + + const GlobalIndexType inPointsCount = inMesh.template getEntitiesCount< 0 >(); + const GlobalIndexType inCellsCount = inMesh.template getEntitiesCount< CellDimension >(); + + // Find the number of output points and cells as well as + // starting indices at which every cell will start writing new refined points and cells + using IndexPair = std::pair< GlobalIndexType, GlobalIndexType >; + Array< IndexPair, Devices::Host > indices( inCellsCount + 1 ); + auto setCounts = [&] ( GlobalIndexType i ) { + const auto cell = inMesh.template getEntity< CellDimension >( i ); + indices[ i ] = EntityRefiner::getExtraPointsAndEntitiesCount( cell ); + }; + ParallelFor< Devices::Host >::exec( GlobalIndexType{ 0 }, inCellsCount, setCounts ); + indices[ inCellsCount ] = { 0, 0 }; // extend exclusive prefix sum by one element to also get result of reduce at the same time + auto reduction = [] ( const IndexPair& a, const IndexPair& b ) -> IndexPair { + return { a.first + b.first, a.second + b.second }; + }; + inplaceExclusiveScan( indices, 0, indices.getSize(), reduction, std::make_pair( 0, 0 ) ); + const auto& reduceResult = indices[ inCellsCount ]; + const GlobalIndexType outPointsCount = inPointsCount + reduceResult.first; + const GlobalIndexType outCellsCount = reduceResult.second; + meshBuilder.setEntitiesCount( outPointsCount, outCellsCount ); + + // Copy the points from inMesh to outMesh + auto copyPoint = [&] ( GlobalIndexType i ) mutable { + meshBuilder.setPoint( i, inMesh.getPoint( i ) ); + }; + ParallelFor< Devices::Host >::exec( GlobalIndexType{ 0 }, inPointsCount, copyPoint ); + + // Refine each cell + auto refineCell = [&] ( GlobalIndexType i ) mutable { + const auto cell = inMesh.template getEntity< CellDimension >( i ); + const auto& indexPair = indices[ i ]; + + // Lambda for adding new points + GlobalIndexType setPointIndex = inPointsCount + indexPair.first; + auto addPoint = [&] ( const PointType& point ) { + const auto pointIdx = setPointIndex++; + meshBuilder.setPoint( pointIdx, point ); + return pointIdx; + }; + + // Lambda for adding new cells + GlobalIndexType setCellIndex = indexPair.second; + auto addCell = [&] ( auto... vertexIndices ) { + auto entitySeed = meshBuilder.getCellSeed( setCellIndex++ ); + entitySeed.setCornerIds( vertexIndices... ); + }; + + EntityRefiner::decompose( cell, addPoint, addCell ); + }; + ParallelFor< Devices::Host >::exec( GlobalIndexType{ 0 }, inCellsCount, refineCell ); + + return meshBuilder; +} + +template< EntityRefinerVersion RefinerVersion, + typename MeshConfig, + std::enable_if_t< std::is_same< typename MeshConfig::CellTopology, Topologies::Triangle >::value + || std::is_same< typename MeshConfig::CellTopology, Topologies::Quadrangle >::value + || std::is_same< typename MeshConfig::CellTopology, Topologies::Tetrahedron >::value + || std::is_same< typename MeshConfig::CellTopology, Topologies::Hexahedron >::value, bool > = true > +auto // returns Mesh +getRefinedMesh( const Mesh< MeshConfig, Devices::Host >& inMesh ) +{ + using Mesh = Mesh< MeshConfig, Devices::Host >; + + Mesh outMesh; + auto meshBuilder = refineMesh< RefinerVersion >( inMesh ); + meshBuilder.deduplicatePoints(); + meshBuilder.build( outMesh ); + return outMesh; +} + +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/GridDetails/Grid1D_impl.h b/src/TNL/Meshes/GridDetails/Grid1D_impl.h index 0ad33b103f5130413f446912e4fb551e8c3844e3..c46ed7175020d967cfcc4e0b58f7c262a45396de 100644 --- a/src/TNL/Meshes/GridDetails/Grid1D_impl.h +++ b/src/TNL/Meshes/GridDetails/Grid1D_impl.h @@ -12,7 +12,6 @@ #include <fstream> #include <iomanip> -#include <TNL/String.h> #include <TNL/Assert.h> #include <TNL/Meshes/GridDetails/GridEntityGetter_impl.h> #include <TNL/Meshes/GridDetails/NeighborGridEntityGetter1D_impl.h> diff --git a/src/TNL/Meshes/GridDetails/Grid2D_impl.h b/src/TNL/Meshes/GridDetails/Grid2D_impl.h index fc290d23d430fa53b909beb7cdb588f1e9174785..77c74437674bcdfbabcf833e8396638ec70aa7ac 100644 --- a/src/TNL/Meshes/GridDetails/Grid2D_impl.h +++ b/src/TNL/Meshes/GridDetails/Grid2D_impl.h @@ -12,7 +12,6 @@ #include <fstream> #include <iomanip> -#include <TNL/String.h> #include <TNL/Assert.h> #include <TNL/Meshes/GridDetails/GridEntityGetter_impl.h> #include <TNL/Meshes/GridDetails/NeighborGridEntityGetter2D_impl.h> diff --git a/src/TNL/Meshes/GridDetails/Grid3D_impl.h b/src/TNL/Meshes/GridDetails/Grid3D_impl.h index 3dafeb2a3816004c90c14730fa6f2f7888fbd2de..7f66fbfc316f57a3126963a64855980f42e894d0 100644 --- a/src/TNL/Meshes/GridDetails/Grid3D_impl.h +++ b/src/TNL/Meshes/GridDetails/Grid3D_impl.h @@ -12,7 +12,6 @@ #include <fstream> #include <iomanip> -#include <TNL/String.h> #include <TNL/Assert.h> #include <TNL/Meshes/GridDetails/GridEntityGetter_impl.h> #include <TNL/Meshes/GridDetails/NeighborGridEntityGetter3D_impl.h> diff --git a/src/TNL/Meshes/MeshBuilder.h b/src/TNL/Meshes/MeshBuilder.h index aa10a8a245bd886439a3b7d66f32129e88bcef01..eae805ef5e7f42ae8af1f4ae010ff5e99773ee71 100644 --- a/src/TNL/Meshes/MeshBuilder.h +++ b/src/TNL/Meshes/MeshBuilder.h @@ -16,6 +16,9 @@ #pragma once +#include <numeric> // std::iota +#include <vector> + #include <TNL/Containers/Vector.h> #include <TNL/Meshes/Topologies/Polyhedron.h> @@ -31,14 +34,16 @@ public: using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; using LocalIndexType = typename MeshTraitsType::LocalIndexType; using PointType = typename MeshTraitsType::PointType; + using PointArrayType = typename MeshTraitsType::PointArrayType; + using BoolVector = Containers::Vector< bool, Devices::Host, GlobalIndexType >; using CellTopology = typename MeshTraitsType::CellTopology; using CellSeedMatrixType = typename MeshTraitsType::CellSeedMatrixType; using CellSeedType = typename CellSeedMatrixType::EntitySeedMatrixSeed; using FaceSeedMatrixType = typename MeshTraitsType::FaceSeedMatrixType; using FaceSeedType = typename FaceSeedMatrixType::EntitySeedMatrixSeed; using NeighborCountsArray = typename MeshTraitsType::NeighborCountsArray; - - void setEntitiesCount( const GlobalIndexType& points, + + void setEntitiesCount( const GlobalIndexType& points, const GlobalIndexType& cells = 0, const GlobalIndexType& faces = 0 ) { @@ -109,20 +114,237 @@ public: return this->cellSeeds.getSeed( index ); } + void deduplicatePoints( const double numericalThreshold = 1e-9 ) + { + // prepare vector with an identity permutationutation + std::vector< GlobalIndexType > permutation( points.getSize() ); + std::iota( permutation.begin(), permutation.end(), (GlobalIndexType) 0 ); + + // workaround for lexicographical sorting + // FIXME: https://mmg-gitlab.fjfi.cvut.cz/gitlab/tnl/tnl-dev/-/issues/79 + auto lexless = [numericalThreshold, this] (const GlobalIndexType& a, const GlobalIndexType& b) -> bool + { + const PointType& left = this->points[ a ]; + const PointType& right = this->points[ b ]; + for( LocalIndexType i = 0; i < PointType::getSize(); i++ ) + if( TNL::abs( left[ i ] - right[ i ] ) > numericalThreshold ) + return left[ i ] < right[ i ]; + return false; + }; + + // sort points in lexicographical order + std::stable_sort( permutation.begin(), permutation.end(), lexless ); + + // old -> new index mapping for points + std::vector< GlobalIndexType > points_perm_to_new( points.getSize() ); + + // find duplicate points + GlobalIndexType uniquePointsCount = 0; + // first index is unique + points_perm_to_new[ permutation[ 0 ] ] = uniquePointsCount++; + for( GlobalIndexType i = 1; i < points.getSize(); i++ ) { + const PointType& curr = points[ permutation[ i ] ]; + const PointType& prev = points[ permutation[ i - 1 ] ]; + if( maxNorm( curr - prev ) > numericalThreshold ) + // unique point + points_perm_to_new[ permutation[ i ] ] = uniquePointsCount++; + else + // duplicate point - use previous index + points_perm_to_new[ permutation[ i ] ] = uniquePointsCount - 1; + } + + // if all points are unique, we are done + if( uniquePointsCount == points.getSize() ) + return; + + std::cout << "Found " << points.getSize() - uniquePointsCount << " duplicate points (total " << points.getSize() << ", unique " << uniquePointsCount << ")" << std::endl; + + // copy this->points and this->pointsSet, drop duplicate points + // (trying to do this in-place is not worth it, since even Array::reallocate + // needs to allocate a temporary array and copy the elements) + PointArrayType newPoints( uniquePointsCount ); + BoolVector newPointsSet( uniquePointsCount ); + std::vector< GlobalIndexType > points_old_to_new( points.getSize() ); + // TODO: this can almost be parallelized, except we have multiple writes for the duplicate points + for( std::size_t i = 0; i < points_perm_to_new.size(); i++ ) { + const GlobalIndexType oldIndex = permutation[ i ]; + const GlobalIndexType newIndex = points_perm_to_new[ oldIndex ]; + newPoints[ newIndex ] = points[ oldIndex ]; + newPointsSet[ newIndex ] = pointsSet[ oldIndex ]; + points_old_to_new[ oldIndex ] = newIndex; + } + points = std::move( newPoints ); + pointsSet = std::move( newPointsSet ); + // reset permutation and points_perm_to_new - we need just points_old_to_new further on + permutation.clear(); + permutation.shrink_to_fit(); + points_perm_to_new.clear(); + points_perm_to_new.shrink_to_fit(); + + auto remap_matrix = [uniquePointsCount, &points_old_to_new] ( auto& seeds ) + { + // TODO: parallelize (we have the IndexPermutationApplier) + for( GlobalIndexType i = 0; i < seeds.getEntitiesCount(); i++ ) { + auto seed = seeds.getSeed( i ); + for( LocalIndexType j = 0; j < seed.getCornersCount(); j++ ) { + const GlobalIndexType newIndex = points_old_to_new[ seed.getCornerId( j ) ]; + seed.setCornerId( j, newIndex ); + } + } + // update the number of columns of the matrix + seeds.getMatrix().setColumnsWithoutReset( uniquePointsCount ); + }; + + // remap points in this->faceSeeds or this->cellSeeds + if( faceSeeds.empty() ) + remap_matrix( cellSeeds ); + else + remap_matrix( faceSeeds ); + } + + void deduplicateFaces() + { + // prepare vector with an identity permutationutation + std::vector< GlobalIndexType > permutation( faceSeeds.getEntitiesCount() ); + std::iota( permutation.begin(), permutation.end(), (GlobalIndexType) 0 ); + + // workaround for lexicographical sorting + // FIXME: https://mmg-gitlab.fjfi.cvut.cz/gitlab/tnl/tnl-dev/-/issues/79 + auto lexless = [this] (const GlobalIndexType& a, const GlobalIndexType& b) -> bool + { + const auto& left = this->faceSeeds.getSeed( a ); + const auto& right = this->faceSeeds.getSeed( b ); + for( LocalIndexType i = 0; i < left.getCornersCount() && i < right.getCornersCount(); i++ ) { + if( left.getCornerId( i ) < right.getCornerId( i ) ) + return true; + if( right.getCornerId( i ) < left.getCornerId( i ) ) + return false; + } + return left.getCornersCount() < right.getCornersCount(); + }; + + // TODO: here we just assume that all duplicate faces have the same ordering of vertices (which is the case for files + // produced by the VTUWriter), but maybe we should try harder (we would have to create a copy of faceSeeds and sort the + // vertex indices in each seed, all that *before* lexicographical sorting) + // (Just for the detection of duplicates, it does not matter that vertices of a polygon get sorted in an arbitrary order + // instead of clock-wise or counter-clockwise.) + auto equiv = [lexless] (const GlobalIndexType& a, const GlobalIndexType& b) -> bool + { + return ! lexless(a, b) && ! lexless(b, a); + }; + + // sort face seeds in lexicographical order + std::stable_sort( permutation.begin(), permutation.end(), lexless ); + + // old -> new index mapping for faces + std::vector< GlobalIndexType > faces_perm_to_new( faceSeeds.getEntitiesCount() ); + + // find duplicate faces + GlobalIndexType uniqueFacesCount = 0; + // first index is unique + faces_perm_to_new[ permutation[ 0 ] ] = uniqueFacesCount++; + for( GlobalIndexType i = 1; i < faceSeeds.getEntitiesCount(); i++ ) { + if( equiv( permutation[ i ], permutation[ i - 1 ] ) ) + // duplicate face - use previous index + faces_perm_to_new[ permutation[ i ] ] = uniqueFacesCount - 1; + else + // unique face + faces_perm_to_new[ permutation[ i ] ] = uniqueFacesCount++; + } + + // if all faces are unique, we are done + if( uniqueFacesCount == faceSeeds.getEntitiesCount() ) + return; + + std::cout << "Found " << faceSeeds.getEntitiesCount() - uniqueFacesCount << " duplicate faces (total " << faceSeeds.getEntitiesCount() << ", unique " << uniqueFacesCount << ")" << std::endl; + + // get corners counts for unique faces + NeighborCountsArray cornersCounts( uniqueFacesCount ); + std::vector< GlobalIndexType > faces_old_to_new( faceSeeds.getEntitiesCount() ); + // TODO: this can almost be parallelized, except we have multiple writes for the duplicate faces + for( std::size_t i = 0; i < faces_perm_to_new.size(); i++ ) { + const GlobalIndexType oldIndex = permutation[ i ]; + const GlobalIndexType newIndex = faces_perm_to_new[ oldIndex ]; + cornersCounts[ newIndex ] = faceSeeds.getEntityCornerCounts()[ oldIndex ]; + faces_old_to_new[ oldIndex ] = newIndex; + } + // reset permutation and faces_perm_to_new - we need just faces_old_to_new further on + permutation.clear(); + permutation.shrink_to_fit(); + faces_perm_to_new.clear(); + faces_perm_to_new.shrink_to_fit(); + // copy this->faceSeeds, drop duplicate faces + FaceSeedMatrixType newFaceSeeds; + newFaceSeeds.setDimensions( uniqueFacesCount, points.getSize() ); + newFaceSeeds.setEntityCornersCounts( std::move( cornersCounts ) ); + // TODO: this can almost be parallelized, except we have multiple writes for the duplicate faces + for( std::size_t i = 0; i < faces_old_to_new.size(); i++ ) { + const GlobalIndexType oldIndex = i; + const GlobalIndexType newIndex = faces_old_to_new[ oldIndex ]; + const auto& oldSeed = faceSeeds.getSeed( oldIndex ); + auto newSeed = newFaceSeeds.getSeed( newIndex ); + for( LocalIndexType j = 0; j < newSeed.getCornersCount(); j++ ) + newSeed.setCornerId( j, oldSeed.getCornerId( j ) ); + } + faceSeeds = std::move( newFaceSeeds ); + + // TODO: refactoring - basically the same lambda as in deduplicatePoints + auto remap_matrix = [uniqueFacesCount, &faces_old_to_new] ( auto& seeds ) + { + // TODO: parallelize (we have the IndexPermutationApplier) + for( GlobalIndexType i = 0; i < seeds.getEntitiesCount(); i++ ) { + auto seed = seeds.getSeed( i ); + for( LocalIndexType j = 0; j < seed.getCornersCount(); j++ ) { + const GlobalIndexType newIndex = faces_old_to_new[ seed.getCornerId( j ) ]; + seed.setCornerId( j, newIndex ); + } + } + // update the number of columns of the matrix + seeds.getMatrix().setColumnsWithoutReset( uniqueFacesCount ); + }; + + // remap cell seeds + remap_matrix( cellSeeds ); + } + bool build( MeshType& mesh ) { if( ! this->validate() ) return false; + pointsSet.reset(); mesh.init( this->points, this->faceSeeds, this->cellSeeds ); return true; } private: - using PointArrayType = typename MeshTraitsType::PointArrayType; - using BoolVector = Containers::Vector< bool, Devices::Host, GlobalIndexType >; - bool validate() const { + // verify that matrix dimensions are consistent with points + if( faceSeeds.empty() ) { + // no face seeds - cell seeds refer to points + if( cellSeeds.getMatrix().getColumns() != points.getSize() ) { + std::cerr << "Mesh builder error: Inconsistent size of the cellSeeds matrix (it has " + << cellSeeds.getMatrix().getColumns() << " columns, but there are " << points.getSize() + << " points)." << std::endl; + return false; + } + } + else { + // cell seeds refer to faces and face seeds refer to points + if( cellSeeds.getMatrix().getColumns() != faceSeeds.getMatrix().getRows() ) { + std::cerr << "Mesh builder error: Inconsistent size of the cellSeeds matrix (it has " + << cellSeeds.getMatrix().getColumns() << " columns, but there are " << faceSeeds.getMatrix().getRows() + << " faces)." << std::endl; + return false; + } + if( faceSeeds.getMatrix().getColumns() != points.getSize() ) { + std::cerr << "Mesh builder error: Inconsistent size of the faceSeeds matrix (it has " + << faceSeeds.getMatrix().getColumns() << " columns, but there are " << points.getSize() + << " points)." << std::endl; + return false; + } + } + if( min( pointsSet ) != true ) { std::cerr << "Mesh builder error: Not all points were set." << std::endl; return false; diff --git a/src/TNL/Meshes/MeshDetails/MeshIntegrityChecker.h b/src/TNL/Meshes/MeshDetails/MeshIntegrityChecker.h deleted file mode 100644 index 13fec6787827ec6177815984096dbfe0ff10e59d..0000000000000000000000000000000000000000 --- a/src/TNL/Meshes/MeshDetails/MeshIntegrityChecker.h +++ /dev/null @@ -1,43 +0,0 @@ -/*************************************************************************** - MeshIntegrityChecker.h - description - ------------------- - begin : Mar 20, 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 <TNL/Meshes/Mesh.h> -#include <TNL/Meshes/MeshDetails/MeshIntegrityCheckerLayer.h> - -namespace TNL { -namespace Meshes { - -template< typename MeshType > -class MeshIntegrityChecker -: public MeshIntegrityCheckerLayer< MeshType, - DimensionTag< MeshType::Config::CellType::dimension > > -{ - typedef Meshes::DimensionTag< MeshType::Config::CellType::dimension > DimensionTag; - typedef MeshIntegrityCheckerLayer< MeshType, DimensionTag > BaseType; - - public: - static bool checkMesh( const MeshType& mesh ) - { - if( ! BaseType::checkEntities( mesh ) ) - return false; - return true; - } -}; - -} // namespace Meshes -} // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/MeshIntegrityCheckerLayer.h b/src/TNL/Meshes/MeshDetails/MeshIntegrityCheckerLayer.h deleted file mode 100644 index 13e415d4567effbc8c8f5b735c7523045f718cad..0000000000000000000000000000000000000000 --- a/src/TNL/Meshes/MeshDetails/MeshIntegrityCheckerLayer.h +++ /dev/null @@ -1,104 +0,0 @@ -/*************************************************************************** - MeshIntegrityCheckerLayer.h - description - ------------------- - begin : Mar 21, 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 <TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h> -#include <TNL/Meshes/DimensionTag.h> - -namespace TNL { -namespace Meshes { - -template< typename MeshType, - typename DimensionTag, - bool EntityStorageTag = MeshEntityTraits< typename MeshType::Config, - DimensionTag::value >::storageEnabled > -class MeshIntegrityCheckerLayer; - -template< typename MeshType, - typename DimensionTag > -class MeshIntegrityCheckerLayer< MeshType, - DimensionTag, - true > - : public MeshIntegrityCheckerLayer< MeshType, - typename DimensionTag::Decrement > -{ - public: - typedef MeshIntegrityCheckerLayer< MeshType, - typename DimensionTag::Decrement > BaseType; - enum { dimension = DimensionTag::value }; - - static bool checkEntities( const MeshType& mesh ) - { - typedef typename MeshType::template EntitiesTraits< dimension >::ContainerType ContainerType; - typedef typename ContainerType::IndexType GlobalIndexType; - std::cout << "Checking entities with dimension " << dimension << " ..." << std::endl; - for( GlobalIndexType entityIdx = 0; - entityIdx < mesh.template getEntitiesCount< dimension >(); - entityIdx++ ) - { - std::cout << "Entity no. " << entityIdx << " \r" << std::flush; - } - std::cout << std::endl; - if( ! BaseType::checkEntities( mesh ) ) - return false; - return true; - } -}; - -template< typename MeshType > -class MeshIntegrityCheckerLayer< MeshType, - DimensionTag< 0 >, - true > -{ - public: - enum { dimension = 0 }; - - static bool checkEntities( const MeshType& mesh ) - { - typedef typename MeshType::template EntitiesTraits< dimension >::ContainerType ContainerType; - typedef typename ContainerType::IndexType GlobalIndexType; - std::cout << "Checking entities with dimension " << dimension << " ..." << std::endl; - for( GlobalIndexType entityIdx = 0; - entityIdx < mesh.template getEntitiesCount< dimension >(); - entityIdx++ ) - { - std::cout << "Entity no. " << entityIdx << " \r" << std::flush; - } - std::cout << std::endl; - return true; - } -}; - -template< typename MeshType, - typename DimensionTag > -class MeshIntegrityCheckerLayer< MeshType, - DimensionTag, - false > - : public MeshIntegrityCheckerLayer< MeshType, - typename DimensionTag::Decrement > -{ -}; - -template< typename MeshType > -class MeshIntegrityCheckerLayer< MeshType, - DimensionTag< 0 >, - false > -{ -}; - -} // namespace Meshes -} // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/initializer/EntitySeedMatrix.h b/src/TNL/Meshes/MeshDetails/initializer/EntitySeedMatrix.h index ae6ac5eeddba371f58d906dffd83f2cf213c05ad..fff6b8251ed92ce0ebb332a54c14fc601d4bbdd0 100644 --- a/src/TNL/Meshes/MeshDetails/initializer/EntitySeedMatrix.h +++ b/src/TNL/Meshes/MeshDetails/initializer/EntitySeedMatrix.h @@ -60,6 +60,13 @@ class EntitySeedMatrix< MeshConfig, EntityTopology, false > this->row.setColumnIndex( cornerIndex, pointIndex ); } + template< typename... IndexTypes > + void setCornerIds( const IndexTypes&... pointIndices ) + { + static_assert( sizeof...( pointIndices ) == getCornersCount(), "invalid number of indices" ); + setCornerIds_impl( 0, pointIndices... ); + } + GlobalIndexType getCornerId( const LocalIndexType& cornerIndex ) const { return this->row.getColumnIndex( cornerIndex ); @@ -67,6 +74,17 @@ class EntitySeedMatrix< MeshConfig, EntityTopology, false > private: RowView row; + + // empty overload to terminate recursion + void setCornerIds_impl( const LocalIndexType& cornerIndex ) + {} + + template< typename... IndexTypes > + void setCornerIds_impl( const LocalIndexType& cornerIndex, const GlobalIndexType& pointIndex, const IndexTypes&... pointIndices ) + { + setCornerId( cornerIndex, pointIndex ); + setCornerIds_impl( cornerIndex + 1, pointIndices... ); + } }; class ConstEntitySeedMatrixSeed diff --git a/src/TNL/Meshes/MeshDetails/initializer/Initializer.h b/src/TNL/Meshes/MeshDetails/initializer/Initializer.h index 9417c244e313d88ae7b55444a044263ad24d4fde..76aff7010d88d6aa6c73fe7f05b0d23a28c236d4 100644 --- a/src/TNL/Meshes/MeshDetails/initializer/Initializer.h +++ b/src/TNL/Meshes/MeshDetails/initializer/Initializer.h @@ -70,7 +70,7 @@ class Initializer protected: // must be declared before its use in expression with decltype() Mesh< MeshConfig >* mesh = nullptr; - + public: using MeshType = Mesh< MeshConfig >; using MeshTraitsType = MeshTraits< MeshConfig >; @@ -92,14 +92,13 @@ class Initializer CellSeedMatrixType& cellSeeds, MeshType& mesh ) { - // copy points + // move points mesh.template setEntitiesCount< 0 >( points.getSize() ); - mesh.getPoints().swap( points ); - points.reset(); + mesh.getPoints() = std::move( points ); this->mesh = &mesh; this->cellSeeds = &cellSeeds; - + if( faceSeeds.empty() ) BaseType::initEntities( *this, cellSeeds, mesh ); else @@ -227,7 +226,7 @@ protected: initializer.template setEntitiesCount< DimensionTag::value >( initializer.getCellSeeds().getEntitiesCount() ); BaseType::initEntities( initializer, faceSeeds, mesh ); } - + using BaseType::findEntitySeedIndex; }; @@ -256,7 +255,7 @@ protected: using SeedIndexedSet = typename MeshTraits< MeshConfig >::template EntityTraits< DimensionTag::value >::SeedIndexedSetType; using SeedMatrixType = typename EntityTraitsType::SeedMatrixType; using NeighborCountsArray = typename MeshTraitsType::NeighborCountsArray; - + public: void createSeeds( InitializerType& initializer, MeshType& mesh ) @@ -280,6 +279,12 @@ protected: void initEntities( InitializerType& initializer, MeshType& mesh ) { + // skip initialization of entities which do not store their subvertices + // (and hence do not participate in any other incidence matrix) + if( ! MeshConfig::subentityStorage( DimensionTag::value, 0 ) ) { + BaseType::initEntities( initializer, mesh ); + return; + } //std::cout << " Initiating entities with dimension " << DimensionTag::value << " ... " << std::endl; // create seeds and set entities count diff --git a/src/TNL/Meshes/Readers/MeshReader.h b/src/TNL/Meshes/Readers/MeshReader.h index e18e6577bf949f52f3b80c442c8dd913d5966e8c..537aed7408eddca59383050bc0d01ba4d4a3823b 100644 --- a/src/TNL/Meshes/Readers/MeshReader.h +++ b/src/TNL/Meshes/Readers/MeshReader.h @@ -174,7 +174,7 @@ public: MeshBuilder meshBuilder; meshBuilder.setEntitiesCount( NumberOfPoints, NumberOfCells, NumberOfFaces ); - + // assign points visit( [&meshBuilder](auto&& array) { PointType p; @@ -196,7 +196,7 @@ public: // let's just assume that the connectivity and offsets arrays have the same type... using mpark::get; const auto& offsets = get< std::decay_t<decltype(connectivity)> >( faceOffsetsArray ); - + // Set corners counts NeighborCountsArray cornersCounts( NumberOfFaces ); std::size_t offsetStart = 0; @@ -252,6 +252,10 @@ public: // reset arrays since they are not needed anymore pointsArray = faceConnectivityArray = cellConnectivityArray = faceOffsetsArray = cellOffsetsArray = typesArray = {}; + // deduplicate faces (important for VTK formats, not for FPMA) + if( NumberOfFaces > 0 ) + meshBuilder.deduplicateFaces(); + if( ! meshBuilder.build( mesh ) ) throw MeshReaderError( "MeshReader", "MeshBuilder failed" ); } @@ -295,20 +299,41 @@ public: std::string getRealType() const { - return pointsType; + if( forcedRealType.empty() ) + return pointsType; + return forcedRealType; } std::string getGlobalIndexType() const { - return connectivityType; + if( forcedGlobalIndexType.empty() ) + return connectivityType; + return forcedGlobalIndexType; } std::string getLocalIndexType() const { - // not stored in any file format - return "short int"; + return forcedLocalIndexType; + } + + void + forceRealType( std::string realType ) + { + forcedRealType = std::move( realType ); + } + + void + forceGlobalIndexType( std::string globalIndexType ) + { + forcedGlobalIndexType = std::move( globalIndexType ); + } + + void + forceLocalIndexType( std::string localIndexType ) + { + forcedLocalIndexType = std::move( localIndexType ); } protected: @@ -324,6 +349,12 @@ protected: int meshDimension, spaceDimension; VTK::EntityShape cellShape = VTK::EntityShape::Vertex; + // string representation of mesh types (forced means specified by the user, otherwise + // the type detected by detectMesh takes precedence) + std::string forcedRealType = ""; + std::string forcedGlobalIndexType = ""; + std::string forcedLocalIndexType = "short int"; // not stored in any file format + // intermediate representation of a grid (this is relevant only for TNL::Meshes::Grid) std::vector< std::int64_t > gridExtent; std::vector< double > gridOrigin, gridSpacing; @@ -334,8 +365,6 @@ protected: faceConnectivityArray, faceOffsetsArray, typesArray; - - // string representation of each array's value type std::string pointsType, connectivityType, offsetsType, typesType; @@ -346,12 +375,25 @@ protected: meshDimension = spaceDimension = 0; cellShape = VTK::EntityShape::Vertex; - gridExtent = {}; - gridOrigin = gridSpacing = {}; + reset_std_vectors( gridExtent, gridOrigin, gridSpacing ); pointsArray = cellConnectivityArray = cellOffsetsArray = faceConnectivityArray = faceOffsetsArray = typesArray = {}; pointsType = connectivityType = offsetsType = typesType = ""; } + + template< typename T > + void reset_std_vectors( std::vector< T >& v ) + { + v.clear(); + v.shrink_to_fit(); + } + + template< typename T, typename... Ts > + void reset_std_vectors( std::vector< T >& v, std::vector< Ts >&... vs ) + { + reset_std_vectors( v ); + reset_std_vectors( vs... ); + } }; } // namespace Readers diff --git a/src/TNL/Meshes/Readers/PVTIReader.h b/src/TNL/Meshes/Readers/PVTIReader.h index 3e9bf7200f1497654445db9ed229526c528ee8a9..5eae866dfb842d70c6b5b0b270034bd701dba742 100644 --- a/src/TNL/Meshes/Readers/PVTIReader.h +++ b/src/TNL/Meshes/Readers/PVTIReader.h @@ -312,7 +312,7 @@ public: { resetBase(); ghostLevels = 0; - pieceSources = {}; + reset_std_vectors( pieceSources ); localReader.reset(); pointTags = cellTags = {}; } diff --git a/src/TNL/Meshes/Readers/PVTUReader.h b/src/TNL/Meshes/Readers/PVTUReader.h index 87379c732cf548ff4f83ce3789c65094e13e5023..6656deeb4adf19e8bf0b9bf911c739a1bc745e08 100644 --- a/src/TNL/Meshes/Readers/PVTUReader.h +++ b/src/TNL/Meshes/Readers/PVTUReader.h @@ -249,7 +249,7 @@ public: { resetBase(); ghostLevels = 0; - pieceSources = {}; + reset_std_vectors( pieceSources ); localReader.reset(); pointTags = cellTags = pointGlobalIndices = cellGlobalIndices = {}; } diff --git a/src/TNL/Meshes/Readers/VTKReader.h b/src/TNL/Meshes/Readers/VTKReader.h index 966e4f4677c1dcbbe742c786f5038615aca88676..255b196ce4d8144b2666b4d2b6302b640f8143b0 100644 --- a/src/TNL/Meshes/Readers/VTKReader.h +++ b/src/TNL/Meshes/Readers/VTKReader.h @@ -67,15 +67,12 @@ public: if( pointsType != "float" && pointsType != "double" ) throw MeshReaderError( "VTKReader", "unsupported data type for POINTS: " + pointsType ); - // global index type is not stored in legacy VTK files - // (binary VTK files don't support int64) - 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::int64_t > cellConnectivityArray, cellOffsetsArray; std::vector< std::uint8_t > typesArray; // read points @@ -159,29 +156,20 @@ public: // validate cell types using PolygonShapeGroupChecker = VTK::EntityShapeGroupChecker< VTK::EntityShape::Polygon >; - //TODO: add EntityShapeGroup for polyhedrons and uncomment line below - //using PolyhedralShapeGroupChecker = VTK::EntityShapeGroupChecker< VTK::EntityShape::Polyhedral >; + using PolyhedronShapeGroupChecker = VTK::EntityShapeGroupChecker< VTK::EntityShape::Polyhedron >; cellShape = (VTK::EntityShape) cellTypes[0]; - for( auto c : cellTypes ) - { + for( auto c : cellTypes ) { auto entityShape = (VTK::EntityShape) c; - if( cellShape != entityShape ) - { - //in case input mesh includes mixed shapes, use more general cellShape ( polygon for 2D, polyhedrals for 3D ) + if( cellShape != entityShape ) { + // if the input mesh includes mixed shapes, use more general cellShape (polygon for 2D, polyhedron for 3D) if( PolygonShapeGroupChecker::bothBelong( cellShape, entityShape ) ) - { cellShape = PolygonShapeGroupChecker::GeneralShape; - } - //TODO: add group check for polyhedrals later - /*else if( PolyhedralEntityShapeGroupChecker::bothBelong( cellShape, entityShape ) ) - { - cellShape = PolyhedralEntityShapeGroupChecker::GeneralShape; - }*/ - else - { - const std::string msg = "Mixed unstructured meshes are not supported. There are cells with type " - + VTK::getShapeName(cellShape) + " and " + VTK::getShapeName(entityShape) + "."; + else if( PolyhedronShapeGroupChecker::bothBelong( cellShape, entityShape ) ) + cellShape = PolyhedronShapeGroupChecker::GeneralShape; + else { + const std::string msg = "Unsupported unstructured meshes with mixed entities: there are cells with type " + + VTK::getShapeName(cellShape) + " and " + VTK::getShapeName(entityShape) + "."; reset(); throw MeshReaderError( "VTKReader", msg ); } @@ -194,38 +182,167 @@ public: inputFile.seekg( sectionPositions["CELLS"] ); getline( inputFile, line ); - // read entities - for( std::size_t entityIndex = 0; entityIndex < NumberOfEntities; entityIndex++ ) { - if( ! inputFile ) - throw MeshReaderError( "VTKReader", "unable to read enough cells, the file may be invalid or corrupted" - " (entityIndex = " + std::to_string(entityIndex) + ")" ); - - VTK::EntityShape entityShape = (VTK::EntityShape) typesArray[ entityIndex ]; - - // TODO: Polyhedrons will require to create polygon subentity seeds from given entityShapes - // and add their entries to faceConnectivityArray and faceOffsetsArray. - // CellConnectivityArray and cellOffsetsArray will contain indices addressing created polygon subentities. - if( entityShape == cellShape || - PolygonShapeGroupChecker::bothBelong( cellShape, entityShape ) ) { - iss.clear(); - iss.str( line ); - // read number of subvertices - const std::int32_t subvertices = readValue< std::int32_t >( dataFormat, inputFile ); - for( int v = 0; v < subvertices; v++ ) { - // legacy VTK files do not support 64-bit integers, even in the BINARY format - const std::int32_t vid = readValue< std::int32_t >( dataFormat, inputFile ); - if( ! inputFile ) - 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 ); + if( formatVersion == "2.0" ) { + // read entities + for( std::size_t entityIndex = 0; entityIndex < NumberOfEntities; entityIndex++ ) { + if( ! inputFile ) + throw MeshReaderError( "VTKReader", "unable to read enough cells, the file may be invalid or corrupted" + " (entityIndex = " + std::to_string(entityIndex) + ")" ); + + const VTK::EntityShape entityShape = (VTK::EntityShape) typesArray[ entityIndex ]; + + if( entityShape == VTK::EntityShape::Polyhedron ) + throw MeshReaderError( "VTKReader", "Reading polyhedrons from a DataFile version 2.0 is not supported. " + "Convert the file to version 5.1 (e.g. using Paraview) and try again." ); + + if( entityShape == cellShape || PolygonShapeGroupChecker::bothBelong( cellShape, entityShape ) ) { + // read number of subvertices + const std::int32_t subvertices = readValue< std::int32_t >( dataFormat, inputFile ); + for( int v = 0; v < subvertices; v++ ) { + // legacy VTK files do not support 64-bit integers, even in the BINARY format + const std::int32_t vid = readValue< std::int32_t >( dataFormat, inputFile ); + if( ! inputFile ) + 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) + ")" ); + cellConnectivityArray.push_back( vid ); + } + cellOffsetsArray.push_back( cellConnectivityArray.size() ); + } + else { + // skip the entity + const std::int32_t subvertices = readValue< std::int32_t >( dataFormat, inputFile ); + for( int v = 0; v < subvertices; v++ ) + skipValue( dataFormat, inputFile, "int" ); } - offsetsArray.push_back( connectivityArray.size() ); } - else { - // skip the entity - const std::int32_t subvertices = readValue< std::int32_t >( dataFormat, inputFile ); - for( int v = 0; v < subvertices; v++ ) - skipValue( dataFormat, inputFile, "int" ); + } + else if( formatVersion == "5.1" ) { + // parse the rest of the line: CELLS <offsets_count> <connectivity_count> + std::int64_t offsets_count = 0; + std::int64_t connectivity_count = 0; + iss.clear(); + iss.str( line ); + iss >> aux >> offsets_count >> connectivity_count; + if( offsets_count < 1 ) + throw MeshReaderError( "VTKReader", "invalid offsets count: " + std::to_string( offsets_count ) ); + + // find to the OFFSETS section + if( ! sectionPositions.count( "OFFSETS" ) ) + throw MeshReaderError( "VTKReader", "unable to find the OFFSETS section, the file may be invalid or corrupted" ); + inputFile.seekg( sectionPositions["OFFSETS"] ); + + // read all offsets into an auxiliary array + std::vector< std::int64_t > allOffsetsArray; + getline( inputFile, line ); + iss.clear(); + iss.str( line ); + std::string aux, datatype; + iss >> aux >> datatype; + for( std::int64_t entityIndex = 0; entityIndex < offsets_count; entityIndex++ ) { + std::int64_t value; + if( datatype == "vtktypeint32" ) + value = readValue< std::int32_t >( dataFormat, inputFile ); + else if( datatype == "vtktypeint64" ) + value = readValue< std::int64_t >( dataFormat, inputFile ); + else + throw MeshReaderError( "VTKReader", "found data type which is not implemented in the reader: " + datatype ); + if( ! inputFile ) + throw MeshReaderError( "VTKReader", "unable to read enough offsets, the file may be invalid or corrupted" + " (entityIndex = " + std::to_string(entityIndex) + ")" ); + allOffsetsArray.push_back( value ); + } + + // find to the CONNECTIVITY section + if( ! sectionPositions.count( "CONNECTIVITY" ) ) + throw MeshReaderError( "VTKReader", "unable to find the CONNECTIVITY section, the file may be invalid or corrupted" ); + inputFile.seekg( sectionPositions["CONNECTIVITY"] ); + + // get datatype + getline( inputFile, line ); + iss.clear(); + iss.str( line ); + iss >> aux >> datatype; + + // arrays for polyhedral mesh + std::vector< std::int64_t > faceConnectivityArray, faceOffsetsArray; + std::int64_t faceIndex = 0; + + // read connectivity + for( std::size_t entityIndex = 0; entityIndex < NumberOfEntities; entityIndex++ ) { + if( ! inputFile ) + throw MeshReaderError( "VTKReader", "unable to read enough cells, the file may be invalid or corrupted" + " (entityIndex = " + std::to_string(entityIndex) + ")" ); + + const VTK::EntityShape entityShape = (VTK::EntityShape) typesArray[ entityIndex ]; + const std::int64_t offsetBegin = allOffsetsArray[ entityIndex ]; + const std::int64_t offsetEnd = allOffsetsArray[ entityIndex + 1 ]; + + // TODO: Polyhedrons will require to create polygon subentity seeds from given entityShapes + // and add their entries to faceConnectivityArray and faceOffsetsArray. + // CellConnectivityArray and cellOffsetsArray will contain indices addressing created polygon subentities. + if( cellShape == VTK::EntityShape::Polyhedron && entityShape != cellShape && PolyhedronShapeGroupChecker::bothBelong( cellShape, entityShape ) ) + throw MeshReaderError( "VTKReader", "Converting a mixed mesh to polyhedral mesh is not implemented yet." ); + + if( entityShape == cellShape && cellShape == VTK::EntityShape::Polyhedron ) { + // read connectivity for current cell into extra array + std::vector< std::int64_t > cell_connectivity; + for( int v = 0; v < offsetEnd - offsetBegin; v++ ) { + std::int64_t value; + if( datatype == "vtktypeint32" ) + value = readValue< std::int32_t >( dataFormat, inputFile ); + else if( datatype == "vtktypeint64" ) + value = readValue< std::int64_t >( dataFormat, inputFile ); + else + throw MeshReaderError( "VTKReader", "found data type which is not implemented in the reader: " + datatype ); + if( ! inputFile ) + 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) + ")" ); + cell_connectivity.push_back( value ); + } + // connectivity[offsetBegin : offsetEnd] describes the faces of + // the cell in the following format (similar to VTU's faces array) + // num_faces_cell_0, + // num_nodes_face_0, node_ind_0, node_ind_1, .. + // num_nodes_face_1, node_ind_0, node_ind_1, .. + // ... + std::size_t i = 1; // skip num_faces for the cell + while( i < cell_connectivity.size() ) { + const std::int64_t num_nodes = cell_connectivity.at( i++ ); + for( std::int64_t n = 0; n < num_nodes; n++ ) + faceConnectivityArray.push_back( cell_connectivity.at( i++ ) ); + faceOffsetsArray.push_back( faceConnectivityArray.size() ); + cellConnectivityArray.push_back( faceIndex++ ); + } + cellOffsetsArray.push_back( cellConnectivityArray.size() ); + } + else if( entityShape == cellShape || PolygonShapeGroupChecker::bothBelong( cellShape, entityShape ) ) { + for( int v = 0; v < offsetEnd - offsetBegin; v++ ) { + std::int64_t vid; + if( datatype == "vtktypeint32" ) + vid = readValue< std::int32_t >( dataFormat, inputFile ); + else if( datatype == "vtktypeint64" ) + vid = readValue< std::int64_t >( dataFormat, inputFile ); + else + throw MeshReaderError( "VTKReader", "found data type which is not implemented in the reader: " + datatype ); + if( ! inputFile ) + 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) + ")" ); + cellConnectivityArray.push_back( vid ); + } + cellOffsetsArray.push_back( cellConnectivityArray.size() ); + } + else { + // skip the entity + for( int v = 0; v < offsetEnd - offsetBegin; v++ ) + skipValue( dataFormat, inputFile, datatype ); + } + } + + // set the arrays to the base class + if( faceIndex > 0 ) { + this->NumberOfFaces = faceIndex; + this->faceConnectivityArray = std::move( faceConnectivityArray ); + this->faceOffsetsArray = std::move( faceOffsetsArray ); } } @@ -234,8 +351,8 @@ public: // set the arrays to the base class this->pointsArray = std::move(pointsArray); - this->cellConnectivityArray = std::move(connectivityArray); - this->cellOffsetsArray = std::move(offsetsArray); + this->cellConnectivityArray = std::move(cellConnectivityArray); + this->cellOffsetsArray = std::move(cellOffsetsArray); this->typesArray = std::move(typesArray); // indicate success by setting the mesh type @@ -264,6 +381,7 @@ public: protected: // output of parseHeader + std::string formatVersion; VTK::FileFormat dataFormat = VTK::FileFormat::ascii; std::string dataset; @@ -281,8 +399,12 @@ protected: // check header getline( str, line ); - if( line != "# vtk DataFile Version 2.0" ) + static const std::string prefix = "# vtk DataFile Version "; + formatVersion = line.substr( prefix.length() ); + if( line.substr( 0, prefix.length() ) != prefix ) throw MeshReaderError( "VTKReader", "failed to parse the VTK file header: unsupported VTK header '" + line + "'" ); + if( formatVersion != "2.0" && formatVersion != "5.1" ) + throw MeshReaderError( "VTKReader", "unsupported VTK DataFile Version: '" + formatVersion + "'" ); // skip title if( ! str ) @@ -314,6 +436,22 @@ protected: iss >> dataset; } + void skip_meta( std::istream& str ) + { + // skip possible metadata + // https://vtk.org/doc/nightly/html/IOLegacyInformationFormat.html + std::string line; + while( true ) { + getline( str, line ); + if( ! str ) + throw MeshReaderError( "VTKReader", "failed to parse a METADATA section: is it terminated by a blank line?" ); + // strip whitespace + line.erase( std::remove_if( line.begin(), line.end(), isspace ), line.end() ); + if( line.empty() ) + break; + } + } + void findSections( std::istream& str ) { while( str ) { @@ -349,7 +487,14 @@ protected: std::int32_t components = 0; std::int32_t tuples = 0; std::string datatype; - iss >> aux >> components >> tuples >> datatype; + iss >> aux; + if( aux == "METADATA" ) { + // skip metadata and read again + skip_meta( str ); + i--; + continue; + } + iss >> components >> tuples >> datatype; if( ! iss ) throw MeshReaderError( "VTKReader", "failed to extract FieldData information from line '" + line + "'" ); // skip the points coordinates @@ -370,14 +515,74 @@ protected: // skip end of line (or any whitespace) str >> std::ws; } + // METADATA is a thing since version 5.1 of the file format (or something else newer than 2.0) + else if( name == "METADATA" ) { + sectionPositions.insert( {"METADATA", currentPosition} ); + skip_meta( str ); + } else if( name == "CELLS" ) { sectionPositions.insert( {"CELLS", currentPosition} ); - // parse the rest of the line: CELLS <cells_count> <values_count> - std::int32_t values_count = 0; - iss >> cells_count >> values_count; - // skip the values - for( std::int32_t j = 0; j < values_count; j++ ) - skipValue( dataFormat, str, "int" ); + if( formatVersion == "2.0" ) { + // index type is not stored in legacy VTK DataFile version 2.0 + // (binary files don't support int64) + connectivityType = offsetsType = "std::int32_t"; + // parse the rest of the line: CELLS <cells_count> <values_count> + std::int32_t values_count = 0; + iss >> cells_count >> values_count; + // skip the values + for( std::int32_t j = 0; j < values_count; j++ ) + skipValue( dataFormat, str, "int" ); + } + else if( formatVersion == "5.1" ) { + // parse the rest of the line: CELLS <offsets_count> <connectivity_count> + std::int32_t offsets_count = 0; + std::int32_t connectivity_count = 0; + iss >> offsets_count >> connectivity_count; + if( offsets_count < 1 ) + throw MeshReaderError( "VTKReader", "invalid offsets count: " + std::to_string( offsets_count ) ); + cells_count = offsets_count - 1; + // drop all whitespace (empty lines etc) before saving a position and reading a line + str >> std::ws; + const std::ios::pos_type offsetsPosition = str.tellg(); + // skip offsets + str >> std::ws; + getline( str, line ); + iss.clear(); + iss.str( line ); + std::string aux, datatype; + iss >> aux >> datatype; + if( aux != "OFFSETS" ) + throw MeshReaderError( "VTKReader", "expected OFFSETS section, found '" + aux + "'" ); + sectionPositions.insert( {"OFFSETS", offsetsPosition} ); + if( datatype == "vtktypeint32" ) + offsetsType = "std::int32_t"; + else if( datatype == "vtktypeint64" ) + offsetsType = "std::int64_t"; + else + throw MeshReaderError( "VTKReader", "unsupported datatype for OFFSETS: " + datatype ); + for( std::int32_t j = 0; j < offsets_count; j++ ) + skipValue( dataFormat, str, datatype ); + // drop all whitespace (empty lines etc) before saving a position and reading a line + str >> std::ws; + const std::ios::pos_type connectivityPosition = str.tellg(); + // skip connectivity + str >> std::ws; + getline( str, line ); + iss.clear(); + iss.str( line ); + iss >> aux >> datatype; + if( aux != "CONNECTIVITY" ) + throw MeshReaderError( "VTKReader", "expected CONNECTIVITY section, found '" + aux + "'" ); + sectionPositions.insert( {"CONNECTIVITY", connectivityPosition} ); + if( datatype == "vtktypeint32" ) + connectivityType = "std::int32_t"; + else if( datatype == "vtktypeint64" ) + connectivityType = "std::int64_t"; + else + throw MeshReaderError( "VTKReader", "unsupported datatype for CONNECTIVITY: " + datatype ); + for( std::int32_t j = 0; j < connectivity_count; j++ ) + skipValue( dataFormat, str, datatype ); + } // skip end of line (or any whitespace) str >> std::ws; } @@ -416,6 +621,12 @@ protected: std::string type; iss >> type; + // handle switching between CELL_DATA and POINT_DATA + if( ( name == "CELL_DATA" && type == "POINT_DATA" ) || ( name == "POINT_DATA" && type == "CELL_DATA" ) ) { + name = type; + continue; + } + const std::int32_t elements = (name == "CELL_DATA") ? cells_count : points_count; // scalars: 1 value per cell/point @@ -462,7 +673,14 @@ protected: std::int32_t components = 0; std::int32_t tuples = 0; std::string datatype; - iss >> array_name >> components >> tuples >> datatype; + iss >> array_name; + if( array_name == "METADATA" ) { + // skip metadata and read again + skip_meta( str ); + i--; + continue; + } + iss >> components >> tuples >> datatype; if( ! iss ) throw MeshReaderError( "VTKReader", "failed to extract FieldData information from line '" + line + "'" ); sectionPositions.insert( {name + "::" + array_name, currentPosition} ); @@ -474,6 +692,10 @@ protected: } continue; } + else if( type == "METADATA" ) { + skip_meta( str ); + continue; + } else { std::cerr << "VTKReader: encountered an unsupported CELL_DATA array type: " << type << ". Ignoring the rest of the file." << std::endl; @@ -491,6 +713,9 @@ protected: throw MeshReaderError( "VTKReader", "parsing error: unexpected section start at byte " + std::to_string(currentPosition) + " (section name is '" + name + "')" ); } + + // clear errors bits on the input stream + str.clear(); } VariantVector @@ -563,8 +788,12 @@ protected: static void skipValue( VTK::FileFormat format, std::istream& str, std::string datatype ) { - if( datatype == "int" ) + if( datatype == "int" ) // implicit in vtk DataFile Version 2.0 + readValue< std::int32_t >( format, str ); + else if( datatype == "vtktypeint32" ) // vtk DataFile Version 5.1 readValue< std::int32_t >( format, str ); + else if( datatype == "vtktypeint64" ) // vtk DataFile Version 5.1 + readValue< std::int64_t >( format, str ); else if( datatype == "float" ) readValue< float >( format, str ); else if( datatype == "double" ) diff --git a/src/TNL/Meshes/Readers/VTUReader.h b/src/TNL/Meshes/Readers/VTUReader.h index 08450b0fee1701932937bc63cb2629c763a91997..27ee3a3aa83c9f64cd693983e160dda1bd01e9b8 100644 --- a/src/TNL/Meshes/Readers/VTUReader.h +++ b/src/TNL/Meshes/Readers/VTUReader.h @@ -96,28 +96,20 @@ class VTUReader cellShape = (VTK::EntityShape) array[0]; meshDimension = getEntityDimension( cellShape ); using PolygonShapeGroupChecker = VTK::EntityShapeGroupChecker< VTK::EntityShape::Polygon >; - //TODO: uncomment line below later for polyhedrals - //using PolyhedralShapeGroupChecker = VTK::EntityShapeGroupChecker< VTK::EntityShape::Polyhedral >; + using PolyhedronShapeGroupChecker = VTK::EntityShapeGroupChecker< VTK::EntityShape::Polyhedron >; // TODO: check only entities of the same dimension (edges, faces and cells separately) - for( auto c : array ) - { + for( auto c : array ) { VTK::EntityShape entityShape = (VTK::EntityShape) c; - if( entityShape != cellShape ) - { + if( entityShape != cellShape ) { if( PolygonShapeGroupChecker::bothBelong( cellShape, entityShape ) ) - { cellShape = PolygonShapeGroupChecker::GeneralShape; - } - //TODO: add group check for polyhedrals later - /*else if( PolyhedralEntityShapeGroupChecker::bothBelong( cellShape, entityShape ) ) - { - cellShape = PolyhedralEntityShapeGroupChecker::GeneralShape; - }*/ - else - { - throw MeshReaderError( "VTUReader", "Mixed unstructured meshes are not supported. There are cells with type " - + VTK::getShapeName(cellShape) + " and " + VTK::getShapeName(entityShape) + "." ); + else if( PolyhedronShapeGroupChecker::bothBelong( cellShape, entityShape ) ) + cellShape = PolyhedronShapeGroupChecker::GeneralShape; + else { + const std::string msg = "Unsupported unstructured meshes with mixed entities: there are cells with type " + + VTK::getShapeName(cellShape) + " and " + VTK::getShapeName(entityShape) + "."; + throw MeshReaderError( "VTUReader", msg ); } } } @@ -148,6 +140,95 @@ class VTUReader }, cellConnectivityArray ); + + if( cellShape == VTK::EntityShape::Polyhedron ) { + // NOTE: the data format for polyhedral meshes in VTK files is not documented well (almost not at all). + // Here are some references: + // - https://itk.org/Wiki/VTK/Polyhedron_Support + // - https://vtk.org/doc/nightly/html/classvtkPolyhedron.html + // - https://github.com/nschloe/meshio/pull/916 + // - https://github.com/nschloe/meshio/blob/b358a88b7c1158d5ee2b2c873f67ba1cb0647686/src/meshio/vtu/_vtu.py#L33-L102 + + const XMLElement* faces = getDataArrayByName( cells, "faces" ); + const XMLElement* faceOffsets = getDataArrayByName( cells, "faceoffsets" ); + const VariantVector vtk_facesArray = readDataArray( faces, "faces" ); + const VariantVector vtk_faceOffsetsArray = readDataArray( faceOffsets, "faceoffsets" ); + const std::string facesType = VTKDataTypes.at( getAttributeString( faces, "type" ) ); + const std::string faceOffsetsType = VTKDataTypes.at( getAttributeString( faceOffsets, "type" ) ); + if( facesType != faceOffsetsType ) + throw MeshReaderError( "VTUReader", "type of the faces array does not match the type of the faceoffsets array" ); + if( faceOffsetsType != offsetsType ) + throw MeshReaderError( "VTUReader", "type of the faceoffsets array does not match the type of the offsets array" ); + + // validate face offsets + std::size_t max_offset = 0; + visit( [this, &max_offset](auto&& array) mutable { + if( array.size() != NumberOfCells ) + throw MeshReaderError( "VTUReader", "size of the faceoffsets data array does not match the NumberOfCells attribute" ); + for( auto c : array ) { + // NOTE: VTK stores -1 for cells that are not a polyhedron. We would need to populate + if( c < 0 ) + continue; + if( c <= (decltype(c)) max_offset ) + throw MeshReaderError( "VTUReader", "the faceoffsets array is not monotonically increasing" ); + max_offset = c; + } + }, + vtk_faceOffsetsArray + ); + // validate faces + visit( [this, max_offset, &vtk_faceOffsetsArray](auto&& vtk_faces) { + if( vtk_faces.size() != max_offset ) + throw MeshReaderError( "VTUReader", "size of the faces data array does not match the faceoffsets array" ); + // let's just assume that the connectivity and offsets arrays have the same type... + using mpark::get; + const auto& vtk_faceOffsets = get< std::decay_t<decltype(vtk_faces)> >( vtk_faceOffsetsArray ); + + // We need to translate the VTK faces and faceoffsets arrays + // into the format suitable for MeshReader (which uses the + // format from FPMA). The data format for the faces array is: + // num_faces_cell_0, + // num_nodes_face_0, node_ind_0, node_ind_1, .. + // num_nodes_face_1, node_ind_0, node_ind_1, .. + // ... + // num_faces_cell_1, + // ... + // See https://vtk.org/Wiki/VTK/Polyhedron_Support for more. + std::decay_t<decltype(vtk_faces)> cellOffsets, cellConnectivity, faceOffsets, faceConnectivity; + std::make_signed_t< std::size_t > cell_off_begin = 0; + std::size_t faceIndex = 0; + for( std::size_t cell = 0; cell < vtk_faceOffsets.size(); cell++ ) { + const std::make_signed_t< std::size_t > cell_off_end = vtk_faceOffsets[ cell ]; + // TODO: VTK stores -1 for cells that are not a polyhedron. We would need to populate + // faceOffsetsArray and faceConnectivityArray with values based on the cell topology. + if( cell_off_end < 0 ) + throw MeshReaderError( "VTUReader", "found invalid offset in the faceoffsets array: " + std::to_string(cell_off_end) ); + if( static_cast< std::size_t >( cell_off_end ) > vtk_faces.size() ) + throw MeshReaderError( "VTUReader", "not enough face indices for cell no " + std::to_string(cell) ); + // faces[cell_off_begin : cell_off_end] -> face data for cell + const std::size_t num_faces = vtk_faces.at( cell_off_begin++ ); + for( std::size_t f = 0; f < num_faces; f++ ) { + const std::size_t num_vertices = vtk_faces.at( cell_off_begin++ ); + for( std::size_t v = 0; v < num_vertices; v++ ) + faceConnectivity.push_back( vtk_faces.at( cell_off_begin++ ) ); + faceOffsets.push_back( faceConnectivity.size() ); + cellConnectivity.push_back( faceIndex++ ); + } + cellOffsets.push_back( cellConnectivity.size() ); + + if( cell_off_begin != cell_off_end ) + throw MeshReaderError( "VTUReader", "error while parsing the faces data array: did not reach the end offset for cell " + std::to_string(cell) ); + } + + this->NumberOfFaces = faceIndex; + this->cellOffsetsArray = std::move( cellOffsets ); + this->cellConnectivityArray = std::move( cellConnectivity ); + this->faceOffsetsArray = std::move( faceOffsets ); + this->faceConnectivityArray = std::move( faceConnectivity ); + }, + vtk_facesArray + ); + } } #endif diff --git a/src/TNL/Meshes/Topologies/Hexahedron.h b/src/TNL/Meshes/Topologies/Hexahedron.h index d1f9f9ee797419ff17203adc73e1de432a65e029..30292bcfc0400133be5b02c15ca5b3b438c3509c 100644 --- a/src/TNL/Meshes/Topologies/Hexahedron.h +++ b/src/TNL/Meshes/Topologies/Hexahedron.h @@ -108,72 +108,72 @@ struct Subtopology< Hexahedron, 2 > * */ -template<> struct SubentityVertexMap< Hexahedron, Edge, 0, 0> { enum { index = 0 }; }; -template<> struct SubentityVertexMap< Hexahedron, Edge, 0, 1> { enum { index = 1 }; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 0, 0> { static constexpr int index = 0; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 0, 1> { static constexpr int index = 1; }; -template<> struct SubentityVertexMap< Hexahedron, Edge, 1, 0> { enum { index = 1 }; }; -template<> struct SubentityVertexMap< Hexahedron, Edge, 1, 1> { enum { index = 2 }; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 1, 0> { static constexpr int index = 1; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 1, 1> { static constexpr int index = 2; }; -template<> struct SubentityVertexMap< Hexahedron, Edge, 2, 0> { enum { index = 2 }; }; -template<> struct SubentityVertexMap< Hexahedron, Edge, 2, 1> { enum { index = 3 }; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 2, 0> { static constexpr int index = 2; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 2, 1> { static constexpr int index = 3; }; -template<> struct SubentityVertexMap< Hexahedron, Edge, 3, 0> { enum { index = 3 }; }; -template<> struct SubentityVertexMap< Hexahedron, Edge, 3, 1> { enum { index = 0 }; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 3, 0> { static constexpr int index = 3; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 3, 1> { static constexpr int index = 0; }; -template<> struct SubentityVertexMap< Hexahedron, Edge, 4, 0> { enum { index = 0 }; }; -template<> struct SubentityVertexMap< Hexahedron, Edge, 4, 1> { enum { index = 4 }; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 4, 0> { static constexpr int index = 0; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 4, 1> { static constexpr int index = 4; }; -template<> struct SubentityVertexMap< Hexahedron, Edge, 5, 0> { enum { index = 1 }; }; -template<> struct SubentityVertexMap< Hexahedron, Edge, 5, 1> { enum { index = 5 }; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 5, 0> { static constexpr int index = 1; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 5, 1> { static constexpr int index = 5; }; -template<> struct SubentityVertexMap< Hexahedron, Edge, 6, 0> { enum { index = 2 }; }; -template<> struct SubentityVertexMap< Hexahedron, Edge, 6, 1> { enum { index = 6 }; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 6, 0> { static constexpr int index = 2; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 6, 1> { static constexpr int index = 6; }; -template<> struct SubentityVertexMap< Hexahedron, Edge, 7, 0> { enum { index = 3 }; }; -template<> struct SubentityVertexMap< Hexahedron, Edge, 7, 1> { enum { index = 7 }; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 7, 0> { static constexpr int index = 3; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 7, 1> { static constexpr int index = 7; }; -template<> struct SubentityVertexMap< Hexahedron, Edge, 8, 0> { enum { index = 4 }; }; -template<> struct SubentityVertexMap< Hexahedron, Edge, 8, 1> { enum { index = 5 }; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 8, 0> { static constexpr int index = 4; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 8, 1> { static constexpr int index = 5; }; -template<> struct SubentityVertexMap< Hexahedron, Edge, 9, 0> { enum { index = 5 }; }; -template<> struct SubentityVertexMap< Hexahedron, Edge, 9, 1> { enum { index = 6 }; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 9, 0> { static constexpr int index = 5; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 9, 1> { static constexpr int index = 6; }; -template<> struct SubentityVertexMap< Hexahedron, Edge, 10, 0> { enum { index = 6 }; }; -template<> struct SubentityVertexMap< Hexahedron, Edge, 10, 1> { enum { index = 7 }; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 10, 0> { static constexpr int index = 6; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 10, 1> { static constexpr int index = 7; }; -template<> struct SubentityVertexMap< Hexahedron, Edge, 11, 0> { enum { index = 7 }; }; -template<> struct SubentityVertexMap< Hexahedron, Edge, 11, 1> { enum { index = 4 }; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 11, 0> { static constexpr int index = 7; }; +template<> struct SubentityVertexMap< Hexahedron, Edge, 11, 1> { static constexpr int index = 4; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 0, 0> { enum { index = 0 }; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 0, 1> { enum { index = 1 }; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 0, 2> { enum { index = 2 }; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 0, 3> { enum { index = 3 }; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 0, 0> { static constexpr int index = 0; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 0, 1> { static constexpr int index = 1; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 0, 2> { static constexpr int index = 2; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 0, 3> { static constexpr int index = 3; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 1, 0> { enum { index = 0 }; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 1, 1> { enum { index = 1 }; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 1, 2> { enum { index = 5 }; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 1, 3> { enum { index = 4 }; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 1, 0> { static constexpr int index = 0; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 1, 1> { static constexpr int index = 1; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 1, 2> { static constexpr int index = 5; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 1, 3> { static constexpr int index = 4; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 2, 0> { enum { index = 1 }; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 2, 1> { enum { index = 2 }; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 2, 2> { enum { index = 6 }; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 2, 3> { enum { index = 5 }; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 2, 0> { static constexpr int index = 1; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 2, 1> { static constexpr int index = 2; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 2, 2> { static constexpr int index = 6; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 2, 3> { static constexpr int index = 5; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 3, 0> { enum { index = 2 }; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 3, 1> { enum { index = 3 }; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 3, 2> { enum { index = 7 }; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 3, 3> { enum { index = 6 }; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 3, 0> { static constexpr int index = 2; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 3, 1> { static constexpr int index = 3; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 3, 2> { static constexpr int index = 7; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 3, 3> { static constexpr int index = 6; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 4, 0> { enum { index = 3 }; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 4, 1> { enum { index = 0 }; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 4, 2> { enum { index = 4 }; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 4, 3> { enum { index = 7 }; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 4, 0> { static constexpr int index = 3; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 4, 1> { static constexpr int index = 0; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 4, 2> { static constexpr int index = 4; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 4, 3> { static constexpr int index = 7; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 5, 0> { enum { index = 4 }; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 5, 1> { enum { index = 5 }; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 5, 2> { enum { index = 6 }; }; -template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 5, 3> { enum { index = 7 }; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 5, 0> { static constexpr int index = 4; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 5, 1> { static constexpr int index = 5; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 5, 2> { static constexpr int index = 6; }; +template<> struct SubentityVertexMap< Hexahedron, Quadrangle, 5, 3> { static constexpr int index = 7; }; } // namespace Topologies } // namespace Meshes diff --git a/src/TNL/Meshes/Topologies/IsDynamicTopology.h b/src/TNL/Meshes/Topologies/IsDynamicTopology.h index 0cb52358a589c963c70f931ae57f69d06db6f8dc..47110449ac1c1ab1b8e34aa7f6dbdce0a1571c62 100644 --- a/src/TNL/Meshes/Topologies/IsDynamicTopology.h +++ b/src/TNL/Meshes/Topologies/IsDynamicTopology.h @@ -14,8 +14,8 @@ namespace Topologies { template< typename Topology, int D = Topology::dimension > struct IsDynamicTopology { - enum : bool { value = !HasCountMember< Subtopology< Topology, D - 1 > >::value || - IsDynamicTopology< Topology, D - 1 >::value }; + static constexpr bool value = ! HasCountMember< Subtopology< Topology, D - 1 > >::value || + IsDynamicTopology< Topology, D - 1 >::value; }; /** @@ -31,9 +31,9 @@ struct IsDynamicTopology< Vertex, 0 > : std::false_type template< typename Topology > struct IsDynamicTopology< Topology, 1 > { - enum : bool { value = !HasCountMember< Subtopology< Topology, 0 > >::value }; + static constexpr bool value = ! HasCountMember< Subtopology< Topology, 0 > >::value; }; } // namespace Topologies } // namespace Meshes -} // namespace TNL \ No newline at end of file +} // namespace TNL diff --git a/src/TNL/Meshes/Topologies/Pyramid.h b/src/TNL/Meshes/Topologies/Pyramid.h index 83edec19494f6d66d7be7cddb54faaa61ad05b42..38db2ee7b5b415d72f1ef36321646fe97fc71bd3 100644 --- a/src/TNL/Meshes/Topologies/Pyramid.h +++ b/src/TNL/Meshes/Topologies/Pyramid.h @@ -38,29 +38,29 @@ struct Subtopology< Pyramid, 2 > }; -template<> struct SubentityVertexMap< Pyramid, Edge, 0, 0> { enum { index = 0 }; }; -template<> struct SubentityVertexMap< Pyramid, Edge, 0, 1> { enum { index = 1 }; }; +template<> struct SubentityVertexMap< Pyramid, Edge, 0, 0> { static constexpr int index = 0; }; +template<> struct SubentityVertexMap< Pyramid, Edge, 0, 1> { static constexpr int index = 1; }; -template<> struct SubentityVertexMap< Pyramid, Edge, 1, 0> { enum { index = 1 }; }; -template<> struct SubentityVertexMap< Pyramid, Edge, 1, 1> { enum { index = 2 }; }; +template<> struct SubentityVertexMap< Pyramid, Edge, 1, 0> { static constexpr int index = 1; }; +template<> struct SubentityVertexMap< Pyramid, Edge, 1, 1> { static constexpr int index = 2; }; -template<> struct SubentityVertexMap< Pyramid, Edge, 2, 0> { enum { index = 2 }; }; -template<> struct SubentityVertexMap< Pyramid, Edge, 2, 1> { enum { index = 3 }; }; +template<> struct SubentityVertexMap< Pyramid, Edge, 2, 0> { static constexpr int index = 2; }; +template<> struct SubentityVertexMap< Pyramid, Edge, 2, 1> { static constexpr int index = 3; }; -template<> struct SubentityVertexMap< Pyramid, Edge, 3, 0> { enum { index = 3 }; }; -template<> struct SubentityVertexMap< Pyramid, Edge, 3, 1> { enum { index = 0 }; }; +template<> struct SubentityVertexMap< Pyramid, Edge, 3, 0> { static constexpr int index = 3; }; +template<> struct SubentityVertexMap< Pyramid, Edge, 3, 1> { static constexpr int index = 0; }; -template<> struct SubentityVertexMap< Pyramid, Edge, 4, 0> { enum { index = 0 }; }; -template<> struct SubentityVertexMap< Pyramid, Edge, 4, 1> { enum { index = 4 }; }; +template<> struct SubentityVertexMap< Pyramid, Edge, 4, 0> { static constexpr int index = 0; }; +template<> struct SubentityVertexMap< Pyramid, Edge, 4, 1> { static constexpr int index = 4; }; -template<> struct SubentityVertexMap< Pyramid, Edge, 5, 0> { enum { index = 1 }; }; -template<> struct SubentityVertexMap< Pyramid, Edge, 5, 1> { enum { index = 4 }; }; +template<> struct SubentityVertexMap< Pyramid, Edge, 5, 0> { static constexpr int index = 1; }; +template<> struct SubentityVertexMap< Pyramid, Edge, 5, 1> { static constexpr int index = 4; }; -template<> struct SubentityVertexMap< Pyramid, Edge, 6, 0> { enum { index = 2 }; }; -template<> struct SubentityVertexMap< Pyramid, Edge, 6, 1> { enum { index = 4 }; }; +template<> struct SubentityVertexMap< Pyramid, Edge, 6, 0> { static constexpr int index = 2; }; +template<> struct SubentityVertexMap< Pyramid, Edge, 6, 1> { static constexpr int index = 4; }; -template<> struct SubentityVertexMap< Pyramid, Edge, 7, 0> { enum { index = 3 }; }; -template<> struct SubentityVertexMap< Pyramid, Edge, 7, 1> { enum { index = 4 }; }; +template<> struct SubentityVertexMap< Pyramid, Edge, 7, 0> { static constexpr int index = 3; }; +template<> struct SubentityVertexMap< Pyramid, Edge, 7, 1> { static constexpr int index = 4; }; template <> struct SubentityVertexCount< Pyramid, Polygon, 0 > @@ -68,10 +68,10 @@ struct SubentityVertexCount< Pyramid, Polygon, 0 > static constexpr int count = 4; }; -template<> struct SubentityVertexMap< Pyramid, Polygon, 0, 0> { enum { index = 0 }; }; -template<> struct SubentityVertexMap< Pyramid, Polygon, 0, 1> { enum { index = 1 }; }; -template<> struct SubentityVertexMap< Pyramid, Polygon, 0, 2> { enum { index = 2 }; }; -template<> struct SubentityVertexMap< Pyramid, Polygon, 0, 3> { enum { index = 3 }; }; +template<> struct SubentityVertexMap< Pyramid, Polygon, 0, 0> { static constexpr int index = 0; }; +template<> struct SubentityVertexMap< Pyramid, Polygon, 0, 1> { static constexpr int index = 1; }; +template<> struct SubentityVertexMap< Pyramid, Polygon, 0, 2> { static constexpr int index = 2; }; +template<> struct SubentityVertexMap< Pyramid, Polygon, 0, 3> { static constexpr int index = 3; }; template <> struct SubentityVertexCount< Pyramid, Polygon, 1 > @@ -79,9 +79,9 @@ struct SubentityVertexCount< Pyramid, Polygon, 1 > static constexpr int count = 3; }; -template<> struct SubentityVertexMap< Pyramid, Polygon, 1, 0> { enum { index = 0 }; }; -template<> struct SubentityVertexMap< Pyramid, Polygon, 1, 1> { enum { index = 1 }; }; -template<> struct SubentityVertexMap< Pyramid, Polygon, 1, 2> { enum { index = 4 }; }; +template<> struct SubentityVertexMap< Pyramid, Polygon, 1, 0> { static constexpr int index = 0; }; +template<> struct SubentityVertexMap< Pyramid, Polygon, 1, 1> { static constexpr int index = 1; }; +template<> struct SubentityVertexMap< Pyramid, Polygon, 1, 2> { static constexpr int index = 4; }; template <> struct SubentityVertexCount< Pyramid, Polygon, 2 > @@ -89,9 +89,9 @@ struct SubentityVertexCount< Pyramid, Polygon, 2 > static constexpr int count = 3; }; -template<> struct SubentityVertexMap< Pyramid, Polygon, 2, 0> { enum { index = 1 }; }; -template<> struct SubentityVertexMap< Pyramid, Polygon, 2, 1> { enum { index = 2 }; }; -template<> struct SubentityVertexMap< Pyramid, Polygon, 2, 2> { enum { index = 4 }; }; +template<> struct SubentityVertexMap< Pyramid, Polygon, 2, 0> { static constexpr int index = 1; }; +template<> struct SubentityVertexMap< Pyramid, Polygon, 2, 1> { static constexpr int index = 2; }; +template<> struct SubentityVertexMap< Pyramid, Polygon, 2, 2> { static constexpr int index = 4; }; template <> struct SubentityVertexCount< Pyramid, Polygon, 3 > @@ -99,9 +99,9 @@ struct SubentityVertexCount< Pyramid, Polygon, 3 > static constexpr int count = 3; }; -template<> struct SubentityVertexMap< Pyramid, Polygon, 3, 0> { enum { index = 2 }; }; -template<> struct SubentityVertexMap< Pyramid, Polygon, 3, 1> { enum { index = 3 }; }; -template<> struct SubentityVertexMap< Pyramid, Polygon, 3, 2> { enum { index = 4 }; }; +template<> struct SubentityVertexMap< Pyramid, Polygon, 3, 0> { static constexpr int index = 2; }; +template<> struct SubentityVertexMap< Pyramid, Polygon, 3, 1> { static constexpr int index = 3; }; +template<> struct SubentityVertexMap< Pyramid, Polygon, 3, 2> { static constexpr int index = 4; }; template <> struct SubentityVertexCount< Pyramid, Polygon, 4 > @@ -109,10 +109,10 @@ struct SubentityVertexCount< Pyramid, Polygon, 4 > static constexpr int count = 3; }; -template<> struct SubentityVertexMap< Pyramid, Polygon, 4, 0> { enum { index = 3 }; }; -template<> struct SubentityVertexMap< Pyramid, Polygon, 4, 1> { enum { index = 0 }; }; -template<> struct SubentityVertexMap< Pyramid, Polygon, 4, 2> { enum { index = 4 }; }; +template<> struct SubentityVertexMap< Pyramid, Polygon, 4, 0> { static constexpr int index = 3; }; +template<> struct SubentityVertexMap< Pyramid, Polygon, 4, 1> { static constexpr int index = 0; }; +template<> struct SubentityVertexMap< Pyramid, Polygon, 4, 2> { static constexpr int index = 4; }; } // namespace Topologies } // namespace Meshes -} // namespace TNL \ No newline at end of file +} // namespace TNL diff --git a/src/TNL/Meshes/Topologies/Quadrangle.h b/src/TNL/Meshes/Topologies/Quadrangle.h index bc4885a8be583f51905f0ffc80360cfb58631bf7..aa881aadc7b875364a434204afeb02000c2651ac 100644 --- a/src/TNL/Meshes/Topologies/Quadrangle.h +++ b/src/TNL/Meshes/Topologies/Quadrangle.h @@ -72,17 +72,17 @@ struct Subtopology< Quadrangle, 1 > * */ -template<> struct SubentityVertexMap< Quadrangle, Edge, 0, 0> { enum { index = 0 }; }; -template<> struct SubentityVertexMap< Quadrangle, Edge, 0, 1> { enum { index = 1 }; }; +template<> struct SubentityVertexMap< Quadrangle, Edge, 0, 0> { static constexpr int index = 0; }; +template<> struct SubentityVertexMap< Quadrangle, Edge, 0, 1> { static constexpr int index = 1; }; -template<> struct SubentityVertexMap< Quadrangle, Edge, 1, 0> { enum { index = 1 }; }; -template<> struct SubentityVertexMap< Quadrangle, Edge, 1, 1> { enum { index = 2 }; }; +template<> struct SubentityVertexMap< Quadrangle, Edge, 1, 0> { static constexpr int index = 1; }; +template<> struct SubentityVertexMap< Quadrangle, Edge, 1, 1> { static constexpr int index = 2; }; -template<> struct SubentityVertexMap< Quadrangle, Edge, 2, 0> { enum { index = 2 }; }; -template<> struct SubentityVertexMap< Quadrangle, Edge, 2, 1> { enum { index = 3 }; }; +template<> struct SubentityVertexMap< Quadrangle, Edge, 2, 0> { static constexpr int index = 2; }; +template<> struct SubentityVertexMap< Quadrangle, Edge, 2, 1> { static constexpr int index = 3; }; -template<> struct SubentityVertexMap< Quadrangle, Edge, 3, 0> { enum { index = 3 }; }; -template<> struct SubentityVertexMap< Quadrangle, Edge, 3, 1> { enum { index = 0 }; }; +template<> struct SubentityVertexMap< Quadrangle, Edge, 3, 0> { static constexpr int index = 3; }; +template<> struct SubentityVertexMap< Quadrangle, Edge, 3, 1> { static constexpr int index = 0; }; } // namespace Topologies } // namespace Meshes diff --git a/src/TNL/Meshes/Topologies/Tetrahedron.h b/src/TNL/Meshes/Topologies/Tetrahedron.h index 048daa1c3c3fe7cc112489e2d40411c4f4ad47b2..ed6e852f3288edad6064e3cfc935ef54ff672ddc 100644 --- a/src/TNL/Meshes/Topologies/Tetrahedron.h +++ b/src/TNL/Meshes/Topologies/Tetrahedron.h @@ -52,41 +52,41 @@ struct Subtopology< Tetrahedron, 2 > }; -template<> struct SubentityVertexMap< Tetrahedron, Edge, 0, 0> { enum { index = 1 }; }; -template<> struct SubentityVertexMap< Tetrahedron, Edge, 0, 1> { enum { index = 2 }; }; +template<> struct SubentityVertexMap< Tetrahedron, Edge, 0, 0> { static constexpr int index = 1; }; +template<> struct SubentityVertexMap< Tetrahedron, Edge, 0, 1> { static constexpr int index = 2; }; -template<> struct SubentityVertexMap< Tetrahedron, Edge, 1, 0> { enum { index = 2 }; }; -template<> struct SubentityVertexMap< Tetrahedron, Edge, 1, 1> { enum { index = 0 }; }; +template<> struct SubentityVertexMap< Tetrahedron, Edge, 1, 0> { static constexpr int index = 2; }; +template<> struct SubentityVertexMap< Tetrahedron, Edge, 1, 1> { static constexpr int index = 0; }; -template<> struct SubentityVertexMap< Tetrahedron, Edge, 2, 0> { enum { index = 0 }; }; -template<> struct SubentityVertexMap< Tetrahedron, Edge, 2, 1> { enum { index = 1 }; }; +template<> struct SubentityVertexMap< Tetrahedron, Edge, 2, 0> { static constexpr int index = 0; }; +template<> struct SubentityVertexMap< Tetrahedron, Edge, 2, 1> { static constexpr int index = 1; }; -template<> struct SubentityVertexMap< Tetrahedron, Edge, 3, 0> { enum { index = 0 }; }; -template<> struct SubentityVertexMap< Tetrahedron, Edge, 3, 1> { enum { index = 3 }; }; +template<> struct SubentityVertexMap< Tetrahedron, Edge, 3, 0> { static constexpr int index = 0; }; +template<> struct SubentityVertexMap< Tetrahedron, Edge, 3, 1> { static constexpr int index = 3; }; -template<> struct SubentityVertexMap< Tetrahedron, Edge, 4, 0> { enum { index = 1 }; }; -template<> struct SubentityVertexMap< Tetrahedron, Edge, 4, 1> { enum { index = 3 }; }; +template<> struct SubentityVertexMap< Tetrahedron, Edge, 4, 0> { static constexpr int index = 1; }; +template<> struct SubentityVertexMap< Tetrahedron, Edge, 4, 1> { static constexpr int index = 3; }; -template<> struct SubentityVertexMap< Tetrahedron, Edge, 5, 0> { enum { index = 2 }; }; -template<> struct SubentityVertexMap< Tetrahedron, Edge, 5, 1> { enum { index = 3 }; }; +template<> struct SubentityVertexMap< Tetrahedron, Edge, 5, 0> { static constexpr int index = 2; }; +template<> struct SubentityVertexMap< Tetrahedron, Edge, 5, 1> { static constexpr int index = 3; }; // i-th subvertex is the opposite vertex of i-th subface -template<> struct SubentityVertexMap< Tetrahedron, Triangle, 0, 0> { enum { index = 1 }; }; -template<> struct SubentityVertexMap< Tetrahedron, Triangle, 0, 1> { enum { index = 2 }; }; -template<> struct SubentityVertexMap< Tetrahedron, Triangle, 0, 2> { enum { index = 3 }; }; +template<> struct SubentityVertexMap< Tetrahedron, Triangle, 0, 0> { static constexpr int index = 1; }; +template<> struct SubentityVertexMap< Tetrahedron, Triangle, 0, 1> { static constexpr int index = 2; }; +template<> struct SubentityVertexMap< Tetrahedron, Triangle, 0, 2> { static constexpr int index = 3; }; -template<> struct SubentityVertexMap< Tetrahedron, Triangle, 1, 0> { enum { index = 2 }; }; -template<> struct SubentityVertexMap< Tetrahedron, Triangle, 1, 1> { enum { index = 0 }; }; -template<> struct SubentityVertexMap< Tetrahedron, Triangle, 1, 2> { enum { index = 3 }; }; +template<> struct SubentityVertexMap< Tetrahedron, Triangle, 1, 0> { static constexpr int index = 2; }; +template<> struct SubentityVertexMap< Tetrahedron, Triangle, 1, 1> { static constexpr int index = 0; }; +template<> struct SubentityVertexMap< Tetrahedron, Triangle, 1, 2> { static constexpr int index = 3; }; -template<> struct SubentityVertexMap< Tetrahedron, Triangle, 2, 0> { enum { index = 0 }; }; -template<> struct SubentityVertexMap< Tetrahedron, Triangle, 2, 1> { enum { index = 1 }; }; -template<> struct SubentityVertexMap< Tetrahedron, Triangle, 2, 2> { enum { index = 3 }; }; +template<> struct SubentityVertexMap< Tetrahedron, Triangle, 2, 0> { static constexpr int index = 0; }; +template<> struct SubentityVertexMap< Tetrahedron, Triangle, 2, 1> { static constexpr int index = 1; }; +template<> struct SubentityVertexMap< Tetrahedron, Triangle, 2, 2> { static constexpr int index = 3; }; -template<> struct SubentityVertexMap< Tetrahedron, Triangle, 3, 0> { enum { index = 0 }; }; -template<> struct SubentityVertexMap< Tetrahedron, Triangle, 3, 1> { enum { index = 1 }; }; -template<> struct SubentityVertexMap< Tetrahedron, Triangle, 3, 2> { enum { index = 2 }; }; +template<> struct SubentityVertexMap< Tetrahedron, Triangle, 3, 0> { static constexpr int index = 0; }; +template<> struct SubentityVertexMap< Tetrahedron, Triangle, 3, 1> { static constexpr int index = 1; }; +template<> struct SubentityVertexMap< Tetrahedron, Triangle, 3, 2> { static constexpr int index = 2; }; } // namespace Topologies } // namespace Meshes diff --git a/src/TNL/Meshes/Topologies/Triangle.h b/src/TNL/Meshes/Topologies/Triangle.h index efe031059d1fa5e7705dc131d69de4a862743ed7..8494a6f4fdd9a6b505074380b93fdcb1d1004f00 100644 --- a/src/TNL/Meshes/Topologies/Triangle.h +++ b/src/TNL/Meshes/Topologies/Triangle.h @@ -45,14 +45,14 @@ struct Subtopology< Triangle, 1 > }; -template<> struct SubentityVertexMap< Triangle, Edge, 0, 0> { enum { index = 1 }; }; -template<> struct SubentityVertexMap< Triangle, Edge, 0, 1> { enum { index = 2 }; }; +template<> struct SubentityVertexMap< Triangle, Edge, 0, 0> { static constexpr int index = 1; }; +template<> struct SubentityVertexMap< Triangle, Edge, 0, 1> { static constexpr int index = 2; }; -template<> struct SubentityVertexMap< Triangle, Edge, 1, 0> { enum { index = 2 }; }; -template<> struct SubentityVertexMap< Triangle, Edge, 1, 1> { enum { index = 0 }; }; +template<> struct SubentityVertexMap< Triangle, Edge, 1, 0> { static constexpr int index = 2; }; +template<> struct SubentityVertexMap< Triangle, Edge, 1, 1> { static constexpr int index = 0; }; -template<> struct SubentityVertexMap< Triangle, Edge, 2, 0> { enum { index = 0 }; }; -template<> struct SubentityVertexMap< Triangle, Edge, 2, 1> { enum { index = 1 }; }; +template<> struct SubentityVertexMap< Triangle, Edge, 2, 0> { static constexpr int index = 0; }; +template<> struct SubentityVertexMap< Triangle, Edge, 2, 1> { static constexpr int index = 1; }; } // namespace Topologies } // namespace Meshes diff --git a/src/TNL/Meshes/Topologies/Vertex.h b/src/TNL/Meshes/Topologies/Vertex.h index f90127624806c4acbfb8e0ee2aa01b5988f0c3f4..40d0e04d119f28d0883c3feeb1b9bbed1874809f 100644 --- a/src/TNL/Meshes/Topologies/Vertex.h +++ b/src/TNL/Meshes/Topologies/Vertex.h @@ -16,8 +16,6 @@ #pragma once -#include <TNL/String.h> - namespace TNL { namespace Meshes { namespace Topologies { diff --git a/src/TNL/Meshes/Topologies/Wedge.h b/src/TNL/Meshes/Topologies/Wedge.h index 2cd3321944dd59fcf20a3230adb5ab1c9c7e3899..4aed46b0653450d2e970e5a5b590abd66530b146 100644 --- a/src/TNL/Meshes/Topologies/Wedge.h +++ b/src/TNL/Meshes/Topologies/Wedge.h @@ -37,32 +37,32 @@ struct Subtopology< Wedge, 2 > static constexpr int count = 5; }; -template<> struct SubentityVertexMap< Wedge, Edge, 0, 0> { enum { index = 0 }; }; -template<> struct SubentityVertexMap< Wedge, Edge, 0, 1> { enum { index = 1 }; }; +template<> struct SubentityVertexMap< Wedge, Edge, 0, 0> { static constexpr int index = 0; }; +template<> struct SubentityVertexMap< Wedge, Edge, 0, 1> { static constexpr int index = 1; }; -template<> struct SubentityVertexMap< Wedge, Edge, 1, 0> { enum { index = 1 }; }; -template<> struct SubentityVertexMap< Wedge, Edge, 1, 1> { enum { index = 2 }; }; +template<> struct SubentityVertexMap< Wedge, Edge, 1, 0> { static constexpr int index = 1; }; +template<> struct SubentityVertexMap< Wedge, Edge, 1, 1> { static constexpr int index = 2; }; -template<> struct SubentityVertexMap< Wedge, Edge, 2, 0> { enum { index = 2 }; }; -template<> struct SubentityVertexMap< Wedge, Edge, 2, 1> { enum { index = 0 }; }; +template<> struct SubentityVertexMap< Wedge, Edge, 2, 0> { static constexpr int index = 2; }; +template<> struct SubentityVertexMap< Wedge, Edge, 2, 1> { static constexpr int index = 0; }; -template<> struct SubentityVertexMap< Wedge, Edge, 3, 0> { enum { index = 3 }; }; -template<> struct SubentityVertexMap< Wedge, Edge, 3, 1> { enum { index = 4 }; }; +template<> struct SubentityVertexMap< Wedge, Edge, 3, 0> { static constexpr int index = 3; }; +template<> struct SubentityVertexMap< Wedge, Edge, 3, 1> { static constexpr int index = 4; }; -template<> struct SubentityVertexMap< Wedge, Edge, 4, 0> { enum { index = 4 }; }; -template<> struct SubentityVertexMap< Wedge, Edge, 4, 1> { enum { index = 5 }; }; +template<> struct SubentityVertexMap< Wedge, Edge, 4, 0> { static constexpr int index = 4; }; +template<> struct SubentityVertexMap< Wedge, Edge, 4, 1> { static constexpr int index = 5; }; -template<> struct SubentityVertexMap< Wedge, Edge, 5, 0> { enum { index = 5 }; }; -template<> struct SubentityVertexMap< Wedge, Edge, 5, 1> { enum { index = 3 }; }; +template<> struct SubentityVertexMap< Wedge, Edge, 5, 0> { static constexpr int index = 5; }; +template<> struct SubentityVertexMap< Wedge, Edge, 5, 1> { static constexpr int index = 3; }; -template<> struct SubentityVertexMap< Wedge, Edge, 6, 0> { enum { index = 3 }; }; -template<> struct SubentityVertexMap< Wedge, Edge, 6, 1> { enum { index = 0 }; }; +template<> struct SubentityVertexMap< Wedge, Edge, 6, 0> { static constexpr int index = 3; }; +template<> struct SubentityVertexMap< Wedge, Edge, 6, 1> { static constexpr int index = 0; }; -template<> struct SubentityVertexMap< Wedge, Edge, 7, 0> { enum { index = 5 }; }; -template<> struct SubentityVertexMap< Wedge, Edge, 7, 1> { enum { index = 2 }; }; +template<> struct SubentityVertexMap< Wedge, Edge, 7, 0> { static constexpr int index = 5; }; +template<> struct SubentityVertexMap< Wedge, Edge, 7, 1> { static constexpr int index = 2; }; -template<> struct SubentityVertexMap< Wedge, Edge, 8, 0> { enum { index = 4 }; }; -template<> struct SubentityVertexMap< Wedge, Edge, 8, 1> { enum { index = 1 }; }; +template<> struct SubentityVertexMap< Wedge, Edge, 8, 0> { static constexpr int index = 4; }; +template<> struct SubentityVertexMap< Wedge, Edge, 8, 1> { static constexpr int index = 1; }; template <> @@ -71,9 +71,9 @@ struct SubentityVertexCount< Wedge, Polygon, 0 > static constexpr int count = 3; }; -template<> struct SubentityVertexMap< Wedge, Polygon, 0, 0> { enum { index = 0 }; }; -template<> struct SubentityVertexMap< Wedge, Polygon, 0, 1> { enum { index = 1 }; }; -template<> struct SubentityVertexMap< Wedge, Polygon, 0, 2> { enum { index = 2 }; }; +template<> struct SubentityVertexMap< Wedge, Polygon, 0, 0> { static constexpr int index = 0; }; +template<> struct SubentityVertexMap< Wedge, Polygon, 0, 1> { static constexpr int index = 1; }; +template<> struct SubentityVertexMap< Wedge, Polygon, 0, 2> { static constexpr int index = 2; }; template <> struct SubentityVertexCount< Wedge, Polygon, 1 > @@ -81,9 +81,9 @@ struct SubentityVertexCount< Wedge, Polygon, 1 > static constexpr int count = 3; }; -template<> struct SubentityVertexMap< Wedge, Polygon, 1, 0> { enum { index = 3 }; }; -template<> struct SubentityVertexMap< Wedge, Polygon, 1, 1> { enum { index = 4 }; }; -template<> struct SubentityVertexMap< Wedge, Polygon, 1, 2> { enum { index = 5 }; }; +template<> struct SubentityVertexMap< Wedge, Polygon, 1, 0> { static constexpr int index = 3; }; +template<> struct SubentityVertexMap< Wedge, Polygon, 1, 1> { static constexpr int index = 4; }; +template<> struct SubentityVertexMap< Wedge, Polygon, 1, 2> { static constexpr int index = 5; }; template <> struct SubentityVertexCount< Wedge, Polygon, 2 > @@ -91,10 +91,10 @@ struct SubentityVertexCount< Wedge, Polygon, 2 > static constexpr int count = 4; }; -template<> struct SubentityVertexMap< Wedge, Polygon, 2, 0> { enum { index = 3 }; }; -template<> struct SubentityVertexMap< Wedge, Polygon, 2, 1> { enum { index = 0 }; }; -template<> struct SubentityVertexMap< Wedge, Polygon, 2, 2> { enum { index = 2 }; }; -template<> struct SubentityVertexMap< Wedge, Polygon, 2, 3> { enum { index = 5 }; }; +template<> struct SubentityVertexMap< Wedge, Polygon, 2, 0> { static constexpr int index = 3; }; +template<> struct SubentityVertexMap< Wedge, Polygon, 2, 1> { static constexpr int index = 0; }; +template<> struct SubentityVertexMap< Wedge, Polygon, 2, 2> { static constexpr int index = 2; }; +template<> struct SubentityVertexMap< Wedge, Polygon, 2, 3> { static constexpr int index = 5; }; template <> struct SubentityVertexCount< Wedge, Polygon, 3 > @@ -102,10 +102,10 @@ struct SubentityVertexCount< Wedge, Polygon, 3 > static constexpr int count = 4; }; -template<> struct SubentityVertexMap< Wedge, Polygon, 3, 0> { enum { index = 4 }; }; -template<> struct SubentityVertexMap< Wedge, Polygon, 3, 1> { enum { index = 1 }; }; -template<> struct SubentityVertexMap< Wedge, Polygon, 3, 2> { enum { index = 2 }; }; -template<> struct SubentityVertexMap< Wedge, Polygon, 3, 3> { enum { index = 5 }; }; +template<> struct SubentityVertexMap< Wedge, Polygon, 3, 0> { static constexpr int index = 4; }; +template<> struct SubentityVertexMap< Wedge, Polygon, 3, 1> { static constexpr int index = 1; }; +template<> struct SubentityVertexMap< Wedge, Polygon, 3, 2> { static constexpr int index = 2; }; +template<> struct SubentityVertexMap< Wedge, Polygon, 3, 3> { static constexpr int index = 5; }; template <> struct SubentityVertexCount< Wedge, Polygon, 4 > @@ -113,11 +113,11 @@ struct SubentityVertexCount< Wedge, Polygon, 4 > static constexpr int count = 4; }; -template<> struct SubentityVertexMap< Wedge, Polygon, 4, 0> { enum { index = 3 }; }; -template<> struct SubentityVertexMap< Wedge, Polygon, 4, 1> { enum { index = 0 }; }; -template<> struct SubentityVertexMap< Wedge, Polygon, 4, 2> { enum { index = 1 }; }; -template<> struct SubentityVertexMap< Wedge, Polygon, 4, 3> { enum { index = 4 }; }; +template<> struct SubentityVertexMap< Wedge, Polygon, 4, 0> { static constexpr int index = 3; }; +template<> struct SubentityVertexMap< Wedge, Polygon, 4, 1> { static constexpr int index = 0; }; +template<> struct SubentityVertexMap< Wedge, Polygon, 4, 2> { static constexpr int index = 1; }; +template<> struct SubentityVertexMap< Wedge, Polygon, 4, 3> { static constexpr int index = 4; }; } // namespace Topologies } // namespace Meshes -} // namespace TNL \ No newline at end of file +} // namespace TNL diff --git a/src/TNL/Meshes/TypeResolver/BuildConfigTags.h b/src/TNL/Meshes/TypeResolver/BuildConfigTags.h index 413e7538ada983726c23d002813e2431cff93988..c0b5c0fd5cb6e12e4ebd5ce80ceab55d3376ca4f 100644 --- a/src/TNL/Meshes/TypeResolver/BuildConfigTags.h +++ b/src/TNL/Meshes/TypeResolver/BuildConfigTags.h @@ -35,81 +35,80 @@ namespace BuildConfigTags { // Configuration for structured grids // 1, 2, and 3 dimensions are enabled by default -template< typename ConfigTag, int Dimension > struct GridDimensionTag { enum { enabled = ( Dimension > 0 && Dimension <= 3 ) }; }; +template< typename ConfigTag, int Dimension > struct GridDimensionTag { static constexpr bool enabled = Dimension > 0 && Dimension <= 3; }; // 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 }; }; +template< typename ConfigTag, typename Real > struct GridRealTag { static constexpr bool enabled = false; }; +template< typename ConfigTag > struct GridRealTag< ConfigTag, float > { static constexpr bool enabled = true; }; +template< typename ConfigTag > struct GridRealTag< ConfigTag, double > { static constexpr bool enabled = true; }; // Grids are enabled on all available devices by default. -template< typename ConfigTag, typename Device > struct GridDeviceTag { enum { enabled = true }; }; +template< typename ConfigTag, typename Device > struct GridDeviceTag { static constexpr bool enabled = true; }; #ifndef HAVE_CUDA -template< typename ConfigTag > struct GridDeviceTag< ConfigTag, Devices::Cuda > { enum { enabled = false }; }; +template< typename ConfigTag > struct GridDeviceTag< ConfigTag, Devices::Cuda > { static constexpr bool enabled = false; }; #endif // 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 }; }; +template< typename ConfigTag, typename Index > struct GridIndexTag { static constexpr bool enabled = false; }; +template< typename ConfigTag > struct GridIndexTag< ConfigTag, int > { static constexpr bool enabled = true; }; +template< typename ConfigTag > struct GridIndexTag< ConfigTag, long int > { static constexpr bool enabled = true; }; // The Grid is enabled for allowed dimensions and Real, Device and Index types. // // By specializing this tag you can enable or disable custom combinations of // the grid template parameters. The default configuration is identical to the // individual per-type tags. -template< typename ConfigTag, typename MeshType > struct GridTag { enum { enabled = false }; }; +template< typename ConfigTag, typename MeshType > struct GridTag { static constexpr bool enabled = false; }; template< typename ConfigTag, int Dimension, typename Real, typename Device, typename Index > struct GridTag< ConfigTag, Grid< Dimension, Real, Device, Index > > { - enum { enabled = GridDimensionTag< ConfigTag, Dimension >::enabled && + static constexpr bool enabled = GridDimensionTag< ConfigTag, Dimension >::enabled && GridRealTag< ConfigTag, Real >::enabled && GridDeviceTag< ConfigTag, Device >::enabled && - GridIndexTag< ConfigTag, Index >::enabled - }; + GridIndexTag< ConfigTag, Index >::enabled; }; // Configuration for unstructured meshes // Meshes are enabled on all available devices by default. -template< typename ConfigTag, typename Device > struct MeshDeviceTag { enum { enabled = false }; }; -template< typename ConfigTag > struct MeshDeviceTag< ConfigTag, Devices::Host > { enum { enabled = true }; }; +template< typename ConfigTag, typename Device > struct MeshDeviceTag { static constexpr bool enabled = false; }; +template< typename ConfigTag > struct MeshDeviceTag< ConfigTag, Devices::Host > { static constexpr bool enabled = true; }; #ifdef HAVE_CUDA -template< typename ConfigTag > struct MeshDeviceTag< ConfigTag, Devices::Cuda > { enum { enabled = true }; }; +template< typename ConfigTag > struct MeshDeviceTag< ConfigTag, Devices::Cuda > { static constexpr bool enabled = true; }; #endif // All available cell topologies are disabled by default. -template< typename ConfigTag, typename CellTopology > struct MeshCellTopologyTag { enum { enabled = false }; }; -//template< typename ConfigTag > struct MeshCellTopologyTag< ConfigTag, Topologies::Edge > { enum { enabled = true }; }; -//template< typename ConfigTag > struct MeshCellTopologyTag< ConfigTag, Topologies::Triangle > { enum { enabled = true }; }; -//template< typename ConfigTag > struct MeshCellTopologyTag< ConfigTag, Topologies::Quadrangle > { enum { enabled = true }; }; -//template< typename ConfigTag > struct MeshCellTopologyTag< ConfigTag, Topologies::Tetrahedron > { enum { enabled = true }; }; -//template< typename ConfigTag > struct MeshCellTopologyTag< ConfigTag, Topologies::Hexahedron > { enum { enabled = true }; }; -//template< typename ConfigTag > struct MeshCellTopologyTag< ConfigTag, Topologies::Polygon > { enum { enabled = true }; }; -//template< typename ConfigTag > struct MeshCellTopologyTag< ConfigTag, Topologies::Wedge > { enum { enabled = true }; }; -//template< typename ConfigTag > struct MeshCellTopologyTag< ConfigTag, Topologies::Pyramid > { enum { enabled = true }; }; -//template< typename ConfigTag > struct MeshCellTopologyTag< ConfigTag, Topologies::Polyhedron > { enum { enabled = true }; }; +template< typename ConfigTag, typename CellTopology > struct MeshCellTopologyTag { static constexpr bool enabled = false; }; +//template< typename ConfigTag > struct MeshCellTopologyTag< ConfigTag, Topologies::Edge > { static constexpr bool enabled = true; }; +//template< typename ConfigTag > struct MeshCellTopologyTag< ConfigTag, Topologies::Triangle > { static constexpr bool enabled = true; }; +//template< typename ConfigTag > struct MeshCellTopologyTag< ConfigTag, Topologies::Quadrangle > { static constexpr bool enabled = true; }; +//template< typename ConfigTag > struct MeshCellTopologyTag< ConfigTag, Topologies::Tetrahedron > { static constexpr bool enabled = true; }; +//template< typename ConfigTag > struct MeshCellTopologyTag< ConfigTag, Topologies::Hexahedron > { static constexpr bool enabled = true; }; +//template< typename ConfigTag > struct MeshCellTopologyTag< ConfigTag, Topologies::Polygon > { static constexpr bool enabled = true; }; +//template< typename ConfigTag > struct MeshCellTopologyTag< ConfigTag, Topologies::Wedge > { static constexpr bool enabled = true; }; +//template< typename ConfigTag > struct MeshCellTopologyTag< ConfigTag, Topologies::Pyramid > { static constexpr bool enabled = true; }; +//template< typename ConfigTag > struct MeshCellTopologyTag< ConfigTag, Topologies::Polyhedron > { static constexpr bool enabled = true; }; // TODO: Simplex has not been tested yet -//template< typename ConfigTag > struct MeshCellTopologyTag< ConfigTag, Topologies::Simplex > { enum { enabled = true }; }; +//template< typename ConfigTag > struct MeshCellTopologyTag< ConfigTag, Topologies::Simplex > { static constexpr bool enabled = true; }; // All sensible space dimensions are enabled by default. -template< typename ConfigTag, typename CellTopology, int SpaceDimension > struct MeshSpaceDimensionTag { enum { enabled = ( SpaceDimension >= CellTopology::dimension && SpaceDimension <= 3 ) }; }; +template< typename ConfigTag, typename CellTopology, int SpaceDimension > struct MeshSpaceDimensionTag { static constexpr bool enabled = SpaceDimension >= CellTopology::dimension && SpaceDimension <= 3; }; // 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 }; }; +template< typename ConfigTag, typename Real > struct MeshRealTag { static constexpr bool enabled = false; }; +template< typename ConfigTag > struct MeshRealTag< ConfigTag, float > { static constexpr bool enabled = true; }; +template< typename ConfigTag > struct MeshRealTag< ConfigTag, double > { static constexpr bool 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 }; }; +template< typename ConfigTag, typename GlobalIndex > struct MeshGlobalIndexTag { static constexpr bool enabled = false; }; +template< typename ConfigTag > struct MeshGlobalIndexTag< ConfigTag, int > { static constexpr bool enabled = true; }; +template< typename ConfigTag > struct MeshGlobalIndexTag< ConfigTag, long int > { static constexpr bool 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 }; }; +template< typename ConfigTag, typename LocalIndex > struct MeshLocalIndexTag { static constexpr bool enabled = false; }; +template< typename ConfigTag > struct MeshLocalIndexTag< ConfigTag, short int > { static constexpr bool enabled = true; }; // Config tag specifying the MeshConfig to use. template< typename ConfigTag > @@ -138,14 +137,13 @@ struct MeshConfigTemplateTag template< typename ConfigTag, typename Device, typename CellTopology, int SpaceDimension, typename Real, typename GlobalIndex, typename LocalIndex > struct MeshTag { - enum { enabled = + static constexpr bool enabled = MeshDeviceTag< ConfigTag, Device >::enabled && MeshCellTopologyTag< ConfigTag, CellTopology >::enabled && MeshSpaceDimensionTag< ConfigTag, CellTopology, SpaceDimension >::enabled && MeshRealTag< ConfigTag, Real >::enabled && MeshGlobalIndexTag< ConfigTag, GlobalIndex >::enabled && - MeshLocalIndexTag< ConfigTag, LocalIndex >::enabled - }; + MeshLocalIndexTag< ConfigTag, LocalIndex >::enabled; }; } // namespace BuildConfigTags diff --git a/src/TNL/Meshes/TypeResolver/GridTypeResolver.hpp b/src/TNL/Meshes/TypeResolver/GridTypeResolver.hpp index 0c284ca21757eeccd1efa605bf7df47173bab6b3..ef264339ef0720b2a5c1373b9a1fae90c00a2704 100644 --- a/src/TNL/Meshes/TypeResolver/GridTypeResolver.hpp +++ b/src/TNL/Meshes/TypeResolver/GridTypeResolver.hpp @@ -12,7 +12,6 @@ #include <utility> -#include <TNL/String.h> #include <TNL/Meshes/Grid.h> #include <TNL/Meshes/TypeResolver/GridTypeResolver.h> diff --git a/src/TNL/Meshes/TypeResolver/MeshTypeResolver.hpp b/src/TNL/Meshes/TypeResolver/MeshTypeResolver.hpp index f6c991f41dde3692e0285262a38cd161a39dc678..fdaca73ec4fbf93008c62ea5bdea356d34db6a33 100644 --- a/src/TNL/Meshes/TypeResolver/MeshTypeResolver.hpp +++ b/src/TNL/Meshes/TypeResolver/MeshTypeResolver.hpp @@ -12,7 +12,6 @@ #include <utility> -#include <TNL/String.h> #include <TNL/Meshes/Grid.h> #include <TNL/Meshes/TypeResolver/MeshTypeResolver.h> #include <TNL/Meshes/VTKTraits.h> diff --git a/src/TNL/Meshes/TypeResolver/resolveMeshType.h b/src/TNL/Meshes/TypeResolver/resolveMeshType.h index 2e8df25d8b5ba03c047dbc2f4e3e9e2f31e82d07..712c12c2dac7a6df5bfa58ab13482e4eb56a9e1c 100644 --- a/src/TNL/Meshes/TypeResolver/resolveMeshType.h +++ b/src/TNL/Meshes/TypeResolver/resolveMeshType.h @@ -40,7 +40,9 @@ template< typename ConfigTag, bool resolveMeshType( Functor&& functor, const std::string& fileName, - const std::string& fileFormat = "auto" ); + const std::string& fileFormat = "auto", + const std::string& realType = "auto", + const std::string& globalIndexType = "auto" ); /** * This function dues the same as \ref resolveMeshType, but also reuses the mesh @@ -63,7 +65,9 @@ template< typename ConfigTag, bool resolveAndLoadMesh( Functor&& functor, const std::string& fileName, - const std::string& fileFormat = "auto" ); + const std::string& fileFormat = "auto", + const std::string& realType = "auto", + const std::string& globalIndexType = "auto" ); /** * This function takes a file name and a mesh instance and attempts to load the diff --git a/src/TNL/Meshes/TypeResolver/resolveMeshType.hpp b/src/TNL/Meshes/TypeResolver/resolveMeshType.hpp index 0f457a75d8422e0d1fc7d8f10000db49713e9ab0..c53f471683304cb9b9f0fd20d5e49dd823d90227 100644 --- a/src/TNL/Meshes/TypeResolver/resolveMeshType.hpp +++ b/src/TNL/Meshes/TypeResolver/resolveMeshType.hpp @@ -26,7 +26,9 @@ template< typename ConfigTag, bool resolveMeshType( Functor&& functor, const std::string& fileName, - const std::string& fileFormat ) + const std::string& fileFormat, + const std::string& realType, + const std::string& globalIndexType ) { std::cout << "Detecting mesh from file " << fileName << " ..." << std::endl; @@ -36,6 +38,11 @@ resolveMeshType( Functor&& functor, reader->detectMesh(); + if( realType != "auto" ) + reader->forceRealType( realType ); + if( globalIndexType != "auto" ) + reader->forceGlobalIndexType( globalIndexType ); + if( reader->getMeshType() == "Meshes::Grid" || reader->getMeshType() == "Meshes::DistributedGrid" ) return GridTypeResolver< ConfigTag, Device >::run( *reader, functor ); else if( reader->getMeshType() == "Meshes::Mesh" || reader->getMeshType() == "Meshes::DistributedMesh" ) @@ -52,7 +59,9 @@ template< typename ConfigTag, bool resolveAndLoadMesh( Functor&& functor, const std::string& fileName, - const std::string& fileFormat ) + const std::string& fileFormat, + const std::string& realType, + const std::string& globalIndexType ) { auto wrapper = [&]( auto& reader, auto&& mesh ) -> bool { @@ -67,7 +76,7 @@ resolveAndLoadMesh( Functor&& functor, } return functor( reader, std::forward<MeshType>(mesh) ); }; - return resolveMeshType< ConfigTag, Device >( wrapper, fileName, fileFormat ); + return resolveMeshType< ConfigTag, Device >( wrapper, fileName, fileFormat, realType, globalIndexType ); } template< typename Mesh > diff --git a/src/TNL/Meshes/VTKTraits.h b/src/TNL/Meshes/VTKTraits.h index 9f9de20ba1fd01defa0f5fc702f7d1b59433de37..65ecf7f1fde9663b5a98dc3be6ad6c530267c040 100644 --- a/src/TNL/Meshes/VTKTraits.h +++ b/src/TNL/Meshes/VTKTraits.h @@ -63,7 +63,9 @@ enum class EntityShape Hexahedron = 12, Wedge = 13, Pyramid = 14, - Polyhedron = 100 + PentagonalPrism = 15, + HexagonalPrism = 16, + Polyhedron = 42 }; inline std::string getShapeName( EntityShape shape ) @@ -98,6 +100,10 @@ inline std::string getShapeName( EntityShape shape ) return "Wedge"; case EntityShape::Pyramid: return "Pyramid"; + case EntityShape::PentagonalPrism: + return "PentagonalPrism"; + case EntityShape::HexagonalPrism: + return "HexagonalPrism"; case EntityShape::Polyhedron: return "Polyhedron"; } @@ -122,6 +128,8 @@ inline int getEntityDimension( EntityShape shape ) case EntityShape::Hexahedron: return 3; case EntityShape::Wedge: return 3; case EntityShape::Pyramid: return 3; + case EntityShape::PentagonalPrism:return 3; + case EntityShape::HexagonalPrism: return 3; case EntityShape::Polyhedron: return 3; } // this can actually happen when an invalid uint8_t value is converted to EntityShape diff --git a/src/TNL/Meshes/Writers/EntitiesListSize.h b/src/TNL/Meshes/Writers/EntitiesListSize.h deleted file mode 100644 index 15e13d9d9f88c2764ba28a7e32bf46c3e4a752a3..0000000000000000000000000000000000000000 --- a/src/TNL/Meshes/Writers/EntitiesListSize.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include <TNL/Meshes/Writers/VerticesPerEntity.h> - -namespace TNL { -namespace Meshes { -namespace Writers { - -template< typename Mesh, - int EntityDimension, - typename EntityType = typename Mesh::template EntityType< EntityDimension > - > -struct EntitiesListSize -{ - using IndexType = typename Mesh::GlobalIndexType; - - static IndexType getSize( const Mesh& mesh ) - { - IndexType entitiesCount = mesh.template getEntitiesCount< EntityType >(); - IndexType verticesPerEntity = VerticesPerEntity< EntityType >::count; - return entitiesCount * ( verticesPerEntity + 1 ); - } -}; - -template< typename Mesh, - int EntityDimension, - typename MeshConfig, - typename Device > -struct EntitiesListSize< Mesh, EntityDimension, MeshEntity< MeshConfig, Device, Topologies::Polygon > > -{ - using IndexType = typename Mesh::GlobalIndexType; - - static IndexType getSize( const Mesh& mesh ) - { - IndexType entitiesCount = mesh.template getEntitiesCount< EntityDimension >(); - IndexType entitiesListSize = entitiesCount; - for(IndexType index = 0; index < entitiesCount; index++) - entitiesListSize += mesh.template getSubentitiesCount< EntityDimension, 0 >( index ); - return entitiesListSize; - } -}; - -} // namespace Writers -} // namespace Meshes -} // namespace TNL diff --git a/src/TNL/Meshes/Writers/NetgenWriter.h b/src/TNL/Meshes/Writers/NetgenWriter.h index c247c0ff48a31ef300e4c467423fc0833894649e..afa2a97a0d95eb785c7643d10d9fd455c34f67c8 100644 --- a/src/TNL/Meshes/Writers/NetgenWriter.h +++ b/src/TNL/Meshes/Writers/NetgenWriter.h @@ -19,8 +19,6 @@ #include <ostream> #include <iomanip> -#include <TNL/String.h> - namespace TNL { namespace Meshes { namespace Writers { diff --git a/src/TNL/Meshes/Writers/PVTIWriter.h b/src/TNL/Meshes/Writers/PVTIWriter.h index 4f391b142e972267eb67bad9415121c7e44c0e17..c37d40a5861b8e9f6f879d6af94d938c8d7fd3ba 100644 --- a/src/TNL/Meshes/Writers/PVTIWriter.h +++ b/src/TNL/Meshes/Writers/PVTIWriter.h @@ -57,27 +57,27 @@ public: const unsigned MinCommonVertices = 0 ); template< typename ValueType > - void writePPointData( const String& name, + void writePPointData( const std::string& name, const int numberOfComponents = 1 ); template< typename ValueType > - void writePCellData( const String& name, + void writePCellData( const std::string& name, const int numberOfComponents = 1 ); template< typename ValueType > - void writePDataArray( const String& name, + void writePDataArray( const std::string& name, const int numberOfComponents = 1 ); // add a single piece and return its source path // (useful for sequential writing, e.g. from tnl-decompose-grid) - std::string addPiece( const String& mainFileName, + std::string addPiece( const std::string& mainFileName, const unsigned subdomainIndex, const typename Grid::CoordinatesType& globalBegin, const typename Grid::CoordinatesType& globalEnd ); // add all pieces and return the source path for the current rank // (useful for parallel writing) - std::string addPiece( const String& mainFileName, + std::string addPiece( const std::string& mainFileName, const DistributedMeshes::DistributedMesh< Grid >& distributedMesh ); ~PVTIWriter(); diff --git a/src/TNL/Meshes/Writers/PVTIWriter.hpp b/src/TNL/Meshes/Writers/PVTIWriter.hpp index fd3d9bfeb0f71a5742dd97fe9cdd2b0d87c80f0e..e28b1eed811dc34ed6e49f94e014ef5c7b078bb9 100644 --- a/src/TNL/Meshes/Writers/PVTIWriter.hpp +++ b/src/TNL/Meshes/Writers/PVTIWriter.hpp @@ -125,7 +125,7 @@ PVTIWriter< Grid >::writeEntities( const Grid& grid, template< typename Grid > template< typename ValueType > void -PVTIWriter< Grid >::writePPointData( const String& name, +PVTIWriter< Grid >::writePPointData( const std::string& name, const int numberOfComponents ) { if( ! vtkfileOpen ) @@ -137,7 +137,7 @@ PVTIWriter< Grid >::writePPointData( const String& name, template< typename Grid > template< typename ValueType > void -PVTIWriter< Grid >::writePCellData( const String& name, +PVTIWriter< Grid >::writePCellData( const std::string& name, const int numberOfComponents ) { if( ! vtkfileOpen ) @@ -149,7 +149,7 @@ PVTIWriter< Grid >::writePCellData( const String& name, template< typename Grid > template< typename ValueType > void -PVTIWriter< Grid >::writePDataArray( const String& name, +PVTIWriter< Grid >::writePDataArray( const std::string& name, const int numberOfComponents ) { if( numberOfComponents != 0 && numberOfComponents != 1 && numberOfComponents != 3 ) @@ -162,12 +162,17 @@ PVTIWriter< Grid >::writePDataArray( const String& name, template< typename Grid > std::string -PVTIWriter< Grid >::addPiece( const String& mainFileName, +PVTIWriter< Grid >::addPiece( const std::string& mainFileName, const unsigned subdomainIndex, const typename Grid::CoordinatesType& globalBegin, const typename Grid::CoordinatesType& globalEnd ) { - if( ! mainFileName.endsWith( ".pvti" ) ) + namespace fs = std::experimental::filesystem; + + // get the basename of the main file (filename without extension) + const fs::path mainPath = mainFileName; + const fs::path basename = mainPath.stem(); + if( mainPath.extension() != ".pvti" ) throw std::logic_error("The mainFileName parameter must be the name of the " ".pvti file (i.e., it must have the .pvti suffix)."); @@ -183,12 +188,6 @@ PVTIWriter< Grid >::addPiece( const String& mainFileName, for( int j = Grid::getMeshDimension(); j < 3; j++ ) extent << "0 0 "; - 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 ); @@ -204,7 +203,7 @@ PVTIWriter< Grid >::addPiece( const String& mainFileName, template< typename Grid > std::string -PVTIWriter< Grid >::addPiece( const String& mainFileName, +PVTIWriter< Grid >::addPiece( const std::string& mainFileName, const DistributedMeshes::DistributedMesh< Grid >& distributedMesh ) { const MPI_Comm communicator = distributedMesh.getCommunicator(); diff --git a/src/TNL/Meshes/Writers/PVTUWriter.h b/src/TNL/Meshes/Writers/PVTUWriter.h index 6d76781ff7ae68b33c05111186ae1276799b09ae..d53b5aa82ef7a4128336450c8ff4996aa2f180f7 100644 --- a/src/TNL/Meshes/Writers/PVTUWriter.h +++ b/src/TNL/Meshes/Writers/PVTUWriter.h @@ -45,25 +45,25 @@ public: const unsigned MinCommonVertices = 0 ); template< typename ValueType > - void writePPointData( const String& name, + void writePPointData( const std::string& name, const int numberOfComponents = 1 ); template< typename ValueType > - void writePCellData( const String& name, + void writePCellData( const std::string& name, const int numberOfComponents = 1 ); template< typename ValueType > - void writePDataArray( const String& name, + void writePDataArray( const std::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, + std::string addPiece( const std::string& mainFileName, const unsigned subdomainIndex ); // add all pieces and return the source path for the current rank // (useful for parallel writing) - std::string addPiece( const String& mainFileName, + std::string addPiece( const std::string& mainFileName, const MPI_Comm communicator ); ~PVTUWriter(); diff --git a/src/TNL/Meshes/Writers/PVTUWriter.hpp b/src/TNL/Meshes/Writers/PVTUWriter.hpp index 03b1e9aee795f5d6dd792ff800ad2ed1084dffbb..004cccd96751f746d3b1a1e3de8fab265185891a 100644 --- a/src/TNL/Meshes/Writers/PVTUWriter.hpp +++ b/src/TNL/Meshes/Writers/PVTUWriter.hpp @@ -69,7 +69,7 @@ PVTUWriter< Mesh >::writeEntities( const Mesh& mesh, template< typename Mesh > template< typename ValueType > void -PVTUWriter< Mesh >::writePPointData( const String& name, +PVTUWriter< Mesh >::writePPointData( const std::string& name, const int numberOfComponents ) { if( ! vtkfileOpen ) @@ -81,7 +81,7 @@ PVTUWriter< Mesh >::writePPointData( const String& name, template< typename Mesh > template< typename ValueType > void -PVTUWriter< Mesh >::writePCellData( const String& name, +PVTUWriter< Mesh >::writePCellData( const std::string& name, const int numberOfComponents ) { if( ! vtkfileOpen ) @@ -93,7 +93,7 @@ PVTUWriter< Mesh >::writePCellData( const String& name, template< typename Mesh > template< typename ValueType > void -PVTUWriter< Mesh >::writePDataArray( const String& name, +PVTUWriter< Mesh >::writePDataArray( const std::string& name, const int numberOfComponents ) { if( numberOfComponents != 0 && numberOfComponents != 1 && numberOfComponents != 3 ) @@ -106,10 +106,15 @@ PVTUWriter< Mesh >::writePDataArray( const String& name, template< typename Mesh > std::string -PVTUWriter< Mesh >::addPiece( const String& mainFileName, +PVTUWriter< Mesh >::addPiece( const std::string& mainFileName, const unsigned subdomainIndex ) { - if( ! mainFileName.endsWith( ".pvtu" ) ) + namespace fs = std::experimental::filesystem; + + // get the basename of the main file (filename without extension) + const fs::path mainPath = mainFileName; + const fs::path basename = mainPath.stem(); + if( mainPath.extension() != ".pvtu" ) throw std::logic_error("The mainFileName parameter must be the name of the " ".pvtu file (i.e., it must have the .pvtu suffix)."); @@ -117,12 +122,6 @@ PVTUWriter< Mesh >::addPiece( const String& mainFileName, 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 ); @@ -138,7 +137,7 @@ PVTUWriter< Mesh >::addPiece( const String& mainFileName, template< typename Mesh > std::string -PVTUWriter< Mesh >::addPiece( const String& mainFileName, +PVTUWriter< Mesh >::addPiece( const std::string& mainFileName, const MPI_Comm communicator ) { std::string source; diff --git a/src/TNL/Meshes/Writers/VTIWriter.h b/src/TNL/Meshes/Writers/VTIWriter.h index 8772205718c859a031a16c5f73c1d88020a49888..367fc96520824abc8d18d23d83e8beed695f4be0 100644 --- a/src/TNL/Meshes/Writers/VTIWriter.h +++ b/src/TNL/Meshes/Writers/VTIWriter.h @@ -12,6 +12,8 @@ #pragma once +#include <ostream> +#include <sstream> #include <type_traits> #include <TNL/Meshes/VTKTraits.h> @@ -54,17 +56,17 @@ public: template< typename Array > void writePointData( const Array& array, - const String& name, + const std::string& name, const int numberOfComponents = 1 ); template< typename Array > void writeCellData( const Array& array, - const String& name, + const std::string& name, const int numberOfComponents = 1 ); template< typename Array > void writeDataArray( const Array& array, - const String& name, + const std::string& name, const int numberOfComponents = 1 ); ~VTIWriter(); diff --git a/src/TNL/Meshes/Writers/VTIWriter.hpp b/src/TNL/Meshes/Writers/VTIWriter.hpp index 0744850d3e1a0760410a1dfad3394ba5cc37cbd2..9ea4684689eae86f01041349786e2e471c17b1b3 100644 --- a/src/TNL/Meshes/Writers/VTIWriter.hpp +++ b/src/TNL/Meshes/Writers/VTIWriter.hpp @@ -121,7 +121,7 @@ template< typename Mesh > template< typename Array > void VTIWriter< Mesh >::writePointData( const Array& array, - const String& name, + const std::string& name, const int numberOfComponents ) { if( ! pieceOpen ) @@ -137,7 +137,7 @@ template< typename Mesh > template< typename Array > void VTIWriter< Mesh >::writeCellData( const Array& array, - const String& name, + const std::string& name, const int numberOfComponents ) { if( ! pieceOpen ) @@ -153,7 +153,7 @@ template< typename Mesh > template< typename Array > void VTIWriter< Mesh >::writeDataArray( const Array& array, - const String& name, + const std::string& name, const int numberOfComponents ) { // use a host buffer if direct access to the array elements is not possible diff --git a/src/TNL/Meshes/Writers/VTKWriter.h b/src/TNL/Meshes/Writers/VTKWriter.h index fbff5c6428c46eadce16cecce8901f1a7f003657..947b438111b1e1b15e84365d066099dc72737297 100644 --- a/src/TNL/Meshes/Writers/VTKWriter.h +++ b/src/TNL/Meshes/Writers/VTKWriter.h @@ -10,6 +10,8 @@ #pragma once +#include <ostream> + #include <TNL/Meshes/VTKTraits.h> namespace TNL { @@ -17,13 +19,6 @@ namespace Meshes { //! \brief Namespace for mesh writers. namespace Writers { -namespace details { - -template< typename Mesh, int EntityDimension > struct MeshEntitiesVTKWriter; -template< typename Mesh, int EntityDimension > struct MeshEntityTypesVTKWriter; - -} // namespace details - template< typename Mesh > class VTKWriter { @@ -31,12 +26,6 @@ class VTKWriter // TODO: check also space dimension when grids allow it // static_assert( Mesh::getSpaceDimension() <= 3, "The VTK format supports only 1D, 2D and 3D meshes." ); - template< int EntityDimension > - using EntitiesWriter = details::MeshEntitiesVTKWriter< Mesh, EntityDimension >; - - template< int EntityDimension > - using EntityTypesWriter = details::MeshEntityTypesVTKWriter< Mesh, EntityDimension >; - public: VTKWriter() = delete; @@ -57,17 +46,17 @@ public: template< typename Array > void writePointData( const Array& array, - const String& name, + const std::string& name, const int numberOfComponents = 1 ); template< typename Array > void writeCellData( const Array& array, - const String& name, + const std::string& name, const int numberOfComponents = 1 ); template< typename Array > void writeDataArray( const Array& array, - const String& name, + const std::string& name, const int numberOfComponents = 1 ); protected: diff --git a/src/TNL/Meshes/Writers/VTKWriter.hpp b/src/TNL/Meshes/Writers/VTKWriter.hpp index ada849aefc9b8726fcd8171e0d2de99dab328420..0861f0ca6b0b2c1ed2d60304e6229eb2dae5122e 100644 --- a/src/TNL/Meshes/Writers/VTKWriter.hpp +++ b/src/TNL/Meshes/Writers/VTKWriter.hpp @@ -13,399 +13,14 @@ #include <limits> #include <TNL/Meshes/Writers/VTKWriter.h> -#include <TNL/Meshes/Writers/VerticesPerEntity.h> -#include <TNL/Meshes/Writers/EntitiesListSize.h> +#include <TNL/Meshes/Writers/detail/VTKOffsetsCountGetter.h> +#include <TNL/Meshes/Writers/detail/VTKMeshEntitiesWriter.h> #include <TNL/Meshes/Grid.h> -#include <TNL/Endianness.h> namespace TNL { namespace Meshes { namespace Writers { -namespace details { - -// legacy VTK files do not support 64-bit integers, even in the BINARY format -inline void -writeInt( VTK::FileFormat format, std::ostream& str, std::int32_t value ) -{ - if( format == VTK::FileFormat::binary ) { - value = forceBigEndian( value ); - str.write( reinterpret_cast<const char*>(&value), sizeof(std::int32_t) ); - } - else { - str << value << " "; - } -} - -template< typename Real > -void -writeReal( VTK::FileFormat format, std::ostream& str, Real value ) -{ - if( format == VTK::FileFormat::binary ) { - value = forceBigEndian( value ); - str.write( reinterpret_cast<const char*>(&value), sizeof(Real) ); - } - else { - str.precision( std::numeric_limits< Real >::digits10 ); - str << value << " "; - } -} - - -// TODO: specialization for disabled entities -// Unstructured meshes, entities -template< typename Mesh, int EntityDimension > -struct MeshEntitiesVTKWriter -{ - static void exec( const Mesh& mesh, std::ostream& str, VTK::FileFormat format ) - { - using EntityType = typename Mesh::template EntityType< EntityDimension >; - using Index = typename Mesh::GlobalIndexType; - - const Index entitiesCount = mesh.template getEntitiesCount< EntityType >(); - for( Index i = 0; i < entitiesCount; i++ ) { - const auto& entity = mesh.template getEntity< EntityType >( i ); - const int verticesPerEntity = entity.template getSubentitiesCount< 0 >(); - writeInt( format, str, verticesPerEntity ); - for( int j = 0; j < verticesPerEntity; j++ ) - writeInt( format, str, entity.template getSubentityIndex< 0 >( j ) ); - if( format == VTK::FileFormat::ascii ) - str << "\n"; - } - } -}; - -// Unstructured meshes, vertices -template< typename Mesh > -struct MeshEntitiesVTKWriter< Mesh, 0 > -{ - static void exec( const Mesh& mesh, std::ostream& str, VTK::FileFormat format ) - { - using EntityType = typename Mesh::template EntityType< 0 >; - using Index = typename Mesh::GlobalIndexType; - - const Index entitiesCount = mesh.template getEntitiesCount< EntityType >(); - const int verticesPerEntity = 1; - for( Index i = 0; i < entitiesCount; i++ ) - { - writeInt( format, str, verticesPerEntity ); - writeInt( format, str, i ); - if( format == VTK::FileFormat::ascii ) - str << "\n"; - } - } -}; - -// 1D grids, cells -template< typename MeshReal, - typename Device, - typename MeshIndex > -struct MeshEntitiesVTKWriter< Meshes::Grid< 1, MeshReal, Device, MeshIndex >, 1 > -{ - using MeshType = Meshes::Grid< 1, MeshReal, Device, MeshIndex >; - - static void exec( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) - { - for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) - { - writeInt( format, str, 2 ); - writeInt( format, str, i ); - writeInt( format, str, i+1 ); - if( format == VTK::FileFormat::ascii ) - str << "\n"; - } - } -}; - -// 1D grids, vertices -template< typename MeshReal, - typename Device, - typename MeshIndex > -struct MeshEntitiesVTKWriter< Meshes::Grid< 1, MeshReal, Device, MeshIndex >, 0 > -{ - using MeshType = Meshes::Grid< 1, MeshReal, Device, MeshIndex >; - - static void exec( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) - { - for( MeshIndex i = 0; i < mesh.getDimensions().x() + 1; i++ ) - { - writeInt( format, str, 1 ); - writeInt( format, str, i ); - if( format == VTK::FileFormat::ascii ) - str << "\n"; - } - } -}; - -// 2D grids, cells -template< typename MeshReal, - typename Device, - typename MeshIndex > -struct MeshEntitiesVTKWriter< Meshes::Grid< 2, MeshReal, Device, MeshIndex >, 2 > -{ - using MeshType = Meshes::Grid< 2, MeshReal, Device, MeshIndex >; - - static void exec( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) - { - for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) - for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) - { - writeInt( format, str, 4 ); - writeInt( format, str, j * ( mesh.getDimensions().x() + 1 ) + i ); - writeInt( format, str, j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); - writeInt( format, str, (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); - writeInt( format, str, (j+1) * ( mesh.getDimensions().x() + 1 ) + i + 1 ); - if( format == VTK::FileFormat::ascii ) - str << "\n"; - } - } -}; - -// 2D grids, faces -template< typename MeshReal, - typename Device, - typename MeshIndex > -struct MeshEntitiesVTKWriter< Meshes::Grid< 2, MeshReal, Device, MeshIndex >, 1 > -{ - using MeshType = Meshes::Grid< 2, MeshReal, Device, MeshIndex >; - - static void exec( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) - { - for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) - for( MeshIndex i = 0; i < ( mesh.getDimensions().x() + 1 ); i++ ) - { - writeInt( format, str, 2 ); - writeInt( format, str, j * ( mesh.getDimensions().x() + 1 ) + i ); - writeInt( format, str, (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); - if( format == VTK::FileFormat::ascii ) - str << "\n"; - } - - for( MeshIndex j = 0; j < (mesh.getDimensions().y()+1); j++ ) - for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) - { - writeInt( format, str, 2 ); - writeInt( format, str, j * ( mesh.getDimensions().x() + 1 ) + i ); - writeInt( format, str, j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); - if( format == VTK::FileFormat::ascii ) - str << "\n"; - } - } -}; - -// 2D grids, vertices -template< typename MeshReal, - typename Device, - typename MeshIndex > -struct MeshEntitiesVTKWriter< Meshes::Grid< 2, MeshReal, Device, MeshIndex >, 0 > -{ - using MeshType = Meshes::Grid< 2, MeshReal, Device, MeshIndex >; - - static void exec( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) - { - for( MeshIndex j = 0; j < ( mesh.getDimensions().y() + 1 ); j++ ) - for( MeshIndex i = 0; i < ( mesh.getDimensions().x() + 1 ); i++ ) - { - writeInt( format, str, 1 ); - writeInt( format, str, j * mesh.getDimensions().x() + i ); - if( format == VTK::FileFormat::ascii ) - str << "\n"; - } - } -}; - -// 3D grids, cells -template< typename MeshReal, - typename Device, - typename MeshIndex > -struct MeshEntitiesVTKWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 3 > -{ - using MeshType = Meshes::Grid< 3, MeshReal, Device, MeshIndex >; - - static void exec( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) - { - for( MeshIndex k = 0; k < mesh.getDimensions().z(); k++ ) - for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) - for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) - { - writeInt( format, str, 8 ); - writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); - writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); - writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); - writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i + 1 ); - writeInt( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); - writeInt( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); - writeInt( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); - writeInt( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i + 1 ); - if( format == VTK::FileFormat::ascii ) - str << "\n"; - } - } -}; - -// 3D grids, faces -template< typename MeshReal, - typename Device, - typename MeshIndex > -struct MeshEntitiesVTKWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 2 > -{ - using MeshType = Meshes::Grid< 3, MeshReal, Device, MeshIndex >; - - static void exec( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) - { - for( MeshIndex k = 0; k < mesh.getDimensions().z(); k++ ) - for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) - for( MeshIndex i = 0; i <= mesh.getDimensions().x(); i++ ) - { - writeInt( format, str, 4 ); - writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); - writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); - writeInt( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); - writeInt( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); - if( format == VTK::FileFormat::ascii ) - str << "\n"; - } - - for( MeshIndex k = 0; k < mesh.getDimensions().z(); k++ ) - for( MeshIndex j = 0; j <= mesh.getDimensions().y(); j++ ) - for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) - { - writeInt( format, str, 4 ); - writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); - writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); - writeInt( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); - writeInt( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); - if( format == VTK::FileFormat::ascii ) - str << "\n"; - } - - for( MeshIndex k = 0; k <= mesh.getDimensions().z(); k++ ) - for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) - for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) - { - writeInt( format, str, 4 ); - writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); - writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); - writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); - writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i + 1 ); - if( format == VTK::FileFormat::ascii ) - str << "\n"; - } - } -}; - -// 3D grids, edges -template< typename MeshReal, - typename Device, - typename MeshIndex > -struct MeshEntitiesVTKWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 1 > -{ - using MeshType = Meshes::Grid< 3, MeshReal, Device, MeshIndex >; - - static void exec( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) - { - for( MeshIndex k = 0; k <= mesh.getDimensions().z(); k++ ) - for( MeshIndex j = 0; j <= mesh.getDimensions().y(); j++ ) - for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) - { - writeInt( format, str, 2 ); - writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); - writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); - if( format == VTK::FileFormat::ascii ) - str << "\n"; - } - - for( MeshIndex k = 0; k <= mesh.getDimensions().z(); k++ ) - for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) - for( MeshIndex i = 0; i <= mesh.getDimensions().x(); i++ ) - { - writeInt( format, str, 2 ); - writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); - writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); - if( format == VTK::FileFormat::ascii ) - str << "\n"; - } - - for( MeshIndex k = 0; k < mesh.getDimensions().z(); k++ ) - for( MeshIndex j = 0; j <= mesh.getDimensions().y(); j++ ) - for( MeshIndex i = 0; i <= mesh.getDimensions().x(); i++ ) - { - writeInt( format, str, 2 ); - writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); - writeInt( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); - if( format == VTK::FileFormat::ascii ) - str << "\n"; - } - } -}; - -// 3D grids, vertices -template< typename MeshReal, - typename Device, - typename MeshIndex > -struct MeshEntitiesVTKWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 0 > -{ - using MeshType = Meshes::Grid< 3, MeshReal, Device, MeshIndex >; - - static void exec( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) - { - for( MeshIndex k = 0; k < ( mesh.getDimensions().z() + 1 ); k++ ) - for( MeshIndex j = 0; j < ( mesh.getDimensions().y() + 1 ); j++ ) - for( MeshIndex i = 0; i < ( mesh.getDimensions().x() + 1 ); i++ ) - { - writeInt( format, str, 1 ); - writeInt( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); - if( format == VTK::FileFormat::ascii ) - str << "\n"; - } - } -}; - - -// TODO: specialization for disabled entities -template< typename Mesh, int EntityDimension > -struct MeshEntityTypesVTKWriter -{ - static void exec( const Mesh& mesh, std::ostream& str, VTK::FileFormat format ) - { - using EntityType = typename Mesh::template EntityType< EntityDimension >; - using Index = typename Mesh::GlobalIndexType; - - const Index entitiesCount = mesh.template getEntitiesCount< EntityType >(); - for( Index i = 0; i < entitiesCount; i++ ) { - const int type = (int) VTK::TopologyToEntityShape< typename EntityType::EntityTopology >::shape; - writeInt( format, str, type ); - if( format == VTK::FileFormat::ascii ) - str << "\n"; - } - } -}; - -template< int Dimension, - typename MeshReal, - typename Device, - typename MeshIndex, - int EntityDimension > -struct MeshEntityTypesVTKWriter< Grid< Dimension, MeshReal, Device, MeshIndex >, EntityDimension > -{ - using MeshType = Grid< Dimension, MeshReal, Device, MeshIndex >; - - static void exec( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) - { - using EntityType = typename MeshType::template EntityType< EntityDimension >; - - const MeshIndex entitiesCount = mesh.template getEntitiesCount< EntityType >(); - for( MeshIndex i = 0; i < entitiesCount; i++ ) { - const int type = (int) VTK::GridEntityShape< EntityType >::shape; - writeInt( format, str, type ); - if( format == VTK::FileFormat::ascii ) - str << "\n"; - } - } -}; - -} // namespace details - template< typename Mesh > void VTKWriter< Mesh >::writeMetadata( int cycle, double time ) @@ -422,12 +37,12 @@ VTKWriter< Mesh >::writeMetadata( int cycle, double time ) str << "FIELD FieldData " << n_metadata << "\n"; if( cycle >= 0 ) { str << "CYCLE 1 1 int\n"; - details::writeInt( format, str, cycle ); + detail::writeValue( format, str, cycle ); str << "\n"; } if( time >= 0 ) { str << "TIME 1 1 double\n"; - details::writeReal( format, str, time ); + detail::writeValue( format, str, time ); str << "\n"; } } @@ -443,20 +58,26 @@ VTKWriter< Mesh >::writeEntities( const Mesh& mesh ) using EntityType = typename Mesh::template EntityType< EntityDimension >; cellsCount = mesh.template getEntitiesCount< EntityType >(); - const std::uint64_t cellsListSize = EntitiesListSize< Mesh, EntityDimension >::getSize( mesh ); + const std::uint64_t offsetsCount = detail::VTKOffsetsCountGetter< Mesh, EntityDimension >::getOffsetsCount( mesh ); - str << std::endl << "CELLS " << cellsCount << " " << cellsListSize << std::endl; - EntitiesWriter< EntityDimension >::exec( mesh, str, format ); + // legacy VTK files always have fixed integer width, even in the BINARY format + // - DataFormat version 2.0: 32-bit + // - DataFormat version 5.1: 64-bit (vtktypeint64) + str << std::endl << "CELLS " << cellsCount + 1 << " " << offsetsCount << std::endl; + str << "OFFSETS vtktypeint64" << std::endl; + detail::VTKMeshEntitiesWriter< Mesh, EntityDimension >::template writeOffsets< std::int64_t >( mesh, str, format ); + str << "CONNECTIVITY vtktypeint64" << std::endl; + detail::VTKMeshEntitiesWriter< Mesh, EntityDimension >::template writeConnectivity< std::int64_t >( mesh, str, format ); str << std::endl << "CELL_TYPES " << cellsCount << std::endl; - EntityTypesWriter< EntityDimension >::exec( mesh, str, format ); + detail::VTKMeshEntityTypesWriter< Mesh, EntityDimension >::exec( mesh, str, format ); } template< typename Mesh > template< typename Array > void VTKWriter< Mesh >::writePointData( const Array& array, - const String& name, + const std::string& name, const int numberOfComponents ) { if( array.getSize() / numberOfComponents != typename Array::IndexType(pointsCount) ) @@ -480,7 +101,7 @@ template< typename Mesh > template< typename Array > void VTKWriter< Mesh >::writeCellData( const Array& array, - const String& name, + const std::string& name, const int numberOfComponents ) { if( array.getSize() / numberOfComponents != typename Array::IndexType(cellsCount) ) @@ -504,7 +125,7 @@ template< typename Mesh > template< typename Array > void VTKWriter< Mesh >::writeDataArray( const Array& array, - const String& name, + const std::string& name, const int numberOfComponents ) { // use a host buffer if direct access to the array elements is not possible @@ -522,16 +143,16 @@ VTKWriter< Mesh >::writeDataArray( const Array& array, // write DataArray header if( numberOfComponents == 1 ) { - str << "SCALARS " << name << " " << getType< typename Array::ValueType >() << " 1" << std::endl; + str << "SCALARS " << name << " " << getType< typename Array::ValueType >() << std::endl; str << "LOOKUP_TABLE default" << std::endl; } else { - str << "VECTORS " << name << " " << getType< typename Array::ValueType >() << " 1" << std::endl; + str << "VECTORS " << name << " " << getType< typename Array::ValueType >() << std::endl; } - using Meshes::Writers::details::writeReal; + using detail::writeValue; for( typename Array::IndexType i = 0; i < array.getSize(); i++ ) { - writeReal( format, str, array[i] ); + writeValue( format, str, array[i] ); if( format == VTK::FileFormat::ascii ) str << "\n"; } @@ -541,17 +162,17 @@ template< typename Mesh > void VTKWriter< Mesh >::writePoints( const Mesh& mesh ) { - using details::writeReal; + using detail::writeValue; pointsCount = mesh.template getEntitiesCount< typename Mesh::Vertex >(); str << "POINTS " << pointsCount << " " << getType< typename Mesh::RealType >() << std::endl; for( std::uint64_t i = 0; i < pointsCount; i++ ) { const auto& vertex = mesh.template getEntity< typename Mesh::Vertex >( i ); const auto& point = vertex.getPoint(); for( int j = 0; j < point.getSize(); j++ ) - writeReal( format, str, point[ j ] ); + writeValue( format, str, point[ j ] ); // VTK needs zeros for unused dimensions for( int j = point.getSize(); j < 3; j++ ) - writeReal( format, str, (typename Mesh::PointType::RealType) 0 ); + writeValue( format, str, (typename Mesh::PointType::RealType) 0 ); if( format == VTK::FileFormat::ascii ) str << "\n"; } @@ -561,11 +182,11 @@ 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; + str << "# vtk DataFile Version 5.1\n" + << "TNL DATA\n" + << ((format == VTK::FileFormat::ascii) ? "ASCII\n" : "BINARY\n") + << "DATASET UNSTRUCTURED_GRID\n"; + headerWritten = true; } } // namespace Writers diff --git a/src/TNL/Meshes/Writers/VTUWriter.h b/src/TNL/Meshes/Writers/VTUWriter.h index 31a1175b8dfde2225fe3460ea6859a8af8bc90b7..047daebbed08d5aa726690d94a91c302f856a029 100644 --- a/src/TNL/Meshes/Writers/VTUWriter.h +++ b/src/TNL/Meshes/Writers/VTUWriter.h @@ -12,18 +12,14 @@ #pragma once +#include <ostream> + #include <TNL/Meshes/VTKTraits.h> namespace TNL { namespace Meshes { namespace Writers { -namespace details { - -template< typename Mesh, int EntityDimension > struct MeshEntitiesVTUCollector; - -} // namespace details - template< typename Mesh > class VTUWriter { @@ -31,9 +27,6 @@ class VTUWriter // TODO: check also space dimension when grids allow it // static_assert( Mesh::getSpaceDimension() <= 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: @@ -52,17 +45,17 @@ public: template< typename Array > void writePointData( const Array& array, - const String& name, + const std::string& name, const int numberOfComponents = 1 ); template< typename Array > void writeCellData( const Array& array, - const String& name, + const std::string& name, const int numberOfComponents = 1 ); template< typename Array > void writeDataArray( const Array& array, - const String& name, + const std::string& name, const int numberOfComponents = 1 ); ~VTUWriter(); diff --git a/src/TNL/Meshes/Writers/VTUWriter.hpp b/src/TNL/Meshes/Writers/VTUWriter.hpp index 385537a521bc1d8a2cd0147ee27632d6849f39de..8a2951b1ed843a14f6e95ae94f71ab26e0e00425 100644 --- a/src/TNL/Meshes/Writers/VTUWriter.hpp +++ b/src/TNL/Meshes/Writers/VTUWriter.hpp @@ -14,9 +14,10 @@ #include <limits> +#include <TNL/Containers/Array.h> #include <TNL/Meshes/Writers/VTUWriter.h> -#include <TNL/Meshes/Writers/VerticesPerEntity.h> -#include <TNL/Meshes/Grid.h> +#include <TNL/Meshes/Writers/detail/VTUMeshEntitiesCollector.h> +#include <TNL/Meshes/Writers/detail/VTUPolyhedralFacesWriter.h> #include <TNL/Endianness.h> #include <TNL/base64.h> #ifdef HAVE_ZLIB @@ -27,345 +28,6 @@ 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 >(); - for( Index i = 0; i < entitiesCount; i++ ) { - const auto& entity = mesh.template getEntity< EntityType >( i ); - const Index verticesPerEntity = entity.template getSubentitiesCount< 0 >(); - 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 >; - using Entity = typename Mesh::template EntityType< 1 >; - - 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::GridEntityShape< Entity >::shape ); - } - } -}; - -// 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 >; - using Entity = typename Mesh::template EntityType< 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 ) - { - 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::GridEntityShape< Entity >::shape ); - } - } -}; - -// 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 >; - using Entity = typename Mesh::template EntityType< 2 >; - - 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::GridEntityShape< Entity >::shape ); - } - } -}; - -// 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 >; - using Entity = typename Mesh::template EntityType< 1 >; - - 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::GridEntityShape< Entity >::shape ); - } - - 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::GridEntityShape< Entity >::shape ); - } - } -}; - -// 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 >; - using Entity = typename Mesh::template EntityType< 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 ) - { - 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::GridEntityShape< Entity >::shape ); - } - } -}; - -// 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 >; - using Entity = typename Mesh::template EntityType< 3 >; - - 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::GridEntityShape< Entity >::shape ); - } - } -}; - -// 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 >; - using Entity = typename Mesh::template EntityType< 2 >; - - 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::GridEntityShape< Entity >::shape ); - } - - 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::GridEntityShape< Entity >::shape ); - } - - 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::GridEntityShape< Entity >::shape ); - } - } -}; - -// 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 >; - using Entity = typename Mesh::template EntityType< 1 >; - - 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::GridEntityShape< Entity >::shape ); - } - - 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::GridEntityShape< Entity >::shape ); - } - - 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::GridEntityShape< Entity >::shape ); - } - } -}; - -// 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 >; - using Entity = typename Mesh::template EntityType< 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 ) - { - 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::GridEntityShape< Entity >::shape ); - } - } -}; - -} // namespace details - template< typename Mesh > void VTUWriter< Mesh >::writeMetadata( int cycle, double time ) @@ -413,7 +75,7 @@ VTUWriter< Mesh >::writeEntities( const Mesh& mesh ) using IndexType = typename Mesh::GlobalIndexType; std::vector< IndexType > connectivity, offsets; std::vector< std::uint8_t > types; - EntitiesCollector< EntityDimension >::exec( mesh, connectivity, offsets, types ); + detail::MeshEntitiesVTUCollector< Mesh, EntityDimension >::exec( mesh, connectivity, offsets, types ); // create array views that can be passed to writeDataArray Containers::ArrayView< IndexType, Devices::Host, std::uint64_t > connectivity_v( connectivity.data(), connectivity.size() ); @@ -425,6 +87,8 @@ VTUWriter< Mesh >::writeEntities( const Mesh& mesh ) writeDataArray( connectivity_v, "connectivity", 0 ); writeDataArray( offsets_v, "offsets", 0 ); writeDataArray( types_v, "types", 0 ); + // write faces if the mesh is polyhedral + detail::VTUPolyhedralFacesWriter< Mesh >::exec( *this, mesh ); str << "</Cells>\n"; } @@ -432,7 +96,7 @@ template< typename Mesh > template< typename Array > void VTUWriter< Mesh >::writePointData( const Array& array, - const String& name, + const std::string& name, const int numberOfComponents ) { if( ! pieceOpen ) @@ -448,7 +112,7 @@ template< typename Mesh > template< typename Array > void VTUWriter< Mesh >::writeCellData( const Array& array, - const String& name, + const std::string& name, const int numberOfComponents ) { if( ! pieceOpen ) @@ -464,7 +128,7 @@ template< typename Mesh > template< typename Array > void VTUWriter< Mesh >::writeDataArray( const Array& array, - const String& name, + const std::string& name, const int numberOfComponents ) { // use a host buffer if direct access to the array elements is not possible diff --git a/src/TNL/Meshes/Writers/detail/VTKMeshEntitiesWriter.h b/src/TNL/Meshes/Writers/detail/VTKMeshEntitiesWriter.h new file mode 100644 index 0000000000000000000000000000000000000000..456c7263b910daf5dd13c29c2713fd150e1951a1 --- /dev/null +++ b/src/TNL/Meshes/Writers/detail/VTKMeshEntitiesWriter.h @@ -0,0 +1,636 @@ +#pragma once + +#include <limits> +#include <ostream> + +#include <TNL/Endianness.h> +#include <TNL/Meshes/Grid.h> +#include <TNL/Meshes/MeshEntity.h> +#include <TNL/Meshes/VTKTraits.h> + +namespace TNL { +namespace Meshes { +namespace Writers { +namespace detail { + +template< typename T > +void +writeValue( VTK::FileFormat format, std::ostream& str, T value ) +{ + if( format == VTK::FileFormat::binary ) { + value = forceBigEndian( value ); + str.write( reinterpret_cast<const char*>(&value), sizeof(T) ); + } + else { + // precision affects only floating-point types, not integers + str.precision( std::numeric_limits< T >::digits10 ); + str << value << " "; + } +} + + +// TODO: specialization for disabled entities +// Unstructured meshes, entities +template< typename Mesh, + int EntityDimension, + typename EntityType = typename Mesh::template EntityType< EntityDimension > > +struct VTKMeshEntitiesWriter +{ + template< typename Index > + static void writeOffsets( const Mesh& mesh, std::ostream& str, VTK::FileFormat format ) + { + Index offset = 0; + writeValue< Index >( format, str, offset ); + + const Index entitiesCount = mesh.template getEntitiesCount< EntityType >(); + for( Index i = 0; i < entitiesCount; i++ ) { + const auto& entity = mesh.template getEntity< EntityType >( i ); + offset += entity.template getSubentitiesCount< 0 >(); + writeValue< Index >( format, str, offset ); + } + + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + + template< typename Index > + static void writeConnectivity( const Mesh& mesh, std::ostream& str, VTK::FileFormat format ) + { + const Index entitiesCount = mesh.template getEntitiesCount< EntityType >(); + for( Index i = 0; i < entitiesCount; i++ ) { + const auto& entity = mesh.template getEntity< EntityType >( i ); + const Index verticesPerEntity = entity.template getSubentitiesCount< 0 >(); + for( Index j = 0; j < verticesPerEntity; j++ ) + writeValue< Index >( format, str, entity.template getSubentityIndex< 0 >( j ) ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +// Unstructured meshes, polyhedrons +template< typename Mesh > +struct VTKMeshEntitiesWriter< Mesh, 3, MeshEntity< typename Mesh::Config, typename Mesh::DeviceType, Topologies::Polyhedron > > +{ + template< typename Index > + static void writeOffsets( const Mesh& mesh, std::ostream& str, VTK::FileFormat format ) + { + Index offset = 0; + writeValue< Index >( format, str, offset ); + + const Index entitiesCount = mesh.template getEntitiesCount< 3 >(); + for( Index i = 0; i < entitiesCount; i++ ) { + const Index num_faces = mesh.template getSubentitiesCount< 3, 2 >( i ); + // one value (num_faces) for each cell + offset++; + // one value (num_vertices) for each face + offset += num_faces; + // list of vertex indices for each face + for( Index f = 0; f < num_faces; f++ ) { + const Index face = mesh.template getSubentityIndex< 3, 2 >( i, f ); + offset += mesh.template getSubentitiesCount< 2, 0 >( face ); + } + writeValue< Index >( format, str, offset ); + } + + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + + template< typename Index > + static void writeConnectivity( const Mesh& mesh, std::ostream& str, VTK::FileFormat format ) + { + const Index entitiesCount = mesh.template getEntitiesCount< 3 >(); + for( Index i = 0; i < entitiesCount; i++ ) { + const Index num_faces = mesh.template getSubentitiesCount< 3, 2 >( i ); + writeValue< Index >( format, str, num_faces ); + + for( Index f = 0; f < num_faces; f++ ) { + const Index face = mesh.template getSubentityIndex< 3, 2 >( i, f ); + const Index num_vertices = mesh.template getSubentitiesCount< 2, 0 >( face ); + writeValue< Index >( format, str, num_vertices ); + for( Index v = 0; v < num_vertices; v++ ) { + const Index vertex = mesh.template getSubentityIndex< 2, 0 >( face, v ); + writeValue< Index >( format, str, vertex ); + } + } + + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +// Unstructured meshes, vertices +template< typename Mesh > +struct VTKMeshEntitiesWriter< Mesh, 0, MeshEntity< typename Mesh::Config, typename Mesh::DeviceType, Topologies::Vertex > > +{ + template< typename Index > + static void writeOffsets( const Mesh& mesh, std::ostream& str, VTK::FileFormat format ) + { + using EntityType = typename Mesh::template EntityType< 0 >; + + Index offset = 0; + writeValue< Index >( format, str, offset ); + + const Index entitiesCount = mesh.template getEntitiesCount< EntityType >(); + for( Index i = 0; i < entitiesCount; i++ ) + writeValue< Index >( format, str, ++offset ); + + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + + template< typename Index > + static void writeConnectivity( const Mesh& mesh, std::ostream& str, VTK::FileFormat format ) + { + using EntityType = typename Mesh::template EntityType< 0 >; + + const Index entitiesCount = mesh.template getEntitiesCount< EntityType >(); + for( Index i = 0; i < entitiesCount; i++ ) { + writeValue< Index >( format, str, i ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +// 1D grids, cells +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct VTKMeshEntitiesWriter< Meshes::Grid< 1, MeshReal, Device, MeshIndex >, 1 > +{ + using MeshType = Meshes::Grid< 1, MeshReal, Device, MeshIndex >; + + template< typename Index > + static void writeOffsets( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + Index offset = 0; + writeValue< Index >( format, str, offset ); + + for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) { + offset += 2; + writeValue< Index >( format, str, offset ); + } + + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + + template< typename Index > + static void writeConnectivity( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) { + writeValue< Index >( format, str, i ); + writeValue< Index >( format, str, i+1 ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +// 1D grids, vertices +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct VTKMeshEntitiesWriter< Meshes::Grid< 1, MeshReal, Device, MeshIndex >, 0 > +{ + using MeshType = Meshes::Grid< 1, MeshReal, Device, MeshIndex >; + + template< typename Index > + static void writeOffsets( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + Index offset = 0; + writeValue< Index >( format, str, offset ); + + for( MeshIndex i = 0; i < mesh.getDimensions().x() + 1; i++ ) + writeValue< Index >( format, str, ++offset ); + + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + + template< typename Index > + static void writeConnectivity( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + for( MeshIndex i = 0; i < mesh.getDimensions().x() + 1; i++ ) { + writeValue< Index >( format, str, i ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +// 2D grids, cells +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct VTKMeshEntitiesWriter< Meshes::Grid< 2, MeshReal, Device, MeshIndex >, 2 > +{ + using MeshType = Meshes::Grid< 2, MeshReal, Device, MeshIndex >; + + template< typename Index > + static void writeOffsets( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + Index offset = 0; + writeValue< Index >( format, str, offset ); + + for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) { + offset += 4; + writeValue< Index >( format, str, offset ); + } + + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + + template< typename Index > + static void writeConnectivity( 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++ ) + { + writeValue< Index >( format, str, j * ( mesh.getDimensions().x() + 1 ) + i ); + writeValue< Index >( format, str, j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + writeValue< Index >( format, str, (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + writeValue< Index >( format, str, (j+1) * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +// 2D grids, faces +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct VTKMeshEntitiesWriter< Meshes::Grid< 2, MeshReal, Device, MeshIndex >, 1 > +{ + using MeshType = Meshes::Grid< 2, MeshReal, Device, MeshIndex >; + + template< typename Index > + static void writeOffsets( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + Index offset = 0; + writeValue< Index >( format, str, offset ); + + for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i < ( mesh.getDimensions().x() + 1 ); i++ ) { + offset += 2; + writeValue< Index >( format, str, offset ); + } + + for( MeshIndex j = 0; j < (mesh.getDimensions().y()+1); j++ ) + for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) { + offset += 2; + writeValue< Index >( format, str, offset ); + } + + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + + template< typename Index > + static void writeConnectivity( 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++ ) + { + writeValue< Index >( format, str, j * ( mesh.getDimensions().x() + 1 ) + i ); + writeValue< Index >( format, str, (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + + for( MeshIndex j = 0; j < (mesh.getDimensions().y()+1); j++ ) + for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) + { + writeValue< Index >( format, str, j * ( mesh.getDimensions().x() + 1 ) + i ); + writeValue< Index >( format, str, j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +// 2D grids, vertices +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct VTKMeshEntitiesWriter< Meshes::Grid< 2, MeshReal, Device, MeshIndex >, 0 > +{ + using MeshType = Meshes::Grid< 2, MeshReal, Device, MeshIndex >; + + template< typename Index > + static void writeOffsets( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + Index offset = 0; + writeValue< Index >( format, str, offset ); + + for( MeshIndex j = 0; j < ( mesh.getDimensions().y() + 1 ); j++ ) + for( MeshIndex i = 0; i < ( mesh.getDimensions().x() + 1 ); i++ ) + writeValue< Index >( format, str, ++offset ); + + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + + template< typename Index > + static void writeConnectivity( 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++ ) + { + writeValue< Index >( format, str, j * mesh.getDimensions().x() + i ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +// 3D grids, cells +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct VTKMeshEntitiesWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 3 > +{ + using MeshType = Meshes::Grid< 3, MeshReal, Device, MeshIndex >; + + template< typename Index > + static void writeOffsets( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + Index offset = 0; + writeValue< Index >( format, str, offset ); + + 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++ ) { + offset += 8; + writeValue< Index >( format, str, offset ); + } + + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + + template< typename Index > + static void writeConnectivity( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + for( MeshIndex k = 0; k < mesh.getDimensions().z(); k++ ) + for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) + { + writeValue< Index >( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + writeValue< Index >( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + writeValue< Index >( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + writeValue< Index >( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + writeValue< Index >( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + writeValue< Index >( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + writeValue< Index >( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + writeValue< Index >( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +// 3D grids, faces +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct VTKMeshEntitiesWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 2 > +{ + using MeshType = Meshes::Grid< 3, MeshReal, Device, MeshIndex >; + + template< typename Index > + static void writeOffsets( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + Index offset = 0; + writeValue< Index >( format, str, offset ); + + 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++ ) { + offset += 4; + writeValue< Index >( format, str, offset ); + } + + 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++ ) { + offset += 4; + writeValue< Index >( format, str, offset ); + } + + 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++ ) { + offset += 4; + writeValue< Index >( format, str, offset ); + } + + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + + template< typename Index > + static void writeConnectivity( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + for( MeshIndex k = 0; k < mesh.getDimensions().z(); k++ ) + for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i <= mesh.getDimensions().x(); i++ ) + { + writeValue< Index >( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + writeValue< Index >( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + writeValue< Index >( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + writeValue< Index >( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + + for( MeshIndex k = 0; k < mesh.getDimensions().z(); k++ ) + for( MeshIndex j = 0; j <= mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) + { + writeValue< Index >( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + writeValue< Index >( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + writeValue< Index >( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + writeValue< Index >( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + + for( MeshIndex k = 0; k <= mesh.getDimensions().z(); k++ ) + for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) + { + writeValue< Index >( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + writeValue< Index >( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + writeValue< Index >( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + writeValue< Index >( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +// 3D grids, edges +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct VTKMeshEntitiesWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 1 > +{ + using MeshType = Meshes::Grid< 3, MeshReal, Device, MeshIndex >; + + template< typename Index > + static void writeOffsets( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + Index offset = 0; + writeValue< Index >( format, str, offset ); + + 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++ ) { + offset += 2; + writeValue< Index >( format, str, offset ); + } + + 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++ ) { + offset += 2; + writeValue< Index >( format, str, offset ); + } + + 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++ ) { + offset += 2; + writeValue< Index >( format, str, offset ); + } + + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + + template< typename Index > + static void writeConnectivity( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + for( MeshIndex k = 0; k <= mesh.getDimensions().z(); k++ ) + for( MeshIndex j = 0; j <= mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i < mesh.getDimensions().x(); i++ ) + { + writeValue< Index >( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + writeValue< Index >( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i + 1 ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + + for( MeshIndex k = 0; k <= mesh.getDimensions().z(); k++ ) + for( MeshIndex j = 0; j < mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i <= mesh.getDimensions().x(); i++ ) + { + writeValue< Index >( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + writeValue< Index >( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + (j+1) * ( mesh.getDimensions().x() + 1 ) + i ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + + for( MeshIndex k = 0; k < mesh.getDimensions().z(); k++ ) + for( MeshIndex j = 0; j <= mesh.getDimensions().y(); j++ ) + for( MeshIndex i = 0; i <= mesh.getDimensions().x(); i++ ) + { + writeValue< Index >( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + writeValue< Index >( format, str, (k+1) * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +// 3D grids, vertices +template< typename MeshReal, + typename Device, + typename MeshIndex > +struct VTKMeshEntitiesWriter< Meshes::Grid< 3, MeshReal, Device, MeshIndex >, 0 > +{ + using MeshType = Meshes::Grid< 3, MeshReal, Device, MeshIndex >; + + template< typename Index > + static void writeOffsets( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + Index offset = 0; + writeValue< Index >( format, str, offset ); + + 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++ ) + writeValue< Index >( format, str, ++offset ); + + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + + template< typename Index > + static void writeConnectivity( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + for( MeshIndex k = 0; k < ( mesh.getDimensions().z() + 1 ); k++ ) + for( MeshIndex j = 0; j < ( mesh.getDimensions().y() + 1 ); j++ ) + for( MeshIndex i = 0; i < ( mesh.getDimensions().x() + 1 ); i++ ) + { + writeValue< Index >( format, str, k * ( mesh.getDimensions().y() + 1 ) * ( mesh.getDimensions().x() + 1 ) + j * ( mesh.getDimensions().x() + 1 ) + i ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + + +// TODO: specialization for disabled entities +template< typename Mesh, int EntityDimension > +struct VTKMeshEntityTypesWriter +{ + static void exec( const Mesh& mesh, std::ostream& str, VTK::FileFormat format ) + { + using EntityType = typename Mesh::template EntityType< EntityDimension >; + using Index = typename Mesh::GlobalIndexType; + + const Index entitiesCount = mesh.template getEntitiesCount< EntityType >(); + for( Index i = 0; i < entitiesCount; i++ ) { + const int type = (int) VTK::TopologyToEntityShape< typename EntityType::EntityTopology >::shape; + writeValue( format, str, type ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +template< int Dimension, + typename MeshReal, + typename Device, + typename MeshIndex, + int EntityDimension > +struct VTKMeshEntityTypesWriter< Grid< Dimension, MeshReal, Device, MeshIndex >, EntityDimension > +{ + using MeshType = Grid< Dimension, MeshReal, Device, MeshIndex >; + + static void exec( const MeshType& mesh, std::ostream& str, VTK::FileFormat format ) + { + using EntityType = typename MeshType::template EntityType< EntityDimension >; + + const MeshIndex entitiesCount = mesh.template getEntitiesCount< EntityType >(); + for( MeshIndex i = 0; i < entitiesCount; i++ ) { + const int type = (int) VTK::GridEntityShape< EntityType >::shape; + writeValue( format, str, type ); + if( format == VTK::FileFormat::ascii ) + str << "\n"; + } + } +}; + +} // namespace detail +} // namespace Writers +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/Writers/detail/VTKOffsetsCountGetter.h b/src/TNL/Meshes/Writers/detail/VTKOffsetsCountGetter.h new file mode 100644 index 0000000000000000000000000000000000000000..abe89c91a32be7ffc7db3e918cc5d14176774c35 --- /dev/null +++ b/src/TNL/Meshes/Writers/detail/VTKOffsetsCountGetter.h @@ -0,0 +1,68 @@ +#pragma once + +#include <TNL/Meshes/Writers/detail/VerticesPerEntity.h> + +namespace TNL { +namespace Meshes { +namespace Writers { +namespace detail { + +template< typename Mesh, + int EntityDimension, + typename EntityType = typename Mesh::template EntityType< EntityDimension > > +struct VTKOffsetsCountGetter +{ + using IndexType = typename Mesh::GlobalIndexType; + + static IndexType getOffsetsCount( const Mesh& mesh ) + { + const IndexType entitiesCount = mesh.template getEntitiesCount< EntityType >(); + const IndexType verticesPerEntity = VerticesPerEntity< EntityType >::count; + return entitiesCount * verticesPerEntity; + } +}; + +template< typename Mesh, int EntityDimension > +struct VTKOffsetsCountGetter< Mesh, EntityDimension, MeshEntity< typename Mesh::Config, typename Mesh::DeviceType, Topologies::Polygon > > +{ + using IndexType = typename Mesh::GlobalIndexType; + + static IndexType getOffsetsCount( const Mesh& mesh ) + { + const IndexType entitiesCount = mesh.template getEntitiesCount< EntityDimension >(); + IndexType offsetsCount = 0; + for( IndexType index = 0; index < entitiesCount; index++ ) + offsetsCount += mesh.template getSubentitiesCount< EntityDimension, 0 >( index ); + return offsetsCount; + } +}; + +template< typename Mesh, int EntityDimension > +struct VTKOffsetsCountGetter< Mesh, EntityDimension, MeshEntity< typename Mesh::Config, typename Mesh::DeviceType, Topologies::Polyhedron > > +{ + using IndexType = typename Mesh::GlobalIndexType; + + static IndexType getOffsetsCount( const Mesh& mesh ) + { + const IndexType entitiesCount = mesh.template getEntitiesCount< EntityDimension >(); + IndexType offsetsCount = 0; + for( IndexType index = 0; index < entitiesCount; index++ ) { + const IndexType num_faces = mesh.template getSubentitiesCount< EntityDimension, EntityDimension - 1 >( index ); + // one value (num_faces) for each cell + offsetsCount++; + // one value (num_vertices) for each face + offsetsCount += num_faces; + // list of vertex indices for each face + for( IndexType f = 0; f < num_faces; f++ ) { + const IndexType face = mesh.template getSubentityIndex< EntityDimension, EntityDimension - 1 >( index, f ); + offsetsCount += mesh.template getSubentitiesCount< EntityDimension - 1, 0 >( face ); + } + } + return offsetsCount; + } +}; + +} // namespace detail +} // namespace Writers +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/Writers/detail/VTUMeshEntitiesCollector.h b/src/TNL/Meshes/Writers/detail/VTUMeshEntitiesCollector.h new file mode 100644 index 0000000000000000000000000000000000000000..b631c50870c7e5e6708938ec5b700bc22db353d5 --- /dev/null +++ b/src/TNL/Meshes/Writers/detail/VTUMeshEntitiesCollector.h @@ -0,0 +1,349 @@ +#pragma once + +#include <TNL/Meshes/VTKTraits.h> +#include <TNL/Meshes/Grid.h> + +namespace TNL { +namespace Meshes { +namespace Writers { +namespace detail { + +// 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 >(); + for( Index i = 0; i < entitiesCount; i++ ) { + const auto& entity = mesh.template getEntity< EntityType >( i ); + const Index verticesPerEntity = entity.template getSubentitiesCount< 0 >(); + 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 >; + using Entity = typename Mesh::template EntityType< 1 >; + + 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::GridEntityShape< Entity >::shape ); + } + } +}; + +// 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 >; + using Entity = typename Mesh::template EntityType< 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 ) + { + 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::GridEntityShape< Entity >::shape ); + } + } +}; + +// 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 >; + using Entity = typename Mesh::template EntityType< 2 >; + + 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::GridEntityShape< Entity >::shape ); + } + } +}; + +// 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 >; + using Entity = typename Mesh::template EntityType< 1 >; + + 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::GridEntityShape< Entity >::shape ); + } + + 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::GridEntityShape< Entity >::shape ); + } + } +}; + +// 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 >; + using Entity = typename Mesh::template EntityType< 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 ) + { + 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::GridEntityShape< Entity >::shape ); + } + } +}; + +// 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 >; + using Entity = typename Mesh::template EntityType< 3 >; + + 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::GridEntityShape< Entity >::shape ); + } + } +}; + +// 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 >; + using Entity = typename Mesh::template EntityType< 2 >; + + 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::GridEntityShape< Entity >::shape ); + } + + 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::GridEntityShape< Entity >::shape ); + } + + 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::GridEntityShape< Entity >::shape ); + } + } +}; + +// 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 >; + using Entity = typename Mesh::template EntityType< 1 >; + + 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::GridEntityShape< Entity >::shape ); + } + + 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::GridEntityShape< Entity >::shape ); + } + + 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::GridEntityShape< Entity >::shape ); + } + } +}; + +// 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 >; + using Entity = typename Mesh::template EntityType< 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 ) + { + 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::GridEntityShape< Entity >::shape ); + } + } +}; + +} // namespace detail +} // namespace Writers +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/Writers/detail/VTUPolyhedralFacesWriter.h b/src/TNL/Meshes/Writers/detail/VTUPolyhedralFacesWriter.h new file mode 100644 index 0000000000000000000000000000000000000000..f2267b97d2d96a50223a045fed63d58e0e19c592 --- /dev/null +++ b/src/TNL/Meshes/Writers/detail/VTUPolyhedralFacesWriter.h @@ -0,0 +1,72 @@ +#pragma once + +#include <type_traits> // std::enable_if_t + +#include <TNL/Containers/ArrayView.h> +#include <TNL/Meshes/Grid.h> +#include <TNL/Meshes/Topologies/Polyhedron.h> + +namespace TNL { +namespace Meshes { +namespace Writers { +namespace detail { + +// specialization for meshes +template< typename Mesh > +struct VTUPolyhedralFacesWriter +{ + // specialization for all meshes except polyhedral + template< typename W, typename M > + static std::enable_if_t< ! std::is_same< typename M::Config::CellTopology, Topologies::Polyhedron >::value > + exec( W& writer, const M& mesh ) + {} + + // specialization for polyhedral meshes + template< typename W, typename M > + static std::enable_if_t< std::is_same< typename M::Config::CellTopology, Topologies::Polyhedron >::value > + exec( W& writer, const M& mesh ) + { + // build the "face stream" for VTK + using IndexType = typename Mesh::GlobalIndexType; + std::vector< IndexType > faces, faceoffsets; + for( IndexType c = 0; c < mesh.template getEntitiesCount< M::getMeshDimension() >(); c++ ) { + const IndexType num_faces = mesh.template getSubentitiesCount< M::getMeshDimension(), M::getMeshDimension() - 1 >( c ); + faces.push_back( num_faces ); + for( IndexType f = 0; f < num_faces; f++ ) { + const auto& face = mesh.template getEntity< M::getMeshDimension() - 1 >( mesh.template getSubentityIndex< M::getMeshDimension(), M::getMeshDimension() - 1 >( c, f ) ); + const IndexType num_vertices = face.template getSubentitiesCount< 0 >(); + faces.push_back( num_vertices ); + for( IndexType v = 0; v < num_vertices; v++ ) { + const IndexType vertex = face.template getSubentityIndex< 0 >( v ); + faces.push_back( vertex ); + } + } + faceoffsets.push_back( faces.size() ); + } + + // create array views that can be passed to writeDataArray + Containers::ArrayView< IndexType, Devices::Host, std::uint64_t > faces_v( faces.data(), faces.size() ); + Containers::ArrayView< IndexType, Devices::Host, std::uint64_t > faceoffsets_v( faceoffsets.data(), faceoffsets.size() ); + + // write cells + writer.writeDataArray( faces_v, "faces", 0 ); + writer.writeDataArray( faceoffsets_v, "faceoffsets", 0 ); + } +}; + +// specialization for grids +template< int Dimension, + typename MeshReal, + typename Device, + typename MeshIndex > +struct VTUPolyhedralFacesWriter< Meshes::Grid< Dimension, MeshReal, Device, MeshIndex > > +{ + template< typename W, typename M > + static void exec( W& writer, const M& mesh ) + {} +}; + +} // namespace detail +} // namespace Writers +} // namespace Meshes +} // namespace TNL diff --git a/src/TNL/Meshes/Writers/VerticesPerEntity.h b/src/TNL/Meshes/Writers/detail/VerticesPerEntity.h similarity index 64% rename from src/TNL/Meshes/Writers/VerticesPerEntity.h rename to src/TNL/Meshes/Writers/detail/VerticesPerEntity.h index 5ae5e356e25739bf3e17da877e16a055cddd3df8..608557e2117ef556f83e3c3f0880535079e3e959 100644 --- a/src/TNL/Meshes/Writers/VerticesPerEntity.h +++ b/src/TNL/Meshes/Writers/detail/VerticesPerEntity.h @@ -1,15 +1,3 @@ -/*************************************************************************** - 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 <TNL/TypeTraits.h> @@ -18,8 +6,7 @@ namespace TNL { namespace Meshes { namespace Writers { - -namespace details { +namespace detail { template< typename T, typename Enable = void > struct has_entity_topology : std::false_type {}; @@ -29,10 +16,8 @@ struct has_entity_topology< T, typename enable_if_type< typename T::EntityTopolo : std::true_type {}; -} // namespace details - template< typename Entity, - bool _is_mesh_entity = details::has_entity_topology< Entity >::value > + bool _is_mesh_entity = has_entity_topology< Entity >::value > struct VerticesPerEntity { static constexpr int count = Topologies::Subtopology< typename Entity::EntityTopology, 0 >::count; @@ -59,6 +44,7 @@ public: 8; }; +} // namespace detail } // namespace Writers } // namespace Meshes } // namespace TNL diff --git a/src/TNL/Solvers/BuildConfigTags.h b/src/TNL/Solvers/BuildConfigTags.h index e75dcfddb0b37df8cdfd8e1eba38ae2e49e46cc4..a6f20396eded5bb037c936af1bbf88b7cb60cd06 100644 --- a/src/TNL/Solvers/BuildConfigTags.h +++ b/src/TNL/Solvers/BuildConfigTags.h @@ -22,26 +22,26 @@ class DefaultBuildConfigTag {}; * All devices are enabled by default. Those which are not available * are disabled. */ -template< typename ConfigTag, typename Device > struct ConfigTagDevice{ enum { enabled = true }; }; +template< typename ConfigTag, typename Device > struct ConfigTagDevice{ static constexpr bool enabled = true; }; #ifndef HAVE_CUDA -template< typename ConfigTag > struct ConfigTagDevice< ConfigTag, Devices::Cuda >{ enum { enabled = false }; }; +template< typename ConfigTag > struct ConfigTagDevice< ConfigTag, Devices::Cuda >{ static constexpr bool enabled = false; }; #endif /**** * All real types are enabled by default. */ -template< typename ConfigTag, typename Real > struct ConfigTagReal{ enum { enabled = true }; }; +template< typename ConfigTag, typename Real > struct ConfigTagReal{ static constexpr bool enabled = true; }; /**** * All index types are enabled by default. */ -template< typename ConfigTag, typename Index > struct ConfigTagIndex{ enum { enabled = true }; }; +template< typename ConfigTag, typename Index > struct ConfigTagIndex{ static constexpr bool enabled = true; }; /**** * The mesh type will be resolved by the Solver by default. * (The detailed mesh configuration is in TNL/Meshes/TypeResolver/BuildConfigTags.h) */ -template< typename ConfigTag > struct ConfigTagMeshResolve{ enum { enabled = true }; }; +template< typename ConfigTag > struct ConfigTagMeshResolve{ static constexpr bool enabled = true; }; /**** * All time discretisations (explicit, semi-impicit and implicit ) are @@ -51,7 +51,7 @@ class ExplicitTimeDiscretisationTag{}; class SemiImplicitTimeDiscretisationTag{}; class ImplicitTimeDiscretisationTag{}; -template< typename ConfigTag, typename TimeDiscretisation > struct ConfigTagTimeDiscretisation{ enum { enabled = true }; }; +template< typename ConfigTag, typename TimeDiscretisation > struct ConfigTagTimeDiscretisation{ static constexpr bool enabled = true; }; /**** * All explicit solvers are enabled by default @@ -70,7 +70,7 @@ public: using Template = ODE::Merson< Problem, SolverMonitor >; }; -template< typename ConfigTag, typename ExplicitSolver > struct ConfigTagExplicitSolver{ enum { enabled = true }; }; +template< typename ConfigTag, typename ExplicitSolver > struct ConfigTagExplicitSolver{ static constexpr bool enabled = true; }; } // namespace Solvers } // namespace TNL diff --git a/src/TNL/Solvers/FastBuildConfigTag.h b/src/TNL/Solvers/FastBuildConfigTag.h index d4a43df06a2d6e9fb9b685381d421c70419c4ff5..f737d6b67127c89bc679e1c7c80a6cb0ccaa93c2 100644 --- a/src/TNL/Solvers/FastBuildConfigTag.h +++ b/src/TNL/Solvers/FastBuildConfigTag.h @@ -21,26 +21,26 @@ class FastBuildConfigTag {}; /**** * Turn off support for float and long double. */ -template<> struct ConfigTagReal< FastBuildConfigTag, float > { enum { enabled = false }; }; -template<> struct ConfigTagReal< FastBuildConfigTag, long double > { enum { enabled = false }; }; +template<> struct ConfigTagReal< FastBuildConfigTag, float > { static constexpr bool enabled = false; }; +template<> struct ConfigTagReal< FastBuildConfigTag, long double > { static constexpr bool enabled = false; }; /**** * Turn off support for short int and long int indexing. */ -template<> struct ConfigTagIndex< FastBuildConfigTag, short int >{ enum { enabled = false }; }; -template<> struct ConfigTagIndex< FastBuildConfigTag, long int >{ enum { enabled = false }; }; +template<> struct ConfigTagIndex< FastBuildConfigTag, short int >{ static constexpr bool enabled = false; }; +template<> struct ConfigTagIndex< FastBuildConfigTag, long int >{ static constexpr bool enabled = false; }; /**** * Please, chose your preferred time discretisation here. */ -template<> struct ConfigTagTimeDiscretisation< FastBuildConfigTag, ExplicitTimeDiscretisationTag >{ enum { enabled = true }; }; -template<> struct ConfigTagTimeDiscretisation< FastBuildConfigTag, SemiImplicitTimeDiscretisationTag >{ enum { enabled = true }; }; -template<> struct ConfigTagTimeDiscretisation< FastBuildConfigTag, ImplicitTimeDiscretisationTag >{ enum { enabled = false }; }; +template<> struct ConfigTagTimeDiscretisation< FastBuildConfigTag, ExplicitTimeDiscretisationTag >{ static constexpr bool enabled = true; }; +template<> struct ConfigTagTimeDiscretisation< FastBuildConfigTag, SemiImplicitTimeDiscretisationTag >{ static constexpr bool enabled = true; }; +template<> struct ConfigTagTimeDiscretisation< FastBuildConfigTag, ImplicitTimeDiscretisationTag >{ static constexpr bool enabled = false; }; /**** * Only the Runge-Kutta-Merson solver is enabled by default. */ -//template<> struct ConfigTagExplicitSolver< FastBuildConfigTag, ExplicitEulerSolverTag >{ enum { enabled = false }; }; +//template<> struct ConfigTagExplicitSolver< FastBuildConfigTag, ExplicitEulerSolverTag >{ static constexpr bool enabled = false; }; } // namespace Solvers @@ -50,14 +50,14 @@ namespace BuildConfigTags { /**** * Turn off support for float and long double. */ -template<> struct GridRealTag< Solvers::FastBuildConfigTag, float > { enum { enabled = false }; }; -template<> struct GridRealTag< Solvers::FastBuildConfigTag, long double > { enum { enabled = false }; }; +template<> struct GridRealTag< Solvers::FastBuildConfigTag, float > { static constexpr bool enabled = false; }; +template<> struct GridRealTag< Solvers::FastBuildConfigTag, long double > { static constexpr bool enabled = false; }; /**** * Turn off support for short int and long int indexing. */ -template<> struct GridIndexTag< Solvers::FastBuildConfigTag, short int >{ enum { enabled = false }; }; -template<> struct GridIndexTag< Solvers::FastBuildConfigTag, long int >{ enum { enabled = false }; }; +template<> struct GridIndexTag< Solvers::FastBuildConfigTag, short int >{ static constexpr bool enabled = false; }; +template<> struct GridIndexTag< Solvers::FastBuildConfigTag, long int >{ static constexpr bool enabled = false; }; } // namespace BuildConfigTags } // namespace Meshes diff --git a/src/Tools/CMakeLists.txt b/src/Tools/CMakeLists.txt index 8af2438fdf675656e6c89d7b51d9152d8bb4890e..8bfc42752c5e0daf73f57423c52b8748fb650dc2 100644 --- a/src/Tools/CMakeLists.txt +++ b/src/Tools/CMakeLists.txt @@ -1,49 +1,44 @@ add_subdirectory(tnl-quickstart) -ADD_EXECUTABLE(tnl-decompose-grid tnl-decompose-grid.cpp ) -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-triangulate-mesh tnl-triangulate-mesh.cpp ) -ADD_EXECUTABLE(tnl-planar-correct-mesh tnl-planar-correct-mesh.cpp ) -ADD_EXECUTABLE(tnl-game-of-life tnl-game-of-life.cpp ) +set( MESH_TOOLS + tnl-init + tnl-diff + tnl-decompose-grid + tnl-grid-to-mesh + tnl-mesh-converter + tnl-triangulate-mesh + tnl-planar-correct-mesh + tnl-refine-mesh + tnl-game-of-life +) +set( CUDA_MESH_TOOLS ) if( BUILD_CUDA ) - CUDA_ADD_EXECUTABLE(tnl-test-distributed-mesh tnl-test-distributed-mesh.cu ) + set( CUDA_MESH_TOOLS ${CUDA_MESH_TOOLS} tnl-test-distributed-mesh ) else() - ADD_EXECUTABLE(tnl-test-distributed-mesh tnl-test-distributed-mesh.cpp ) + set( MESH_TOOLS ${MESH_TOOLS} tnl-test-distributed-mesh ) endif() -ADD_EXECUTABLE(tnl-init tnl-init.cpp ) -ADD_EXECUTABLE(tnl-diff tnl-diff.cpp ) -ADD_EXECUTABLE(tnl-image-converter tnl-image-converter.cpp ) -if( PNG_FOUND ) - target_link_libraries(tnl-image-converter ${PNG_LIBRARIES} ) -endif() -if( JPEG_FOUND ) - target_link_libraries(tnl-image-converter ${JPEG_LIBRARIES} ) -endif() - -ADD_EXECUTABLE(tnl-dicom-reader tnl-dicom-reader.cpp ) -if( DCMTK_FOUND ) - target_link_libraries(tnl-dicom-reader ${DCMTK_LIBRARIES} ) -endif() +foreach( target IN ITEMS ${MESH_TOOLS} ) + add_executable( ${target} ${target}.cpp ) +endforeach() +foreach( target IN ITEMS ${CUDA_MESH_TOOLS} ) + cuda_add_executable( ${target} ${target}.cu ) +endforeach() find_package( ZLIB ) -if( ZLIB_FOUND ) - foreach( target IN ITEMS tnl-init tnl-diff tnl-decompose-grid tnl-grid-to-mesh tnl-mesh-converter tnl-triangulate-mesh tnl-planar-correct-mesh tnl-game-of-life tnl-test-distributed-mesh ) - target_compile_definitions(${target} PUBLIC "-DHAVE_ZLIB") - target_include_directories(${target} PUBLIC ${ZLIB_INCLUDE_DIRS}) - target_link_libraries(${target} ${ZLIB_LIBRARIES}) - endforeach() -endif() - find_package( tinyxml2 QUIET ) -if( tinyxml2_FOUND ) - foreach( target IN ITEMS tnl-init tnl-diff tnl-decompose-grid tnl-grid-to-mesh tnl-mesh-converter tnl-triangulate-mesh tnl-planar-correct-mesh tnl-game-of-life tnl-test-distributed-mesh ) - target_compile_definitions(${target} PUBLIC "-DHAVE_TINYXML2") - target_link_libraries(${target} tinyxml2::tinyxml2) - endforeach() -endif() +foreach( target IN ITEMS ${MESH_TOOLS} ${CUDA_MESH_TOOLS} ) + if( ZLIB_FOUND ) + target_compile_definitions( ${target} PUBLIC "-DHAVE_ZLIB" ) + target_include_directories( ${target} PUBLIC ${ZLIB_INCLUDE_DIRS} ) + target_link_libraries( ${target} ${ZLIB_LIBRARIES} ) + endif() + if( tinyxml2_FOUND ) + target_compile_definitions( ${target} PUBLIC "-DHAVE_TINYXML2" ) + target_link_libraries( ${target} tinyxml2::tinyxml2 ) + endif() + install( TARGETS ${target} DESTINATION bin ) +endforeach() find_package( METIS QUIET ) if( METIS_FOUND ) @@ -62,27 +57,33 @@ if( METIS_FOUND ) install( TARGETS tnl-decompose-mesh DESTINATION bin ) endif() + +add_executable( tnl-grid-setup tnl-grid-setup.cpp ) + +add_executable( tnl-image-converter tnl-image-converter.cpp ) +if( PNG_FOUND ) + target_link_libraries( tnl-image-converter ${PNG_LIBRARIES} ) +endif() +if( JPEG_FOUND ) + target_link_libraries( tnl-image-converter ${JPEG_LIBRARIES} ) +endif() + +add_executable( tnl-dicom-reader tnl-dicom-reader.cpp ) +if( DCMTK_FOUND ) + target_link_libraries( tnl-dicom-reader ${DCMTK_LIBRARIES} ) +endif() + IF( BUILD_CUDA ) - CUDA_ADD_EXECUTABLE( tnl-cuda-arch tnl-cuda-arch.cu ) - INSTALL( TARGETS tnl-cuda-arch - DESTINATION bin ) + cuda_add_executable( tnl-cuda-arch tnl-cuda-arch.cu ) + install( TARGETS tnl-cuda-arch DESTINATION bin ) ENDIF() -INSTALL( TARGETS tnl-init - tnl-diff - tnl-decompose-grid - tnl-grid-setup - tnl-grid-to-mesh - tnl-mesh-converter - tnl-triangulate-mesh - tnl-planar-correct-mesh - tnl-game-of-life - tnl-test-distributed-mesh - tnl-dicom-reader +install( TARGETS tnl-grid-setup tnl-image-converter + tnl-dicom-reader DESTINATION bin ) -INSTALL( PROGRAMS tnl-err2eoc +install( PROGRAMS tnl-err2eoc tnl-benchmark-to-html.py tnl-log-to-html.py DESTINATION bin ) diff --git a/src/Tools/tnl-decompose-grid.cpp b/src/Tools/tnl-decompose-grid.cpp index b57558cc5b74f6bcf6097467c51ad6810129d216..c47055b3201a4875d3074df41a94eaad9e6eaed4 100644 --- a/src/Tools/tnl-decompose-grid.cpp +++ b/src/Tools/tnl-decompose-grid.cpp @@ -26,12 +26,12 @@ namespace BuildConfigTags { /**** * Turn on all grids. */ -template<> struct GridRealTag< DecomposeGridConfigTag, float > { enum { enabled = true }; }; -template<> struct GridRealTag< DecomposeGridConfigTag, double > { enum { enabled = true }; }; -template<> struct GridRealTag< DecomposeGridConfigTag, long double > { enum { enabled = true }; }; +template<> struct GridRealTag< DecomposeGridConfigTag, float > { static constexpr bool enabled = true; }; +template<> struct GridRealTag< DecomposeGridConfigTag, double > { static constexpr bool enabled = true; }; +template<> struct GridRealTag< DecomposeGridConfigTag, long double > { static constexpr bool enabled = true; }; -template<> struct GridIndexTag< DecomposeGridConfigTag, int > { enum { enabled = true }; }; -template<> struct GridIndexTag< DecomposeGridConfigTag, long int > { enum { enabled = true }; }; +template<> struct GridIndexTag< DecomposeGridConfigTag, int > { static constexpr bool enabled = true; }; +template<> struct GridIndexTag< DecomposeGridConfigTag, long int > { static constexpr bool enabled = true; }; } // namespace BuildConfigTags } // namespace Meshes diff --git a/src/Tools/tnl-decompose-mesh.cpp b/src/Tools/tnl-decompose-mesh.cpp index 97383e9f3791af5ff52b88739b23deb2a2a266b0..b85d01dd016c96d1529fd002974ec89dbb929e3f 100644 --- a/src/Tools/tnl-decompose-mesh.cpp +++ b/src/Tools/tnl-decompose-mesh.cpp @@ -38,30 +38,30 @@ 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 }; }; +template<> struct GridRealTag< DecomposeMeshConfigTag, float > { static constexpr bool enabled = false; }; +template<> struct GridRealTag< DecomposeMeshConfigTag, double > { static constexpr bool enabled = false; }; +template<> struct GridRealTag< DecomposeMeshConfigTag, long double > { static constexpr bool 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::Quadrangle > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< DecomposeMeshConfigTag, Topologies::Tetrahedron > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< DecomposeMeshConfigTag, Topologies::Hexahedron > { enum { enabled = true }; }; +template<> struct MeshCellTopologyTag< DecomposeMeshConfigTag, Topologies::Edge > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< DecomposeMeshConfigTag, Topologies::Triangle > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< DecomposeMeshConfigTag, Topologies::Quadrangle > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< DecomposeMeshConfigTag, Topologies::Tetrahedron > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< DecomposeMeshConfigTag, Topologies::Hexahedron > { static constexpr bool enabled = true; }; // Meshes are enabled only for the space dimension equal to the cell dimension. template< typename CellTopology, int SpaceDimension > struct MeshSpaceDimensionTag< DecomposeMeshConfigTag, CellTopology, SpaceDimension > -{ enum { enabled = ( SpaceDimension == CellTopology::dimension ) }; }; +{ static constexpr bool enabled = SpaceDimension == CellTopology::dimension; }; // Meshes are enabled only for types explicitly listed below. -template<> struct MeshRealTag< DecomposeMeshConfigTag, float > { enum { enabled = true }; }; -template<> struct MeshRealTag< DecomposeMeshConfigTag, double > { enum { enabled = true }; }; -template<> struct MeshGlobalIndexTag< DecomposeMeshConfigTag, int > { enum { enabled = true }; }; -template<> struct MeshGlobalIndexTag< DecomposeMeshConfigTag, long int > { enum { enabled = true }; }; -template<> struct MeshLocalIndexTag< DecomposeMeshConfigTag, short int > { enum { enabled = true }; }; +template<> struct MeshRealTag< DecomposeMeshConfigTag, float > { static constexpr bool enabled = true; }; +template<> struct MeshRealTag< DecomposeMeshConfigTag, double > { static constexpr bool enabled = true; }; +template<> struct MeshGlobalIndexTag< DecomposeMeshConfigTag, int > { static constexpr bool enabled = true; }; +template<> struct MeshGlobalIndexTag< DecomposeMeshConfigTag, long int > { static constexpr bool enabled = true; }; +template<> struct MeshLocalIndexTag< DecomposeMeshConfigTag, short int > { static constexpr bool enabled = true; }; // Config tag specifying the MeshConfig template to use. template<> diff --git a/src/Tools/tnl-diff.cpp b/src/Tools/tnl-diff.cpp index c946dc80b5481314b8fa7a2d304d83c2ecc1500b..ce7fe42d4b19d23f72d80ceb975c5b8f8a7116c3 100644 --- a/src/Tools/tnl-diff.cpp +++ b/src/Tools/tnl-diff.cpp @@ -22,14 +22,14 @@ namespace BuildConfigTags { /**** * Turn off support for float and long double. */ -//template<> struct GridRealTag< TNLDiffBuildConfigTag, float > { enum { enabled = false }; }; -template<> struct GridRealTag< TNLDiffBuildConfigTag, long double > { enum { enabled = false }; }; +//template<> struct GridRealTag< TNLDiffBuildConfigTag, float > { static constexpr bool enabled = false; }; +template<> struct GridRealTag< TNLDiffBuildConfigTag, long double > { static constexpr bool enabled = false; }; /**** * Turn off support for short int and long int indexing. */ -template<> struct GridIndexTag< TNLDiffBuildConfigTag, short int >{ enum { enabled = false }; }; -template<> struct GridIndexTag< TNLDiffBuildConfigTag, long int >{ enum { enabled = false }; }; +template<> struct GridIndexTag< TNLDiffBuildConfigTag, short int >{ static constexpr bool enabled = false; }; +template<> struct GridIndexTag< TNLDiffBuildConfigTag, long int >{ static constexpr bool enabled = false; }; } // namespace BuildConfigTags } // namespace Meshes diff --git a/src/Tools/tnl-game-of-life.cpp b/src/Tools/tnl-game-of-life.cpp index e2f19c2ea6abb0fffcdb99c1339a6f61b62f9294..e570cd10c75e00224d3aa696b959bbc3a5e3c5eb 100644 --- a/src/Tools/tnl-game-of-life.cpp +++ b/src/Tools/tnl-game-of-life.cpp @@ -29,26 +29,26 @@ namespace BuildConfigTags { // disable all grids template< int Dimension, typename Real, typename Device, typename Index > struct GridTag< MyConfigTag, Grid< Dimension, Real, Device, Index > > -{ enum { enabled = false }; }; +{ static constexpr bool enabled = false; }; // Meshes are enabled only for topologies explicitly listed below. -//template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Edge > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Triangle > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Quadrangle > { enum { enabled = true }; }; -//template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Tetrahedron > { enum { enabled = true }; }; -//template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Hexahedron > { enum { enabled = true }; }; +//template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Edge > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Triangle > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Quadrangle > { static constexpr bool enabled = true; }; +//template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Tetrahedron > { static constexpr bool enabled = true; }; +//template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Hexahedron > { static constexpr bool enabled = true; }; // Meshes are enabled only for the space dimension equal to the cell dimension. template< typename CellTopology, int SpaceDimension > struct MeshSpaceDimensionTag< MyConfigTag, CellTopology, SpaceDimension > -{ enum { enabled = ( SpaceDimension == CellTopology::dimension ) }; }; +{ static constexpr bool enabled = SpaceDimension == CellTopology::dimension; }; // Meshes are enabled only for types explicitly listed below. -template<> struct MeshRealTag< MyConfigTag, float > { enum { enabled = true }; }; -template<> struct MeshRealTag< MyConfigTag, double > { enum { enabled = true }; }; -template<> struct MeshGlobalIndexTag< MyConfigTag, int > { enum { enabled = true }; }; -template<> struct MeshGlobalIndexTag< MyConfigTag, long int > { enum { enabled = true }; }; -template<> struct MeshLocalIndexTag< MyConfigTag, short int > { enum { enabled = true }; }; +template<> struct MeshRealTag< MyConfigTag, float > { static constexpr bool enabled = true; }; +template<> struct MeshRealTag< MyConfigTag, double > { static constexpr bool enabled = true; }; +template<> struct MeshGlobalIndexTag< MyConfigTag, int > { static constexpr bool enabled = true; }; +template<> struct MeshGlobalIndexTag< MyConfigTag, long int > { static constexpr bool enabled = true; }; +template<> struct MeshLocalIndexTag< MyConfigTag, short int > { static constexpr bool enabled = true; }; // Config tag specifying the MeshConfig template to use. template<> diff --git a/src/Tools/tnl-grid-to-mesh.cpp b/src/Tools/tnl-grid-to-mesh.cpp index 193f606206644340fecb791ca1d4df3bb25ae0d8..34d917838807d3db7016b56045d28fbc0ca16127 100644 --- a/src/Tools/tnl-grid-to-mesh.cpp +++ b/src/Tools/tnl-grid-to-mesh.cpp @@ -25,14 +25,14 @@ namespace BuildConfigTags { /**** * Turn off support for float and long double. */ -template<> struct GridRealTag< GridToMeshConfigTag, float > { enum { enabled = false }; }; -template<> struct GridRealTag< GridToMeshConfigTag, long double > { enum { enabled = false }; }; +template<> struct GridRealTag< GridToMeshConfigTag, float > { static constexpr bool enabled = false; }; +template<> struct GridRealTag< GridToMeshConfigTag, long double > { static constexpr bool enabled = false; }; /**** * Turn off support for short int and long int indexing. */ -template<> struct GridIndexTag< GridToMeshConfigTag, short int >{ enum { enabled = false }; }; -template<> struct GridIndexTag< GridToMeshConfigTag, long int >{ enum { enabled = false }; }; +template<> struct GridIndexTag< GridToMeshConfigTag, short int >{ static constexpr bool enabled = false; }; +template<> struct GridIndexTag< GridToMeshConfigTag, long int >{ static constexpr bool enabled = false; }; /**** * Unstructured meshes are disabled, only grids can be on input. @@ -175,7 +175,7 @@ struct MeshCreator< Meshes::Grid< 3, Real, Device, Index > > }; template< typename Grid > -bool convertGrid( Grid& grid, const String& outputFileName, const String& outputFormat ) +bool convertGrid( Grid& grid, const std::string& outputFileName, const std::string& outputFormat ) { using MeshCreator = MeshCreator< Grid >; using Mesh = typename MeshCreator::MeshType; @@ -186,37 +186,55 @@ bool convertGrid( Grid& grid, const String& outputFileName, const String& output return false; } - if( outputFormat == "vtk" ) { + std::string format = outputFormat; + if( outputFormat == "auto" ) { + namespace fs = std::experimental::filesystem; + format = fs::path( outputFileName ).extension(); + if( format.length() > 0 ) + // remove dot from the extension + format = format.substr(1); + } + + if( format == "vtk" ) { using VTKWriter = Meshes::Writers::VTKWriter< Mesh >; - std::ofstream file( outputFileName.getString() ); + std::ofstream file( outputFileName ); VTKWriter writer( file ); writer.template writeEntities< Mesh::getMeshDimension() >( mesh ); + return true; } - else if( outputFormat == "vtu" ) { + if( format == "vtu" ) { using VTKWriter = Meshes::Writers::VTUWriter< Mesh >; - std::ofstream file( outputFileName.getString() ); + std::ofstream file( outputFileName ); VTKWriter writer( file ); writer.template writeEntities< Mesh::getMeshDimension() >( mesh ); + return true; } - else if( outputFormat == "netgen" ) { + if( format == "ng" ) { using NetgenWriter = Meshes::Writers::NetgenWriter< Mesh >; - std::fstream file( outputFileName.getString() ); + std::fstream file( outputFileName ); NetgenWriter::writeMesh( mesh, file ); + return true; } - return true; + if( outputFormat == "auto" ) + std::cerr << "File '" << outputFileName << "' has unsupported format (based on the file extension): " << format << "."; + else + std::cerr << "Unsupported output file format: " << outputFormat << "."; + std::cerr << " Supported formats are 'vtk', 'vtu' and 'ng'." << std::endl; + return false; } 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 path." ); - config.addRequiredEntry< String >( "output-file-format", "Output mesh file format." ); - config.addEntryEnum( "tnl" ); + config.addRequiredEntry< std::string >( "input-file", "Input file with the mesh." ); + config.addEntry< std::string >( "input-file-format", "Input mesh file format.", "auto" ); + config.addRequiredEntry< std::string >( "output-file", "Output mesh file path." ); + config.addEntry< std::string >( "output-file-format", "Output mesh file format.", "auto" ); + config.addEntryEnum( "auto" ); config.addEntryEnum( "vtk" ); config.addEntryEnum( "vtu" ); - config.addEntryEnum( "netgen" ); + config.addEntryEnum( "ng" ); } int @@ -230,13 +248,15 @@ main( int argc, char* argv[] ) if( ! parseCommandLine( argc, argv, conf_desc, parameters ) ) return EXIT_FAILURE; - 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" ); + const std::string inputFileName = parameters.getParameter< std::string >( "input-file" ); + const std::string inputFileFormat = parameters.getParameter< std::string >( "input-file-format" ); + const std::string outputFileName = parameters.getParameter< std::string >( "output-file" ); + const std::string outputFileFormat = parameters.getParameter< std::string >( "output-file-format" ); auto wrapper = [&] ( const auto& reader, auto&& grid ) { return convertGrid( grid, outputFileName, outputFileFormat ); }; - return ! Meshes::resolveAndLoadMesh< GridToMeshConfigTag, Devices::Host >( wrapper, inputFileName ); + const bool status = Meshes::resolveAndLoadMesh< GridToMeshConfigTag, Devices::Host >( wrapper, inputFileName, inputFileFormat ); + return static_cast< int >( ! status ); } diff --git a/src/Tools/tnl-init.cpp b/src/Tools/tnl-init.cpp index b1f2626d9c880587fb9db952f7aa25daf027ed09..645451bc68a1ccbb1e6f14be34bd6beb0045d857 100644 --- a/src/Tools/tnl-init.cpp +++ b/src/Tools/tnl-init.cpp @@ -27,14 +27,14 @@ namespace Meshes { namespace BuildConfigTags { // Configure real types -template<> struct GridRealTag< TnlInitConfigTag, float > { enum { enabled = true }; }; -template<> struct GridRealTag< TnlInitConfigTag, double > { enum { enabled = true }; }; -template<> struct GridRealTag< TnlInitConfigTag, long double > { enum { enabled = false }; }; +template<> struct GridRealTag< TnlInitConfigTag, float > { static constexpr bool enabled = true; }; +template<> struct GridRealTag< TnlInitConfigTag, double > { static constexpr bool enabled = true; }; +template<> struct GridRealTag< TnlInitConfigTag, long double > { static constexpr bool enabled = false; }; // Configure index types -template<> struct GridIndexTag< TnlInitConfigTag, short int >{ enum { enabled = false }; }; -template<> struct GridIndexTag< TnlInitConfigTag, int >{ enum { enabled = true }; }; -template<> struct GridIndexTag< TnlInitConfigTag, long int >{ enum { enabled = true }; }; +template<> struct GridIndexTag< TnlInitConfigTag, short int >{ static constexpr bool enabled = false; }; +template<> struct GridIndexTag< TnlInitConfigTag, int >{ static constexpr bool enabled = true; }; +template<> struct GridIndexTag< TnlInitConfigTag, long int >{ static constexpr bool enabled = true; }; // Unstructured meshes are disabled, only grids can be on input. diff --git a/src/Tools/tnl-mesh-converter.cpp b/src/Tools/tnl-mesh-converter.cpp index b97639118b740e8db9169c872ca44d6706d225e3..6275e1e8e63e5bea30eb42b4b1857857919097be 100644 --- a/src/Tools/tnl-mesh-converter.cpp +++ b/src/Tools/tnl-mesh-converter.cpp @@ -10,10 +10,11 @@ #include <TNL/Config/parseCommandLine.h> #include <TNL/Meshes/TypeResolver/resolveMeshType.h> +#include <TNL/Meshes/Writers/FPMAWriter.h> #include <TNL/Meshes/Writers/VTKWriter.h> #include <TNL/Meshes/Writers/VTUWriter.h> -//#include <TNL/Meshes/Writers/VTIWriter.h> -//#include <TNL/Meshes/Writers/NetgenWriter.h> +#include <TNL/Meshes/Writers/VTIWriter.h> +#include <TNL/Meshes/Writers/NetgenWriter.h> using namespace TNL; @@ -26,38 +27,39 @@ namespace BuildConfigTags { /**** * Turn off support for float and long double. */ -template<> struct GridRealTag< MeshConverterConfigTag, float > { enum { enabled = false }; }; -template<> struct GridRealTag< MeshConverterConfigTag, long double > { enum { enabled = false }; }; +template<> struct GridRealTag< MeshConverterConfigTag, float > { static constexpr bool enabled = false; }; +template<> struct GridRealTag< MeshConverterConfigTag, long double > { static constexpr bool enabled = false; }; /**** * Turn off support for short int and long int indexing. */ -template<> struct GridIndexTag< MeshConverterConfigTag, short int >{ enum { enabled = false }; }; -template<> struct GridIndexTag< MeshConverterConfigTag, long int >{ enum { enabled = false }; }; +template<> struct GridIndexTag< MeshConverterConfigTag, short int >{ static constexpr bool enabled = false; }; +template<> struct GridIndexTag< MeshConverterConfigTag, long int >{ static constexpr bool enabled = false; }; /**** * Unstructured meshes. */ -template<> struct MeshCellTopologyTag< MeshConverterConfigTag, Topologies::Edge > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MeshConverterConfigTag, Topologies::Triangle > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MeshConverterConfigTag, Topologies::Quadrangle > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MeshConverterConfigTag, Topologies::Polygon > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MeshConverterConfigTag, Topologies::Tetrahedron > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MeshConverterConfigTag, Topologies::Hexahedron > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MeshConverterConfigTag, Topologies::Wedge > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MeshConverterConfigTag, Topologies::Pyramid > { enum { enabled = true }; }; +template<> struct MeshCellTopologyTag< MeshConverterConfigTag, Topologies::Edge > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MeshConverterConfigTag, Topologies::Triangle > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MeshConverterConfigTag, Topologies::Quadrangle > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MeshConverterConfigTag, Topologies::Polygon > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MeshConverterConfigTag, Topologies::Tetrahedron > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MeshConverterConfigTag, Topologies::Hexahedron > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MeshConverterConfigTag, Topologies::Wedge > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MeshConverterConfigTag, Topologies::Pyramid > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MeshConverterConfigTag, Topologies::Polyhedron > { static constexpr bool enabled = true; }; // Meshes are enabled only for the space dimension equal to the cell dimension. template< typename CellTopology, int SpaceDimension > struct MeshSpaceDimensionTag< MeshConverterConfigTag, CellTopology, SpaceDimension > -{ enum { enabled = ( SpaceDimension == CellTopology::dimension ) }; }; +{ static constexpr bool enabled = SpaceDimension == CellTopology::dimension; }; // Meshes are enabled only for types explicitly listed below. -template<> struct MeshRealTag< MeshConverterConfigTag, float > { enum { enabled = true }; }; -template<> struct MeshRealTag< MeshConverterConfigTag, double > { enum { enabled = true }; }; -template<> struct MeshGlobalIndexTag< MeshConverterConfigTag, int > { enum { enabled = true }; }; -template<> struct MeshGlobalIndexTag< MeshConverterConfigTag, long int > { enum { enabled = true }; }; -template<> struct MeshLocalIndexTag< MeshConverterConfigTag, short int > { enum { enabled = true }; }; +template<> struct MeshRealTag< MeshConverterConfigTag, float > { static constexpr bool enabled = true; }; +template<> struct MeshRealTag< MeshConverterConfigTag, double > { static constexpr bool enabled = true; }; +template<> struct MeshGlobalIndexTag< MeshConverterConfigTag, int > { static constexpr bool enabled = true; }; +template<> struct MeshGlobalIndexTag< MeshConverterConfigTag, long int > { static constexpr bool enabled = true; }; +template<> struct MeshLocalIndexTag< MeshConverterConfigTag, short int > { static constexpr bool enabled = true; }; // Config tag specifying the MeshConfig template to use. template<> @@ -68,19 +70,18 @@ struct MeshConfigTemplateTag< MeshConverterConfigTag > typename Real = double, typename GlobalIndex = int, typename LocalIndex = GlobalIndex > - struct MeshConfig + struct MeshConfig : public DefaultConfig< Cell, SpaceDimension, Real, GlobalIndex, LocalIndex > { - using CellTopology = Cell; - using RealType = Real; - using GlobalIndexType = GlobalIndex; - using LocalIndexType = LocalIndex; - - static constexpr int spaceDimension = SpaceDimension; - static constexpr int meshDimension = Cell::dimension; - static constexpr bool subentityStorage( int entityDimension, int subentityDimension ) { - return subentityDimension == 0 && entityDimension == meshDimension; + // faces must be stored for polyhedral meshes + if( std::is_same< Cell, TNL::Meshes::Topologies::Polyhedron >::value ) { + if( subentityDimension == 0 && entityDimension == Cell::dimension - 1 ) + return true; + if( subentityDimension == Cell::dimension - 1 && entityDimension == Cell::dimension ) + return true; + } + return subentityDimension == 0 && entityDimension == Cell::dimension; } static constexpr bool superentityStorage( int entityDimension, int superentityDimension ) @@ -104,50 +105,130 @@ struct MeshConfigTemplateTag< MeshConverterConfigTag > } // namespace Meshes } // namespace TNL +// specialization for polyhedral meshes +template< typename Mesh, + std::enable_if_t< std::is_same< typename Mesh::Cell::EntityTopology, TNL::Meshes::Topologies::Polyhedron >::value, bool > = true > +bool writeMesh( const Mesh& mesh, std::ostream& out, const std::string& format ) +{ + if( format == "fpma" ) { + using Writer = Meshes::Writers::FPMAWriter< Mesh >; + Writer writer( out ); + writer.writeEntities( mesh ); + return true; + } + if( format == "vtk" ) { + using Writer = Meshes::Writers::VTKWriter< Mesh >; + Writer writer( out ); + writer.template writeEntities< Mesh::getMeshDimension() >( mesh ); + return true; + } + if( format == "vtu" ) { + using Writer = Meshes::Writers::VTUWriter< Mesh >; + Writer writer( out ); + writer.template writeEntities< Mesh::getMeshDimension() >( mesh ); + return true; + } + return false; +} -template< typename Mesh > -bool convertMesh( const Mesh& mesh, const String& inputFileName, const String& outputFileName, const String& outputFormat ) +// specialization for unstructured meshes except polyhedral +template< typename Mesh, + std::enable_if_t< ! std::is_same< typename Mesh::Cell::EntityTopology, TNL::Meshes::Topologies::Polyhedron >::value, bool > = true > +bool writeMesh( const Mesh& mesh, std::ostream& out, const std::string& format ) +{ + if( format == "vtk" ) { + using Writer = Meshes::Writers::VTKWriter< Mesh >; + Writer writer( out ); + writer.template writeEntities< Mesh::getMeshDimension() >( mesh ); + return true; + } + if( format == "vtu" ) { + using Writer = Meshes::Writers::VTUWriter< Mesh >; + Writer writer( out ); + writer.template writeEntities< Mesh::getMeshDimension() >( mesh ); + return true; + } + if( format == "ng" ) { + using NetgenWriter = Meshes::Writers::NetgenWriter< Mesh >; + NetgenWriter::writeMesh( mesh, out ); + return true; + } + return false; +} + +// specialization for grids +template< int Dimension, typename Real, typename Device, typename Index > +bool writeMesh( const TNL::Meshes::Grid< Dimension, Real, Device, Index >& mesh, + std::ostream& out, + const std::string& format ) { - if( outputFormat == "vtk" ) { + using Mesh = TNL::Meshes::Grid< Dimension, Real, Device, Index >; + if( format == "vtk" ) { using Writer = Meshes::Writers::VTKWriter< Mesh >; - std::ofstream file( outputFileName ); - Writer writer( file ); + Writer writer( out ); writer.template writeEntities< Mesh::getMeshDimension() >( mesh ); + return true; } - else if( outputFormat == "vtu" ) { + if( format == "vtu" ) { using Writer = Meshes::Writers::VTUWriter< Mesh >; - std::ofstream file( outputFileName ); - Writer writer( file ); + Writer writer( out ); writer.template writeEntities< Mesh::getMeshDimension() >( mesh ); + return true; + } + if( format == "vti" ) { + using Writer = Meshes::Writers::VTIWriter< Mesh >; + Writer writer( out ); + writer.writeImageData( mesh ); + return true; } - // FIXME: VTIWriter is not specialized for meshes -// else if( outputFormat == "vti" ) { -// using Writer = Meshes::Writers::VTIWriter< Mesh >; -// std::ofstream file( outputFileName ); -// Writer writer( file ); -// writer.writeImageData( mesh ); -// } - // FIXME: NetgenWriter is not specialized for grids -// else if( outputFormat == "netgen" ) { -// using NetgenWriter = Meshes::Writers::NetgenWriter< Mesh >; -// std::fstream file( outputFileName ); -// NetgenWriter::writeMesh( mesh, file ); -// } - - return true; + return false; +} + +template< typename Mesh > +bool convertMesh( const Mesh& mesh, const std::string& inputFileName, const std::string& outputFileName, const std::string& outputFormat ) +{ + std::string format = outputFormat; + if( outputFormat == "auto" ) { + namespace fs = std::experimental::filesystem; + format = fs::path( outputFileName ).extension(); + if( format.length() > 0 ) + // remove dot from the extension + format = format.substr(1); + } + + std::ofstream file( outputFileName ); + if( writeMesh( mesh, file, format ) ) + return true; + + if( outputFormat == "auto" ) + std::cerr << "File '" << outputFileName << "' has unsupported format (based on the file extension): " << format << "."; + else + std::cerr << "Unsupported output file format: " << outputFormat << "."; + std::cerr << " Supported formats are 'vtk', 'vtu', 'vti' (only grids), 'ng' (only static unstructured meshes) and 'fpma' (only polyhedral meshes)." << std::endl; + return false; } 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 path." ); - config.addRequiredEntry< String >( "output-file-format", "Output mesh file format." ); + config.addRequiredEntry< std::string >( "input-file", "Input file with the mesh." ); + config.addEntry< std::string >( "input-file-format", "Input mesh file format.", "auto" ); + config.addEntry< std::string >( "real-type", "Type to use for the representation of spatial coordinates in the output mesh. When 'auto', the real type from the input mesh is used.", "auto" ); + config.addEntryEnum( "auto" ); + config.addEntryEnum( "float" ); + config.addEntryEnum( "double" ); + config.addEntry< std::string >( "global-index-type", "Type to use for the representation of global indices in the output mesh. When 'auto', the global index type from the input mesh is used.", "auto" ); + config.addEntryEnum( "auto" ); + config.addEntryEnum( "std::int32_t" ); + config.addEntryEnum( "std::int64_t" ); + config.addRequiredEntry< std::string >( "output-file", "Output mesh file path." ); + config.addEntry< std::string >( "output-file-format", "Output mesh file format.", "auto" ); + config.addEntryEnum( "auto" ); config.addEntryEnum( "vtk" ); config.addEntryEnum( "vtu" ); -// config.addEntryEnum( "vti" ); -// config.addEntryEnum( "netgen" ); + config.addEntryEnum( "vti" ); + config.addEntryEnum( "ng" ); + config.addEntryEnum( "fpma" ); } int main( int argc, char* argv[] ) @@ -160,14 +241,17 @@ int main( int argc, char* argv[] ) if( ! parseCommandLine( argc, argv, conf_desc, parameters ) ) return EXIT_FAILURE; - const String inputFileName = parameters.getParameter< String >( "input-file" ); - const String inputFileFormat = parameters.getParameter< String >( "input-file-format" ); - const String outputFileName = parameters.getParameter< String >( "output-file" ); - const String outputFileFormat = parameters.getParameter< String >( "output-file-format" ); + const std::string inputFileName = parameters.getParameter< std::string >( "input-file" ); + const std::string inputFileFormat = parameters.getParameter< std::string >( "input-file-format" ); + const std::string realType = parameters.getParameter< std::string >( "real-type" ); + const std::string globalIndexType = parameters.getParameter< std::string >( "global-index-type" ); + const std::string outputFileName = parameters.getParameter< std::string >( "output-file" ); + const std::string outputFileFormat = parameters.getParameter< std::string >( "output-file-format" ); auto wrapper = [&] ( auto& reader, auto&& mesh ) -> bool { return convertMesh( mesh, inputFileName, outputFileName, outputFileFormat ); }; - return ! Meshes::resolveAndLoadMesh< MeshConverterConfigTag, Devices::Host >( wrapper, inputFileName, inputFileFormat ); + const bool status = Meshes::resolveAndLoadMesh< MeshConverterConfigTag, Devices::Host >( wrapper, inputFileName, inputFileFormat, realType, globalIndexType ); + return static_cast< int >( ! status ); } diff --git a/src/Tools/tnl-planar-correct-mesh.cpp b/src/Tools/tnl-planar-correct-mesh.cpp index c4802b66ada8ee204cca6c25bf75c5756a53612c..9ad689b92812b3bd23a2036450dc1f790b87f8f7 100644 --- a/src/Tools/tnl-planar-correct-mesh.cpp +++ b/src/Tools/tnl-planar-correct-mesh.cpp @@ -1,6 +1,7 @@ #include <TNL/Config/parseCommandLine.h> #include <TNL/Meshes/TypeResolver/resolveMeshType.h> #include <TNL/Meshes/Writers/VTKWriter.h> +#include <TNL/Meshes/Writers/VTUWriter.h> #include <TNL/Meshes/Writers/FPMAWriter.h> #include <TNL/Meshes/Geometry/getPlanarMesh.h> @@ -15,27 +16,27 @@ namespace BuildConfigTags { /**** * Turn off all grids. */ -template<> struct GridRealTag< MeshPlanarCorrectConfigTag, float > { enum { enabled = false }; }; -template<> struct GridRealTag< MeshPlanarCorrectConfigTag, double > { enum { enabled = false }; }; -template<> struct GridRealTag< MeshPlanarCorrectConfigTag, long double > { enum { enabled = false }; }; +template<> struct GridRealTag< MeshPlanarCorrectConfigTag, float > { static constexpr bool enabled = false; }; +template<> struct GridRealTag< MeshPlanarCorrectConfigTag, double > { static constexpr bool enabled = false; }; +template<> struct GridRealTag< MeshPlanarCorrectConfigTag, long double > { static constexpr bool enabled = false; }; /**** * Unstructured meshes. */ -template<> struct MeshCellTopologyTag< MeshPlanarCorrectConfigTag, Topologies::Polygon > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MeshPlanarCorrectConfigTag, Topologies::Polyhedron > { enum { enabled = true }; }; +template<> struct MeshCellTopologyTag< MeshPlanarCorrectConfigTag, Topologies::Polygon > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MeshPlanarCorrectConfigTag, Topologies::Polyhedron > { static constexpr bool enabled = true; }; // Meshes are enabled only for the space dimension equal to 3 template< typename CellTopology, int SpaceDimension > struct MeshSpaceDimensionTag< MeshPlanarCorrectConfigTag, CellTopology, SpaceDimension > -{ enum { enabled = ( SpaceDimension == 3 ) }; }; +{ static constexpr bool enabled = SpaceDimension == 3; }; // Meshes are enabled only for types explicitly listed below. -template<> struct MeshRealTag< MeshPlanarCorrectConfigTag, float > { enum { enabled = true }; }; -template<> struct MeshRealTag< MeshPlanarCorrectConfigTag, double > { enum { enabled = true }; }; -template<> struct MeshGlobalIndexTag< MeshPlanarCorrectConfigTag, long int > { enum { enabled = true }; }; -template<> struct MeshGlobalIndexTag< MeshPlanarCorrectConfigTag, int > { enum { enabled = true }; }; -template<> struct MeshLocalIndexTag< MeshPlanarCorrectConfigTag, short int > { enum { enabled = true }; }; +template<> struct MeshRealTag< MeshPlanarCorrectConfigTag, float > { static constexpr bool enabled = true; }; +template<> struct MeshRealTag< MeshPlanarCorrectConfigTag, double > { static constexpr bool enabled = true; }; +template<> struct MeshGlobalIndexTag< MeshPlanarCorrectConfigTag, long int > { static constexpr bool enabled = true; }; +template<> struct MeshGlobalIndexTag< MeshPlanarCorrectConfigTag, int > { static constexpr bool enabled = true; }; +template<> struct MeshLocalIndexTag< MeshPlanarCorrectConfigTag, short int > { static constexpr bool enabled = true; }; // Config tag specifying the MeshConfig template to use. template<> @@ -87,7 +88,7 @@ struct MeshConfigTemplateTag< MeshPlanarCorrectConfigTag > using namespace TNL::Meshes; template< typename Mesh > -auto getPlanarMeshHelper( const Mesh& mesh, const String& decompositionType ) +auto getPlanarMeshHelper( const Mesh& mesh, const std::string& decompositionType ) { using namespace TNL::Meshes; @@ -106,12 +107,38 @@ template<> struct PlanarMeshWriter< Topologies::Polygon > { template< typename Mesh > - static void exec( const Mesh& mesh, const String& outputFileName ) + static bool exec( const Mesh& mesh, const std::string& outputFileName, const std::string& outputFormat ) { - using Writer = Meshes::Writers::VTKWriter< Mesh >; - std::ofstream file( outputFileName ); - Writer writer( file ); - writer.template writeEntities< Mesh::getMeshDimension() >( mesh ); + std::string format = outputFormat; + if( outputFormat == "auto" ) { + namespace fs = std::experimental::filesystem; + format = fs::path( outputFileName ).extension(); + if( format.length() > 0 ) + // remove dot from the extension + format = format.substr(1); + } + + if( format == "vtk" ) { + using Writer = Meshes::Writers::VTKWriter< Mesh >; + std::ofstream file( outputFileName ); + Writer writer( file ); + writer.template writeEntities< Mesh::getMeshDimension() >( mesh ); + return true; + } + if( format == "vtu" ) { + using Writer = Meshes::Writers::VTUWriter< Mesh >; + std::ofstream file( outputFileName ); + Writer writer( file ); + writer.template writeEntities< Mesh::getMeshDimension() >( mesh ); + return true; + } + + if( outputFormat == "auto" ) + std::cerr << "File '" << outputFileName << "' has unsupported format (based on the file extension): " << format << "."; + else + std::cerr << "Unsupported output file format: " << outputFormat << "."; + std::cerr << " Supported formats are 'vtk' and 'vtu'." << std::endl; + return false; } }; @@ -119,31 +146,38 @@ template<> struct PlanarMeshWriter< Topologies::Polyhedron > { template< typename Mesh > - static void exec( const Mesh& mesh, const String& outputFileName ) + static bool exec( const Mesh& mesh, const std::string& outputFileName, const std::string& outputFormat ) { + if( outputFormat != "auto" && outputFormat != "fpma" ) { + std::cerr << "Unsupported output file format: " << outputFormat << ". Only 'fpma' is supported for polyhedral meshes." << std::endl; + return false; + } + using Writer = Meshes::Writers::FPMAWriter< Mesh >; std::ofstream file( outputFileName ); Writer writer( file ); writer.writeEntities( mesh ); + return true; } }; template< typename Mesh > -bool triangulateMesh( const Mesh& mesh, const String& outputFileName, const String& decompositionType ) +bool triangulateMesh( const Mesh& mesh, const std::string& outputFileName, const std::string& outputFormat, const std::string& decompositionType ) { const auto planarMesh = getPlanarMeshHelper( mesh, decompositionType ); using PlanarMesh = decltype( planarMesh ); using CellTopology = typename PlanarMesh::Cell::EntityTopology; - PlanarMeshWriter< CellTopology >::exec( planarMesh, outputFileName ); - return true; + return PlanarMeshWriter< CellTopology >::exec( planarMesh, outputFileName, outputFormat ); } 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 path." ); - config.addRequiredEntry< String >( "decomposition-type", "Type of decomposition to use for non-planar polygons." ); + config.addRequiredEntry< std::string >( "input-file", "Input file with the mesh." ); + config.addEntry< std::string >( "input-file-format", "Input mesh file format.", "auto" ); + config.addRequiredEntry< std::string >( "output-file", "Output mesh file path." ); + config.addEntry< std::string >( "output-file-format", "Output mesh file format.", "auto" ); + config.addRequiredEntry< std::string >( "decomposition-type", "Type of decomposition to use for non-planar polygons." ); config.addEntryEnum( "c" ); config.addEntryEnum( "p" ); } @@ -158,14 +192,15 @@ int main( int argc, char* argv[] ) if( ! parseCommandLine( argc, argv, conf_desc, parameters ) ) return EXIT_FAILURE; - const String inputFileName = parameters.getParameter< String >( "input-file" ); - const String inputFileFormat = "auto"; - const String outputFileName = parameters.getParameter< String >( "output-file" ); - const String decompositionType = parameters.getParameter< String >( "decomposition-type" ); + const std::string inputFileName = parameters.getParameter< std::string >( "input-file" ); + const std::string inputFileFormat = parameters.getParameter< std::string >( "input-file-format" ); + const std::string outputFileName = parameters.getParameter< std::string >( "output-file" ); + const std::string outputFileFormat = parameters.getParameter< std::string >( "output-file-format" ); + const std::string decompositionType = parameters.getParameter< std::string >( "decomposition-type" ); auto wrapper = [&] ( auto& reader, auto&& mesh ) -> bool { - return triangulateMesh( mesh, outputFileName, decompositionType ); + return triangulateMesh( mesh, outputFileName, outputFileFormat, decompositionType ); }; return ! Meshes::resolveAndLoadMesh< MeshPlanarCorrectConfigTag, Devices::Host >( wrapper, inputFileName, inputFileFormat ); } diff --git a/src/Tools/tnl-refine-mesh.cpp b/src/Tools/tnl-refine-mesh.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b69bc1f78fb56b805c943046b07042f24b7ad84e --- /dev/null +++ b/src/Tools/tnl-refine-mesh.cpp @@ -0,0 +1,174 @@ +#include <TNL/Config/parseCommandLine.h> +#include <TNL/Meshes/TypeResolver/resolveMeshType.h> +#include <TNL/Meshes/Writers/VTKWriter.h> +#include <TNL/Meshes/Writers/VTUWriter.h> +#include <TNL/Meshes/Geometry/getRefinedMesh.h> + +using namespace TNL; + +struct MeshRefineConfigTag {}; + +namespace TNL { +namespace Meshes { +namespace BuildConfigTags { + +/**** + * Turn off all grids. + */ +template<> struct GridRealTag< MeshRefineConfigTag, float > { static constexpr bool enabled = false; }; +template<> struct GridRealTag< MeshRefineConfigTag, double > { static constexpr bool enabled = false; }; +template<> struct GridRealTag< MeshRefineConfigTag, long double > { static constexpr bool enabled = false; }; + +/**** + * Unstructured meshes. + */ +template<> struct MeshCellTopologyTag< MeshRefineConfigTag, Topologies::Triangle > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MeshRefineConfigTag, Topologies::Quadrangle > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MeshRefineConfigTag, Topologies::Tetrahedron > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MeshRefineConfigTag, Topologies::Hexahedron > { static constexpr bool enabled = true; }; + +// Meshes are enabled only for the space dimension equal to the cell dimension. +template< typename CellTopology, int SpaceDimension > +struct MeshSpaceDimensionTag< MeshRefineConfigTag, CellTopology, SpaceDimension > +{ static constexpr bool enabled = SpaceDimension == CellTopology::dimension; }; + +// Meshes are enabled only for types explicitly listed below. +template<> struct MeshRealTag< MeshRefineConfigTag, float > { static constexpr bool enabled = true; }; +template<> struct MeshRealTag< MeshRefineConfigTag, double > { static constexpr bool enabled = true; }; +template<> struct MeshGlobalIndexTag< MeshRefineConfigTag, long int > { static constexpr bool enabled = true; }; +template<> struct MeshGlobalIndexTag< MeshRefineConfigTag, int > { static constexpr bool enabled = true; }; +template<> struct MeshLocalIndexTag< MeshRefineConfigTag, short int > { static constexpr bool enabled = true; }; + +// Config tag specifying the MeshConfig template to use. +template<> +struct MeshConfigTemplateTag< MeshRefineConfigTag > +{ + template< typename Cell, + int SpaceDimension = Cell::dimension, + typename Real = float, + typename GlobalIndex = int, + typename LocalIndex = short int > + struct MeshConfig : public DefaultConfig< Cell, SpaceDimension, Real, GlobalIndex, LocalIndex > + { + static constexpr bool subentityStorage( int entityDimension, int subentityDimension ) + { + return subentityDimension == 0 && entityDimension == Cell::dimension; + } + + static constexpr bool superentityStorage( int entityDimension, int superentityDimension ) + { + return false; + } + + static constexpr bool entityTagsStorage( int entityDimension ) + { + return false; + } + + static constexpr bool dualGraphStorage() + { + return false; + } + }; +}; + +} // namespace BuildConfigTags +} // namespace Meshes +} // namespace TNL + +template< typename Mesh > +Mesh getRefinedMeshHelper( const Mesh& mesh, const std::string& decompositionType ) +{ + using namespace TNL::Meshes; + return getRefinedMesh< EntityRefinerVersion::EdgeBisection >( mesh ); +} + +template< typename Mesh > +bool refineMesh( Mesh& mesh, const std::string& outputFileName, const std::string& outputFormat, const std::string& decompositionType, int iterations ) +{ + for( int i = 1; i <= iterations; i++ ) { + std::cout << "Refining mesh (iteration " << i << ")" << std::endl; + mesh = getRefinedMeshHelper( mesh, decompositionType ); + } + + std::string format = outputFormat; + if( outputFormat == "auto" ) { + namespace fs = std::experimental::filesystem; + format = fs::path( outputFileName ).extension(); + if( format.length() > 0 ) + // remove dot from the extension + format = format.substr(1); + } + + if( format == "vtk" ) { + using Writer = Meshes::Writers::VTKWriter< Mesh >; + std::ofstream file( outputFileName ); + Writer writer( file ); + writer.template writeEntities< Mesh::getMeshDimension() >( mesh ); + return true; + } + if( format == "vtu" ) { + using Writer = Meshes::Writers::VTUWriter< Mesh >; + std::ofstream file( outputFileName ); + Writer writer( file ); + writer.template writeEntities< Mesh::getMeshDimension() >( mesh ); + return true; + } + + if( outputFormat == "auto" ) + std::cerr << "File '" << outputFileName << "' has unsupported format (based on the file extension): " << format << "."; + else + std::cerr << "Unsupported output file format: " << outputFormat << "."; + std::cerr << " Supported formats are 'vtk' and 'vtu'." << std::endl; + return false; +} + +void configSetup( Config::ConfigDescription& config ) +{ + config.addDelimiter( "General settings:" ); + config.addRequiredEntry< std::string >( "input-file", "Input file with the mesh." ); + config.addEntry< std::string >( "input-file-format", "Input mesh file format.", "auto" ); + config.addEntry< std::string >( "real-type", "Type to use for the representation of spatial coordinates in the output mesh. When 'auto', the real type from the input mesh is used.", "auto" ); + config.addEntryEnum( "auto" ); + config.addEntryEnum( "float" ); + config.addEntryEnum( "double" ); + config.addEntry< std::string >( "global-index-type", "Type to use for the representation of global indices in the output mesh. When 'auto', the global index type from the input mesh is used.", "auto" ); + config.addEntryEnum( "auto" ); + config.addEntryEnum( "std::int32_t" ); + config.addEntryEnum( "std::int64_t" ); + config.addRequiredEntry< std::string >( "output-file", "Output mesh file path." ); + config.addEntry< std::string >( "output-file-format", "Output mesh file format.", "auto" ); + config.addEntryEnum( "auto" ); + config.addEntryEnum( "vtk" ); + config.addEntryEnum( "vtu" ); + config.addEntry< std::string >( "decomposition-type", "Type of decomposition to use.", "edge-bisection" ); + config.addEntryEnum( "edge-bisection" ); + config.addEntry< int >( "iterations", "Number of mesh refinement iterations.", 1 ); +} + +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 std::string inputFileName = parameters.getParameter< std::string >( "input-file" ); + const std::string inputFileFormat = parameters.getParameter< std::string >( "input-file-format" ); + const std::string realType = parameters.getParameter< std::string >( "real-type" ); + const std::string globalIndexType = parameters.getParameter< std::string >( "global-index-type" ); + const std::string outputFileName = parameters.getParameter< std::string >( "output-file" ); + const std::string outputFileFormat = parameters.getParameter< std::string >( "output-file-format" ); + const std::string decompositionType = parameters.getParameter< std::string >( "decomposition-type" ); + const int iterations = parameters.getParameter< int >( "iterations" ); + + auto wrapper = [&] ( auto& reader, auto&& mesh ) -> bool + { + return refineMesh( mesh, outputFileName, outputFileFormat, decompositionType, iterations ); + }; + const bool status = Meshes::resolveAndLoadMesh< MeshRefineConfigTag, Devices::Host >( wrapper, inputFileName, inputFileFormat, realType, globalIndexType ); + return static_cast< int >( ! status ); +} diff --git a/src/Tools/tnl-test-distributed-mesh.h b/src/Tools/tnl-test-distributed-mesh.h index 47e3266767035b8f6288d994852bb18f671e95e6..ddbe71100694285db0f45f7df19b06450c4b0393 100644 --- a/src/Tools/tnl-test-distributed-mesh.h +++ b/src/Tools/tnl-test-distributed-mesh.h @@ -31,26 +31,26 @@ namespace BuildConfigTags { // disable all grids template< int Dimension, typename Real, typename Device, typename Index > struct GridTag< MyConfigTag, Grid< Dimension, Real, Device, Index > > -{ enum { enabled = false }; }; +{ static constexpr bool enabled = false; }; // Meshes are enabled only for topologies explicitly listed below. -//template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Edge > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Triangle > { enum { enabled = true }; }; -//template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Quadrangle > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Tetrahedron > { enum { enabled = true }; }; -//template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Hexahedron > { enum { enabled = true }; }; +//template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Edge > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Triangle > { static constexpr bool enabled = true; }; +//template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Quadrangle > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Tetrahedron > { static constexpr bool enabled = true; }; +//template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Hexahedron > { static constexpr bool enabled = true; }; // Meshes are enabled only for the space dimension equal to the cell dimension. template< typename CellTopology, int SpaceDimension > struct MeshSpaceDimensionTag< MyConfigTag, CellTopology, SpaceDimension > -{ enum { enabled = ( SpaceDimension == CellTopology::dimension ) }; }; +{ static constexpr bool enabled = SpaceDimension == CellTopology::dimension; }; // Meshes are enabled only for types explicitly listed below. -template<> struct MeshRealTag< MyConfigTag, float > { enum { enabled = true }; }; -template<> struct MeshRealTag< MyConfigTag, double > { enum { enabled = true }; }; -template<> struct MeshGlobalIndexTag< MyConfigTag, int > { enum { enabled = true }; }; -template<> struct MeshGlobalIndexTag< MyConfigTag, long int > { enum { enabled = true }; }; -template<> struct MeshLocalIndexTag< MyConfigTag, short int > { enum { enabled = true }; }; +template<> struct MeshRealTag< MyConfigTag, float > { static constexpr bool enabled = true; }; +template<> struct MeshRealTag< MyConfigTag, double > { static constexpr bool enabled = true; }; +template<> struct MeshGlobalIndexTag< MyConfigTag, int > { static constexpr bool enabled = true; }; +template<> struct MeshGlobalIndexTag< MyConfigTag, long int > { static constexpr bool enabled = true; }; +template<> struct MeshLocalIndexTag< MyConfigTag, short int > { static constexpr bool enabled = true; }; // Config tag specifying the MeshConfig template to use. template<> diff --git a/src/Tools/tnl-triangulate-mesh.cpp b/src/Tools/tnl-triangulate-mesh.cpp index 4c83b97944ae3fd01f9387ed21643457795ab746..5dbfe4c5f402619c46c8883505162d8ee95792c7 100644 --- a/src/Tools/tnl-triangulate-mesh.cpp +++ b/src/Tools/tnl-triangulate-mesh.cpp @@ -15,32 +15,32 @@ namespace BuildConfigTags { /**** * Turn off all grids. */ -template<> struct GridRealTag< MeshTriangulatorConfigTag, float > { enum { enabled = false }; }; -template<> struct GridRealTag< MeshTriangulatorConfigTag, double > { enum { enabled = false }; }; -template<> struct GridRealTag< MeshTriangulatorConfigTag, long double > { enum { enabled = false }; }; +template<> struct GridRealTag< MeshTriangulatorConfigTag, float > { static constexpr bool enabled = false; }; +template<> struct GridRealTag< MeshTriangulatorConfigTag, double > { static constexpr bool enabled = false; }; +template<> struct GridRealTag< MeshTriangulatorConfigTag, long double > { static constexpr bool enabled = false; }; /**** * Unstructured meshes. */ -template<> struct MeshCellTopologyTag< MeshTriangulatorConfigTag, Topologies::Polygon > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MeshTriangulatorConfigTag, Topologies::Polyhedron > { enum { enabled = true }; }; +template<> struct MeshCellTopologyTag< MeshTriangulatorConfigTag, Topologies::Polygon > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MeshTriangulatorConfigTag, Topologies::Polyhedron > { static constexpr bool enabled = true; }; // Meshes are enabled only for the space dimension equal to the cell dimension. template< typename CellTopology, int SpaceDimension > struct MeshSpaceDimensionTag< MeshTriangulatorConfigTag, CellTopology, SpaceDimension > -{ enum { enabled = ( SpaceDimension == CellTopology::dimension ) }; }; +{ static constexpr bool enabled = SpaceDimension == CellTopology::dimension; }; // Polygonal Meshes are enable for the space dimension equal to 2 or 3 template< int SpaceDimension > struct MeshSpaceDimensionTag< MeshTriangulatorConfigTag, Topologies::Polygon, SpaceDimension > -{ enum { enabled = ( SpaceDimension >= 2 && SpaceDimension <= 3 ) }; }; +{ static constexpr bool enabled = SpaceDimension >= 2 && SpaceDimension <= 3; }; // Meshes are enabled only for types explicitly listed below. -template<> struct MeshRealTag< MeshTriangulatorConfigTag, float > { enum { enabled = true }; }; -template<> struct MeshRealTag< MeshTriangulatorConfigTag, double > { enum { enabled = true }; }; -template<> struct MeshGlobalIndexTag< MeshTriangulatorConfigTag, long int > { enum { enabled = true }; }; -template<> struct MeshGlobalIndexTag< MeshTriangulatorConfigTag, int > { enum { enabled = true }; }; -template<> struct MeshLocalIndexTag< MeshTriangulatorConfigTag, short int > { enum { enabled = true }; }; +template<> struct MeshRealTag< MeshTriangulatorConfigTag, float > { static constexpr bool enabled = true; }; +template<> struct MeshRealTag< MeshTriangulatorConfigTag, double > { static constexpr bool enabled = true; }; +template<> struct MeshGlobalIndexTag< MeshTriangulatorConfigTag, long int > { static constexpr bool enabled = true; }; +template<> struct MeshGlobalIndexTag< MeshTriangulatorConfigTag, int > { static constexpr bool enabled = true; }; +template<> struct MeshLocalIndexTag< MeshTriangulatorConfigTag, short int > { static constexpr bool enabled = true; }; // Config tag specifying the MeshConfig template to use. template<> @@ -90,7 +90,7 @@ struct MeshConfigTemplateTag< MeshTriangulatorConfigTag > } // namespace TNL template< typename Mesh > -auto getDecomposedMeshHelper( const Mesh& mesh, const String& decompositionType ) +auto getDecomposedMeshHelper( const Mesh& mesh, const std::string& decompositionType ) { using namespace TNL::Meshes; @@ -117,35 +117,52 @@ auto getDecomposedMeshHelper( const Mesh& mesh, const String& decompositionType } template< typename Mesh > -bool triangulateMesh( const Mesh& mesh, const String& outputFileName, const String& outputFormat, const String& decompositionType ) +bool triangulateMesh( const Mesh& mesh, const std::string& outputFileName, const std::string& outputFormat, const std::string& decompositionType ) { const auto decomposedMesh = getDecomposedMeshHelper( mesh, decompositionType ); - if( outputFormat == "vtk" ) { + std::string format = outputFormat; + if( outputFormat == "auto" ) { + namespace fs = std::experimental::filesystem; + format = fs::path( outputFileName ).extension(); + if( format.length() > 0 ) + // remove dot from the extension + format = format.substr(1); + } + + if( format == "vtk" ) { using Writer = Meshes::Writers::VTKWriter< decltype( decomposedMesh ) >; std::ofstream file( outputFileName ); Writer writer( file ); writer.template writeEntities< Mesh::getMeshDimension() >( decomposedMesh ); + return true; } - else if( outputFormat == "vtu" ) { + if( format == "vtu" ) { using Writer = Meshes::Writers::VTUWriter< decltype( decomposedMesh ) >; std::ofstream file( outputFileName ); Writer writer( file ); writer.template writeEntities< Mesh::getMeshDimension() >( decomposedMesh ); + return true; } - return true; + if( outputFormat == "auto" ) + std::cerr << "File '" << outputFileName << "' has unsupported format (based on the file extension): " << format << "."; + else + std::cerr << "Unsupported output file format: " << outputFormat << "."; + std::cerr << " Supported formats are 'vtk' and 'vtu'." << std::endl; + return false; } 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 path." ); - config.addRequiredEntry< String >( "output-file-format", "Output mesh file format." ); + config.addRequiredEntry< std::string >( "input-file", "Input file with the mesh." ); + config.addEntry< std::string >( "input-file-format", "Input mesh file format.", "auto" ); + config.addRequiredEntry< std::string >( "output-file", "Output mesh file path." ); + config.addRequiredEntry< std::string >( "output-file-format", "Output mesh file format." ); config.addEntryEnum( "vtk" ); config.addEntryEnum( "vtu" ); - config.addRequiredEntry< String >( "decomposition-type", "Type of decomposition to use." ); + config.addRequiredEntry< std::string >( "decomposition-type", "Type of decomposition to use." ); config.addEntryEnum( "cc" ); config.addEntryEnum( "cp" ); config.addEntryEnum( "pc" ); @@ -162,15 +179,16 @@ int main( int argc, char* argv[] ) if( ! parseCommandLine( argc, argv, conf_desc, parameters ) ) return EXIT_FAILURE; - const String inputFileName = parameters.getParameter< String >( "input-file" ); - const String inputFileFormat = "auto"; - const String outputFileName = parameters.getParameter< String >( "output-file" ); - const String outputFileFormat = parameters.getParameter< String >( "output-file-format" ); - const String decompositionType = parameters.getParameter< String >( "decomposition-type" ); + const std::string inputFileName = parameters.getParameter< std::string >( "input-file" ); + const std::string inputFileFormat = parameters.getParameter< std::string >( "input-file-format" ); + const std::string outputFileName = parameters.getParameter< std::string >( "output-file" ); + const std::string outputFileFormat = parameters.getParameter< std::string >( "output-file-format" ); + const std::string decompositionType = parameters.getParameter< std::string >( "decomposition-type" ); auto wrapper = [&] ( auto& reader, auto&& mesh ) -> bool { return triangulateMesh( mesh, outputFileName, outputFileFormat, decompositionType ); }; - return ! Meshes::resolveAndLoadMesh< MeshTriangulatorConfigTag, Devices::Host >( wrapper, inputFileName, inputFileFormat ); + const bool status = Meshes::resolveAndLoadMesh< MeshTriangulatorConfigTag, Devices::Host >( wrapper, inputFileName, inputFileFormat ); + return static_cast< int >( ! status ); } diff --git a/src/UnitTests/Meshes/FPMAReaderTest.cpp b/src/UnitTests/Meshes/FPMAReaderTest.cpp index bedfb05f7aa3d522c4873f876a8218da36b1ccc0..e2126a10317095294e1abca79d24e68c4482ab9e 100644 --- a/src/UnitTests/Meshes/FPMAReaderTest.cpp +++ b/src/UnitTests/Meshes/FPMAReaderTest.cpp @@ -20,16 +20,33 @@ namespace BuildConfigTags { // disable all grids template< int Dimension, typename Real, typename Device, typename Index > -struct GridTag< MyConfigTag, Grid< Dimension, Real, Device, Index > >{ enum { enabled = false }; }; +struct GridTag< MyConfigTag, Grid< Dimension, Real, Device, Index > >{ static constexpr bool enabled = false; }; // enable meshes used in the tests -template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Polyhedron > { enum { enabled = true }; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Polyhedron > { static constexpr bool enabled = true; }; } // namespace BuildConfigTags } // namespace Meshes } // namespace TNL -TEST( FPMAReaderTest, polyhedrons ) +TEST( FPMAReaderTest, two_polyhedra ) +{ + using MeshType = Mesh< DefaultConfig< Topologies::Polyhedron > >; + const MeshType mesh = loadMeshFromFile< MeshType, Readers::FPMAReader >( "polyhedrons/two_polyhedra.fpma" ); + + // test that the mesh was actually loaded + const auto vertices = mesh.template getEntitiesCount< 0 >(); + const auto faces = mesh.template getEntitiesCount< MeshType::getMeshDimension() - 1 >(); + const auto cells = mesh.template getEntitiesCount< MeshType::getMeshDimension() >(); + EXPECT_EQ( vertices, 22 ); + EXPECT_EQ( faces, 16 ); + EXPECT_EQ( cells, 2 ); + + test_reader< Readers::FPMAReader, Writers::FPMAWriter >( mesh, TEST_FILE_NAME ); + test_resolveAndLoadMesh< Writers::FPMAWriter, MyConfigTag >( mesh, TEST_FILE_NAME ); +} + +TEST( FPMAReaderTest, cube1m_1 ) { using MeshType = Mesh< DefaultConfig< Topologies::Polyhedron > >; const MeshType mesh = loadMeshFromFile< MeshType, Readers::FPMAReader >( "polyhedrons/cube1m_1.fpma" ); diff --git a/src/UnitTests/Meshes/MeshGeometryTest.h b/src/UnitTests/Meshes/MeshGeometryTest.h index 146e04eaaf72015306acf786f29ccbe82f340d91..01c2430a9009c9df3179b594033469e43ca8a450 100644 --- a/src/UnitTests/Meshes/MeshGeometryTest.h +++ b/src/UnitTests/Meshes/MeshGeometryTest.h @@ -20,6 +20,7 @@ #include <TNL/Meshes/Geometry/isPlanar.h> #include <TNL/Meshes/Geometry/getDecomposedMesh.h> #include <TNL/Meshes/Geometry/getPlanarMesh.h> +#include <TNL/Meshes/Geometry/getRefinedMesh.h> #include <TNL/Meshes/Writers/VTKWriter.h> @@ -28,41 +29,41 @@ namespace MeshGeometryTest { using namespace TNL; using namespace TNL::Meshes; -class TestPolygon2DMeshConfig : public DefaultConfig< Topologies::Polygon > +template< typename... Ts > +struct TestMeshConfigBase : public DefaultConfig< Ts... > { -public: static constexpr bool subentityStorage( int entityDimension, int subentityDimension ) { return true; } static constexpr bool superentityStorage( int entityDimension, int superentityDimension ) { return true; } }; -class TestPolygon3DMeshConfig : public DefaultConfig< Topologies::Polygon > +struct TestTriangleMeshConfig : public TestMeshConfigBase< Topologies::Triangle > +{}; + +struct TestQuadrangleMeshConfig : public TestMeshConfigBase< Topologies::Quadrangle > +{}; + +struct TestTetrahedronMeshConfig : public TestMeshConfigBase< Topologies::Tetrahedron > +{}; + +struct TestHexahedronMeshConfig : public TestMeshConfigBase< Topologies::Hexahedron > +{}; + +struct TestPolygon2DMeshConfig : public TestMeshConfigBase< Topologies::Polygon > +{}; + +struct TestPolygon3DMeshConfig : public TestMeshConfigBase< Topologies::Polygon > { -public: static constexpr int spaceDimension = 3; - static constexpr bool subentityStorage( int entityDimension, int subentityDimension ) { return true; } - static constexpr bool superentityStorage( int entityDimension, int superentityDimension ) { return true; } }; -class TestWedgeMeshConfig : public DefaultConfig< Topologies::Wedge > -{ -public: - static constexpr bool subentityStorage( int entityDimension, int subentityDimension ) { return true; } - static constexpr bool superentityStorage( int entityDimension, int superentityDimension ) { return true; } -}; +struct TestWedgeMeshConfig : public TestMeshConfigBase< Topologies::Wedge > +{}; -class TestPyramidMeshConfig : public DefaultConfig< Topologies::Pyramid > -{ -public: - static constexpr bool subentityStorage( int entityDimension, int subentityDimension ) { return true; } - static constexpr bool superentityStorage( int entityDimension, int superentityDimension ) { return true; } -}; +struct TestPyramidMeshConfig : public TestMeshConfigBase< Topologies::Pyramid > +{}; -class TestPolyhedronMeshConfig : public DefaultConfig< Topologies::Polyhedron > -{ -public: - static constexpr bool subentityStorage( int entityDimension, int subentityDimension ) { return true; } - static constexpr bool superentityStorage( int entityDimension, int superentityDimension ) { return true; } -}; +struct TestPolyhedronMeshConfig : public TestMeshConfigBase< Topologies::Polyhedron > +{}; TEST( MeshGeometryTest, Polygon2DAreaTest ) { @@ -1107,6 +1108,199 @@ TEST( MeshGeometryTest, PolyhedronGetPlanarMeshTest ) } } +TEST( MeshGeometryTest, TriangleGetRefinedMeshTest ) +{ + using TriangleTestMesh = Mesh< TestTriangleMeshConfig >; + using PointType = typename TriangleTestMesh::PointType; + + /**** + * We set-up the following situation + point2 edge3 point3 + |\-------------------| + | \ | + | \ triangle1 | + | \ | + + .... + edge1 edge0 edge4 + .... + + + | triangle0 \ | + | \ | + ---------------------| + point0 edge2 point1 + */ + + PointType point0( 0.0, 0.0 ), + point1( 1.0, 0.0 ), + point2( 0.0, 1.0 ), + point3( 1.0, 1.0 ); + + MeshBuilder< TriangleTestMesh > meshBuilder; + meshBuilder.setEntitiesCount( 4, 2 ); + + meshBuilder.setPoint( 0, point0 ); + meshBuilder.setPoint( 1, point1 ); + meshBuilder.setPoint( 2, point2 ); + meshBuilder.setPoint( 3, point3 ); + + meshBuilder.getCellSeed( 0 ).setCornerId( 0, 0 ); + meshBuilder.getCellSeed( 0 ).setCornerId( 1, 1 ); + meshBuilder.getCellSeed( 0 ).setCornerId( 2, 2 ); + meshBuilder.getCellSeed( 1 ).setCornerId( 0, 1 ); + meshBuilder.getCellSeed( 1 ).setCornerId( 1, 2 ); + meshBuilder.getCellSeed( 1 ).setCornerId( 2, 3 ); + + TriangleTestMesh mesh; + ASSERT_TRUE( meshBuilder.build( mesh ) ); + const TriangleTestMesh refinedMesh = getRefinedMesh< EntityRefinerVersion::EdgeBisection >( mesh ); + + EXPECT_EQ( refinedMesh.getEntitiesCount< 2 >(), 8 ); + EXPECT_EQ( refinedMesh.getEntitiesCount< 1 >(), 16 ); + EXPECT_EQ( refinedMesh.getEntitiesCount< 0 >(), 9 ); +}; + +TEST( MeshGeometryTest, QuadrangleGetRefinedMeshTest ) +{ + using QuadrangleTestMesh = Mesh< TestQuadrangleMeshConfig >; + using PointType = typename QuadrangleTestMesh::PointType; + + PointType point0( 0.0, 0.0 ), + point1( 1.0, 0.0 ), + point2( 0.0, 1.0 ), + point3( 1.0, 1.0 ), + point4( 0.0, 2.0 ), + point5( 1.0, 2.0 ); + + MeshBuilder< QuadrangleTestMesh > meshBuilder; + meshBuilder.setEntitiesCount( 6, 2 ); + + meshBuilder.setPoint( 0, point0 ); + meshBuilder.setPoint( 1, point1 ); + meshBuilder.setPoint( 2, point2 ); + meshBuilder.setPoint( 3, point3 ); + meshBuilder.setPoint( 4, point4 ); + meshBuilder.setPoint( 5, point5 ); + + meshBuilder.getCellSeed( 0 ).setCornerId( 0, 0 ); + meshBuilder.getCellSeed( 0 ).setCornerId( 1, 1 ); + meshBuilder.getCellSeed( 0 ).setCornerId( 2, 3 ); + meshBuilder.getCellSeed( 0 ).setCornerId( 3, 2 ); + meshBuilder.getCellSeed( 1 ).setCornerId( 0, 2 ); + meshBuilder.getCellSeed( 1 ).setCornerId( 1, 3 ); + meshBuilder.getCellSeed( 1 ).setCornerId( 2, 5 ); + meshBuilder.getCellSeed( 1 ).setCornerId( 3, 4 ); + + QuadrangleTestMesh mesh; + ASSERT_TRUE( meshBuilder.build( mesh ) ); + const QuadrangleTestMesh refinedMesh = getRefinedMesh< EntityRefinerVersion::EdgeBisection >( mesh ); + + EXPECT_EQ( refinedMesh.getEntitiesCount< 2 >(), 8 ); + EXPECT_EQ( refinedMesh.getEntitiesCount< 1 >(), 22 ); + EXPECT_EQ( refinedMesh.getEntitiesCount< 0 >(), 15 ); +}; + +TEST( MeshGeometryTest, TetrahedronGetRefinedMeshTest ) +{ + using TetrahedronTestMesh = Mesh< TestTetrahedronMeshConfig >; + using PointType = typename TetrahedronTestMesh::PointType; + + PointType point0( 0.0, 0.0, 0.0 ), + point1( 1.0, 0.0, 0.0 ), + point2( 0.0, 1.0, 0.0 ), + point3( 0.0, 0.0, 1.0 ), + point4( 1.0, 1.0, 1.0 ); + + MeshBuilder< TetrahedronTestMesh > meshBuilder; + meshBuilder.setEntitiesCount( 5, 2 ); + + meshBuilder.setPoint( 0, point0 ); + meshBuilder.setPoint( 1, point1 ); + meshBuilder.setPoint( 2, point2 ); + meshBuilder.setPoint( 3, point3 ); + meshBuilder.setPoint( 4, point4 ); + + meshBuilder.getCellSeed( 0 ).setCornerId( 0, 0 ); + meshBuilder.getCellSeed( 0 ).setCornerId( 1, 1 ); + meshBuilder.getCellSeed( 0 ).setCornerId( 2, 2 ); + meshBuilder.getCellSeed( 0 ).setCornerId( 3, 3 ); + meshBuilder.getCellSeed( 1 ).setCornerId( 0, 1 ); + meshBuilder.getCellSeed( 1 ).setCornerId( 1, 2 ); + meshBuilder.getCellSeed( 1 ).setCornerId( 2, 3 ); + meshBuilder.getCellSeed( 1 ).setCornerId( 3, 4 ); + + TetrahedronTestMesh mesh; + ASSERT_TRUE( meshBuilder.build( mesh ) ); + const TetrahedronTestMesh refinedMesh = getRefinedMesh< EntityRefinerVersion::EdgeBisection >( mesh ); + + EXPECT_EQ( refinedMesh.getEntitiesCount< 3 >(), 16 ); + EXPECT_EQ( refinedMesh.getEntitiesCount< 2 >(), 44 ); + EXPECT_EQ( refinedMesh.getEntitiesCount< 1 >(), 41 ); + EXPECT_EQ( refinedMesh.getEntitiesCount< 0 >(), 14 ); +}; + +TEST( MeshGeometryTest, HexahedronGetRefinedMeshTest ) +{ + using HexahedronTestMesh = Mesh< TestHexahedronMeshConfig >; + using PointType = typename HexahedronTestMesh::PointType; + + PointType point0( 0.0, 0.0, 0.0 ), + point1( 1.0, 0.0, 0.0 ), + point2( 1.0, 1.0, 0.0 ), + point3( 0.0, 1.0, 0.0 ), + point4( 0.0, 0.0, 1.0 ), + point5( 1.0, 0.0, 1.0 ), + point6( 1.0, 1.0, 1.0 ), + point7( 0.0, 1.0, 1.0 ), + point8( 0.0, 0.0, 2.0 ), + point9( 1.0, 0.0, 2.0 ), + point10( 1.0, 1.0, 2.0 ), + point11( 0.0, 1.0, 2.0 ); + + MeshBuilder< HexahedronTestMesh > meshBuilder; + meshBuilder.setEntitiesCount( 12, 2 ); + + meshBuilder.setPoint( 0, point0 ); + meshBuilder.setPoint( 1, point1 ); + meshBuilder.setPoint( 2, point2 ); + meshBuilder.setPoint( 3, point3 ); + meshBuilder.setPoint( 4, point4 ); + meshBuilder.setPoint( 5, point5 ); + meshBuilder.setPoint( 6, point6 ); + meshBuilder.setPoint( 7, point7 ); + meshBuilder.setPoint( 8, point8 ); + meshBuilder.setPoint( 9, point9 ); + meshBuilder.setPoint( 10, point10 ); + meshBuilder.setPoint( 11, point11 ); + + meshBuilder.getCellSeed( 0 ).setCornerId( 0, 0 ); + meshBuilder.getCellSeed( 0 ).setCornerId( 1, 1 ); + meshBuilder.getCellSeed( 0 ).setCornerId( 2, 2 ); + meshBuilder.getCellSeed( 0 ).setCornerId( 3, 3 ); + meshBuilder.getCellSeed( 0 ).setCornerId( 4, 4 ); + meshBuilder.getCellSeed( 0 ).setCornerId( 5, 5 ); + meshBuilder.getCellSeed( 0 ).setCornerId( 6, 6 ); + meshBuilder.getCellSeed( 0 ).setCornerId( 7, 7 ); + meshBuilder.getCellSeed( 1 ).setCornerId( 0, 4 ); + meshBuilder.getCellSeed( 1 ).setCornerId( 1, 5 ); + meshBuilder.getCellSeed( 1 ).setCornerId( 2, 6 ); + meshBuilder.getCellSeed( 1 ).setCornerId( 3, 7 ); + meshBuilder.getCellSeed( 1 ).setCornerId( 4, 8 ); + meshBuilder.getCellSeed( 1 ).setCornerId( 5, 9 ); + meshBuilder.getCellSeed( 1 ).setCornerId( 6, 10 ); + meshBuilder.getCellSeed( 1 ).setCornerId( 7, 11 ); + + HexahedronTestMesh mesh; + ASSERT_TRUE( meshBuilder.build( mesh ) ); + const HexahedronTestMesh refinedMesh = getRefinedMesh< EntityRefinerVersion::EdgeBisection >( mesh ); + + EXPECT_EQ( refinedMesh.getEntitiesCount< 3 >(), 16 ); + EXPECT_EQ( refinedMesh.getEntitiesCount< 2 >(), 68 ); + EXPECT_EQ( refinedMesh.getEntitiesCount< 1 >(), 96 ); + EXPECT_EQ( refinedMesh.getEntitiesCount< 0 >(), 45 ); +}; + } // namespace MeshTest #endif diff --git a/src/UnitTests/Meshes/MeshReaderTest.h b/src/UnitTests/Meshes/MeshReaderTest.h index 9290057ddc490d3645e3d6c270aff4dfff5f83df..0ee553d8554778f3cf3848efe25a1b620a87dd09 100644 --- a/src/UnitTests/Meshes/MeshReaderTest.h +++ b/src/UnitTests/Meshes/MeshReaderTest.h @@ -30,7 +30,7 @@ void test_reader( const MeshType& mesh, std::string outputFileName ) // 1. resolveMeshType resolves the mesh type correctly // 2. resolveAndLoadMesh loads the mesh template< template<typename> class WriterType, typename ConfigTag, typename MeshType > -void test_resolveAndLoadMesh( const MeshType& mesh, std::string outputFileName ) +void test_resolveAndLoadMesh( const MeshType& mesh, std::string outputFileName, std::string globalIndexType = "auto" ) { // write the mesh into the file (new scope is needed to properly close the file) { @@ -57,7 +57,7 @@ void test_resolveAndLoadMesh( const MeshType& mesh, std::string outputFileName ) return true; }; - const bool status = TNL::Meshes::resolveAndLoadMesh< ConfigTag, TNL::Devices::Host >( wrapper, outputFileName ); + const bool status = TNL::Meshes::resolveAndLoadMesh< ConfigTag, TNL::Devices::Host >( wrapper, outputFileName, "auto", "auto", globalIndexType ); EXPECT_TRUE( status ); EXPECT_EQ( std::remove( outputFileName.c_str() ), 0 ); diff --git a/src/UnitTests/Meshes/NetgenReaderTest.cpp b/src/UnitTests/Meshes/NetgenReaderTest.cpp index d7caa9957b0c908109e3d055ee66a72652848686..71c755525c2bf3847c4ec4c9832e36884075d309 100644 --- a/src/UnitTests/Meshes/NetgenReaderTest.cpp +++ b/src/UnitTests/Meshes/NetgenReaderTest.cpp @@ -19,12 +19,12 @@ namespace BuildConfigTags { // disable all grids template< int Dimension, typename Real, typename Device, typename Index > -struct GridTag< MyConfigTag, Grid< Dimension, Real, Device, Index > >{ enum { enabled = false }; }; +struct GridTag< MyConfigTag, Grid< Dimension, Real, Device, Index > >{ static constexpr bool enabled = false; }; // enable meshes used in the tests -//template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Edge > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Triangle > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Tetrahedron > { enum { enabled = true }; }; +//template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Edge > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Triangle > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Tetrahedron > { static constexpr bool enabled = true; }; } // namespace BuildConfigTags } // namespace Meshes diff --git a/src/UnitTests/Meshes/VTIReaderTest.cpp b/src/UnitTests/Meshes/VTIReaderTest.cpp index 3b41a3b7020c5c42b437fa42e7162f7151614a9d..b0c6847ba48997ddd98b62135b52cb66ce59cafc 100644 --- a/src/UnitTests/Meshes/VTIReaderTest.cpp +++ b/src/UnitTests/Meshes/VTIReaderTest.cpp @@ -5,6 +5,7 @@ #include <TNL/Meshes/Writers/VTIWriter.h> #include <TNL/Meshes/TypeResolver/resolveMeshType.h> +#include "data/loader.h" #include "MeshReaderTest.h" using namespace TNL::Meshes; @@ -18,13 +19,13 @@ namespace Meshes { namespace BuildConfigTags { // enable all index types in the GridTypeResolver -template<> struct GridIndexTag< MyConfigTag, short int >{ enum { enabled = true }; }; -template<> struct GridIndexTag< MyConfigTag, int >{ enum { enabled = true }; }; -template<> struct GridIndexTag< MyConfigTag, long int >{ enum { enabled = true }; }; +template<> struct GridIndexTag< MyConfigTag, short int >{ static constexpr bool enabled = true; }; +template<> struct GridIndexTag< MyConfigTag, int >{ static constexpr bool enabled = true; }; +template<> struct GridIndexTag< MyConfigTag, long int >{ static constexpr bool enabled = true; }; // disable float and long double (RealType is not stored in VTI and double is the default) -template<> struct GridRealTag< MyConfigTag, float > { enum { enabled = false }; }; -template<> struct GridRealTag< MyConfigTag, long double > { enum { enabled = false }; }; +template<> struct GridRealTag< MyConfigTag, float > { static constexpr bool enabled = false; }; +template<> struct GridRealTag< MyConfigTag, long double > { static constexpr bool enabled = false; }; } // namespace BuildConfigTags } // namespace Meshes @@ -77,6 +78,42 @@ TEST( VTIReaderTest, Grid3D ) test_meshfunction< Readers::VTIReader, Writers::VTIWriter >( grid, TEST_FILE_NAME, "PointData" ); test_meshfunction< Readers::VTIReader, Writers::VTIWriter >( grid, TEST_FILE_NAME, "CellData" ); } + +// ASCII data, produced by TNL writer +TEST( VTIReaderTest, Grid2D_vti ) +{ + using GridType = Grid< 2, double, TNL::Devices::Host, int >; + const GridType mesh = loadMeshFromFile< GridType, Readers::VTIReader >( "quadrangles/grid_2x3.vti" ); + + // test that the mesh was actually loaded + const auto vertices = mesh.template getEntitiesCount< 0 >(); + const auto cells = mesh.template getEntitiesCount< GridType::getMeshDimension() >(); + EXPECT_EQ( vertices, 12 ); + EXPECT_EQ( cells, 6 ); + + test_reader< Readers::VTIReader, Writers::VTIWriter >( mesh, TEST_FILE_NAME ); + test_resolveAndLoadMesh< Writers::VTIWriter, MyConfigTag >( mesh, TEST_FILE_NAME ); + test_meshfunction< Readers::VTIReader, Writers::VTIWriter >( mesh, TEST_FILE_NAME, "PointData" ); + test_meshfunction< Readers::VTIReader, Writers::VTIWriter >( mesh, TEST_FILE_NAME, "CellData" ); +} + +// ASCII data, produced by TNL writer +TEST( VTIReaderTest, Grid3D_vti ) +{ + using GridType = Grid< 3, double, TNL::Devices::Host, long int >; + const GridType mesh = loadMeshFromFile< GridType, Readers::VTIReader >( "hexahedrons/grid_2x3x4.vti" ); + + // test that the mesh was actually loaded + const auto vertices = mesh.template getEntitiesCount< 0 >(); + const auto cells = mesh.template getEntitiesCount< GridType::getMeshDimension() >(); + EXPECT_EQ( vertices, 60 ); + EXPECT_EQ( cells, 24 ); + + test_reader< Readers::VTIReader, Writers::VTIWriter >( mesh, TEST_FILE_NAME ); + test_resolveAndLoadMesh< Writers::VTIWriter, MyConfigTag >( mesh, TEST_FILE_NAME ); + test_meshfunction< Readers::VTIReader, Writers::VTIWriter >( mesh, TEST_FILE_NAME, "PointData" ); + test_meshfunction< Readers::VTIReader, Writers::VTIWriter >( mesh, TEST_FILE_NAME, "CellData" ); +} #endif #include "../main.h" diff --git a/src/UnitTests/Meshes/VTKReaderTest.cpp b/src/UnitTests/Meshes/VTKReaderTest.cpp index 9d2df309bb762d2f3c0a26298165d238392f25df..a1fafba52ac4401a0209a564a20af9b3d4b622e4 100644 --- a/src/UnitTests/Meshes/VTKReaderTest.cpp +++ b/src/UnitTests/Meshes/VTKReaderTest.cpp @@ -20,13 +20,16 @@ namespace BuildConfigTags { // disable all grids template< int Dimension, typename Real, typename Device, typename Index > -struct GridTag< MyConfigTag, Grid< Dimension, Real, Device, Index > >{ enum { enabled = false }; }; +struct GridTag< MyConfigTag, Grid< Dimension, Real, Device, Index > >{ static constexpr bool enabled = false; }; // enable meshes used in the tests -//template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Edge > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Triangle > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Tetrahedron > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Polygon > { enum { enabled = true }; }; +//template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Edge > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Triangle > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Quadrangle > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Tetrahedron > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Hexahedron > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Polygon > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Polyhedron > { static constexpr bool enabled = true; }; } // namespace BuildConfigTags } // namespace Meshes @@ -47,7 +50,7 @@ TEST( VTKReaderTest, mrizka_1 ) EXPECT_EQ( cells, 242 ); test_reader< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME ); - test_resolveAndLoadMesh< Writers::VTKWriter, MyConfigTag >( mesh, TEST_FILE_NAME ); + test_resolveAndLoadMesh< Writers::VTKWriter, MyConfigTag >( mesh, TEST_FILE_NAME, "int" ); // force GlobalIndex to int (VTK DataFormat 2.0 uses int32, but 5.1 uses int64) test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "PointData" ); test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "CellData" ); } @@ -65,7 +68,7 @@ TEST( VTKReaderTest, tetrahedrons ) EXPECT_EQ( cells, 1312 ); test_reader< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME ); - test_resolveAndLoadMesh< Writers::VTKWriter, MyConfigTag >( mesh, TEST_FILE_NAME ); + test_resolveAndLoadMesh< Writers::VTKWriter, MyConfigTag >( mesh, TEST_FILE_NAME, "int" ); // force GlobalIndex to int (VTK DataFormat 2.0 uses int32, but 5.1 uses int64) test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "PointData" ); test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "CellData" ); } @@ -83,7 +86,7 @@ TEST( VTKReaderTest, triangles_2x2x2_original_with_metadata_and_cell_data ) EXPECT_EQ( cells, 8 ); test_reader< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME ); - test_resolveAndLoadMesh< Writers::VTKWriter, MyConfigTag >( mesh, TEST_FILE_NAME ); + test_resolveAndLoadMesh< Writers::VTKWriter, MyConfigTag >( mesh, TEST_FILE_NAME, "int" ); // force GlobalIndex to int (VTK DataFormat 2.0 uses int32, but 5.1 uses int64) test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "PointData" ); test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "CellData" ); } @@ -101,7 +104,7 @@ TEST( VTKReaderTest, triangles_2x2x2_minimized_ascii ) EXPECT_EQ( cells, 8 ); test_reader< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME ); - test_resolveAndLoadMesh< Writers::VTKWriter, MyConfigTag >( mesh, TEST_FILE_NAME ); + test_resolveAndLoadMesh< Writers::VTKWriter, MyConfigTag >( mesh, TEST_FILE_NAME, "int" ); // force GlobalIndex to int (VTK DataFormat 2.0 uses int32, but 5.1 uses int64) test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "PointData" ); test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "CellData" ); } @@ -119,7 +122,80 @@ TEST( VTKReaderTest, triangles_2x2x2_minimized_binary ) EXPECT_EQ( cells, 8 ); test_reader< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME ); - test_resolveAndLoadMesh< Writers::VTKWriter, MyConfigTag >( mesh, TEST_FILE_NAME ); + test_resolveAndLoadMesh< Writers::VTKWriter, MyConfigTag >( mesh, TEST_FILE_NAME, "int" ); // force GlobalIndex to int (VTK DataFormat 2.0 uses int32, but 5.1 uses int64) + test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "PointData" ); + test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "CellData" ); +} + +// ASCII data, produced by Paraview (DataFile version 5.1) +TEST( VTKReaderTest, triangles_2x2x2_ascii_51 ) +{ + using MeshType = Mesh< DefaultConfig< Topologies::Triangle > >; + const MeshType mesh = loadMeshFromFile< MeshType, Readers::VTKReader >( "triangles_2x2x2/version_5.1_ascii.vtk" ); + + // test that the mesh was actually loaded + const auto vertices = mesh.template getEntitiesCount< 0 >(); + const auto cells = mesh.template getEntitiesCount< MeshType::getMeshDimension() >(); + EXPECT_EQ( vertices, 9 ); + EXPECT_EQ( cells, 8 ); + + test_reader< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME ); + test_resolveAndLoadMesh< Writers::VTKWriter, MyConfigTag >( mesh, TEST_FILE_NAME, "int" ); // force GlobalIndex to int (VTK DataFormat 2.0 uses int32, but 5.1 uses int64) + test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "PointData" ); + test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "CellData" ); +} + +// binary data, produced by Paraview (DataFile version 5.1) +TEST( VTKReaderTest, triangles_2x2x2_binary_51 ) +{ + using MeshType = Mesh< DefaultConfig< Topologies::Triangle > >; + const MeshType mesh = loadMeshFromFile< MeshType, Readers::VTKReader >( "triangles_2x2x2/version_5.1_binary.vtk" ); + + // test that the mesh was actually loaded + const auto vertices = mesh.template getEntitiesCount< 0 >(); + const auto cells = mesh.template getEntitiesCount< MeshType::getMeshDimension() >(); + EXPECT_EQ( vertices, 9 ); + EXPECT_EQ( cells, 8 ); + + test_reader< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME ); + test_resolveAndLoadMesh< Writers::VTKWriter, MyConfigTag >( mesh, TEST_FILE_NAME, "int" ); // force GlobalIndex to int (VTK DataFormat 2.0 uses int32, but 5.1 uses int64) + test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "PointData" ); + test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "CellData" ); +} + + +// binary data, produced by TNL writer +TEST( VTKReaderTest, quadrangles ) +{ + using MeshType = Mesh< DefaultConfig< Topologies::Quadrangle > >; + const MeshType mesh = loadMeshFromFile< MeshType, Readers::VTKReader >( "quadrangles/grid_2x3.vtk" ); + + // test that the mesh was actually loaded + const auto vertices = mesh.template getEntitiesCount< 0 >(); + const auto cells = mesh.template getEntitiesCount< MeshType::getMeshDimension() >(); + EXPECT_EQ( vertices, 12 ); + EXPECT_EQ( cells, 6 ); + + test_reader< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME ); + test_resolveAndLoadMesh< Writers::VTKWriter, MyConfigTag >( mesh, TEST_FILE_NAME, "int" ); // force GlobalIndex to int (VTK DataFormat 2.0 uses int32, but 5.1 uses int64) + test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "PointData" ); + test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "CellData" ); +} + +// binary data, produced by TNL writer +TEST( VTKReaderTest, hexahedrons ) +{ + using MeshType = Mesh< DefaultConfig< Topologies::Hexahedron > >; + const MeshType mesh = loadMeshFromFile< MeshType, Readers::VTKReader >( "hexahedrons/grid_2x3x4.vtk" ); + + // test that the mesh was actually loaded + const auto vertices = mesh.template getEntitiesCount< 0 >(); + const auto cells = mesh.template getEntitiesCount< MeshType::getMeshDimension() >(); + EXPECT_EQ( vertices, 60 ); + EXPECT_EQ( cells, 24 ); + + test_reader< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME ); + test_resolveAndLoadMesh< Writers::VTKWriter, MyConfigTag >( mesh, TEST_FILE_NAME, "int" ); // force GlobalIndex to int (VTK DataFormat 2.0 uses int32, but 5.1 uses int64) test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "PointData" ); test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "CellData" ); } @@ -137,12 +213,51 @@ TEST( VTKReaderTest, polygons ) EXPECT_EQ( cells, 90 ); test_reader< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME ); - test_resolveAndLoadMesh< Writers::VTKWriter, MyConfigTag >( mesh, TEST_FILE_NAME ); + test_resolveAndLoadMesh< Writers::VTKWriter, MyConfigTag >( mesh, TEST_FILE_NAME, "int" ); // force GlobalIndex to int (VTK DataFormat 2.0 uses int32, but 5.1 uses int64) + test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "PointData" ); + test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "CellData" ); +} + +// ASCII data, produced by Paraview +TEST( VTKReaderTest, two_polyhedra ) +{ + using MeshType = Mesh< DefaultConfig< Topologies::Polyhedron > >; + const MeshType mesh = loadMeshFromFile< MeshType, Readers::VTKReader >( "polyhedrons/two_polyhedra.vtk" ); + + // test that the mesh was actually loaded + const auto vertices = mesh.template getEntitiesCount< 0 >(); + const auto faces = mesh.template getEntitiesCount< MeshType::getMeshDimension() - 1 >(); + const auto cells = mesh.template getEntitiesCount< MeshType::getMeshDimension() >(); + EXPECT_EQ( vertices, 22 ); + EXPECT_EQ( faces, 16 ); + EXPECT_EQ( cells, 2 ); + + test_reader< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME ); + test_resolveAndLoadMesh< Writers::VTKWriter, MyConfigTag >( mesh, TEST_FILE_NAME, "int" ); // force GlobalIndex to int (VTK DataFormat 2.0 uses int32, but 5.1 uses int64) + test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "PointData" ); + test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "CellData" ); +} + +// binary data, produced by Paraview +TEST( VTKReaderTest, cube1m_1 ) +{ + using MeshType = Mesh< DefaultConfig< Topologies::Polyhedron > >; + const MeshType mesh = loadMeshFromFile< MeshType, Readers::VTKReader >( "polyhedrons/cube1m_1.vtk" ); + + // test that the mesh was actually loaded + const auto vertices = mesh.template getEntitiesCount< 0 >(); + const auto faces = mesh.template getEntitiesCount< MeshType::getMeshDimension() - 1 >(); + const auto cells = mesh.template getEntitiesCount< MeshType::getMeshDimension() >(); + EXPECT_EQ( vertices, 2358 ); + EXPECT_EQ( faces, 2690 ); + EXPECT_EQ( cells, 395 ); + + test_reader< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME ); + test_resolveAndLoadMesh< Writers::VTKWriter, MyConfigTag >( mesh, TEST_FILE_NAME, "int" ); // force GlobalIndex to int (VTK DataFormat 2.0 uses int32, but 5.1 uses int64) test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "PointData" ); test_meshfunction< Readers::VTKReader, Writers::VTKWriter >( mesh, TEST_FILE_NAME, "CellData" ); } -// TODO: test case for DataFile version 5.1: triangles_2x2x2/DataFile_version_5.1_exported_from_paraview.vtk #endif #include "../main.h" diff --git a/src/UnitTests/Meshes/VTUReaderTest.cpp b/src/UnitTests/Meshes/VTUReaderTest.cpp index 9a37f765db40706dbb3d2dc551555802fa406248..8bead316c45b8802a8b777d0bd24186ade00313d 100644 --- a/src/UnitTests/Meshes/VTUReaderTest.cpp +++ b/src/UnitTests/Meshes/VTUReaderTest.cpp @@ -20,13 +20,16 @@ namespace BuildConfigTags { // disable all grids template< int Dimension, typename Real, typename Device, typename Index > -struct GridTag< MyConfigTag, Grid< Dimension, Real, Device, Index > >{ enum { enabled = false }; }; +struct GridTag< MyConfigTag, Grid< Dimension, Real, Device, Index > >{ static constexpr bool enabled = false; }; // enable meshes used in the tests -//template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Edge > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Triangle > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Tetrahedron > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Polygon > { enum { enabled = true }; }; +//template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Edge > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Triangle > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Quadrangle > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Tetrahedron > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Hexahedron > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Polygon > { static constexpr bool enabled = true; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Polyhedron > { static constexpr bool enabled = true; }; } // namespace BuildConfigTags } // namespace Meshes @@ -177,6 +180,42 @@ TEST( VTUReaderTest, triangles_2x2x2_minimized_compressed_paraview ) test_meshfunction< Readers::VTUReader, Writers::VTUWriter >( mesh, TEST_FILE_NAME, "CellData" ); } +// encoded data, produced by Paraview +TEST( VTUReaderTest, quadrangles ) +{ + using MeshType = Mesh< DefaultConfig< Topologies::Quadrangle > >; + const MeshType mesh = loadMeshFromFile< MeshType, Readers::VTUReader >( "quadrangles/grid_2x3.vtu" ); + + // test that the mesh was actually loaded + const auto vertices = mesh.template getEntitiesCount< 0 >(); + const auto cells = mesh.template getEntitiesCount< MeshType::getMeshDimension() >(); + EXPECT_EQ( vertices, 12 ); + EXPECT_EQ( cells, 6 ); + + test_reader< Readers::VTUReader, Writers::VTUWriter >( mesh, TEST_FILE_NAME ); + test_resolveAndLoadMesh< Writers::VTUWriter, MyConfigTag >( mesh, TEST_FILE_NAME ); + test_meshfunction< Readers::VTUReader, Writers::VTUWriter >( mesh, TEST_FILE_NAME, "PointData" ); + test_meshfunction< Readers::VTUReader, Writers::VTUWriter >( mesh, TEST_FILE_NAME, "CellData" ); +} + +// encoded data, produced by Paraview +TEST( VTUReaderTest, hexahedrons ) +{ + using MeshType = Mesh< DefaultConfig< Topologies::Hexahedron > >; + const MeshType mesh = loadMeshFromFile< MeshType, Readers::VTUReader >( "hexahedrons/grid_2x3x4.vtu" ); + + // test that the mesh was actually loaded + const auto vertices = mesh.template getEntitiesCount< 0 >(); + const auto cells = mesh.template getEntitiesCount< MeshType::getMeshDimension() >(); + EXPECT_EQ( vertices, 60 ); + EXPECT_EQ( cells, 24 ); + + test_reader< Readers::VTUReader, Writers::VTUWriter >( mesh, TEST_FILE_NAME ); + test_resolveAndLoadMesh< Writers::VTUWriter, MyConfigTag >( mesh, TEST_FILE_NAME ); + test_meshfunction< Readers::VTUReader, Writers::VTUWriter >( mesh, TEST_FILE_NAME, "PointData" ); + test_meshfunction< Readers::VTUReader, Writers::VTUWriter >( mesh, TEST_FILE_NAME, "CellData" ); +} + // ASCII data, produced by TNL writer TEST( VTUReaderTest, polygons ) { @@ -195,7 +234,48 @@ TEST( VTUReaderTest, polygons ) test_meshfunction< Readers::VTUReader, Writers::VTUWriter >( mesh, TEST_FILE_NAME, "CellData" ); } +// ASCII data, hand-converted from the FPMA format +TEST( VTUReaderTest, two_polyhedra ) +{ + using MeshType = Mesh< DefaultConfig< Topologies::Polyhedron > >; + const MeshType mesh = loadMeshFromFile< MeshType, Readers::VTUReader >( "polyhedrons/two_polyhedra.vtu" ); + + // test that the mesh was actually loaded + const auto vertices = mesh.template getEntitiesCount< 0 >(); + const auto faces = mesh.template getEntitiesCount< MeshType::getMeshDimension() - 1 >(); + const auto cells = mesh.template getEntitiesCount< MeshType::getMeshDimension() >(); + EXPECT_EQ( vertices, 22 ); + EXPECT_EQ( faces, 16 ); + EXPECT_EQ( cells, 2 ); + + test_reader< Readers::VTUReader, Writers::VTUWriter >( mesh, TEST_FILE_NAME ); + test_resolveAndLoadMesh< Writers::VTUWriter, MyConfigTag >( mesh, TEST_FILE_NAME ); + test_meshfunction< Readers::VTUReader, Writers::VTUWriter >( mesh, TEST_FILE_NAME, "PointData" ); + test_meshfunction< Readers::VTUReader, Writers::VTUWriter >( mesh, TEST_FILE_NAME, "CellData" ); +} + +// compressed data, produced by TNL writer +TEST( VTUReaderTest, cube1m_1 ) +{ + using MeshType = Mesh< DefaultConfig< Topologies::Polyhedron > >; + const MeshType mesh = loadMeshFromFile< MeshType, Readers::VTUReader >( "polyhedrons/cube1m_1.vtu" ); + + // test that the mesh was actually loaded + const auto vertices = mesh.template getEntitiesCount< 0 >(); + const auto faces = mesh.template getEntitiesCount< MeshType::getMeshDimension() - 1 >(); + const auto cells = mesh.template getEntitiesCount< MeshType::getMeshDimension() >(); + EXPECT_EQ( vertices, 2358 ); + EXPECT_EQ( faces, 2690 ); + EXPECT_EQ( cells, 395 ); + + test_reader< Readers::VTUReader, Writers::VTUWriter >( mesh, TEST_FILE_NAME ); + test_resolveAndLoadMesh< Writers::VTUWriter, MyConfigTag >( mesh, TEST_FILE_NAME ); + test_meshfunction< Readers::VTUReader, Writers::VTUWriter >( mesh, TEST_FILE_NAME, "PointData" ); + test_meshfunction< Readers::VTUReader, Writers::VTUWriter >( mesh, TEST_FILE_NAME, "CellData" ); +} + // TODO: test cases for the appended data block: minimized_appended_binary_compressed.vtu, minimized_appended_binary.vtu, minimized_appended_encoded_compressed.vtu, minimized_appended_encoded.vtu +// TODO: test case for mixed 3D mesh: data/polyhedrons/hexahedron_and_two_polyhedra.vtu #endif #include "../main.h" diff --git a/src/UnitTests/Meshes/data/hexahedrons/grid_2x3x4.vti b/src/UnitTests/Meshes/data/hexahedrons/grid_2x3x4.vti new file mode 100644 index 0000000000000000000000000000000000000000..c11d52388a04e0e872f8356b833a4e9ec88cf513 --- /dev/null +++ b/src/UnitTests/Meshes/data/hexahedrons/grid_2x3x4.vti @@ -0,0 +1,7 @@ +<?xml version="1.0"?> +<VTKFile type="ImageData" version="1.0" byte_order="LittleEndian" header_type="UInt32"> +<ImageData WholeExtent="0 2 0 3 0 4 " Origin="0.000000e+00 0.000000e+00 0.000000e+00 " Spacing="1.000000e+00 1.000000e+00 1.000000e+00 "> +<Piece Extent="0 2 0 3 0 4 "> +</Piece> +</ImageData> +</VTKFile> diff --git a/src/UnitTests/Meshes/data/hexahedrons/grid_2x3x4.vtk b/src/UnitTests/Meshes/data/hexahedrons/grid_2x3x4.vtk new file mode 100644 index 0000000000000000000000000000000000000000..fb9809e9d28f4e66d11449d2aef37d4957ce0b67 Binary files /dev/null and b/src/UnitTests/Meshes/data/hexahedrons/grid_2x3x4.vtk differ diff --git a/src/UnitTests/Meshes/data/hexahedrons/grid_2x3x4.vtu b/src/UnitTests/Meshes/data/hexahedrons/grid_2x3x4.vtu new file mode 100644 index 0000000000000000000000000000000000000000..2747da2660ca24b23d4eff0e24026839530b1927 --- /dev/null +++ b/src/UnitTests/Meshes/data/hexahedrons/grid_2x3x4.vtu @@ -0,0 +1,22 @@ +<VTKFile type="UnstructuredGrid" version="1.0" byte_order="LittleEndian" header_type="UInt64"> + <UnstructuredGrid> + <Piece NumberOfPoints="60" NumberOfCells="24"> + <Points> + <DataArray type="Float64" Name="Points" NumberOfComponents="3" format="binary"> + oAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPA/AAAAAAAAAAAAAAAAAADwPwAAAAAAAPA/AAAAAAAAAAAAAAAAAAAAQAAAAAAAAPA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAADwPwAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhAAAAAAAAAAAAAAAAAAADwPwAAAAAAAAhAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8D8AAAAAAADwPwAAAAAAAAAAAAAAAAAA8D8AAAAAAAAAQAAAAAAAAAAAAAAAAAAA8D8AAAAAAAAAAAAAAAAAAPA/AAAAAAAA8D8AAAAAAADwPwAAAAAAAPA/AAAAAAAA8D8AAAAAAAAAQAAAAAAAAPA/AAAAAAAA8D8AAAAAAAAAAAAAAAAAAABAAAAAAAAA8D8AAAAAAADwPwAAAAAAAABAAAAAAAAA8D8AAAAAAAAAQAAAAAAAAABAAAAAAAAA8D8AAAAAAAAAAAAAAAAAAAhAAAAAAAAA8D8AAAAAAADwPwAAAAAAAAhAAAAAAAAA8D8AAAAAAAAAQAAAAAAAAAhAAAAAAAAA8D8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAADwPwAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAPA/AAAAAAAAAEAAAAAAAADwPwAAAAAAAPA/AAAAAAAAAEAAAAAAAAAAQAAAAAAAAPA/AAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAAAADwPwAAAAAAAABAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAhAAAAAAAAAAEAAAAAAAADwPwAAAAAAAAhAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAAhAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAAAAADwPwAAAAAAAAAAAAAAAAAACEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAACEAAAAAAAAAAAAAAAAAAAPA/AAAAAAAACEAAAAAAAADwPwAAAAAAAPA/AAAAAAAACEAAAAAAAAAAQAAAAAAAAPA/AAAAAAAACEAAAAAAAAAAAAAAAAAAAABAAAAAAAAACEAAAAAAAADwPwAAAAAAAABAAAAAAAAACEAAAAAAAAAAQAAAAAAAAABAAAAAAAAACEAAAAAAAAAAAAAAAAAAAAhAAAAAAAAACEAAAAAAAADwPwAAAAAAAAhAAAAAAAAACEAAAAAAAAAAQAAAAAAAAAhAAAAAAAAACEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEEAAAAAAAADwPwAAAAAAAAAAAAAAAAAAEEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAEEAAAAAAAAAAAAAAAAAAAPA/AAAAAAAAEEAAAAAAAADwPwAAAAAAAPA/AAAAAAAAEEAAAAAAAAAAQAAAAAAAAPA/AAAAAAAAEEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEEAAAAAAAADwPwAAAAAAAABAAAAAAAAAEEAAAAAAAAAAQAAAAAAAAABAAAAAAAAAEEAAAAAAAAAAAAAAAAAAAAhAAAAAAAAAEEAAAAAAAADwPwAAAAAAAAhAAAAAAAAAEEAAAAAAAAAAQAAAAAAAAAhAAAAAAAAAEEA= + </DataArray> + </Points> + <Cells> + <DataArray type="Int64" Name="connectivity" format="binary"> + AAYAAAAAAAAAAAAAAAAAAAEAAAAAAAAABAAAAAAAAAADAAAAAAAAAAwAAAAAAAAADQAAAAAAAAAQAAAAAAAAAA8AAAAAAAAAAQAAAAAAAAACAAAAAAAAAAUAAAAAAAAABAAAAAAAAAANAAAAAAAAAA4AAAAAAAAAEQAAAAAAAAAQAAAAAAAAAAMAAAAAAAAABAAAAAAAAAAHAAAAAAAAAAYAAAAAAAAADwAAAAAAAAAQAAAAAAAAABMAAAAAAAAAEgAAAAAAAAAEAAAAAAAAAAUAAAAAAAAACAAAAAAAAAAHAAAAAAAAABAAAAAAAAAAEQAAAAAAAAAUAAAAAAAAABMAAAAAAAAABgAAAAAAAAAHAAAAAAAAAAoAAAAAAAAACQAAAAAAAAASAAAAAAAAABMAAAAAAAAAFgAAAAAAAAAVAAAAAAAAAAcAAAAAAAAACAAAAAAAAAALAAAAAAAAAAoAAAAAAAAAEwAAAAAAAAAUAAAAAAAAABcAAAAAAAAAFgAAAAAAAAAMAAAAAAAAAA0AAAAAAAAAEAAAAAAAAAAPAAAAAAAAABgAAAAAAAAAGQAAAAAAAAAcAAAAAAAAABsAAAAAAAAADQAAAAAAAAAOAAAAAAAAABEAAAAAAAAAEAAAAAAAAAAZAAAAAAAAABoAAAAAAAAAHQAAAAAAAAAcAAAAAAAAAA8AAAAAAAAAEAAAAAAAAAATAAAAAAAAABIAAAAAAAAAGwAAAAAAAAAcAAAAAAAAAB8AAAAAAAAAHgAAAAAAAAAQAAAAAAAAABEAAAAAAAAAFAAAAAAAAAATAAAAAAAAABwAAAAAAAAAHQAAAAAAAAAgAAAAAAAAAB8AAAAAAAAAEgAAAAAAAAATAAAAAAAAABYAAAAAAAAAFQAAAAAAAAAeAAAAAAAAAB8AAAAAAAAAIgAAAAAAAAAhAAAAAAAAABMAAAAAAAAAFAAAAAAAAAAXAAAAAAAAABYAAAAAAAAAHwAAAAAAAAAgAAAAAAAAACMAAAAAAAAAIgAAAAAAAAAYAAAAAAAAABkAAAAAAAAAHAAAAAAAAAAbAAAAAAAAACQAAAAAAAAAJQAAAAAAAAAoAAAAAAAAACcAAAAAAAAAGQAAAAAAAAAaAAAAAAAAAB0AAAAAAAAAHAAAAAAAAAAlAAAAAAAAACYAAAAAAAAAKQAAAAAAAAAoAAAAAAAAABsAAAAAAAAAHAAAAAAAAAAfAAAAAAAAAB4AAAAAAAAAJwAAAAAAAAAoAAAAAAAAACsAAAAAAAAAKgAAAAAAAAAcAAAAAAAAAB0AAAAAAAAAIAAAAAAAAAAfAAAAAAAAACgAAAAAAAAAKQAAAAAAAAAsAAAAAAAAACsAAAAAAAAAHgAAAAAAAAAfAAAAAAAAACIAAAAAAAAAIQAAAAAAAAAqAAAAAAAAACsAAAAAAAAALgAAAAAAAAAtAAAAAAAAAB8AAAAAAAAAIAAAAAAAAAAjAAAAAAAAACIAAAAAAAAAKwAAAAAAAAAsAAAAAAAAAC8AAAAAAAAALgAAAAAAAAAkAAAAAAAAACUAAAAAAAAAKAAAAAAAAAAnAAAAAAAAADAAAAAAAAAAMQAAAAAAAAA0AAAAAAAAADMAAAAAAAAAJQAAAAAAAAAmAAAAAAAAACkAAAAAAAAAKAAAAAAAAAAxAAAAAAAAADIAAAAAAAAANQAAAAAAAAA0AAAAAAAAACcAAAAAAAAAKAAAAAAAAAArAAAAAAAAACoAAAAAAAAAMwAAAAAAAAA0AAAAAAAAADcAAAAAAAAANgAAAAAAAAAoAAAAAAAAACkAAAAAAAAALAAAAAAAAAArAAAAAAAAADQAAAAAAAAANQAAAAAAAAA4AAAAAAAAADcAAAAAAAAAKgAAAAAAAAArAAAAAAAAAC4AAAAAAAAALQAAAAAAAAA2AAAAAAAAADcAAAAAAAAAOgAAAAAAAAA5AAAAAAAAACsAAAAAAAAALAAAAAAAAAAvAAAAAAAAAC4AAAAAAAAANwAAAAAAAAA4AAAAAAAAADsAAAAAAAAAOgAAAAAAAAA= + </DataArray> + <DataArray type="Int64" Name="offsets" format="binary"> + wAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAGAAAAAAAAAAgAAAAAAAAACgAAAAAAAAAMAAAAAAAAAA4AAAAAAAAAEAAAAAAAAAASAAAAAAAAABQAAAAAAAAAFgAAAAAAAAAYAAAAAAAAABoAAAAAAAAAHAAAAAAAAAAeAAAAAAAAACAAAAAAAAAAIgAAAAAAAAAkAAAAAAAAACYAAAAAAAAAKAAAAAAAAAAqAAAAAAAAACwAAAAAAAAALgAAAAAAAAAwAAAAAAAAAA= + </DataArray> + <DataArray type="UInt8" Name="types" format="binary"> + GAAAAAAAAAAMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw= + </DataArray> + </Cells> + </Piece> + </UnstructuredGrid> +</VTKFile> diff --git a/src/UnitTests/Meshes/data/polyhedrons/cube1m_1.vtk b/src/UnitTests/Meshes/data/polyhedrons/cube1m_1.vtk new file mode 100644 index 0000000000000000000000000000000000000000..3683bc542fb29403af0df455d911c2f6cf6f8271 Binary files /dev/null and b/src/UnitTests/Meshes/data/polyhedrons/cube1m_1.vtk differ diff --git a/src/UnitTests/Meshes/data/polyhedrons/cube1m_1.vtu b/src/UnitTests/Meshes/data/polyhedrons/cube1m_1.vtu new file mode 100644 index 0000000000000000000000000000000000000000..5baa036db153fdbf3d262731952b2f7bc04ca88b --- /dev/null +++ b/src/UnitTests/Meshes/data/polyhedrons/cube1m_1.vtu @@ -0,0 +1,29 @@ +<?xml version="1.0"?> +<VTKFile type="UnstructuredGrid" version="1.0" byte_order="LittleEndian" header_type="UInt64" compressor="vtkZLibDataCompressor"> +<UnstructuredGrid> +<Piece NumberOfPoints="2358" NumberOfCells="395"> +<Points> +<DataArray type="Float64" Name="Points" NumberOfComponents="3" format="binary"> +AQAAAAAAAAAQ3QAAAAAAABDdAAAAAAAAIHQAAAAAAAA=eJykvHk8lN/7P46khUpKKpTShkp7KD0rJNKiSHtalBZJi0hFpaKkBa0oChHJvm/Z933fZphhZsyMFxWp5Dvv5p7bN71f38/n9/jNP/djnvc1z3Od61znOtd1zn2Px7vjxsbGmRD6/fmHuP798fhD7n/7+ZuvXto/KCiINaS9f/Bv/FEqBjy88i98Nms3D6cPwf9u77/zDrY7lP///f3f7EP5/6if4Er5H/vv8V/HZ/D6pz3/rZ3B6596/d3//6l//709yv9gn6HtDO3/3/KCfg/V99/k/992oPxL///mGXr/3/z1v7f3P/kL5V9+/3e//nu/h17/Z/8Yev9/52eD9vhT7/+pn//uT/8d/7ff/Vu7/zZ+f4/zf7/+3e7Q3/2bf/zbeP//G6ehn7/n51D5oXYa6kf/5q//u6vg8/d8/jd/Fej7b/PPOHgD774HKgP38a5+xH0uOow+8ORjyfu/4VQucT+YxH/znuQQ3x8RVy++nAKDxN/viOPxveDja0tInM/3jn91yCG+B5H8v8elMoPQz+dPfYTiye/S7w1516gh94PJfqnsfMNr/8N/75fQW+LqDg9txirXIyV/6iuU+aeepHw+KcdvPwKbKiJ43wd/z2/X9y89+ePSidF3L8sdCn4P1VAR+a81YXDf8cBWawQHzUurraKFfXCp9qzrZfYnnDmxfBZ38SDOl4+Ann190TO5DpJnimhY9qubiXx8Hxf/PO9Qq3eIxtKRhx9gRCCfx4pN8oy5UdNavC6N366ugCeQkA/hy5cyETei+rE2PZCQz+HzN3AI/ihIHr2aqxTnycevsNC9MMm7Vzccvspr1Wc/TSf61QlOX3TXJ49UUp/feDaHwLP+5PFkk+MhGJ/fdqvkQubmnRPN+zKI/oaR/Hw8DyPDk2nu+96Reo6o1Phi4JVC2DODlA9W6UxeFp6JznCq3NeaZBLnt+dPjuPvcWWVDxnXD3z/Z9FIeYH//8b120h5vh+GEP5RNcS/XhP9avkL58eTOtzwnaY7+VTaEDsz+XhHNonzx5GFRcfn3w168greAXeTZmhkIWmqwTWzyW0oPZCzqD4hEtImfutbe2NhvWv55l8VDFL+/M4EqUsPcjGic2CepBYDiRHW9beOfYRDjVvPhtJ0bDtT3/vuFRPnRRS1no1Mw9qQjZz7vmFQcbzuu+MkAxaN514EKYRg/C+dkme5cQQPHQVT7vkG0cMJPIyPv2lD10HRn/byqej1Y3y9zXjL11OtjZwv/HnCIb//iXeS80kwz/lxjkt+Hzrf/pj3lPa/4pNrUZ+S65vBcRi19dxTK7mnMKeuzJbM9UO//hyrSWWpeP0roi37YCC8dfTHLn0QjvjnKi3z34ZB893ErboL0nF5kpJ6weonhHwWNIxGervuiSB43oDeWeRSvSUW7waC+7qqs6B5z13u2DQ/PFJ54NaoVYA8I5NH50YHE/KhBJ4F1rYZNhonvYh2I1CsfXXljb5obBvY/bk1NJrAI0FbkiAvziwhefj+9AEfqfUPgpTLh/jZW8IvK7DmtOKLVrVwQp9QXJvtpiZ+ogw2qSpq59ziyH4VNa988NygBAErP2QduTC0v5/4dgjJJPGyvp1Zp+bm8PtrkopZTxfaGlc+x5S5FzavRA4eVZnNDXlaSeL7G/q2jlldhg0i4VdEODUk7nhN4huVWYCfC36VmqyvRXtv43ihtUEYfX/STsqLJBwsmxzy5mQ10S9v9CqI0jbNayDn05/9rcaESQ6daq4RxPoThv2P37dIvagl5QXzmT8/2wm7PUZE7A771m8Ucl4L/KQ0B4FWns3k72/NjctXc31PfB+qx2tMCV9t9yEmBRL+72+9uRgF/1vtK67o5yDefIKF3bxYvJkspMvxDcXAeq9pOrZlML+z8RAjOR1bTsd1Pd3si1+3NDTvdhdCQ22R6qrx5SSuL2OY29kqkP9E4u4dohdeFBH4mrxBfKHv4+bsPAIvINv9zX8uDQt3xZcMGxODFb2XF21RSERpVTR1ztd8RGq9XXThVekQnkLs/rCf169wiJVXfiqNz+D3Ky8PHw4utLbbGA8rTNwanJaMA9LR+Yw3FbhYOqeuZ1sQcuLapy0YnoMrst5afZIluDn2YH6JVwQ2C33W8yrLwcMpwzZ0l5Rh0r0715feq4Fjt23SuqW+cDnQpTW6LQPXW6bPb1peTeKTk1ruTCyIA8tQMjxAuQxGohtvyHTEQuG+WxUzJJWQr8P6vsSm11PCMEzGbEmK9UcCr8Buzyqpp06hqElN4/GEweH7pRH99hU4Efq8+vTacIInh7Tn8fD+b/lzkjB3v/bslAdxRD6fRcShONL/NC4nLdbuTkW2mfWqSWXF5Lwes9tbs3pvBqyUz1hcMUvj2/liKnKfTIlrVE/C8boUxpLn2aQ8v1+liKouOLVCIR3KFwJNronF8vnPpRLxIZmIP2E4pjO/Ju1MDYzHaRbVBiRCbGHe0Wuc1wTOiwt3prWzd2QMwSthbyph67g9gcQF6x9/XtDJeSHw699xWJlK9DuMwENhJynSPXsi5a958JvHg0bmbUPXRUEe9YPjtkAmo5bQM4vUh79O1g/h7eTPx4ZGEv89bydzERkwsE4u+xExPsVkXvHH+uDAJfrR8de6M3RdEnwX3B+6HgmuQ9cjwVWgD78f5YQ8h+h3EKF/JannmxN3f3rpRaCm+95GI5diLGcedk1pZ+D6tuMiW0ekY+kWxosz10qxg5YYLxrPRUT5yTceO2MxIlvSUsS9ABIz/Z29nrDxeJj1ctu8BNAvHJ0/qygX3DkG+3dps6CdtvnK18pPaLy4LsOEmY/2lQbLNqZxwP6+LXRjXCzBXwHbokmSex5w8OHVfxZEf8iEfdmwPaaK364Gk8T58uV8+XEsOJ3/VXHhfSgYtsv+SS8vg3fsWbPQo2x0iRYuPKGRjWETz8/ymZqNdG578a4PTFT2DNzqZBWg40ujRKfVR7gOv7XueRYT7sKj9J8EVaJ4Zf8yvTu+UPDcKv9+IguLP3jOn7o0B+0G/neWtMVDX6E05Zc3A0pBZ191DJSR8t/DvmYbpzAQ6v3TK9yqAu0vr714qxVB8LChvsCi65VDPmGHHNIOsl6fd8hl1xDjEv6nv8lVkvOcP14cct0VrMOCceytaauboVKEY6cZx+wpWaAy1/30EmJjc9KZ8ZM7iiG6++A5/YXRZB7Lxwv5eXhaGpmvrsrAaeajEqh0/eLZJx4RnU1PuLJsNPUwrjSy8gn7l5H+8H4gX2X7wlJ811a7t2R7MYlP3nutROzpS5w/OEfo8ogSpBwYI/5gWyveHjgVUvMkBI7HqpOk9QrhcySiSe8XDSd+VH48qBoP5QUKxVtsSjF1SR73zZh2kqe2pPDX2r5yXDmafe+9SSuJjxzpalzcUEXgdHi1TRsbpReOu5KyugXJlVBbMEuWeXCQJ8qoyfXs1DrQNh43pfnRSPyW/DS1lhX1BN6CQwpWW+VMeHmPms+p+D4/JObse6D5rg6Vozw7J0UnYIbErmGq53IxKzzhR9LcVtxJvLvYuDKQsE8mId+MKWN6OAOIH8LTDAnR8b0DByMwk33vrHNPDj5qNT5aYd6AvibbEW7P07Ezc7RFp3MajoZnDS/tqcb3gl/0gcsJ8Omgf1SwSsLaE0Yva4VbsTDivJaxRwTMPEuWN/4MJ/gbsErKtkHYPA9Od45uyoxMxq3O/jF1p+jo3e/mEzQ6nmg3FWaWSu33kupwX+xHz8D0zL/0nBl62MD4Ys5fdmAkPfVSNs/Aoc4Mnj5RhD40pJ59bGy9vhgu20MWBipGYjglZNcClyrSPnw940n+ofofOPohe5oRFQfp9SJuo7L/4hmqD1++BaO/0gxOl0RA64DohjFqBVgfIj1v2GUqPw9PH8yv+HGbiJfLB/cVBPPo97QybSPrsD9woVYy3xXgv/meUUncnPaqQo3LIeI9ncQFcfg3TSqb3IcQxG3NPV8yuBurcNdtg5QVJQyn1sr29c6sROed3sfrZBuwIZa5RP+8D1ZnJd2cJVaNm3KrvT7y4mPBHdWu8d/jcUzW9Oz2jhJCvoqUv+dwuC6c2QSz9cvpxa7lcLLR2XojOgINXXNSdNobcGTP/VRuTRlCl/bobmlMxJxRNk7L82vgrnFSYiC1HDeXK5k+MUzApkfzLGfMpGCaw/z4s5urMO35LuU4o4+Qkqn4aaJIRfOTxZlUXv7Vuuvw+9imj7gSSQtWSm/DsSVyxcdTK8H57Kq17bQ3AtgbNZR20/Fosr/1JucSmO6O3ND1NByUY17B3SYtGP40L5W6tBQj+8ZTPeoToF/9+sLGpzTIHjCflSRdPoSnFY6LDjKenG0cYp96wg51f9mBr3/dX/pHHJCga9vVk/xjtnseGHaxBb+WpD7onF03BKdhyQ8FjROHsxBz8UHL04BSKL33yFj6so23bi39EqldgPGKtsePrCzEXUZ11dbmdpiLONZmzM5GqY63/aQ5xVimcqd0c1QrNsc+cZMcWYjIm2kz9Mfmwt08t/VEHw0LOZpyzJtlWLai+HS4TzZ2bzutXzOL9lf9+Ue+ID24rybIE/j+NrgPJ/g93z87/9yX+M9+U+lATOxnCvYJNV/8x/0DvMvvOB86HAUXXeOi2FYqLBcYLZsUlgargWDXiRbeWH19n5fm6DbECSV3UJS84HW9d5Hp9lyUL9zeam9GI3FPrbuKOi9T8G1ysFrlHjoWOnRq2OiHkzy32/0OH5nWgt1Vj+kzv0ZBM2DchPSMIJj2bxozNneQx3neimUSYyJhNdHGy0mlGWdtg6dMORqLV8ayO0WlYqFjuiuua92gPt+CPEaluKfz29VoIfG+c+6rN9ukEvq04c7OGwovEQBXpQjP5nvBRLutCA+ena/Dek/qU1HlZConx4B415uLodxABG4w2hz1Iw0NSQ1zdX06cIDqPHA14gnqZz1IkFqYhejyG2Vdm1gkvuHK3GNJNakQfrfiqudOJvZoXws6IfoW63XXab0e84HgGZS33txvPXtfJMHDGcKTgb7Uliir0na8fKMoGWIQBs2WnTrSvtF8/h4OHF/MMJ4Y+o6Qz/sr/gj86Df/vQ5CPhgyXV7Pvh5+z9dnHQu7bFrsm4PDwX7xnaMf+5qPz+AS+vvD5duWgsthSXycyibkI+Dz5vWMErGIv/z2j/z1//JbQb77W598LsETRvZ3qD//sX+szB2yH1FB5lN/7ptVkrggn+fny7VkuxbTTCuXiaSQ47jzZP3L9uBXmFlrd1zVqRL3nAuzo3ya8Kq4fql/QgLS+1pitneUIkF52700LQo209/YuczLxotJYtYbMsvgF5cm/tOOCs1hlnenpQXDPSKt/OL2cgzvq825uKMeMS3zV/T6hGCc9qQTLfK1uKX4hXtiBnVIu7Vku3/KV5Lyvq31jeqbEvBYc33hQ/0SiD9hiRzwbyDbbVLsObpbvAKceeefVVRWk/yGwYlve/qqoHXDf+1tXrwW4CxJyjUdw3r8aPpVJh9WT+JG5jZK//yoJfDqv8aBb9dPeGdhbulR4A3H6huKAVcYSM7S318Yl42KFZEx+sdCCZyOROfo+BfnY2CfpRJ2eWQMH09sw5ak3Ly7q4Mx4GJy+KtkEA5y5hUZHmeDKTN1pM3RNJL/Nz6XhR2W4eEzx6UR8h8IHgbJ86c+bYR85p/4TyahTwTZrsawYpufjzqgeHyOAuVRCIzPOomV7I/k44Ys2E6wuLi83ZfAw/n6tLMR0HTUTGdaJGkPgX0E9lIwu7VL0zGN0GeQh4+nkv0VtCuoXwX+K9jXFeCCOoF/v4rQJ5qv53gO0d+sIfJcnDJ5xvteRMyr8CF5Tzk5f/6Uz4L76UAefzzJH7Ry9+5HvHyfr/8nEr9y34NnzySiTs4Zwk/soymXkLhgH4DfTjaJC+b9735XDta3f+wbOFQN4Q8m4hgTK8Z9PDlVOJWMY/LDxFyVP9KhpbLuYeGrFHI9+sBeKJ7t346Pej5mIr7J8JtquLySG0qsg614cU2xOfU/+yuEvKC9oecMe+y0u4qt8jE3QzTlMa/OVdmns7w+3x/HK1oWBhRmwmhv89remELULxgZKj8vDVfv+768Kp+AvdI0g8+rK3BEfmBu1JZkaBaeKavlxsHY99XlyxYVSHxXHPh9dwWadXfd1x+bBPUbP3u+W9RB+oPvC45ZBA7vjNklOz8FZ7JbjlDbeXnW84Mmk41L8WrTyvv6rlGEPjUwT9W6O+dLMjoVgzfMzi7AY1mvE5pKtaSeuyP1v2dGx0Jl1v4tStdLULM6OXCNciHRbgKcV/tsdjZsIttd8/TMVA+5AKw/9E0qVKcR4iaGhT3huSSexm30F5WtxrC1RrSSawX8du9k4W2tu4/f5HqyXT6eiQpJ429LfzaRuIBHYu74U9YxtVhGmaEz4XI2LsxVvOBoG4F3AdfjZfZVY4LIsO3B2mWD8umPDEMzy2HxlO1qp5QPJe8TNifUM2AdXv16nGgZtom8V7uSGo5vfe4j+3SSyXHZcOv17pyybGg+us4blzC8L3Z6c12hDA3NRWuGl2XijfKnpoNXciFDbZD+WF4C/ZKDjPGtpRh26t6MznvZuJ19VlKrswSNP28eiY6uhUdoKO2Bch40a49/3lGeBwfOhPEVYVUoFa0WVxWqxJimpGsWWVlYeaOvJOhHKUqaK2RTGrPwK+H5fD3eeAc0F2t8aa+AcH/BrR88vZMX7AoSj83Geu1MN7Xb1cDNlml6DpFICtx4K2x4Cwyzu110zyXghO/Ly2eWJqI3WOZXCC+fLjJy0m2hJ8JeLFPzsVAgNtmsDwWTisAjtlmKcZlD8FZckwqStR2bj0DvRB5PJMFDx88yFdsNOdloKpoS7DY2FgfqMsY6z6PA+cQPhmVaFizbJar0RqSiVDyf9l2bAqnyntLmG0kEnoPbK2U0tr2gwLBYpqz5RhT0o42GMRbm8ONGKYOMh3m2UY8lLLMh99RqxzALOmIDV3S7PIxA1BhOyOPaDH6cDGgj5etVNrHseeue9ptVTl6BkQi1TRCiHMpBt+iZZ+u/U3FW5QjtQl04dGRUrZCQidzQ85O7jVshatS/vW96AC6YjBE5plsKndSLoycrU0j80tcTzZ6cXJS5Wi7zH0Ml8aqn79JZSSVYuEy1PyuDV2/dYXzy0YtEYfnMw+MyePN08ziHpU31CLAr9Vl/JAla8fV1vlNLsWBFEDdsTh1+VI+tHv4jGxNnXFz1Q5mX93+fPVed00rqaf/kG2d5UR7En6vcPKlIQfh0g3TpLwnQ9Ih5fEM1AyI31C//9KZCr9F8do59Fk7Z7JGV3hdK4K2IWCZcck45l8Rz+4/VvPFqhtQrygiXujxEfBtWcXf2a8SoeC6UNGnEov1uUdUfc7BvwnFPR+ePcN6zwS/cjoLjL2XLJ+zk5Z2G8qm5s2KxlxlVu2sUFU5nOuZKTcvCCbdYXbpaBqIPqMj4WdXggJzEIZWEfLQ67+2fdiEUy6+Id2Stqoaz18rLx2bz8qWYmYvXjfGCGi1hdoV4MWR/aHyn2ZXgSYPM7WtyaTjnuXSyt2gD3nbEqE6aXIRNaaGMioIYDNygVCUplsK2QZixLr8U6kH6uqqa+RBe2py2Va4Szhb14lVmeWS7/Tt9VuavKoWsvFH/SV5dK7qvo00zMBwO21I8rNrKMW3Dq+KEyyWkPreQ/nI5ry52S3J+OH1JLmpnl74p0ktBhvDz9S6etdC8bJ5hNbwQo57pMdRm5cF5R9DBUdYV2Oogc7q2JB9b92yf8CwzF4dds1Q88hoxPHOb40RKPsqLKPUsiQzUrfFz+BJWDNOZTRoD1s2k/4do+n5rjy2FtNiYO8pbB+Phb/0TymFZMiwjV6SG1D8zTNG1w6oIldmd8yyn0mCznnWGpv4Wi0Ylqj2RK8SPML1rGZYtJL55UZDV6yO8fPSeD/XzQyqJswJM1soZ1uKSVsrezLkVpB1mdJWv33KuEcuPOvc/eF+Gh0Yep1WCP/DH0a4GDjMTLtmmVJPyWqPqaxs6mjE/SXvG0h2lpF/Rri1ll45txZnwn6FKc0pI3MeRc5q7nIp9X0Rt0qLLSJy/ftaR6+rQ/Oa/749y/9ofFeQFgnqXfz7QTv7uT5w5JL/iEryUv/IuwXmCIF8S4Pz6ogllMsH0Bdo+0Lj4qL/wGhMbxGdZjPJvxa/ouRtP94Yh6pZb5+duDipq5Z8+VaKR8o56Ou9sAln4ueLspwN2dDDFq7on8eo5vjwXzsNPstlr6Lz1u1vj5vMgCO3RfeEtL8DbSB4+LuCn48R/thXWNeP1o3kyNnnvkJ3qqu36vY04Z2xEyUZ38dVmkdhzvfAL24ANpVN0S5extQj36Yn+opWE2NlZl5wM2vH93MeN+xuq0UtZ6k95l4Lx6lc1D57vwDGrgF9Zi+shqRPF40mGzJ07F0ct48DLQ8J6zSjefL+bVk3l5X2+kSu/fXJsQ6tHwaryaQ3IH24jt00oEr+4uddiyun8/XDdBlJPvj4s/j6bDRU/vFdGt42LgP2pufrrZ7LBNlvaf/9I8xB5JnH+TkGC7ySefAKBc9AT/pynJ5WU5/OwsOjrZ/+Z0oP4xJGrMoOE29BcmfhlrXQtYmhSvP5GEf1l8sc/ioL9huJ7P/PyFH5/uYQdGuHkWzfce0k2iQvOuQ7ohzF+Xu9Al+cChchpXEw8bOuVZhAD9XjvBRv02Xjt0nWoJ3zQP5O3GvtcV2PiaNHSntkvGYQ/hPPWd89VClVMcMq9WqReDMq3qof2VN1vI+RZhH9GYqJ1v+b2ajZfnwscUv6Z2Y9N81vb4Rx7f5PdP0xST3VTE7eAYDYKwsvzDuh1kPK/9RfuIHDWEJxF4EP4j9D5/GeZJM7nZ/Lt4C3gJ/Q04fD1jGSSdcdv+dMMPv6FQ+K/201rI3A2Od8F8/zPfS4iDrDoQ+rUSD4/m8nXP5dL7gvwr/VkfBmQLPW+rptInDdVk+dWAvkXrruE1YsayX3geWml2q6tH4k6rYbkEZxnPpM1Vcl+RCH3pQVxhq9fKxlXBPhm3WAHuXYqef7Ivx9L2IFO6M8h654/nyvqhIOTeXGqYhauzvTUmaJUQ57XRPpNKVuytpDoVyWJ8+u1NL595nOJcWHBvn7CrOgfn8Cls268nM4h/JZBnlPz/YFDjougXhT0X2B/Pp5GjDubnBeCdn/jMqwh4zJYRwv2lQT1q4D/j/15np0Mb7xxlst+Q9SHjaQ9+OMTBtWVqTtd3xDnvmu5f8b7VBo5ngJcsO8/dH3i87aS48l/DobY//KgEM85dv5ZZ0u3DTnvDSH07CD3u/7wCxaT5PkTb4fwKpPexm1voaeS7PNPHBWyFcpRseosEv90f/ULS15+tzbZcfTYcSw4eL3/cWZeJDoso0aVVrTirUus0DIev0CeumFaYYwKheAZxDNXpbpNGkvgU9oJnnBsvJHzbEWugJ8NF7w8ln8zG9aPjV86Dq/GdIeKOVvGtuMpM9/4sEsSKOt2LTltWwfDdz90VD92IP8LXXkxr/55YvH2TlpRHf7Tjdo6Nkw4QTz5KNiVfFzqN0Igz1tXl+WHLhobhv4q/7kBig2YPYJ9sntiG+Qd80bNOpeIouDupdPH1WJ512H/CVXtcJVRvqh9rxw7bJ0uVmwrQ+hsNY0xBzh4rhXkrRteg3s31G5JXi0F+4aF/ci3XPyKNPQaS6uDd8AmyZT5xRg2+uuX8TVsjFT0tr46hVd/lSgo2NtVkTz9dhrmqcpFOJ3xMOaKdS1k1o0723KtA3ILpyYuq6mDgsrrOrOyCpL/su0/ubYXi9EnIZSf6VeNc66u/8jFMOAWvaf9rWs1Hn+9u/9RUT76E1iJx2U64HS3LsknoAwv1m5Ts9MqRlD1ApEjXh1Ic1c6u7L+P/WK6p4wXt3ofuzVDa9KBvJTA59rfKqCz1PG6dXrSmAy6qSm71YG9leGpZYdq4X/9Pm187VrSH2MeqLeTiuuxOoct4U3ptSS+OZHveZTN/kjb0VG0q5MCmZ49jo7fW0icTr3/JXIGirWjdZ3wSwaTh98ydgiHo5ddcKbB+wbMWdHGFtRvwUL+o7Tknh1qFiRFe3n5Ub8mv9kZt9JGsnz6HDF9+vfqFBi+zKuj2vB8eNyi35u5tWFMbPPpcVR8KWv4nSKJR1X3h6XzZaMwLzZ+9aMyWzEt19inMoRg/pMX1w0S7KBgmEiem7zNahYvyKi2KknDi1C368OHKvHCvYmu0BePyZ+Z+54djQb7bkPk9+eroNfWpritiAavJ89yO3XyUK8vPzqz2troGCX9KO1n4reDcZee85W4KLT3di75wux06FzhaUnFSNDg+7X36nj15nUQhg2VVbPsquGsmjh+idCJSg0k1jaJ1+Dq4wb35/w+tHlcbdabFcVejXG5V6Uy8GRIwd05cKbgaSPF2mGlXDgjtbd9qwQWRbXl86UrMc+hr5oUEoJ3m2U6x5tUQbhpxkX1o2rxw3l6peNViW4Xy9pYeBbBpG5d0aHrqTAqcaUOut+K9yKn8jZnoiEZvyBXPvXlZCz69nn39aMbedmRKoPz0XjNOvLFwJqMar/9flGKg0zNi1q+xbug9TpVY+8efVH/dJNIhVGdBK/vld5LWtlDcqMtrZNlx/Et6/rmrZOlIrFez57v93RRuLGOZrtn982ITb8mfzIFVREf2xePk0xCSp2+8xihAQ8raQ8a5+h/khrCqFPyxCeBoiPvbkmpp+CSOH7LjPcwnh11YPFlzj1SD9cEdVzjYOGJxYGX5pSscd0VfnsnVwwDRaKGyxj4hwqeeV4GvyWdV4t4cV9vnwH4hbUP2MkhQ2RZ6GoZj5Xc0I0Ke9tc9nUh8mCZ9juBrs5vnDQ95hnp8Ih231zw1nLzTycL7+Wxce9mdAdPuHOnNIgPn8Vh88T2PEnTwch/4tJ4nweJl+fCjahTwxf/j2Hj0/kEvrHEPwdfH69dqK/qX/qn9tO8ESS+hxhON6L7m/DtOeL13SlJuHBwM5e7j42sixHq0h/oULyTnTIJeccNBz3eF4Wz0bH7qMOJ5l0bGo6qT9eLBisvOO20xqYsJl3IOibAY3EMyljZuWKMeFc1kQRL2/HF93DbhsmxeCkdL/P8xEsfFEz9Zy2blB+/0+LrQ/V2hF4NLjrvHULehK9Pmqbx0MpdN3UzrR2vL71sTIlmbeuLn+/3VE4l89/m4uFRkEDm28Mygtv7+TOfcjk65nbQuifSuAd/P5ebcN3xTA9xXuZfH3ucPj9nUjHtsyarREhkZCMSis6v5jNt7MRm/CrLNKefB4GHhRMfmVDySN5BOP42/7Lckh58VkV2fKjqLg7vGn++SehOEH5fO1qdgu+GL6QsuTFxf7GJ4opUzLhPvaGXn5CG9pWHzkYXkpBkqj5owWhqTA9P9Z71BgKn4dJgce2ap+LixJhf9zuy+0fNAwcXXbloU0TUlO2B2w+FoP0Ww/V7j5vwcnbRoaJ+o2QMrhg2jcuFbtc4quadKk4u3K6jKRiPXatdzMOfp7Pq0NuHNQDHRzpWS0y32uRqc5ixNYV46jdde/5n1qwarvS4kCjFuycd2dU9NtYmF92DjN4RYX7yc/aK+QpCPd+rlwzMx+uc5X92Hea+fqoNfHPvelpOLFl9Ae3QhqyqRfkMqj10GWf6XtzMw8p89fdy/9FwcINE5oqrGsg9UN0m0tBBdIspqiI27bD+7SY1z9ry2HndYem8aEWN1OtO6rM2iGndEL5q1M9zH8YLdL/VIZ1+uy6piI6bB2dv/48WIdpl4Qul7RU4NGRjimfDFtxsPH4JoldJVDSqzs3ll0PyyMRipUyPD+W8jD5drQCT/za57Yp1+KXkrEvO6SFP47JXGLcM4h52kHiutzhvd7OmfzxTSXmozuHwPP58qcG81D+eUUJmbcJ9jH45x4VJD7hkKV80gUmdssmUxbufAt50ZOmlTa8/ilnumZd52JdU/bhFanZYL30KhB9UYH9yxVzjk8flC9TjY5SLqrEP5O+PL+fw8ASscUqljNjEFh84NGJumw0jrx3V+0qHY2rfYxchieBtebFOMVVRejt1z0Uu5mDFm1Jmx8dadAZviTIKqaAbLfqtmrsPel0LD9reClUu4wvX8jCSImbFE5oKCl/aNrtl+EOdNjXTrr/0D0ZEnEj15ufryB4OHh6eIzsp8pYOGgPn7mnk8B59aRAfwG/3NpT6la8+pMv/5HAy7Fv5qnti87SIN/vcbbQPwuNSaW6DxbnItL2bOKhje3QXHqp1U00AouqO2PLRlfDeEuxkCavPq6/v+X9g4thGKn8fPvmlAKsL/shfsSGjc66zhc+N6IJ/YtQfmxjiXo4F/Yyl+JyZuRihcK7r0c8U3GD5qU1hdWGgD3iESWuOViUa+MgX5aJirH2m6sYTMRwZ5tY3c7EyuWpMetZYfi2zXf2A148OFs6SWxnSCkc2grXyrvG8vnPceDQfJG9c3gGVBQxn54VgjHb/5l4bjYTovH+89XXpaOpU+xOYU8m5EquHeyXasNqlQt+nYp5iJ5pVzR3dRyfp5sJaafGdcF78iFc+PK6htwbfru6bII/j8QF/Qoy33D0fEE2njya/uRcawQ0FL0Xdw904FsBJy5gQxbsZte9qizPIfXnyxcSPCEkD79fJcjPGbcprusT+r7V+KqsYRN2SINEs9yYZ4hBgGbpop1bGNjluGH6rqoMlJ+3rr9zK44vz1s3Xbx2BmfVp2O8zsowa/1PuHNGP1mtiY3qffb75u+MQdL5eVrGBQ3gzbo1xpkcAk8BO4jxvLWzjsC5OP0BCye9z4aJyHVt1xf1kKYkFMrqcvDBSCyjrigb3a228aKPagh+Jg6s3Ku6mh6Ed7V717Z2NhE4B1XDa2pSjVMhOovbY7+KgtOzRn2SPsuA9fHH/c9WpWLbvYik4MJagp+NfYESTlFeKQgYlzVuT1s1gbMIPIZsV+W71tKt3HasTcu8O9UgGitijV9M2SZol0XqUyMp99SqvB6Soheman1ug8vAfO2bN/xw4EsaJ2gPDc3Zs8uzmRyEVWQYsE8l8fE6Oh8/wRoiT4ezjsQqFyYN33o43DTdDGzSyEs77UuHwsYl/4gsaQObu0lXPyMW8f+oaq7i5W2+yt+dkt+xcULkNb1CKQmNa0wmzRFjAKKBu7PKaRhbUVT5IzwNmWc2veqNa4H7Z6t58ilsvFQ63/tkUiTe5Vx8ZnyLhvUfgw5cm8tAEv391IyOVEg1ndPoNGDw5QcYpJ4rnlp+ap1Hw9W1r5ee2MGCkcmWh/YVEfx2b7cjhRO197QFA0d890/6cDsVO9oemx3mUHBviezRx1ZtMPFbKJ38IgJLwmO1jba04PST6f3GFoN2SDc7wbY6LcAH9ZQMkbZwjWiB7OHENuP9LFyUzYhrXhqLK1Fv1HQ1qQTO5uUZr++8yEhF7s+tcUccqaRftVjFZBl0FBHjW42Jj8YcFFrGgcL85J+BLkV4LbSJ9smviIyfy7+Gmr1rKCLidjk5j8QzuAo7F5Tx55FVHunPtsvpoctfl0F//UyL1nF1mJn9UlSpLwATrQ2/pLZTkWDzVLhFtg6XQpKjZ/Di+0OphOiYVVS0Fx88N6+qAWN43rd3TArcdpstyg9rQvISy8qLjHqkXJY1S4osgOnV7mPpixrAbrvbsKu1geS/nMoI2curH46l7tZ/sqMCkb8qQw745OLx26iIPudapJ8+la8TUYGziy6Pa74bgymc56EyjU3Y1Cy5VTa+hOTpKWG9uv6eioLFwnr7daowXNyqZ7J1CphcDdnuB/XoHl52kuNbDhMTbRn34Z+g91p6v+YXCqRL8+NCbxThWIr6iaL2CPgN3M9+NKMREu1Tj5mvrib5uQd3n9g+mgZZtt+rjnf1JD71gX9Kkk0rnv4MeDT+agVKji3yabwSi73zONv17rZi2/guhTtSpUjZGHVtlmMJZGs9njx6UQNxkZLCN7q1mBSZt9G0OAz2N3tYrTOYeK6/uf3L5FJ4vM1qNkwOh8I/E2MW7WLBoFXmgN4SKlT2bPSOmpUAP4tta9UvdYA2Tfjxq/QyeAsL6boYpIHbmaqz+RAdlQUGomtty2G8pPZ44NZkrF6Asx5zeevStwkeYzqKoXlljWXx5GgcvpE1QsaPjtLWrXnT1evwhOP9lJ6ViB/vz2evNuPgH6kv94+Mb8T19ZvXP7SKgMKTQrnHGmwCryZ5gndeDk/bSods7LCLea1V2LWYWntR7S02OMw91z6OCY32ALWxibUkD5+fDZtR37PCRlJI+UuJF+MO17bB2PGli/KwWhL/LU9n4lbC3tbYY5V/4mYsAm/CyR29ywImJBJ2Y8Oq0CrCewSvrlft3XMqIB9rpXxXRlu148JHzasZ+akorlM4qupZBj2zu+oj97BRP64s3D8sHFNCK8bWbajELLtFtHBHnh3iFUf+FEvE1Ym/FDwTC3DTfHe46GkmgrXrPj25HAOvqHU5B9QKSfnWZUEf5n1I5fNklfFxURYoF3Ulv40JRkSL2dO4vExwy+ps1xl2wFh9JzPiRRlytiy8stQ0G6bvLI02zmcgeUGy1ornhWCcepK/4Xgmv102Gyve7ju0KakY29MXWneNz8G77REj9qR1oMAgI/q1VDoOmqhx1StzCPtwSfs8jqKKcw8X4OQPk50vJnLQU/lRo1CzhrBDOc5spbnuD+VizG3b3eW7sjFlk/FXKeMSEk/kjhRJuV6FHLHx50e15pH4iPTE4BPzywg7FJO4TeN40WvD8oY8P/T3+y5D30tYrjS88UA3BzW+IRVq3MH3YDIVDYLbUYUd1v73uBNYKLTUDJUPi/kTd+OgPca72rUmF4dep3cf3lILn/lXroZ0MBB/YukWLaM3JG5EL/75kMbLL3zvib2KLYDkt4Gdj1nVsDEVomqdYMNw3Mob6jdTiX32hr/2ZR9W39Tr6qpHYq5laW4ZG90HT/20HgjHhmEl3huyGzB6vNz2tt0ssl1+Xt70135t9vhTXUpCjaQ+Z+pcyiWNcxB84ZLXmL5GPG2WvnKhn4P6WXLrhrcm4omo0eeeqWzMfswwmyhehZ3coGApw3BIi4yxE1JjYXOnxqP6hjq8bzDePdv3Nag1I7x9MhlIUVh3fPPzChKXgNorjUl0bBgx61Dz2HKEsoIn1Rz5iFqloPRFhR14GWK5WLq1HJNKn3NUmxOIdln4z7bkN61qkkfp8rKp05e247S5WJXhzVJYFr40+doVh4jfb96yYS95pfGbbCXuMQp6nnZmE3qy0fLUt23RkgZC/498+UtM5NFUnd5klaKqwobXbhhf/1ttUJ8fZX47oPLPfunw8t5jnpIpUbUETzx+ZdVlBM6qxKSuryqTD9dBe4P30baOSqx/67H4y68KHEkoXrF/HxWXSh4J6xjGwmSSXpWwSB32eDGNFMvL4XdsXKTIsEI8vyrptcyqFK6J0noOhvX4+VH9sJNJKbryXHqnmVTC0GeExRHxOqTedN+VcyYVDjq0J6tWF0N1wZNGi8UUlPmzptV6FuLIrpOH5zypg6zi2aO7d1fgbY7Ix9vTKnBowhqhLRfK0HBdg146phkbMHXL8/o0fG06n9B9oQL7iqe5H5pcjSOG2VNsH+TBz5x+wLSqDcKvNc0NcsvxIUZCpMM9C+JVLob9i+iQXpArzXDIA+tQ+kJ6TAUovZYHPJ/R8MPmxszFakXQOT9RonGgCEvaLcKEk9uRP7ekQO0cr36cPn3N93+qIRbvu/WWVDuOnDFdMlWyBLtsurVPvc7HSFn9d/Hfqbiwwb1HrLkYHGeZ/Lh71Qh+2O12cjoF220pI3u3VuLMZovLcxrKEf9+jGgMrRxhp+YOj2HQgLlH5KvkIuFgutTM4Fg55Hb7haZ5tCB3+0PtCmYBjm7SmWJaWQn3W6GhF0e1QrMj5bz7vDSM8VmYNvZtEXLdvs3wUKVD/PLEHa5bSnHovNzb8ZYlENGriXEzb4fwyZO1e5oLYebz9djWzmrca1zzvsCdjuESDfnZA2/QnvfDbccTXnwQVUu6F8MAI94jz6EmFY9MPd1CHpeBGj76pfCHNvR2h0sxx6fhpu75uqK3tfCNHxOq/6AdzfEj7g8PisBa/Wezzl6jwXxvoUbrqiqkLnEQLvLMxJWKCLPpVi3o3Lp2B6u+DK1zM6QyX+Ti8nEJ5qn0RmixSkNH2lbi4q3sx1phWehylpfZw8uHPp20nVhjV4N3YzQnH/8UgldW9+/WZrbCIpwbOEO0Cu7pN3YmCMei/sQp7UULqZggdHlPnGkVzg9QP917X4DorI0O0+fx4k+zmU/S/DpsXbJv523pJDxWXpKx4kozxq96/XJuTwUW3FTo+fY5Gjsj6TIDhlSIqWyJmuRfiatBoTsMD4XigHeRtN8sCqZpPHun/qUUtJDAliuv03FjPZ1JbaqFw9wF+3Zep+DSAXV3P/Ng/Ofnx9IoCLVTkZrgXgv3IyYDLpe88ZbpXro5qg7hXRKVZxa2YE3U6I8rlsXiu97+xLfa9QjLfiY0k9VKjotZiN661bMbcVja8ug020YoOHWPWlEWBZ+z02qsFzTBSfF4tVpOPckfU6y81O8rFeOF/rOzPIjH+yit6qfw6o/ukOfCwoNx79m7JaUbVvHytq+S97mva3HRVbi2ozEU15xnLXc9xcUkP0dTY6/B9zoTQ62L946jwLKMYqUSM6inmKPyZbF4AT7oV/w438yP245MMs4Xyc69Zbu1CSZtu0Vf0wb95/f6wbPL0PNA/jkg9a/zQ/453eC5rEBPwfs+f54rC+Ihl4jPtVC5ZLzTeV4+ifPjZD2BZ0LPdPouVdtOfAmgvm4tGfz/AAFenHRnk6sdlTwn5F8DMbH93ZvWk3TY6wp1DyRRifPFaEQ8evrP7HY6TnUz7rVatCP6x6RylxOpEPM0/JC3i4bbWRPWeba3o3Ta9OaVJTG4NXqsqpN5K6h6Bzdn8OoyAT/n8Ial/1TRYHzq0KQ6WzoqzHZ5pRmkwFX41nJLVQamtgQXzv7URspLbX7tpDO/jZBvRcpXj7s/zVOwSi5sr3IDAztGFH4Wu8WrY9S75iMgFuaPnmeVZNCw51TJjK9hzcRzEVEw9lngmy3CQNPZfJHsb61YfD160bG2NNyad7BkfUsLbu99vFFYlYLINrExSYfSYHBuopnHwTZIHrlsoLS+CVVm1+9GxhdC+1PvL7ExVBwf69Uq0kiD0gEzix0/U8EsS5nlodcCN3XWAu0LdNIOi6+dCTF2asUui6BAsYktYC0JLjL1zyCfw/nQmb5uPS+eTXS0sxU3zB6Ct+N+3ZSVJT+ycWdrW12XLRvBn6TyPIVbEKvCpF72z4Wv3Q37jn4maktmRIUqtYAdOWXSSK0CuLw5NyFqIhfL4iYP67ChoXDRGso9uSKU5m7v6L/Nwa+yLU57QiiIdrrZTF3MW4/lmxXTZnTidq5R2rtZrWig6E5N1Kz8Cw+9oCc37HUhZHuKJ0u87IBQYB/dYqADC7ftXxv3MhdZSX4mb1czUe9fIJVu1w6x6z8aou4X4YjdAkbSYhZmD2DUGBM6bqZe5dZllWDj1kdaGSFsuD5/dn6raBvSv2V4jO0qwrnF4dOHuXCQ9OPBiJ/LGAR/Ht4/GhURFNuBD8+S5n7cweT5k0vrTr9yJL2ftmjcc15/e2bLjotmwkZhdMPysU3IXd09Yti4KrA/jSsK2MLBC+0kSrlnA1Q/PBfLc6gl8Q06nlvCPXhx+/TWlOWqVaiapt9nycsrsfOfeslWKgIc5FOu5JZAd4SNzJaDDNxzPNzW4EBFVr/77g9VJagPWMmwMuvAsnndL8oCmjHZ4tWrz7Mq0LZ6XKDWunZYrhuben1FE2zol6J+bi+HiP5TrbAbHfCoGXXBYBgVu01CqsbWVmIAvlteBXHJc4qgIx5lS71KyHOKsDOJq5ZYt8BL72WQnWM5MhU+njNfzEHWR1vxuj05cA3VjJjlQIPJ9ISskT8YsJP40dL7Mg1p57WeCVfQMCbPaVt+AxuLR0pFO/Lq5fM3fcaq/GzFRfrB26/7WVC+rj3wfVUhLhx6KxMl245f4z7u5Ggw8SijqG/U8hLoeTnM+bShCR5RfRr6NR3omdyT2bkyDh/Prlk0I64FjuN/iG6bz8FhyVmnR4nHo3RNkL8+lYp9Hc0aszd3EO3m8vONV80IXOKSsG4Zh9AzFT+0u+vyj1AJHi4hn4Zgo22GX+9RCB42PNb62vfLl2Dppg/yx9a3Q7bKaNItTTYRL3PI53wEz3sI/k+Lj7eRON+fi9Ejbp0ercOE/tWQh5OVOZCMOqy2uSsPns7M9cHX6ITdOEjIb0gMXlKJrs/NN6u5DeS59pWKPunb9nlYA7U9oYlUlO4KuHV6Qie231Q+VV9RTOCtJD5DZK9RFK8+NazcR5syoxkLXswcfjGGQ8gX8uVXU0j5P/2klvQTYeEJ611YTcj4oHM97mY9iQveT/rjOZkgDkziZ7kKb6tEi4fNraNnONhNsT97VFxgt8H3IAS/E8yv3/JcJsZKFGTGmQrwMjxNy6iL02Lz590KwfNCdX89P8P3Zzq+rtS72qRbPsSfaWDIbzWew5t3An/my7fjQZbPNZmqUlJesK8l5PpPdvqwMgidOfNJjd5I4hbWaw69dS3k7+taNpF4TWGV+Fb1PFgbfynoiqHgRKD58XPaHIwINY+f9y4PqxfufFAp1AKTnRuaJqzhIC9wZQ5Y2Zg3QTyt5J92/n6jPRcZzn3DVLUL+fuTw2lDznHyyXNbgTxXKs9uk3gOXz6TDkvDtNUpb0oQGjNDvLe3BcZ3GiRWvmZgi6OscYx1HRgrlkuPzKEgbyovsTnDxbCXsvuV9ZrwacdZkV29vHi25+nB+VM48GzK/GH1ox6bq97dMf/SgKhG0eZlSR1I+i6vqje6HNfdLChj3anYNYyx2vlrGwyTX98SLajHEnbN47oDDXAOj3H/PJuJBXYH741wq0QjY5xTWDMFvZEyk0TmsNHRpqrY9qgSuwzrf2rIN+GwwXc7JU8GsrSpn3/Nq8Gef3xDFe43I9rr8EMPKZ4+muWL9qmUo7ZgRuCtcxQcLOFuqt7Awus4/+itE8vhccLJ8o4bHT8ulXrKUhgozXz2QKOpiozbv/2Khz+WcLcc71ZDrjvvIz5M+mVLhXDwCueskBqEpJcax9dTYamStEK2gwZ1sysZUw7WYISa7Kl43SZeHrT/yL6Rtdjarvmq2qEClt17L4QbUXFtxblG97IWWLzctLhAoRRCuRcYX/a14L7dZ9k5eyhwpXjvV2c0YtMH6TWLee17VC3N5aq2oGxJ0I2CH83Iav1oatDD8+uXE/0jv1bDfmasodL9CtCcF1bkdzRBMUNTwXhuEw6dCUpZxqlGesPPjg31DRjm5WU4xYWKbub+S+797di3/6T0szEFsFddbHjAm4JZcdvWHCng1SchVc75XSVIburvOlBZj9LhXxecf0HHiAf1FXT1QkgYNzaN66ZiYN6am8G8/uwXpiy1uFwNY9WU8qNiVNhdH95Re6wFSdEWRZfmlEJa2NfP0JUCqanSV4+ubUEWtS6KO5Nnl5HyF0a1N8D1XAPt0fZWzHlqvq2vqAxF71tkWnvq0Z4S8Y3rzIL8cKcbU9zq+M9PHOJC3sjV0WQ4G/Ej7Adm8PiddVd47ipkY/od6QjHljbMmLP658NzZTh+jbrteRQNnj5HFzRzGEiNqriUzKu3S8VMh80UZeLkcpu3CrcYqBdhmaS4VKCo8cNTfwYb5vPWq4os4tX7S/ONLtmV4uj+hg3DbFjY0xVXZMrzI2Fz94X94tWkPq2yB4zlLtCgM5HCUk4sh8bVR9Iz7zMIeTZi3Cg3t1N54yOlO82pgAMXyWkWco/acO2I+Jwm1RI8WxwWuaC/DQueGeyR0WYicnbcPNqcEpyX8pa+xOzAot8v4LShiNGpOuJCFYrkDdt03DrwUzbSZHpoB3znPbr8wKQYbau+OQ+bx+CfL2ymQPhbgJHzuSY07U7grP/cDq5W07mvb+h4sSOrbqCsGvqRD1dtkKBD8WTPrWi9Vnjh5EITwzq8uhTTW5zaAv8NjOOsizT4OYaMnGdQg6isL7kfL7XjQ51ztawIBary2UYKphQUypYXjT5Ix+mwO2FzmBScPJGgJnuyHpam0zi7eXUY50ba8kvLqBg7t7L1iByvzvMWk3h8rQ1vMy1sxa04hH2q+Pb0YRPjzsbx2lULMjTrkDKPk7NkJYesT/jrSe1fz0Pyn1NuGPKeHCGvXEU+B8lvl8sfx3uV/HExYGGEqUnNEjE2lOa9pp0wKkPckyWLYh5XEf/HwEScbVuew6EyLBsw3BvKq49/pq83b+L5UcvBgxskeHW/Xzbt/npaIYxPK/j2RtBBE/JlNWTXwW3LFZX7s+tx+IXHu1hRDhq6H7l/+ViHgPVP831SS5C1/LHeaUcOwt4qLe67VonxX+taPMbXoqB8zO5h65jQWnhDsq6hDHO1zcypl0qxbcUjI4fDDORPMOaqvqjEueRF4ybq1MEvpcOmO7oD28dOMf3qVIXQHr+V2zcWQWx84AmZzx2IaFilnF5chuB6HU1mYClmrL5947McC3b+tPzltCo4OWml+vs0YemZk6/7GpiY9Uw+4lNUGaZcDEna6kHFsGvUulAnJjSeZ+2dbFYJbbX4ByKGNMxPzNe5coe3fkk0d8ksKcc+9blhOpmtKLmunnLnWwdGSn4R22JQgbsKMy/bJNIhdO6TY6kBG++8JxVOpJXi4H7pd5mpVJy+tm1fZCEHdULT709hVkDxWI+tyxIKuGg6lu/CxngJ2hfpqiq8mTsmRWF1I8o0gl4o6PPitNZpjTuZdZh75sTl0AAanubslj84shPc50tHnHOphc78wzWHXCiYcrd9rnU2B3P8vp05Y1uPtgiaXX5nI7rtX46WSeHi09gEM5GOBhj4Rt904dSRuF7d/TVt6Q0Y9e6saGBFBYmvnqp6fJVtHSFfjasD9yRk1lSC1dH3gr6EA9Xdk0dcWl6FD7NOvWv3qYedp7SE000ulL7Xna/4XoeOrRoj9HvLsfmYee+t0bx4RjNNlYlrRLu/RJtlUzGyxypbybe2Qb2v0n/FpgZkGn0fdXxlHYqv00dOMecgP1ndaYQkb3yuDNvxmDc/Bfzuhuen2ftVQ6nmRnrHvGqsKc19ai3Dm09ZTWcHXpVgwYO58nZTy0F/anF45rMOnFybiltd1ViQ/WnbJ49KFLmUsUan0/B4HXemwfgmCG3fcmvL2XIc9jy1Y1JNG76IvZx+sKQCCV3cnF28dUdccjdz4ngmWGmr1K9ZlaEjIk5hPLMU5o9FPe1dmbi2S2YdI6IZVce+m50Sq4XVw6MvL49uxXu1bKOHb6pR2zT8rptjNWYrzKGuX0PDlugibnxPKaKr8oxFlGvx5W7AtBMPmVBvN7B/ySpAibmYwaqztdge/0VVz4/D38ff3oyB9Q8ma/LyEoEdaLvVBszu12JPDGeF3hzKELwSkkofIse3NJH2PHTcp3GfWSlanaK+TRSikPvkoje/zDsWlA9FqcUUMV683XwsMeTL1GZU3KFNNV6diA+SQVNs9nbgiWkm99BPKn4tOd/gWBUAqdvCmz58akem84jRK283EvKRKND1d1ri04Y8WdO2VbsopLy/yvz+oFwGqqW61xX710NU8e6sSqli/OcYxb2FjU0u47VjxlNh/1z9zPqOCHztupK2sYUJ39jWzdfeD/JkLzRMWv4PB8P9Jqr+00lFIGvkFI5HPPYemZU6biKDL39qUM+rRYvNxC5xsbe0TS1Gjwrji44uPfqZRH+ZeKZxdterzEH9KebZY1LsOhCyZMcee/EmjN2+qYhmXQguQ2XYu0Q2LqaPrU2Y3wyW1rn7ytOykERlJfwUbsXBFzNTmY28+bSXLh+mlQXVCV2Pzvzi5U0fc066j+RAqa+6p/xNONiT30vdo7dAaoc1CntY2N4dygnQ9cOtwmDTr98p6LFVW32gno0zp2gtNxcnoyVfe/x2EwE/B4+i81YdvpOI+rzPJ4ebNoNWSd/6NZkJa8cY6xHq+ej9Fb7i+CUqwcOC2Icur6+bI3G5/e3qKScE7bJhGB/I44lEw3jftAJRKhbKjcwV4a3nF66n3pRWL4LabSFKjRsVEfbdiZc/8dax5ee60iZnY9W9pkCHBF5er16iZbGKiY0fhRxrNydjZ2V+5zLbFoScPR+per4ZV9ft7P8VH4XoRQffiK+kwjpjhuzGp+1Yalp33fFcIVZ+KL9flUxDjJl51tyYRrgtpQSOl0jGhBOWy8drtYJ+8NJF801ULCoVslupEQwFW7cXMvF03B0utTsvhYrq4c9VDoyMx3LdoGVOchTMzDt1cmBbK340R+9dqV8JCzHT+2oxTajcveBjtiMNmglXOtI1iuHnnL/iiWYLnr1eEtfNpuHFmTNvBuRisSdKxqXjMZXAW0jcsSr9qfP6FvyYKzEudUI7WpYqFp4wTYN9fYqFbj4VT5NV365Z0Ywt4ZfnzxxXgdryw8sTxej859vf1COVtVClzycHVRd/7nOrb8ObD1G9gcoUTPo+43Ts/DTYvLwnaqvYjlObMreVr2uCRL7pp3c5eeiO3HYpY2wLfz+jpwHb9b6pP3fLx9310624nu2I29iX4t3QhGGLz1Wd/lkCax+zZ5Gr6dhRJGOneq0J14tMXpxcWIUdcxoVmm2asSdJ5i2EW9DhmrkicF465nd33KlJpUDnkGXN4r3N2GcT1/quNw3+16dXfkxhQbnlwuMQXt26Tdj/Z6/MMxj167en8/KyrbY5tS/qWsn55fA0037vSQ62FhS1i2SwSPnfPGBi+LepmrumcLHdSCLQP/oD3w8DaeR8mV420d/9ZyZRDw++//Pn+xltJC54D6fSXfL4dF7devSxZv+6Xja2z1uyae5AHF/Pcjox77hY81bJQKkvAe7LykXq3nD5+7H1dPJ/pAS8v5v5v/4X1ke79zhlKoff3+80uGvfS3+69iMZf/h4KxGvov/c760f/F/g/FO2ymLSXL4dbJmEHd7CTv9yeRFvHVKTfDzcgEIj/TzP2GHZpxza/6nsOsOiSJowYhYD5hwOIwbEnO4sxUwwY0RUUMwBVETlVNTDhFlBEQOCWRQ9DKgoJlSCICA5bE7sLisqZvlGenr6pmf3vG/+8EzRW91dqd+q7pmBLtmzZjatqoRHc6dFFcfeQe892K+A5R9bqSMmSzn7r5no+rUTkzenfYxLWL2F6CWmskd8RKmawePxtyJOyzh6uXwmKZD8ZxVx8QfpUcPqsYjTY7mcB6tgp2/QxCDHIvhzXYXS1xcjkR6t5bBtm29D6EXiWJfxhWt7VlZATkS0atcpFUdHdlXE2hWxExyfk8x1ta2ZvArF5wQo2/dwRcu+Ohg+vs/6PmZi8NJ9udft/GsOn5S/Z+q2iMMtmM+FJoei0yxl8DCxRb/QDy85+uZ7Q/bWLJNB53Y3xQ5tHrP2qUfjuVrE7uNEIDlo9EgO8zQwebN12ffK5H046Pke8j4ejLvxe3Hwc0Llen9ThPgM17N8ItnnfrR8fmbhqN/OrB8xOBreZF68EHkH8XFm/VHP4KvO/pMnzYyGffcmiq4uzAKx67HbmoE6GHior/PHTkp4GbmrX89kJn/4e/323FA9iG/ckh08IYeEEHFmq40ZcMfXJ/eyixZiXXY3Op0khz9Hxp+ebZMDIZ7Lp62rIwff6o/mF5+SofY/0iHtQoPNZ0VqeLF5yJVZbeQQNW2XrItfAce//P2N4RLICnw357fGeeC9bMXtdqO0sGHUgEmP40Qg3Wc/6O68fKq9DJZ8D4z1+poBU96tHOXqrgSLVNHXoRoFVD7YtPsZWSZ8TZl+OeSJDhb4WpwbZCeD3RGjMzy8cyDm7Oh12/UKeB79uYK6jxiG6XdbJA7NhAPHLFxaJajB1e/woc5PxRA80G/bxwG5HJ8Aj/lf9wdJ0DmJqvnU++B0EPl8uP3f6/KBgX5JxUzee0zr8PaglwZOdH7drUZhJuzZ7Pr9x3o1zP/xut8iRt7Nn+XW6HAzF0Lyq6ycPFQFlol/dtsZp4L9ecGrZ255AwkPA6deZfLJvlPXxbxx0gJsDersdiYXbvTK3pYTUgQjr+9o8W2qCib4FZlVfJUJOZNaHf3TUwFlnstfud3XwJY9+dm6xjmo3y9a2KXqsudBHQ00GqAdvZ7B8f5mAVYjmLwRjVMLVbcsyoramQ12Vy5NnJxSxLbXQkJXu+sNe0tgSuquFZ2+5cHI9mvMgy/ooOXakzHTLeWw9sLYAy8m5MN+x+1jkzeo4Yfuxuy1zSTQPn5XyOxRIhi/OLdoI5MXdd20eK06WwSfzKt+LhssAsshi+t3ZHB3brP5diWDpGC1d41nc6dCGCVP/fvLdz1LZ/JmN+sWcR/zOToajwxCzw1ulsbgQDwe+2oMgkiWw3bPwvd/F+XCOKtG3muZvOFaSb0XU9Jy4LL35wPfGH5v55+pdnGmFoZuvuORuFaEnl/aJgFLsxdPty/VweBXved+XSSCuoU7p3dXyGBBSVLffgyued9wzJwmDP5vJJHbqOxlXL23Zv7W8U1VufDYMffhDS8JVw/UOz0edG17FhyXeE7oZqeAjg4RH5vrdBB195j2oYjB8z8NfbgUgleFBH7208HX45/ez5LlgGvYgI9/jlDBlJLKXtey9eDkuKTJvCZSuPGokVn1SDEnh4mzA70XXxVDwyXz6iUnSjh6+bwyZVDY70Fp4g1CR/ueIq5ujN6TqGf7LYQB0YWhnVaquX7L3+PXRgSXNfK1wTW1IIstO/7HCR33nCpdRyjnV1HC0RF/Hbxoe+fAjb8U0G391zsu9hKo+6S2e2qZDmadW792l0QFz4Z0WVNcWwQJ+YOuZVYohqqb6o8LUauh36mDaoeQfJjXItnqpruOpRfBllUjD5eOzIddVax9agzQgsRncEVNLSWsObpVblNHBDM1ba5VTWP8rMesYfZxCrjV/XRjfYiI67dOjdJmLklK+PI5yGbMnQJoHnst49pcLTyxeHV8niuzTqX2DFofJuHGUz81LrHwthqNc6+Yo7tHn32z30cHiuKe1VK9RHD2nXj6lita2BFW1lnB+Gts5ewbvi0lsLn6jkrdMnWUvIj88Hkm+vlSfD4JvYe4kNMbrz5jkQ1BCcEnljD2gvP6ETYjdz73y4KA+rN1nWqTfD83I6KgRWYuXP9+x26juwQSk6MDF69j8v1r3ie/H88Gh98db81k1l3cvrLzx1pe7URwpGRG9Xwm37j3QZ++RSaCNc/6D9zB+H1eUGG78zvlUDBVbid/KIJhK13inLopYfiOLgt+nvst3NXkXliiGFRjK926WFUOl+6O6ju1mwbmHemg6dJHAvqgcVNqrpXA73WCmyzNl0Phiiipf5kcyWOvBO1bTVPDzcuyC+bNpXCzzDmg6VUJVLoeG1L2lMEFbSxGWXwvhA/b705OqKiDfmfvT7H7qITC5sPMVnYVcetF+fPaTFzIcrc6m/27iIvPyJ7VEN1DfalZpliwvrjaSAb16CKFuY8bnB7VWg2DxlR6X9MhHfJdvi0bZyuGgHrvG6/fpoO/9jscdIvIA/sOk2IbM/Et8tnyU84dtDAyodvCrwxOL81oUMvnHGP33gUWJYyf9N9o9mLbiAJwzQuOVhyXcvSAjO+tAueIwe3RhH7e3hImb59+KOmMDoor3x10IEICxfsc5laaIebo5flxlhiKLh2Wrt5E+Gdeuv5yyM/9tz1HD7tkSaCVZNVS92MyiPSMrJZeMQfyh/ttbTFXDZWS57/vdC0fZtcNzzMPKYTIi6dKdq5QgE3kiusdJxfCZYl89siTBTAlevznTC8pjHQfei53kxxmfb36xGuwFGSbTj7aPE0F5r7uT1ZHidF6tiYdDhx1TDF7K4MmHc7fqztMBH8GDvJx1IihzCs7rO45GcgMH1aGDVLBg5GDF+tspfBo6VVnl2timPVI0mXmRqb/H26e2TNFUDZtR20/Bt+OXWv/eLZCAve9Ft3ud1AKayp3h0ALGcSs6NSvi7MSVldLdn6pyoZc2XCzQIkEltQ6W2o1UA37x5Q0njyWie8nSlQjP8kh+UFJmz/+EsMxt0ev2p7Phge9bOYekiihypLhd9uJC2BBccCtzkx++9avZVllcxUssFy1d46ByfOe2JW8Z+xqXqvPW/2q6mHW5MLBr83FUCj3Xr1lqwz+apX/pMxSC01CGgaebCSB40eGfnFk8k83v2uqjMtKCHdsdOPwJik0npdQsqapHKrLPL4H+GghIPugc92rBXC36WPvKcz4PJvXEnVbyeCVg5Fyy1YF8PM1fh79VfBbi9l9c5OLwO1ihuOwjoWQ9nm49FqGGtV9LNVQscqYU1M8RVA8wMXjhpcKkpxPeViV6WFamdJ2doEIbmTeg4LLKtA1KLuf7KoDD8dl8z65iaFY8f1pR3sNLD0cKm8WrYY42c2QlAcFsGzNgPZtqihg2p72V/ZMUEPLKqNq91smhohSl7nHn0vhwbczY3bMLoZPzz6Lcn+TQM+VRwvW1cnj6PKoSTY2FyQQJIb8IY8KIdN/l+ePDgxeGJraY+n5QvAJ2v1pbOLP58VtJ59qxcjT2b7B5gARxJivM7OMz+Hoyf7jNW8viFl6Jng/E52b76mFEY75OSprCVxOtq41s42Y63fJx7JbToxdovFkg7xKsYdygR52VTq/tXkjxq8mzJ9l/p3BH/7Lr/vaFsGYha1XblWKoObhBtPM6hdATIBb9NkjWnhZJisueCuGDqMr3jkckg4FASM/tz2lgXqNGnoOVIvh7vCAxiOlmSgOREq4OHC6Gyg2rs5B79mxlHD1tB8Hrwy455YBwYOV1o6M/Rb9PN7ppoUhFqenSXZloPjQQcb576Tm75uH1syDucsCThhKJJDTt3+i51wdnFxZ60qrdalsXUjO5b9R632/qicksHkxoYt62H+71+w1vDjRwWeRrRpOavYfF/troPWA5wPmLIqFV63u33ufpwbD1lPv3s/TwxzfuMFrGySg9pEqWGfWb2RADy3s3D9bVqPBM/jeI8LJvbkKxrV6/HeBnQ6cv1sMnOuRAHXjxxVX662EnlNvtDraVwkbIjOCP6ozodMtG8MtuQJ2N7cYV3upEoZGlDQpXv0ULvR28zj1XA+Nln8ebZ+jgoQ0V/Vv8AyyRk36VCtOyfG3XBlx/GO719C3YrWcB3cVMOfw6Ycpw7SwoG/ACcuAdKjQdKD3+EVaCLQemDklWgG7Tm9o86R3LMw4ERJ6p60GDvjt9yz6qgS/b0v+1HeKhBt+q68vGqGB0I1Pyj42UwAcjCuybXAfvpSYa8OYfDbj4sntUWZqsHtrMWCQYypYJ3ZUfPHUQDOPBv5nJungTUOpqs3jp4jeowjmD3xh67RRDem+R3y1Nrdg1enc/WdDtKh9fS2Uedw4lLD0KXSoeDjh7GAdSFcqpq46LUfjjHrF0R2c/bte2auADx/rJywrjIMuJ+41Ce+lAreFa7z8zeXgWHZw8rkmz2Bn5J5eHa1UEN9jet6mSmq49KXx6seJd2H1UOuGNie1sLeGwcpLKYP5Wwr6rvqSChbvvFpWn6uCOP/lpVUvyWCAb+rOSmmpUNmsufOV8xrIaNMmPKlABp82LghsNzQDzeu6Ds2rlQYWP7Af7B30CM0rj51XL6bfLnGvh267y+kxYuf01XW7KsF2sPupQTXSwKNH5ciOHfWgmucMlqsUMN5QcaTTpQzOT7vvrJqdVU8Of7UdfTbrQDbHp9wOZSqQL7r2fv/sVESvqkV2wuRLyD6TYat9tbr2N3SI/qWIpcfz+YSrkV09TGDzZTa/PqJjz5c85+j4fRsZV20Zegz7PnByHgJ9Ryyeff+PlqK/gssDp47b3kkKf3RND1bEMPgSLm+vPkvB5G1Jq4b/EEOtCg0WZrRm8o1RU8PdGX2o8nTrF9lLocm66fUtWhRD0MrDa68xuOhxyaH2DdYwfNz/XjjnrY6la0HeefGao79LQOV0TPTlTx3o3ILVrlU1sAe+W9ndkkAFv/X6Ama+W+u8Dts6WgWT4x/mSpg47XTmxrmsB1qYE/B+VtF8GdQP2je7KYMH7ayadhMP04C+k1W1W2vlEDvEss2wBRI4uU7eZ/wkLcufyZuGn/5S1VMGnc78XfdqhArCXJo9NHQugkPnpxl8j0nA3MG86RytHE5kZrW/xODb/QuCVvzZRA5limc7rzWScXlHtzW//9ZwuhTmQJOYdWsJvfe83+3bf5PCom/L1+TUUoDo1MT28Q+KoP67K+ENT6ngkt/AzrOqyji83cQiKNidwfNjrSs6wlUph+envml9LmyRAhr1epy71VYGk3fvcCtRFMF3J98vNkw+RudTPb0alUZJpUie39VsXNHBjNrvx+dWkSO9uOig+plgp98W6/jv0xlC8p0+7YK//nVdDu/2lsXcrFLEttfz3ze0uJg7H4r4K1B7fz1M9h77KeGwlv/eosXkPd3l/D3lnJ3YBLw/4/1QDZs/OZ2zDVFw9PJ+NRp4e2vjzeFMHlN+fra7DCIee2y2mKiBQ6vc6uieqOFrxQNmdQplEH/av3OGuxrazS0cayVRw9VtdmPOf5XB1bAfDy0LdTDGYUD42n5M3A9WvZvArEvuyfYNPaK08PNpvTUrGByR5bJ+xmUmj17i89A2rgg6B2Q7xU/VwAYn99g7oQqQPntsOYBZfzpMvf7JyU8JO0a2nZFop4Lutpmz1tuw7Rl7KW8/V47at9Rx9Cnu1Ze4B6ggJnfhPekoPf9clIaSEz7/1FBBPWeN/ZrobZx3r7DWEVrose5aw9q3FRz/wm/+O6ss0kGYekjzwm0y8GqRuMShsxhcGxw4d7qaDrwHrMs4zPjBXIl02h9OBP/ssPnWvtpTOTz1Chs8PCWfo0fuHjsheY0Mlhx1GpSxScTHY65SyHzU8EgvRi5Db3WdHVRBDzt9gkYuc5ZDt+iCDQVfCH4LrKq29qomh5J1zybtS5fB6jl7Pq02L0bj78eO/7mGa59gq6/yeqAURKfrXlcelcPFx++Cfk/QwsFXDTYN7KKEM7d9fJJDGfnFvnwrY+LNz7egH62shBqLHZOGFyhhcOS+6qmblLDp4fC1muEq8PHdkHl6nxQ+trIZoO+thr5VXUKmWinA02rs0nnt5DAhfcSzbKUC8gwLenVy0kD/0xPWLMqQQ5+fYfiFDmKWTQxIqqAE12PuvRPd5WC376n/6NMqWHpj5B/Dh6nhZZOEa7GXVPBH9LkhHs/lYF3cr/X3Fsy6EZPvdOCWDNKzbjxSbVJB5her0G7PddBj+o6XHy4qYZTkQC/9HAUVP1XQ8/CCsGOrlaBcWSGi9QstJK3/y37LLDlUUjlE1egk57ePUMBl87luSRvkHO6at+7yu1dXRTDg9YyAmYWEfuxe4P466/Oh2pGaI0TOCo5enmc6SiDk4rwRvUJUMLbGu1YLmmmhTpucqocb5YCvX8TU2x3VkDlukqXbajWMrP7F4o/HYni32rzwUDojF/9JXbxu6qBQ7L1wWs0CsEkMfNuQiR8nA1vfiV+lhDmdql34ULcQxiWu73F4lRamFzjXObpdAZMvLLf5MUUEr6TbxtowOKOrzd54p9UKGL7cv7lhnQTO9pgxyjlNC7YLf6sQZqGCI1k3Y/JTpRDXY/D2tDwG/2W79EmeoAS4lnjabKoUvm/+e1xoLQbnV2sZfXiOFuaveGPbspYUqvT60ujbMBU3znh/cUHGfjF6z2x7NVSfaO3d20YN2zetqG8dmAcfzfKDeg0qgo27vxQWtVLC3ccv3RLX5UKG2eepN3vo4MmwpztaMePxDH4f8SyfXfdHkfUdrftp7DpOcKlD7pR3U1+nw7um3+5k+mpg8OEG9dc01YHV9UOR7Wew71f7oOX7/ZA0cJAfm9j/phqcLNe03hylhxIv27qacXl8/DlQw+KWN9C00pTa3VJ1LH8NxID2uqsqC/X7TcvStVC1wWmX6oeyEA4pYXHIQC2T930do4hKh5XJ787vqcHEp4LlY2r7aOB+/zGLrvvlcfRzNxctvTZCDbmVYs1bxhaica+h4tYQ9n3Sp0n8w9+R5L2/8h/v/U4ZbD5wWRs1eDl1azTHSwPZt3wXRTYqhvH9xotO3FfCtUjRxlj3IpBMa3z79WYdiJMNHrUYnKfeVCvpuEEDs6u+ajohRw3N69+SNbZg/Pc3cWyNWUUcn8U71dv73dHCi8i+fk9+qOG+L6xXv9Ohfk9rUL+MPHD7cj4JGsSnpZaj43GW008w+UuiVYOZQ/XQxal012ilEt4ED0xvGM30vzxmru9lLRq/rwp2xflbLxmrR+M3K4KD02Oe6FaqYczo8bf3fNbCmS7zXZdNVMOIU/t6Stw07DhV3DifBuVn1StTQeUGGnVbgxqetcp5XsNVC6WdXPY97qOHoH7bl9zczuBoyYsHlZfq+fH2thqtUyd0iL9EB/X/0nRZbl0Enhd2Jw8eo0N8brJy6FmM5huoQ3LYzcozX4fm+4ht30fL9qvn+i2XzxzCp5yuLRJ8J8z4X/J/+vth+O/6+Acraw3QwEO/l3usLuvBKXTUa+uGSujv8NKrHoPveiSXhm1hxvnz8aLQGBU7ryJqXhpODrrg/RlNJhchOa8qgqVXR/ZolqiD4i8+YbZWagg7ankn3UaF3nOSrkX1pBtaWHVq5b3Sgyq4cnnKku1XtLC9p1kdxUUN9NiQ7Z0TrKbWOxWLEzQcvRw/iNXsOlgEWbl3RT3TdfB94qH2x8w1rL9JYWHN5/3PvtTDrmlPI598UsHA/aGtfI//xMHLpOPe6+BY0Ir+Sc5aiE11On98goJrL1vSVrrWTANrfhz7Zuig4Pw38dSTR5Waa2CZ+Ry/2nPEMFk0r/kdKZFnxaRDHTYtlrP8tYgP42/l/F/L2HEyOMT3qPmn3lq4/If2L/NGUhSv+hRx8WrpxvB6bQsKYcueosIPn0kce1rL8e9TPSWgOXajyfEnGqh5cHDEmdk6cC2FygoGF/P2o/9xHpAfl7QwQRz5oEpmPkc3e/nipLuTFlxDwqN+7uuj8ejZfnWQ+eb3mKQdOI7pYdWXV6vf7NRBXedPve4fE7PtdYLxo3jC+ldV1t8fqU3aL98fsV9oWb/QUX6hQXSRno1XrH8NLaLiJMbtKup9vji+Svjvl+TO4cs4Phhvonv8vSgzsxZ7NrR47hnCjd9rcKBni+fR3P8RnXy/D7dH/zXAKGmr554tznPt318PkPbfq4Xn8mrPf34XlEevreLuZV7bmHZhoHRvyrRTCdr37J/EjCOZu/ca8Ji5D4UT++Yz/adwdHRFwqvnPfdK+8dR3100M0P0F4Jxuq8MZvq/J5ADniem0/P+r+24cVPy5H/X0MwMzwfPDz8/Zx8etaO0Xgo1jzjA8+X//oWgP5ov3R++j24h7r9XesHEvAyC+SF9FVP8ngLSu47iGwlIjxpqHo/Z+yKBvmqOXdXi+Vs1uPRVzSutdwno79PT80X8tZy8+N/TMQjspZy/px6s36Z42G14BsgOL3PtT41zCw9KeQDPRb2ZeT7n6DOtT2a52j0HZDcPOTrv/afceNK5e5e9K5n+ogD1Kxe0L5dniJy7D/fcJ/35XV7E5w1H539XUyLQa9MQBSNvIR3JO0fAB8XVHHY+j6jxqFh/iufoqF81d4/fj4v6JeNHcktgx4/5xFJxQAF03DGjLkyn/Qnbn6l4hOm0HdP2Tdsxjk/G45CSu8fyNSVv0V+d4xwWSrh7t9idq4NSTpr0Y2x/2B75UoiCdo4GpxbP0wTy6TDt6cAch3RQ7cu3ynG4w/0/b6vl/u6hr2HP3CX2G6oTfgNaJzDjxXp4QfF7Afz/M+O2DDmV4hoHnRtsfpgWQey5/LuIzmkQFybPDW6dzdFts151iwpKhGGRHp/mHiB0ZF+53HeyEDUc3FpqF757kmtiXlnQ9EnG4dJ6d1l93QbnubUXnJ+ZxbWn1w+aDz5PN6DZR2ZeR1m/EQH2L9yu3oOP/RZ2F3H3FZ+lbE9xvQHGx03mg+/x/2k/QvdJsGJ8WLMN1d9Q40sX+AWypzSB36H495KdZxpFT+Lu4zb33Vpd9xIiqpadqfl7Chv3sgR2h/w+g6OjdfE2eZ6RsgN0/xii308N82zxmprfQy4umtIfppuyS1N2TMdNM+rC+sTjx++ND2jXYnZ4gEjQHs1fTn3/gMRNvA7ScRa386nvfaW0Xg58vFPxQ/soMl8k5zzKTgxs/CN+gPOXKVH3xdr2+Rx9eOnTi9u8SfxD8TMZ8Dp1vHByRPsoHI9SOP782enZ9mSdxfHNeBw1COj8OEz4Yzodd3E7ms6PqwZKTuls3NSycfM66zdplNwese2E873kMKxV/ZxMVj8qyh7T2N+rBXaO5UP7EeaD/BTr6w7XLxpHOiVXHRWXo7j22E+w32B6syYxD7uHJrH+nUrJL41tn2hCbolsOylHtxBV62e3IQ3qd3VrqWtP6Oj9SJmgH1rtRVAK8ZsN0amhfudz2fnIOLr/sdysJ1b57LgkgPi+pvgRO+bjMRGnRzPeJWL1+kRAR39fmOTPty9he/w+CyQPMRXPDew8VJTe9WB8fWflH0LjMgO3vtN8jK/7Buh3YcKemEpirn2Vlduah/6hY+2R6AHbP86T+HmJQYCHsD+N/lRQeDomj9L/G5aeLpCz8XVHIdAvlud62ysTYrYVcPRHFs4BQSk58KNtlz4/4x7NP3BPhz2OEWQ8Hau0zbVqL4Hhqaub2ypIe6+cjL+SkmUCvEXHG1NxiI4rOB/l51VYX3qOPx/3G4DOH1tYuA4I/S6Cm/1DWvncfcz9ziVigdNEf5IfW2fapn+LTGT7lVFyiDVhP2Zm5d85ySHtXwdUbBQVFMn6JeGP10NkX8R+8DqM7KeI6hf7g0bQnl4H0IXzRSIfjAdoe8N6MG6fJD+j8zYs51/hbOP5JcnfjeuX8KdxP5138uO1gYsPmM77zrAZxvfZXHu8jtN0U/i5cWzd5dtK0wHhm0KOfmXhuifZDjkCelHX9g33SjOgrs8x/xHxZB3C/nT9XfSYuzcJvsT+1MJh4vt5c8h6hueN829MR/LD3zV5SbXXsO0JvkPykLPzfUbx0QCuV5jK503l/3Seb0ZdWA84D8X4C8ubzlvxukuvxzguY9xE8+HTybqO8Qi9fuD54PwW07Ec8HenMR3nFzRuw+Pjy9XAyZ/GQ7g9WjffcO0xXsV5OZ1n4Hyazic+tqkkc7gghaYX+5pfHx0nkJfxOppBUEfBuITGTfj7NfT6jS+0fhSwciT5DDy9PCEqKAPa7O/90mr+K+77O3deX7AXqel88JlJPG8Kj5/pPbKPrn0q6KcumFrlZCJHR3m5GPj5t5lZlxkFK0vryQDnU3T9gj9fYb2jHFd1JHE+dau+Xn7wa0A4lfg9HifGLZiO4kEK9G9038vuaz5sM0u+MvMdiS8HrrnIt5VmAJIb0deH1h9fxGxLBYfxu3o4Lkym7C0Nuh9c6XugJ1mfrznNmtz9txQY8LRZ/d2TyXiLXNoxeOAVLPmm3Ha+OcE7sw5eltSbkQ47boa7High8n+itJ4aGpMNf4wduzE/mPghinNZMDj02yer+cQePJw3rt7rXgCe9bImwhWCO1L7XtpbOlEKrSr5eOQ3I/P68aNE5pkngq85fT0n+hN748cDA+fnmE77nal4gX9vHL/j9UMPuO6D/Rv/DtcV+HVcjNtIXOSvq0R+/P0siSBe8fNREq9oPn2dw+r53FVBm/VvBkQ1Jvi/c1Xb0sjRGrhffdCUDTvpfBfHVWK/OG4jeclBPDen38epxG49AtwXVXFVQDvf4hUprsQ/P/9Wtyw7QcPiRtIe5bNq+KNJxgXXx8TOy/3soAaOvC3uZRNC6N8uD1/ctZ6c8wv+emaAkmFWP/4OyhPEY/x7P+upMVbz1fBl3waZ62NSp9uzNj5bncHgsrO/7au+jOhl3NudDlVOKln5kHomH2eR9nw8pafkLAPxu8OXUwro+pOGw238+pNSMH5sZzR+RXqRc+sqtje+nRE+tB/w61K5gvYoruQDzlO8pzS/HrPtFvv/LK49rq/gegvNB+el2K6xPfPzJ9Ie6UMMuO6B7ZNvlwZA9axnAhyG4x0/vzdQuMYAfLzzlKLruPa4HZ8PjWcIjsD3NO7gxx8D0HUdft3HALsrLKtSP+c2u07kcftzfH3KOf1gOs4z6XhHy9uUHr5DRZ/uodGs3Yqo9neofJXGzxoOn+N+sT2i8RKcT9s7P14TfynPh8ZLAMcTTPdPnnTVbkMe4LiB6eXrzx8iwHEG0+9o591s3UgErWsumdw9VMHhEoxT8DjwuojXSUy/3rhGVu2ROWycI/Yf+XTmrJdMno3y5myBPbs1mDaioFAEFx+3eDX2Nqnr+UcFDas4ToLGX4/EW/TckgTko8YPDXck621FH4t2nX6IYG976/U9bVn/KCXrNKqX58BM+L4gaR/ht2LYpAnTwjMg/pKLbvsIQl/v0a7j7/liVm8k3yy7MuGvu30KYVPGBbv8YNL/6tJTFsdbi5EeHAj9sHN0Tp3deSbwJ+sHgnzQwNkL5oP9CdGLKLoG6P0lnMcYx7d4HSPzwnU/jOcwHdf3UpqJq1lXJPTy9cIOf8cQr58vOP54X5G/32jgxsmv0xtgn9+SJnsKyPpsFXjyod8CCcXvGTV/A/87k84kfuA8A/uVcdyO64c0zjGg/ZVhRJ5N9wWuWLKR1B0Rrn/B+skbmJLs49g1lfjlkeONA3oaMqhxPubql2i8RI90XZPeLzUuN7J/S+/rYj6m9mlN1VnRPlQcoPwgVxAnMF6h4w3GN2djzY/2NMgpO5RR6wTJm+k6NLY/Om7x8ZAB4P2lVQu7YzycTulTWA/GePyFehgT/wm+1vdfHb5FKwZFTol3xGeSL2Mc10fZv9u7Yf/YN2LxV9UL3l3O55L6YJ/IrCPDn8lgQesqp+pYkDiG/QbXmzEd2ZtKsM+B5YfyvyxKnxpqvliOmC4Cuo6P26H9SOJvuD3yEyI/7GdNliob5TgQP8HrAbZ7TMf90esEv36CcUEetT+kF8wft6f3E7w/XulgPYzBWTYHmfWcyBf5QxbCo9VVAjrmt/R2oL5eTjLQuA23xzgCx2NML7c7uwKOD17/aTyC/sZDiLiK78ajGtZvcik9qQR1AIyP6f0W7B8oTyX6RvabCU4GX1uf9cTPyvdhr2UL8k2E00XQ7saCEeNuEznj8dVe/PuZw0NJ/oPy0DxA+x0k/20sEnVY306C5teE0Es++L9ulyyDxRcH1HCbQPxn0m3bkBiZDn3v+DUZT+ys0qKxH0Vs/Zisk9mf89rl54gB1V3IvBDeFbHnsqg4cZDEA4wHaXyM29M4m6ZjPtg+6fzBjHcZTOIjOt+6/ca6DTgWw6KJNe7GhEsEfEzthxjfnzEz85IuvijpJQffBT3s4s1IHvhHr4P1Ty6XwcupV73svv4D3yyZzeAVBaqbj5cCXU/Ddkb7K8YPdBzA6/2UWrI63yPfCOjG9yPVgPJJERvX0gW/4++rkPiBx8Hn94wbN6aH9f/zzx6X01n5E389mzG3Y2jMC3bdJPaC4wGSP9F/hTk/hi3snsTGK8IHjx/5Pamj6LuLNi/bkc/i3jyOjuuTfD1rTdQtDSbiNl3HJXmT8boNjkMKav/KAMbjj4H7u3LndfMNO9MA1bkKqHm8YvOsAup38Zy//Uqu2E/4OJXgKIxHp817mepvlgP8cyUGwPvW9HxSm87v+tSKyAf7DcYn/LqLAYzHW4NA3liuxtcpkp8aXwewPt9QeamBW2/pvLlyz6bTUgqyoLzOqirk6Lpz0bYX5+cj+5tC7GvunYbnt/bIB5SPEDuL3Cvdbt82h22fBT2qvRq3sDuZ/62ZDmur63LhiM/CXqExZP4Dx8XPsNuQSe23MPnrtQ3+CZVzoGqB3qxDFNGHfmv3Ht6R6SxOEwnsJSjiwwi73CLwkB6MvlSDxD3pbvvu+/fIoU/AjqqGKFL3OlKxyYZpMiYP6rai/4YxZL34WHn0upq+SpZO4h2tb1x3QHoheQPGs6by5GO3A7fJJRJ42vbE71UekH5N1a0aLHxjPrGDFrboDSUOO+j9TOE5EnyOGI8H189N1a345+VI3o/XI0zHdaUUT8vpSd314H36z4xtjeh9clKHpeX2X/fz5/VzmyqRymDa2Gffdm0j/IfHBLS0cVSCo9lE0ZN/5IvJvezzdpap4ZPjmabz99PjUQjyHnRpBPu0qJ1cgGPLXx+wXgHjVrwUZVxRUnIT1h/xvMrrvEESwOdBML3JkPDgXjfFAj9C+8wyav/FYDKfNpXH0/vA9Lki/nx1gv1enN9hfriOieRJ4ije7+316N0jkZrESfRXAY8d3n3f0pXEb4SXJXBriSZuyHUSV5Cfy4DGvchvtKw+SPvyetFvGihq0/V9/TIJVK91avqgOBIP+34oPPngohwy018rW5WROITjDao3Ef/A8Yl/DpTgLXQugPhHtlvv+5eTJTBz06lbS4PJOlS+LulE8HX1w3WtG5F1aIvcJblvOzk877m+zPMdXV8VCdZlTEfjIfKmcSNeJzCeo9dlGq+iOqQOvhe07ep7VcXKjch7RfSX62N+KMEyalP3zDQyzskVHN7p4tWwzT/5Skt3Md/+/nG+hj9+g2BcOP/hn7sx/FIepvTw/+qT1k/jryEHoseTdQTXl8r7dxdRclODs19+6xM7JQK7RX5L5lN/+sjkKcH5MKbX4ooZjYm+xtUceWtEvBgsW17dfyiEtF94oWtxWgRdpxfqP3xMpX1Hj4gBnf8i/oD2NSQQVKl9oGI74ZM3O7ZCrqWUbZ/Fzo+M5/nXJenKxDfs+P9hrxfV6tdhGI8oODr//CqhI79NY9urqfYE39P1yJg1E8N/S1FDXfWOie9WCdeL8nq3K+6HxMmFe1ZuO+xH1ytM401T9T9T9UL6PAuKp8Sv8HMo/Och8Tr7gp1nEdU+SYATyu15hobLq0e36L78QxKxY3xOsPw7NePIOoz2g1Vw4ETvkzOuyqhxSwV+gvclaFyI8xkaF9LrPs5HaZyA13u8b8DfT8DjkVPyVlP1bQO3PuP/8+vEdP5B8jL+PEj/OH7Q+yK/kkelyfeOpC1TgKb5yYod15G4WF7fj5TBlYCXE2xcxL+Ut4/9zNDlDN7061kk6jdG9kt503o+pBz7YddhMn9UZ1HC6lzp6ZnvZBRdzuIMMk+0jyBi/YDwwfsCaB+a0FF8Y98P50z8F+UxGSzOKKL0nga4nojp+PwhX2/CvAfbPa73YvqEkkCPw49UUHPD1Fe2HoUC/eM6erleRxVTdpVMfRcN0+k6Lam74zyfzg/5+x4aSt/0fhrZt6T3CUz9jo/XcTvMT0ONv0hwvpLGc/T5beN4jx4P+d3o+sftRWoyfxSnlBQ+NgjG9+/yE8qj/H1mC1QQ/7fB8oS1jJqn0O8vNHjQrUqBGvxjuixo8rcY5l7z27o8ifSP8IRcoCcUbwth2ff3p/oAGRfCkyT+0nk2Py8S1kEwfsJ1Wz5fLbc/S+9D4/H8V/3x9UbGQ9uNKTul4yU9LzqO0ziOX680cOde6Hnh8yf4no67pp4Toy/6uUz+Rc4Z8Z/vED7n+O/tDNy5xf/aP/08Cf8y3T++TD2fwZ8XjX9TTPClx/+r/gnff5cvvnAcp9dXmp+ZmfFxCM8l/vs86fM3xM6M90/3Sy76d6ba/Wq8+HfGf0+PX9jPv9u76eccjbenxyfEPfTvaTkYHw+Nm0zJWwXG7d+UHk2NS0XJ+9d6NW5fpu3m3/2MHrfKhLyE7Y3//7/2/yv/E/qFKf+m+/lVf/gXpuo9xsdN+jFun6bmI/w/5vvv9vVf5Ysv2h/oi7bD/zcembK7WMH+Hc5r6fNfuD3+Pd7XQ/U8Uv/jfyeCPI/K/z393oFE/vv7uXHEC57rQP09FfDHf40/BxIFdDsz6qL30Y3y2awzwcdgYr5ais4+j7NZ+LwKyrc1Ajmg97CpBfk4lgPvvYtmuC5BnUvYjN/T+ISj8/WbKNALqkvRz3nj53ayBONE/aQC/T4J/PwfLQc0H4UJ/jLj/GMVAv70c1BYf7SeaDun9WfcLmJN0BO5cZiyb3SFs9/Dpt5boKH2FzenCvIuvtyIvvh6IfkVfd4Bt6PltentvIif73Hw6PahgDxnTT/3iPUQDvY1b65t8TyVmlcU9B2oPE+eC8HXVQhbNrml7ix9/iRVEE/w/Gg6mgeZH44ntDzo56bR7wqo8WN7EFPtr7LPNxRQ44xk9UDvi7F8OisE8Q3bH+b/X+2b/3v6nF0ep1dM54+LzqtJvlb+PY3tOijX86hCgf1geZSTBXEBy0sYF/j90n5hoOw8XOBHv1pnjPuRAXqfbWj38xwHvW9E53Hor1bgR+iv2sTvtCbko6bkQfatjccTOn6QfujxoHuFQL/oL11Pw3pQCujY7ozHBXwJ4wLmw/frcI4/36/Jc+R8/BBJ8TPlN5HsuPIFcQjbOU3n2acZXhdVAr9Dl9bEemx63aX7p+NPrM+NKXvbiqD0xrEfcT30VH8SSm+ED5YTjgNDtjtd9HQh+xUTtoTtbPEcnzuicSMdr/B46LiL+80SPEeD4yNN5/Oh44WZWfk5KpmO+y4Gplfc5BjpWqKHJ/MXaT0dhecE0F8RNX4DF1+M6xX3Tz/vqeP2U4zjqljBvPjnmRSUn9HnBEzRifz5filcX/j8hfEB3WlNjF9L2a9BcP4Oj9O4H+k5P6LlgO2PpmM9YD2e3FAnqMVHqWA+/Hnjy8DFF/q5MqQOhQCf/Ts/LB+ZQA54XaXHb/z5dq1gHUGXmtO/8ThAcARf7qbWA9P65v2OweV8OWC9akzkIb/GQXi9Q3ol59dR/LjOnY/n6YOLs8LnQUzFOXQJ45nx+BcJfHxF/IMfr8l6S/sjlq8p/Rnl25muz0dR+vm132O9898zSPrh2xmxB3qcdP7FHw/Js/h6p3EPiVv0/hxuT4+D3mfm/e4/53V0PUA4LpzH89/vSPAflheNF/ly+HW+hClG+WwW4k7+fP4r7jQg+31Lzp/tnOAS9PO8EN+O8RVpct0ylVegi/Znsg4b12+qQE//Lc8j8jf+fHskmMr3jOd1v8oD6fcR6UzgSL1JHGkcL5rGl/9ffkvXFdMouzWlhzzB+P1qH58ovUNwD457NA7l8/mvuAePUyngY7xuoTOxvppej43HdzIe4+dl8wRyp/EqziPRPY1XhfGRdy+Ig0RfxvMnYR6L9cDXj4k8VmAPv5ZH+XxDCB7C8YHGLzx5m8Q9tF7wpTbOR5CfnDWBJ0hd0Gi98B/5H2+enRUCfzCOe2m8Rs4HGV+nY02s63T9l9R1MZ3O7/+1HiewH3wZTNT1aH8ynT/z5/lr+dHz5ccnOg/MEMjBeDyL5OKZ8bqD6ToFb/wMjqX1jNvz6g//oR2frhbYAz0OGicZ31f/9b3xOICvPEEejuMBjWeNxwMcn0lcwXk4XR+j8axx+6e+i2BG6hOYj6n8g89fLeCD7Y6eF23XpnArP39SUv3TdSw6f9X9n/mMAf4HVLLUxQ== +</DataArray> +</Points> +<Cells> +<DataArray type="Int32" Name="connectivity" format="binary"> +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 +</DataArray> +<DataArray type="Int32" Name="offsets" format="binary"> +AQAAAAAAAAAsBgAAAAAAACwGAAAAAAAApQIAAAAAAAA=eJwV02tMEFQUB/ADKKQgio8kzB5u9KCHZeDKVtEEsUbNVVLQTNdK8dXQpm3S0BHlRlakjA2djC1d0sBIK5uQFc2WitOgNUcjHzit3GzAdEVb9evD79O95/x3d87NiIgZ3EcBpaygki3spJ1vOMVvjJCaEHEzuRRRxkpep44P+Jzj/MIQSYkRk8lmNkWUsZpN1LOHDrrp5zIJSRETyWY2RSxiDdU08CEddHOGYZJHRUwlhwcpZhFreZMG2jjECc4xyOjREVnkkM8zLGUDW2iilYN0088gickRE7iF+ylmMct4jXfYSRsdHONnLvEvU1IibuUBCillNRt5jx18Qhe9DHCFlGsibiKXx3mBV6ihnl3so4sfuchfjBsTMZ2ZPMoCXmQVG9nGbvZzmF4GGCZpbMQ07uARnuZl1vEWDbRwgCP08TsjjE+Vy13MpZRyqqmjmb108gMX+ZuMNDtAHo/xPCup5G120EYnxznLHySMi5jEjdzLXBZSThXv0sR+DtPLWQaJdLnkMIf5LOFVNtPIx3xLL6cZJmm8OrLJo5BnWU4l77Obz/iOPs5zmcQJEZnMYh7PsZwqttHCl5xggCukZviP5FHIQpaynloa2MMXHOUU57nK2IlqmUUBJbxEBTXU00w7X9PDGa4yZpJZMpN8nmIZ69hMI610cITTDJMy2f5wOw/xBItZwxs00sIBjtHPJWJKRBozyKWAElZRxVZ28Slf0cMFRsi6NuIe5lNGFds5yEl+JX1qxG08TAnrqaOVowyRnCmffMpYSy2t9DBE+nX6UMASNtFEJycZIj0r4m6epII6PqKLc/xD5jRzYR7lbKCevXzPBUZd7x5zWEAFtbTzE3/+fz494k6KKaea7eyjhyHSbvAuillBDc0coo//AIbN2LY= +</DataArray> +<DataArray type="UInt8" Name="types" format="binary"> +AQAAAAAAAACLAQAAAAAAAIsBAAAAAAAADgAAAAAAAAA=eJzT0hoFgwQAACPNQM8= +</DataArray> +<DataArray type="Int32" Name="faces" format="binary"> +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 +</DataArray> +<DataArray type="Int32" Name="faceoffsets" format="binary"> +AQAAAAAAAAAsBgAAAAAAACwGAAAAAAAAHwMAAAAAAAA=eJwN1GtMl2UUAPAjlzAgCRUhi8RLoCBZhHmXBMnUvKd4QfOWpmbaRVdzrlTSLbPbyvWhOay0xlpjtkqb6QjU1bRyMttqTosPySS2qIkuYv0+/D68z/P8n/ec857zr4iI3XxHeo+IpdTSQn5CxDo+5zpzEiPe4Wf6J0Vs5muSkiNm8hbNDL4lYiNf0TslYj4f8QeFPe1Rz99MuDViD+fJS43YxDG6KEuL2MlZMtIjFvE+lxl5W0QNZ+jXyx6fcI2xGfb4gczbI5ZwgN8YkhmxhZMk9JYvtbRQ3CdiOYdpJ79vxHMc418mZUW8zLdcp7Cf+vAhbQzLdh91tDIiR414gzP0vSNiFq9ygpuU9pczR0m+M2Iyu6jnJuV3yYWTtDEmN2Ir9Vyl4O6INRwnYYD7eZefyMuTO/v5noSBcmAnTaQOiqhkD6fpYuLgiNUc4hqFQyKWUUszGfdETGMnF8nMj1hMLb+TW+CZt7lA0tCICrbzBX8yepi6cZB2JhZGPM+PJBZ55lkO08644RE7OE92sRx5j1/IujdiNns5Tc8Rfs82mrjBqPsiXuQwVym4X834mH8YURKxniP8xaAHIhbwOk10U1UqJxrJHhmxkINcofhBfcJn/MrAURHVvMlFeo2OmMpezpExxj6HuELuWPHwGke4QeU4fUUrQ8dHrKSODsomRLxCE6kT3c1+WhheFrGWD7hM9kN6mn2cJWWS82ynjjZKytWXE/xHWYV8qKWVIZPNN7tpILFSTanhFGkP6wlqaCRtSsR4tvENPR5xng0cpZvJU8VEAynT9Dk7aCR5esRc9tFAyqNiZjun6KBshvMcp5vpM80VF8iaFTGDLXxJF+Wz9R/Jc9SeAwyYK2Y6mTLPGvmPWaOZovnW6GLFAt+O6irfjdSF5pBmihaZB7qoWew+qpf4b2RAtTU6WbVUj5OzTC/RyfrHIy5RtdyMMnRFxEt0ULVSzPRZ5T4ukrPae2mm5AnxkbomYh7nyF2r72hn65NqwwvrzDKl6/U86Ru8lwZyntKXdFO9MeJTyp/Wy6RviniGS5RvjvgfzpIVxQ== +</DataArray> +</Cells> +</Piece> +</UnstructuredGrid> +</VTKFile> diff --git a/src/UnitTests/Meshes/data/polyhedrons/hexahedron_and_two_polyhedra.vtu b/src/UnitTests/Meshes/data/polyhedrons/hexahedron_and_two_polyhedra.vtu new file mode 100644 index 0000000000000000000000000000000000000000..a6e5eb7f24f59717f1cdb7bb1faab4ae2a279789 --- /dev/null +++ b/src/UnitTests/Meshes/data/polyhedrons/hexahedron_and_two_polyhedra.vtu @@ -0,0 +1,70 @@ +<VTKFile type="UnstructuredGrid" version="1.0" byte_order="LittleEndian" header_type="UInt64"> + <UnstructuredGrid> + <Piece NumberOfPoints="18" NumberOfCells="3"> + <PointData> + </PointData> + <CellData> + </CellData> + <Points> + <DataArray type="Float32" Name="Points" NumberOfComponents="3" format="ascii" RangeMin="-0.5" RangeMax="0.5"> + -0.505 0 0.054 -0.501 0 0.054 + -0.497 0 0.054 -0.501 0 0.050 + -0.505 0 0.050 -0.497 0 0.050 + -0.501 0.00422 0.050 -0.501 0.00422 0.054 + -0.497 0.00422 0.054 -0.497 0.00422 0.050 + -0.501 -0.00422 0.050 -0.505 -0.00422 0.050 + -0.497 -0.00422 0.050 -0.505 -0.00422 0.054 + -0.497 -0.00422 0.054 -0.5034639835357666 -0.00422 0.050 + -0.499563992023468 -0.00422 0.050 -0.501 -0.00422 0.054 + <InformationKey name="L2_NORM_FINITE_RANGE" location="vtkDataArray" length="2"> + <Value index="0"> + 0.50016638905 + </Value> + <Value index="1"> + 0.50842888496 + </Value> + </InformationKey> + <InformationKey name="L2_NORM_RANGE" location="vtkDataArray" length="2"> + <Value index="0"> + 0.50016638905 + </Value> + <Value index="1"> + 0.50842888496 + </Value> + </InformationKey> + </DataArray> + </Points> + <Cells> + <DataArray type="Int64" Name="connectivity" format="ascii" RangeMin="0" RangeMax="17"> + 3 1 2 5 6 7 + 8 9 3 1 17 10 + 0 13 11 15 4 5 + 2 14 12 1 17 10 + 16 3 + </DataArray> + <DataArray type="Int64" Name="offsets" format="ascii" RangeMin="8" RangeMax="26"> + 8 17 26 + </DataArray> + <DataArray type="UInt8" Name="types" format="ascii" RangeMin="12" RangeMax="42"> + 12 42 42 + </DataArray> + <DataArray type="Int64" IdType="1" Name="faces" format="ascii" RangeMin="0" RangeMax="17"> + 6 4 3 1 17 10 + 4 1 0 13 17 5 + 17 13 11 15 10 5 + 11 4 3 10 15 4 + 13 0 4 11 4 4 + 0 1 3 6 4 5 + 2 14 12 4 2 1 + 17 14 5 14 17 10 + 16 12 5 10 3 5 + 12 16 4 17 1 3 + 10 4 3 1 2 5 + </DataArray> + <DataArray type="Int64" IdType="1" Name="faceoffsets" format="ascii" RangeMin="-1" RangeMax="66"> + -1 33 66 + </DataArray> + </Cells> + </Piece> + </UnstructuredGrid> +</VTKFile> diff --git a/src/UnitTests/Meshes/data/polyhedrons/two_polyhedra.fpma b/src/UnitTests/Meshes/data/polyhedrons/two_polyhedra.fpma new file mode 100644 index 0000000000000000000000000000000000000000..ccd2ad753e5fc43bfb90711d5f3500f75d951a99 --- /dev/null +++ b/src/UnitTests/Meshes/data/polyhedrons/two_polyhedra.fpma @@ -0,0 +1,37 @@ +# 22: number of vertices +22 +# vertex coordinates (x,y,z)-order +-1.25 1.1665 1.203 -1.20683 1.16951 1.20537 -1.16843 1.19337 1.17878 -1.21025 1.21901 1.15383 -1.25 1.2128 1.1567 -1.20816 1.25 1.16756 -1.25 1.25 1.18056 -1.14802 1.21553 1.21165 -1.16186 1.25 1.21385 -1.20307 1.17486 1.25 -1.25 1.18056 1.25 -1.15677 1.22115 1.25 -1.18056 1.25 1.25 -1.25 1.25 1.25 -1.09277 1.20806 1.19263 -1.07219 1.22167 1.17994 -1.07215 1.25 1.18679 -1.05697 1.21124 1.19697 -1.04607 1.21508 1.22076 -1.0214 1.25 1.22293 -1.06418 1.22115 1.25 -1.04167 1.25 1.25 +# 16: number of faces +16 +# the first number means the number of vertices and the rest of numbers in the same line is vertex indicies attached to the face +5 0 1 2 3 4 +4 4 3 5 6 +5 5 3 2 7 8 +4 9 1 0 10 +5 11 7 2 1 9 +4 8 7 11 12 +5 13 12 11 9 10 +5 13 10 0 4 6 +5 13 6 5 8 12 +5 8 7 14 15 16 +5 16 15 17 18 19 +6 20 18 17 14 7 11 +3 17 15 14 +4 21 19 18 20 +4 21 20 11 12 +5 12 8 16 19 21 +# 2: number of cells +2 +# the first number means the number of faces and the rest of numbers in the same line are face indicies attached to the cell +9 0 1 2 3 4 5 6 7 8 +8 9 10 11 12 13 5 14 15 +# Additional information only, does contain topology information +# 1: The number of special selections, here there is only one selection named by boudary_face +1 +# boundary_face" is the name +boundary_face +# "3" means a boundary and "5" means 5 faces, and the last line is the face indices. You can see these faces in the figure, surrounded by green edges. +3 +5 +6 7 8 14 15 diff --git a/src/UnitTests/Meshes/data/polyhedrons/two_polyhedra.vtk b/src/UnitTests/Meshes/data/polyhedrons/two_polyhedra.vtk new file mode 100644 index 0000000000000000000000000000000000000000..c4f7373ef73aa084eec15d767a6e655c0364fe41 --- /dev/null +++ b/src/UnitTests/Meshes/data/polyhedrons/two_polyhedra.vtk @@ -0,0 +1,39 @@ +# vtk DataFile Version 5.1 +vtk output +ASCII +DATASET UNSTRUCTURED_GRID +POINTS 22 float +-1.25 1.1665 1.203 -1.20683 1.16951 1.20537 -1.16843 1.19337 1.17878 +-1.21025 1.21901 1.15383 -1.25 1.2128 1.1567 -1.20816 1.25 1.16756 +-1.25 1.25 1.18056 -1.14802 1.21553 1.21165 -1.16186 1.25 1.21385 +-1.20307 1.17486 1.25 -1.25 1.18056 1.25 -1.15677 1.22115 1.25 +-1.18056 1.25 1.25 -1.25 1.25 1.25 -1.09277 1.20806 1.19263 +-1.07219 1.22167 1.17994 -1.07215 1.25 1.18679 -1.05697 1.21124 1.19697 +-1.04607 1.21508 1.22076 -1.0214 1.25 1.22293 -1.06418 1.22115 1.25 +-1.04167 1.25 1.25 +METADATA +INFORMATION 2 +NAME L2_NORM_RANGE LOCATION vtkDataArray +DATA 2 2.00425 2.16506 +NAME L2_NORM_FINITE_RANGE LOCATION vtkDataArray +DATA 2 2.00425 2.16506 + +CELLS 3 97 +OFFSETS vtktypeint64 +0 46 97 +CONNECTIVITY vtktypeint64 +8 5 0 1 2 3 4 4 4 +3 5 6 5 5 3 2 7 8 +4 9 1 0 10 5 11 7 2 +1 9 4 8 7 11 12 5 13 +12 11 9 10 5 13 10 0 4 +6 9 5 13 6 5 8 12 5 +8 7 14 15 16 5 16 15 17 +18 19 6 20 18 17 14 7 11 +3 17 15 14 4 21 19 18 20 +4 8 7 11 12 4 21 20 11 +12 5 12 8 16 19 21 +CELL_TYPES 2 +42 +42 + diff --git a/src/UnitTests/Meshes/data/polyhedrons/two_polyhedra.vtu b/src/UnitTests/Meshes/data/polyhedrons/two_polyhedra.vtu new file mode 100644 index 0000000000000000000000000000000000000000..cc5a51f66f3cc7f058f93f0acaf056f9ef882fff --- /dev/null +++ b/src/UnitTests/Meshes/data/polyhedrons/two_polyhedra.vtu @@ -0,0 +1,46 @@ +<VTKFile type="UnstructuredGrid" version="1.0" byte_order="LittleEndian" header_type="UInt64"> + <UnstructuredGrid> + <Piece NumberOfPoints="22" NumberOfCells="2"> + <Points> + <DataArray type="Float32" Name="Points" NumberOfComponents="3" format="ascii"> + -1.25 1.1665 1.203 -1.20683 1.16951 1.20537 -1.16843 1.19337 1.17878 -1.21025 1.21901 1.15383 -1.25 1.2128 1.1567 -1.20816 1.25 1.16756 -1.25 1.25 1.18056 -1.14802 1.21553 1.21165 -1.16186 1.25 1.21385 -1.20307 1.17486 1.25 -1.25 1.18056 1.25 -1.15677 1.22115 1.25 -1.18056 1.25 1.25 -1.25 1.25 1.25 -1.09277 1.20806 1.19263 -1.07219 1.22167 1.17994 -1.07215 1.25 1.18679 -1.05697 1.21124 1.19697 -1.04607 1.21508 1.22076 -1.0214 1.25 1.22293 -1.06418 1.22115 1.25 -1.04167 1.25 1.25 + </DataArray> + </Points> + <Cells> + <DataArray type="Int64" Name="connectivity" format="ascii"> + 0 0 + </DataArray> + <DataArray type="Int64" Name="offsets" format="ascii"> + 1 2 + </DataArray> + <DataArray type="UInt8" Name="types" format="ascii"> + 42 42 + </DataArray> + <DataArray type="Int64" Name="faces" format="ascii"> + 8 + 5 0 1 2 3 4 + 4 4 3 5 6 + 5 5 3 2 7 8 + 4 9 1 0 10 + 5 11 7 2 1 9 + 4 8 7 11 12 + 5 13 12 11 9 10 + 5 13 10 0 4 6 + 9 + 5 13 6 5 8 12 + 5 8 7 14 15 16 + 5 16 15 17 18 19 + 6 20 18 17 14 7 11 + 3 17 15 14 + 4 21 19 18 20 + 4 8 7 11 12 + 4 21 20 11 12 + 5 12 8 16 19 21 + </DataArray> + <DataArray type="Int64" Name="faceoffsets" format="ascii"> + 46 97 + </DataArray> + </Cells> + </Piece> + </UnstructuredGrid> +</VTKFile> diff --git a/src/UnitTests/Meshes/data/quadrangles/grid_2x3.vti b/src/UnitTests/Meshes/data/quadrangles/grid_2x3.vti new file mode 100644 index 0000000000000000000000000000000000000000..4304ca1f7a0fc47364e39379af1b4f66ff388a62 --- /dev/null +++ b/src/UnitTests/Meshes/data/quadrangles/grid_2x3.vti @@ -0,0 +1,7 @@ +<?xml version="1.0"?> +<VTKFile type="ImageData" version="1.0" byte_order="LittleEndian" header_type="UInt32"> +<ImageData WholeExtent="0 2 0 3 0 0 " Origin="0.000000e+00 0.000000e+00 0 " Spacing="1.000000e+00 1.000000e+00 0 "> +<Piece Extent="0 2 0 3 0 0 "> +</Piece> +</ImageData> +</VTKFile> diff --git a/src/UnitTests/Meshes/data/quadrangles/grid_2x3.vtk b/src/UnitTests/Meshes/data/quadrangles/grid_2x3.vtk new file mode 100644 index 0000000000000000000000000000000000000000..410d4c0f7cf35b99d6c0f6480324abe67b0428aa Binary files /dev/null and b/src/UnitTests/Meshes/data/quadrangles/grid_2x3.vtk differ diff --git a/src/UnitTests/Meshes/data/quadrangles/grid_2x3.vtu b/src/UnitTests/Meshes/data/quadrangles/grid_2x3.vtu new file mode 100644 index 0000000000000000000000000000000000000000..5c5f8564f73f76c9862813bb3c42ba79a210ccb2 --- /dev/null +++ b/src/UnitTests/Meshes/data/quadrangles/grid_2x3.vtu @@ -0,0 +1,22 @@ +<VTKFile type="UnstructuredGrid" version="1.0" byte_order="LittleEndian" header_type="UInt64"> + <UnstructuredGrid> + <Piece NumberOfPoints="12" NumberOfCells="6"> + <Points> + <DataArray type="Float64" Name="Points" NumberOfComponents="3" format="binary"> + IAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPA/AAAAAAAAAAAAAAAAAADwPwAAAAAAAPA/AAAAAAAAAAAAAAAAAAAAQAAAAAAAAPA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAADwPwAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhAAAAAAAAAAAAAAAAAAADwPwAAAAAAAAhAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAhAAAAAAAAAAAA= + </DataArray> + </Points> + <Cells> + <DataArray type="Int64" Name="connectivity" format="binary"> + wAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAABAAAAAAAAAADAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAFAAAAAAAAAAQAAAAAAAAAAwAAAAAAAAAEAAAAAAAAAAcAAAAAAAAABgAAAAAAAAAEAAAAAAAAAAUAAAAAAAAACAAAAAAAAAAHAAAAAAAAAAYAAAAAAAAABwAAAAAAAAAKAAAAAAAAAAkAAAAAAAAABwAAAAAAAAAIAAAAAAAAAAsAAAAAAAAACgAAAAAAAAA= + </DataArray> + <DataArray type="Int64" Name="offsets" format="binary"> + MAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAADAAAAAAAAAAQAAAAAAAAABQAAAAAAAAAGAAAAAAAAAA= + </DataArray> + <DataArray type="UInt8" Name="types" format="binary"> + BgAAAAAAAAAJCQkJCQk= + </DataArray> + </Cells> + </Piece> + </UnstructuredGrid> +</VTKFile> diff --git a/src/UnitTests/Meshes/data/triangles_2x2x2/version_5.1_ascii.vtk b/src/UnitTests/Meshes/data/triangles_2x2x2/version_5.1_ascii.vtk new file mode 100644 index 0000000000000000000000000000000000000000..02ff76b1fd36712a6ed7dc3f33e5a13c028ab7e6 --- /dev/null +++ b/src/UnitTests/Meshes/data/triangles_2x2x2/version_5.1_ascii.vtk @@ -0,0 +1,78 @@ +# vtk DataFile Version 5.1 +vtk output +ASCII +DATASET UNSTRUCTURED_GRID +FIELD FieldData 2 +TIME 1 1 float +3.1536e+07 +METADATA +INFORMATION 0 + +CYCLE 1 1 int +366 +METADATA +INFORMATION 0 + +POINTS 9 float +0 0 0 25 0 0 50 0 0 +0 25 0 25 25 0 50 25 0 +0 50 0 25 50 0 50 50 0 + +METADATA +INFORMATION 2 +NAME L2_NORM_RANGE LOCATION vtkDataArray +DATA 2 0 70.7107 +NAME L2_NORM_FINITE_RANGE LOCATION vtkDataArray +DATA 2 0 70.7107 + +CELLS 9 24 +OFFSETS vtktypeint64 +0 3 6 9 12 15 18 21 24 + +CONNECTIVITY vtktypeint64 +0 1 4 0 4 3 1 2 5 +1 5 4 3 4 7 3 7 6 +4 5 8 4 8 7 +CELL_TYPES 8 +5 +5 +5 +5 +5 +5 +5 +5 + +CELL_DATA 8 +SCALARS pressure float +LOOKUP_TABLE default +6.90056e+06 6.90045e+06 6.90097e+06 6.90073e+06 6.90028e+06 6.90006e+06 6.90056e+06 6.90045e+06 +METADATA +INFORMATION 0 + +FIELD FieldData 5 +c_0 1 8 float +23.3681 0.93711 2677.75 431.5 0.0582516 0.00303224 23.3681 0.93711 +METADATA +INFORMATION 0 + +c_1 1 8 float +11653.9 11675.9 8502.15 11245 11676.7 11676.8 11653.9 11675.9 +METADATA +INFORMATION 0 + +x_0 1 8 float +0.00200117 8.02539e-05 0.239515 0.0369546 4.98867e-06 2.59681e-07 0.00200117 8.02539e-05 +METADATA +INFORMATION 0 + +x_1 1 8 float +0.997999 0.99992 0.760485 0.963045 0.999995 1 0.997999 0.99992 +METADATA +INFORMATION 0 + +nbOfPhases 1 8 float +1 1 1 1 1 1 1 1 +METADATA +INFORMATION 0 + diff --git a/src/UnitTests/Meshes/data/triangles_2x2x2/DataFile_version_5.1_exported_from_paraview.vtk b/src/UnitTests/Meshes/data/triangles_2x2x2/version_5.1_binary.vtk similarity index 100% rename from src/UnitTests/Meshes/data/triangles_2x2x2/DataFile_version_5.1_exported_from_paraview.vtk rename to src/UnitTests/Meshes/data/triangles_2x2x2/version_5.1_binary.vtk