diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b7a6290c35d22368bda40551dcb32c0be0fbfea8..c34fa57cd936e6e9a5fadf1bf3b45785aa26abbf 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -25,5 +25,5 @@ test:coverage: - build:coverage script: - cd build - - make unit_tests + - make run_unit_tests - make coverage diff --git a/CMakeLists.txt b/CMakeLists.txt index 3fe16ca68deb862550e1f0bf3bd4bc2124e48a92..53ae505bca6492f407a895ef267322bf738454cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,14 +31,23 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(PASTIS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") set(PASTIS_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}") +# Change RelWithDebInfo to compile assertions +SET("CMAKE_CXX_FLAGS_RELWITHDEBINFO" + "-g -O2" + CACHE STRING "Flags used by the compiler during release builds with debug info and assertions" + FORCE ) +SET("CMAKE_C_FLAGS_RELWITHDEBINFO" + "-g -O2" + CACHE STRING "Flags used by the compiler during release builds with debug info and assertions" + FORCE ) + # Add new build types -message("* Adding build types...") set(CMAKE_CXX_FLAGS_COVERAGE - "-g -Wall -O0 --coverage" + "-g -O0 --coverage" CACHE STRING "Flags used by the C++ compiler during coverage builds." FORCE ) set(CMAKE_C_FLAGS_COVERAGE - "-g -Wall -O0 --coverage" + "-g -O0 --coverage" CACHE STRING "Flags used by the C compiler during coverage builds." FORCE ) set(CMAKE_EXE_LINKER_FLAGS_COVERAGE @@ -89,6 +98,28 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(PASTIS_CXX_FLAGS "${PASTIS_CXX_FLAGS} -Wsign-compare -Wunused -Wunused-member-function -Wunused-private-field") endif() +#------------------------------------------------------ +# defaults use of MPI +set(PASTIS_ENABLE_MPI AUTO CACHE STRING + "Choose one of: AUTO ON OFF") + +if (NOT PASTIS_ENABLE_MPI MATCHES "^(AUTO|ON|OFF)$") + message(FATAL_ERROR "PASTIS_ENABLE_MPI='${PASTIS_ENABLE_MPI}'. Must be set to one of AUTO, ON or OFF") +endif() + +# checks for MPI +if (PASTIS_ENABLE_MPI MATCHES "^(AUTO|ON)$") + find_package(MPI) +endif() + +if (${MPI_FOUND}) + set(PASTIS_CXX_FLAGS "${PASTIS_CXX_FLAGS} ${MPI_CXX_COMPILER_FLAGS}") + include_directories(SYSTEM ${MPI_CXX_INCLUDE_DIRS}) +elseif(PASTIS_ENABLE_MPI STREQUAL "ON") + message(FATAL_ERROR "Could not find MPI library while requested") +endif() + +set(PASTIS_HAS_MPI ${MPI_FOUND}) #------------------------------------------------------ @@ -113,7 +144,7 @@ include(GetKokkosCompilerFlags) set_target_properties(kokkos PROPERTIES COMPILE_FLAGS "-w") # sets Kokkos debug flags when non release build -if (CMAKE_BUILD_TYPE MATCHES "Release") +if (CMAKE_BUILD_TYPE MATCHES "^Release$") set (KOKKOS_ENABLE_DEBUG OFF) else() set (KOKKOS_ENABLE_DEBUG ON) @@ -155,6 +186,9 @@ include_directories(src/output) include_directories(src/utils) include_directories(src/scheme) +# Pastis generated sources +include_directories(${PASTIS_BINARY_DIR}/src/utils) + # Pastis tests set(CATCH_MODULE_PATH "${PASTIS_SOURCE_DIR}/packages/Catch2") @@ -194,13 +228,13 @@ if("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage") add_custom_target(run_unit_tests ALL COMMAND ${CMAKE_CTEST_COMMAND} -j ${PROCESSOR_COUNT} - DEPENDS unit_tests pastis + DEPENDS unit_tests mpi_unit_tests pastis COMMENT "Executing unit tests." ) add_custom_target(coverage ALL - COMMAND ${GCOVR} ${GCOVR_OPTIONS} + COMMAND ${GCOVR} ${GCOVR_OPTIONS} --exclude-unreachable-branches --sort-percentage DEPENDS run_unit_tests COMMENT "Running gcovr to build coverage report." ) @@ -217,6 +251,13 @@ if("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage") endif() +#------------------------------------------------------ +# Search for ParMETIS + +if(${MPI_FOUND}) + find_package(ParMETIS REQUIRED) +endif() + # ----------------------------------------------------- link_libraries("-rdynamic") @@ -230,6 +271,9 @@ add_executable( # Libraries target_link_libraries( pastis - kokkos + PastisMesh PastisUtils - PastisMesh) + kokkos + ${PARMETIS_LIBRARIES} + ${MPI_CXX_LINK_FLAGS} ${MPI_CXX_LIBRARIES} +) diff --git a/cmake/FindParMETIS.cmake b/cmake/FindParMETIS.cmake new file mode 100644 index 0000000000000000000000000000000000000000..54269b0ae7d3140dbd0900a4ad1cf6b4924d7128 --- /dev/null +++ b/cmake/FindParMETIS.cmake @@ -0,0 +1,22 @@ +# Looking for ParMETIS + +find_path(PARMETIS_INCLUDE_DIR parmetis.h + PATH_SUFFIX include parmetis) + +if (EXISTS "${PARMETIS_INCLUDE_DIR}/parmetis.h") + message("-- Found parmetis.h in ${PARMETIS_INCLUDE_DIR}") + find_library(LIB_PARMETIS parmetis) + if("${LIB_PARMETIS}" STREQUAL "LIB_PARMETIS-NOTFOUND") + message(FATAL_ERROR "Could not find parmetis library") + endif() + find_library(LIB_METIS metis) + if("${LIB_PARMETIS}" STREQUAL "LIB_METIS-NOTFOUND") + message(FATAL_ERROR "Could not find metis library") + endif() + set(PARMETIS_LIBRARIES ${LIB_PARMETIS} ${LIB_METIS}) + message("-- Found parmetis/metis libraries ${PARMETIS_LIBRARIES}") +else() + message(FATAL_ERROR "Could not find parmetis.h") +endif() + +mark_as_advanced(PARMETIS_INCLUDE_DIR PARMETIS_LIBRARIES) diff --git a/src/algebra/TinyMatrix.hpp b/src/algebra/TinyMatrix.hpp index e693a70f75771a59ddeed83fab2e4f3b7c5883b0..2dde6fc1ac9d72a298b4157d0388fe3045103f16 100644 --- a/src/algebra/TinyMatrix.hpp +++ b/src/algebra/TinyMatrix.hpp @@ -12,7 +12,10 @@ template <size_t N, typename T=double> class TinyMatrix { -private: + public: + using data_type = T; + + private: T m_values[N*N]; static_assert((N>0),"TinyMatrix size must be strictly positive"); @@ -173,6 +176,14 @@ public: return *this; } + PASTIS_INLINE + constexpr void operator+=(const volatile TinyMatrix& A) volatile + { + for (size_t i=0; i<N*N; ++i) { + m_values[i] += A.m_values[i]; + } + } + PASTIS_INLINE constexpr TinyMatrix& operator-=(const TinyMatrix& A) { @@ -219,13 +230,7 @@ public: } PASTIS_INLINE - constexpr TinyMatrix& operator=(const TinyMatrix& A) noexcept - { - for (size_t i=0; i<N*N; ++i) { - m_values[i] = A.m_values[i]; - } - return *this; - } + constexpr TinyMatrix& operator=(const TinyMatrix& A) noexcept = default; PASTIS_INLINE constexpr TinyMatrix& operator=(TinyMatrix&& A) noexcept = default; @@ -238,11 +243,10 @@ public: this->_unpackVariadicInput(t, std::forward<Args>(args)...); } + // One does not use the '=default' constructor to avoid (unexpected) + // performances issues PASTIS_INLINE - constexpr TinyMatrix() noexcept - { - ; - } + constexpr TinyMatrix() noexcept {} PASTIS_INLINE constexpr TinyMatrix(const ZeroType&) noexcept @@ -265,18 +269,13 @@ public: } PASTIS_INLINE - constexpr TinyMatrix(const TinyMatrix& A) noexcept - { - for (size_t i=0; i<N*N; ++i) { - m_values[i] = A.m_values[i]; - } - } + constexpr TinyMatrix(const TinyMatrix&) noexcept = default; PASTIS_INLINE TinyMatrix(TinyMatrix&& A) noexcept = default; PASTIS_INLINE - ~TinyMatrix()=default; + ~TinyMatrix() = default; }; template <size_t N, typename T> @@ -297,7 +296,7 @@ template <size_t N, typename T> PASTIS_INLINE constexpr T det(const TinyMatrix<N,T>& A) { - static_assert(std::is_arithmetic<T>::value, "determinent is not defined for non arithmetic types"); + static_assert(std::is_arithmetic<T>::value, "determinent is not defined for non-arithmetic types"); static_assert(std::is_floating_point<T>::value, "determinent for arbitrary dimension N is defined for floating point types only"); TinyMatrix<N,T> M = A; @@ -341,7 +340,7 @@ template <typename T> PASTIS_INLINE constexpr T det(const TinyMatrix<1,T>& A) { - static_assert(std::is_arithmetic<T>::value, "determinent is not defined for non arithmetic types"); + static_assert(std::is_arithmetic<T>::value, "determinent is not defined for non-arithmetic types"); return A(0,0); } @@ -349,7 +348,7 @@ template <typename T> PASTIS_INLINE constexpr T det(const TinyMatrix<2,T>& A) { - static_assert(std::is_arithmetic<T>::value, "determinent is not defined for non arithmetic types"); + static_assert(std::is_arithmetic<T>::value, "determinent is not defined for non-arithmetic types"); return A(0,0)*A(1,1)-A(1,0)*A(0,1); } @@ -357,7 +356,7 @@ template <typename T> PASTIS_INLINE constexpr T det(const TinyMatrix<3,T>& A) { - static_assert(std::is_arithmetic<T>::value, "determinent is not defined for non arithmetic types"); + static_assert(std::is_arithmetic<T>::value, "determinent is not defined for non-arithmetic types"); return A(0,0)*(A(1,1)*A(2,2)-A(2,1)*A(1,2)) -A(1,0)*(A(0,1)*A(2,2)-A(2,1)*A(0,2)) @@ -372,7 +371,7 @@ constexpr TinyMatrix<N-1,T> getMinor(const TinyMatrix<N,T>& A, { static_assert(N>=2, "minor calculation requires at least 2x2 matrices"); Assert((I<N) and (J<N)); - TinyMatrix<N-1, T> M; + TinyMatrix<N-1,T> M; for (size_t i=0; i<I; ++i) { for (size_t j=0; j<J; ++j) { M(i,j)=A(i,j); @@ -400,7 +399,7 @@ template <typename T> PASTIS_INLINE constexpr TinyMatrix<1,T> inverse(const TinyMatrix<1,T>& A) { - static_assert(std::is_arithmetic<T>::value, "inverse is not defined for non arithmetic types"); + static_assert(std::is_arithmetic<T>::value, "inverse is not defined for non-arithmetic types"); static_assert(std::is_floating_point<T>::value, "inverse is defined for floating point types only"); TinyMatrix<1,T> A_1(1./A(0,0)); @@ -413,7 +412,7 @@ constexpr T cofactor(const TinyMatrix<N,T>& A, const size_t& i, const size_t& j) { - static_assert(std::is_arithmetic<T>::value, "cofactor is not defined for non arithmetic types"); + static_assert(std::is_arithmetic<T>::value, "cofactor is not defined for non-arithmetic types"); const T sign = ((i+j)%2) ? -1 : 1; return sign * det(getMinor(A, i, j)); @@ -423,7 +422,7 @@ template <typename T> PASTIS_INLINE constexpr TinyMatrix<2,T> inverse(const TinyMatrix<2,T>& A) { - static_assert(std::is_arithmetic<T>::value, "inverse is not defined for non arithmetic types"); + static_assert(std::is_arithmetic<T>::value, "inverse is not defined for non-arithmetic types"); static_assert(std::is_floating_point<T>::value, "inverse is defined for floating point types only"); const T determinent = det(A); @@ -439,7 +438,7 @@ template <typename T> PASTIS_INLINE constexpr TinyMatrix<3,T> inverse(const TinyMatrix<3,T>& A) { - static_assert(std::is_arithmetic<T>::value, "inverse is not defined for non arithmetic types"); + static_assert(std::is_arithmetic<T>::value, "inverse is not defined for non-arithmetic types"); static_assert(std::is_floating_point<T>::value, "inverse is defined for floating point types only"); const T determinent = det(A); diff --git a/src/algebra/TinyVector.hpp b/src/algebra/TinyVector.hpp index 7b7d407eefa5bd40a0a6cc96bc11d1438ad24093..2b7a080cadb08f523bc02692b16ca228fdaca9f2 100644 --- a/src/algebra/TinyVector.hpp +++ b/src/algebra/TinyVector.hpp @@ -13,6 +13,9 @@ template <size_t N, typename T=double> class TinyVector { + public: + using data_type = T; + private: T m_values[N]; static_assert((N>0),"TinyVector size must be strictly positive"); @@ -150,6 +153,14 @@ class TinyVector return *this; } + PASTIS_INLINE + constexpr void operator+=(const volatile TinyVector& v) volatile + { + for (size_t i=0; i<N; ++i) { + m_values[i] += v.m_values[i]; + } + } + PASTIS_INLINE constexpr TinyVector& operator-=(const TinyVector& v) { @@ -184,13 +195,7 @@ class TinyVector } PASTIS_INLINE - const TinyVector& operator=(const TinyVector& v) noexcept - { - for (size_t i=0; i<N; ++i) { - m_values[i] = v.m_values[i]; - } - return *this; - } + TinyVector& operator=(const TinyVector&) noexcept = default; PASTIS_INLINE constexpr TinyVector& operator=(TinyVector&& v) noexcept = default; @@ -203,11 +208,10 @@ class TinyVector this->_unpackVariadicInput(t, std::forward<Args>(args)...); } + // One does not use the '=default' constructor to avoid (unexpected) + // performances issues PASTIS_INLINE - constexpr TinyVector() noexcept - { - ; - } + constexpr TinyVector() noexcept {} PASTIS_INLINE constexpr TinyVector(const ZeroType&) noexcept @@ -219,12 +223,7 @@ class TinyVector } PASTIS_INLINE - constexpr TinyVector(const TinyVector& v) noexcept - { - for (size_t i=0; i<N; ++i) { - m_values[i] = v.m_values[i]; - } - } + constexpr TinyVector(const TinyVector&) noexcept = default; PASTIS_INLINE constexpr TinyVector(TinyVector&& v) noexcept = default; diff --git a/src/main.cpp b/src/main.cpp index a0ab842a3ca1b0961ce64fddfeb1fb04d68cf714..951eddd4362b509cb2eadde3f97b470895473163 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,351 +22,362 @@ #include <GmshReader.hpp> +#include <SynchronizerManager.hpp> + #include <limits> #include <map> int main(int argc, char *argv[]) { std::string filename = initialize(argc, argv); - std::map<std::string, double> method_cost_map; - try { - if (filename != "") { - pout() << "Reading (gmsh) " << rang::style::underline << filename << rang::style::reset << " ...\n"; - Timer gmsh_timer; - gmsh_timer.reset(); - GmshReader gmsh_reader(filename); - method_cost_map["Mesh building"] = gmsh_timer.seconds(); - - std::shared_ptr<IMesh> p_mesh = gmsh_reader.mesh(); - - switch (p_mesh->meshDimension()) { - case 1: { - std::vector<std::string> sym_boundary_name_list = {"XMIN", "XMAX"}; - std::vector<std::shared_ptr<BoundaryConditionDescriptor>> bc_descriptor_list; - for (const auto& sym_boundary_name : sym_boundary_name_list){ - std::shared_ptr<BoundaryDescriptor> boudary_descriptor - = std::shared_ptr<BoundaryDescriptor>(new NamedBoundaryDescriptor(sym_boundary_name)); - SymmetryBoundaryConditionDescriptor* sym_bc_descriptor - = new SymmetryBoundaryConditionDescriptor(boudary_descriptor); - - bc_descriptor_list.push_back(std::shared_ptr<BoundaryConditionDescriptor>(sym_bc_descriptor)); - } + SynchronizerManager::create(); + + if (filename != "") { + pout() << "Reading (gmsh) " << rang::style::underline << filename << rang::style::reset << " ...\n"; + Timer gmsh_timer; + gmsh_timer.reset(); + GmshReader gmsh_reader(filename); + method_cost_map["Mesh building"] = gmsh_timer.seconds(); - using ConnectivityType = Connectivity1D; - using MeshType = Mesh<ConnectivityType>; - using MeshDataType = MeshData<MeshType>; - using UnknownsType = FiniteVolumesEulerUnknowns<MeshDataType>; - - const MeshType& mesh = dynamic_cast<const MeshType&>(*gmsh_reader.mesh()); - - Timer timer; - timer.reset(); - MeshDataType mesh_data(mesh); - - std::vector<BoundaryConditionHandler> bc_list; - { - for (const auto& bc_descriptor : bc_descriptor_list) { - switch (bc_descriptor->type()) { - case BoundaryConditionDescriptor::Type::symmetry: { - const SymmetryBoundaryConditionDescriptor& sym_bc_descriptor - = dynamic_cast<const SymmetryBoundaryConditionDescriptor&>(*bc_descriptor); - for (size_t i_ref_node_list=0; i_ref_node_list<mesh.connectivity().numberOfRefNodeList(); - ++i_ref_node_list) { - const RefNodeList& ref_node_list = mesh.connectivity().refNodeList(i_ref_node_list); - const RefId& ref = ref_node_list.refId(); - if (ref == sym_bc_descriptor.boundaryDescriptor()) { - SymmetryBoundaryCondition<MeshType::dimension>* sym_bc - = new SymmetryBoundaryCondition<MeshType::dimension>(MeshFlatNodeBoundary<MeshType::dimension>(mesh, ref_node_list)); - std::shared_ptr<SymmetryBoundaryCondition<MeshType::dimension>> bc(sym_bc); - bc_list.push_back(BoundaryConditionHandler(bc)); - } + std::shared_ptr<IMesh> p_mesh = gmsh_reader.mesh(); + + switch (p_mesh->dimension()) { + case 1: { + std::vector<std::string> sym_boundary_name_list = {"XMIN", "XMAX"}; + std::vector<std::shared_ptr<BoundaryConditionDescriptor>> bc_descriptor_list; + for (const auto& sym_boundary_name : sym_boundary_name_list){ + std::shared_ptr<BoundaryDescriptor> boudary_descriptor + = std::shared_ptr<BoundaryDescriptor>(new NamedBoundaryDescriptor(sym_boundary_name)); + SymmetryBoundaryConditionDescriptor* sym_bc_descriptor + = new SymmetryBoundaryConditionDescriptor(boudary_descriptor); + + bc_descriptor_list.push_back(std::shared_ptr<BoundaryConditionDescriptor>(sym_bc_descriptor)); + } + + using ConnectivityType = Connectivity1D; + using MeshType = Mesh<ConnectivityType>; + using MeshDataType = MeshData<MeshType>; + using UnknownsType = FiniteVolumesEulerUnknowns<MeshDataType>; + + const MeshType& mesh = dynamic_cast<const MeshType&>(*gmsh_reader.mesh()); + + Timer timer; + timer.reset(); + MeshDataType mesh_data(mesh); + + std::vector<BoundaryConditionHandler> bc_list; + { + for (const auto& bc_descriptor : bc_descriptor_list) { + switch (bc_descriptor->type()) { + case BoundaryConditionDescriptor::Type::symmetry: { + const SymmetryBoundaryConditionDescriptor& sym_bc_descriptor + = dynamic_cast<const SymmetryBoundaryConditionDescriptor&>(*bc_descriptor); + for (size_t i_ref_node_list=0; i_ref_node_list<mesh.connectivity().numberOfRefItemList<ItemType::node>(); + ++i_ref_node_list) { + const RefNodeList& ref_node_list = mesh.connectivity().refItemList<ItemType::node>(i_ref_node_list); + const RefId& ref = ref_node_list.refId(); + if (ref == sym_bc_descriptor.boundaryDescriptor()) { + SymmetryBoundaryCondition<MeshType::Dimension>* sym_bc + = new SymmetryBoundaryCondition<MeshType::Dimension>(MeshFlatNodeBoundary<MeshType::Dimension>(mesh, ref_node_list)); + std::shared_ptr<SymmetryBoundaryCondition<MeshType::Dimension>> bc(sym_bc); + bc_list.push_back(BoundaryConditionHandler(bc)); } - break; - } - default: { - perr() << "Unknown BCDescription\n"; - std::exit(1); } + break; + } + default: { + perr() << "Unknown BCDescription\n"; + std::exit(1); } } } + } - UnknownsType unknowns(mesh_data); - - unknowns.initializeSod(); + UnknownsType unknowns(mesh_data); - AcousticSolver<MeshDataType> acoustic_solver(mesh_data, bc_list); + unknowns.initializeSod(); - using Rd = TinyVector<MeshType::dimension>; + AcousticSolver<MeshDataType> acoustic_solver(mesh_data, bc_list); - const CellValue<const double>& Vj = mesh_data.Vj(); + using Rd = TinyVector<MeshType::Dimension>; - const double tmax=0.2; - double t=0; + const CellValue<const double>& Vj = mesh_data.Vj(); - int itermax=std::numeric_limits<int>::max(); - int iteration=0; + const double tmax=0.2; + double t=0; - CellValue<double>& rhoj = unknowns.rhoj(); - CellValue<double>& ej = unknowns.ej(); - CellValue<double>& pj = unknowns.pj(); - CellValue<double>& gammaj = unknowns.gammaj(); - CellValue<double>& cj = unknowns.cj(); + int itermax=std::numeric_limits<int>::max(); + int iteration=0; - BlockPerfectGas block_eos(rhoj, ej, pj, gammaj, cj); + CellValue<double>& rhoj = unknowns.rhoj(); + CellValue<double>& ej = unknowns.ej(); + CellValue<double>& pj = unknowns.pj(); + CellValue<double>& gammaj = unknowns.gammaj(); + CellValue<double>& cj = unknowns.cj(); - VTKWriter vtk_writer("mesh", 0.01); + BlockPerfectGas block_eos(rhoj, ej, pj, gammaj, cj); - while((t<tmax) and (iteration<itermax)) { - vtk_writer.write(mesh, {NamedItemValue{"density", rhoj}, - NamedItemValue{"velocity", unknowns.uj()}, - NamedItemValue{"coords", mesh.xr()}}, t); - double dt = 0.4*acoustic_solver.acoustic_dt(Vj, cj); - if (t+dt>tmax) { - dt=tmax-t; - } - acoustic_solver.computeNextStep(t,dt, unknowns); + VTKWriter vtk_writer("mesh", 0.01); - block_eos.updatePandCFromRhoE(); - - t += dt; - ++iteration; - } + while((t<tmax) and (iteration<itermax)) { vtk_writer.write(mesh, {NamedItemValue{"density", rhoj}, NamedItemValue{"velocity", unknowns.uj()}, - NamedItemValue{"coords", mesh.xr()}}, t, true); // forces last output - - pout() << "* " << rang::style::underline << "Final time" << rang::style::reset - << ": " << rang::fgB::green << t << rang::fg::reset << " (" << iteration << " iterations)\n"; + NamedItemValue{"coords", mesh.xr()}, + NamedItemValue{"cell_owner", mesh.connectivity().cellOwner()}, + NamedItemValue{"node_owner", mesh.connectivity().nodeOwner()}},t); + double dt = 0.4*acoustic_solver.acoustic_dt(Vj, cj); + if (t+dt>tmax) { + dt=tmax-t; + } + acoustic_solver.computeNextStep(t,dt, unknowns); - method_cost_map["AcousticSolverWithMesh"] = timer.seconds(); + block_eos.updatePandCFromRhoE(); - { // gnuplot output for density - const CellValue<const Rd>& xj = mesh_data.xj(); - const CellValue<const double>& rhoj = unknowns.rhoj(); - std::ofstream fout("rho"); - for (CellId j=0; j<mesh.numberOfCells(); ++j) { - fout << xj[j][0] << ' ' << rhoj[j] << '\n'; - } + t += dt; + ++iteration; + } + vtk_writer.write(mesh, {NamedItemValue{"density", rhoj}, + NamedItemValue{"velocity", unknowns.uj()}, + NamedItemValue{"coords", mesh.xr()}, + NamedItemValue{"cell_owner", mesh.connectivity().cellOwner()}, + NamedItemValue{"node_owner", mesh.connectivity().nodeOwner()}}, t, true); // forces last output + + pout() << "* " << rang::style::underline << "Final time" << rang::style::reset + << ": " << rang::fgB::green << t << rang::fg::reset << " (" << iteration << " iterations)\n"; + + method_cost_map["AcousticSolverWithMesh"] = timer.seconds(); + + { // gnuplot output for density + const CellValue<const Rd>& xj = mesh_data.xj(); + const CellValue<const double>& rhoj = unknowns.rhoj(); + std::ofstream fout("rho"); + for (CellId j=0; j<mesh.numberOfCells(); ++j) { + fout << xj[j][0] << ' ' << rhoj[j] << '\n'; } + } - break; + break; + } + case 2: { + // test case boundary condition description + std::vector<std::string> sym_boundary_name_list = {"XMIN", "XMAX", "YMIN", "YMAX"}; + std::vector<std::shared_ptr<BoundaryConditionDescriptor>> bc_descriptor_list; + for (const auto& sym_boundary_name : sym_boundary_name_list){ + std::shared_ptr<BoundaryDescriptor> boudary_descriptor + = std::shared_ptr<BoundaryDescriptor>(new NamedBoundaryDescriptor(sym_boundary_name)); + SymmetryBoundaryConditionDescriptor* sym_bc_descriptor + = new SymmetryBoundaryConditionDescriptor(boudary_descriptor); + + bc_descriptor_list.push_back(std::shared_ptr<BoundaryConditionDescriptor>(sym_bc_descriptor)); } - case 2: { - // test case boundary condition description - std::vector<std::string> sym_boundary_name_list = {"XMIN", "XMAX", "YMIN", "YMAX"}; - std::vector<std::shared_ptr<BoundaryConditionDescriptor>> bc_descriptor_list; - for (const auto& sym_boundary_name : sym_boundary_name_list){ - std::shared_ptr<BoundaryDescriptor> boudary_descriptor - = std::shared_ptr<BoundaryDescriptor>(new NamedBoundaryDescriptor(sym_boundary_name)); - SymmetryBoundaryConditionDescriptor* sym_bc_descriptor - = new SymmetryBoundaryConditionDescriptor(boudary_descriptor); - - bc_descriptor_list.push_back(std::shared_ptr<BoundaryConditionDescriptor>(sym_bc_descriptor)); - } - using ConnectivityType = Connectivity2D; - using MeshType = Mesh<ConnectivityType>; - using MeshDataType = MeshData<MeshType>; - using UnknownsType = FiniteVolumesEulerUnknowns<MeshDataType>; - - const MeshType& mesh = dynamic_cast<const MeshType&>(*gmsh_reader.mesh()); - - Timer timer; - timer.reset(); - MeshDataType mesh_data(mesh); - - std::vector<BoundaryConditionHandler> bc_list; - { - for (const auto& bc_descriptor : bc_descriptor_list) { - switch (bc_descriptor->type()) { - case BoundaryConditionDescriptor::Type::symmetry: { - const SymmetryBoundaryConditionDescriptor& sym_bc_descriptor - = dynamic_cast<const SymmetryBoundaryConditionDescriptor&>(*bc_descriptor); - for (size_t i_ref_face_list=0; i_ref_face_list<mesh.connectivity().numberOfRefFaceList(); - ++i_ref_face_list) { - const RefFaceList& ref_face_list = mesh.connectivity().refFaceList(i_ref_face_list); - const RefId& ref = ref_face_list.refId(); - if (ref == sym_bc_descriptor.boundaryDescriptor()) { - SymmetryBoundaryCondition<MeshType::dimension>* sym_bc - = new SymmetryBoundaryCondition<MeshType::dimension>(MeshFlatNodeBoundary<MeshType::dimension>(mesh, ref_face_list)); - std::shared_ptr<SymmetryBoundaryCondition<MeshType::dimension>> bc(sym_bc); - bc_list.push_back(BoundaryConditionHandler(bc)); - } + using ConnectivityType = Connectivity2D; + using MeshType = Mesh<ConnectivityType>; + using MeshDataType = MeshData<MeshType>; + using UnknownsType = FiniteVolumesEulerUnknowns<MeshDataType>; + + const MeshType& mesh = dynamic_cast<const MeshType&>(*gmsh_reader.mesh()); + + Timer timer; + timer.reset(); + MeshDataType mesh_data(mesh); + + std::vector<BoundaryConditionHandler> bc_list; + { + for (const auto& bc_descriptor : bc_descriptor_list) { + switch (bc_descriptor->type()) { + case BoundaryConditionDescriptor::Type::symmetry: { + const SymmetryBoundaryConditionDescriptor& sym_bc_descriptor + = dynamic_cast<const SymmetryBoundaryConditionDescriptor&>(*bc_descriptor); + for (size_t i_ref_face_list=0; i_ref_face_list<mesh.connectivity().numberOfRefItemList<ItemType::face>(); + ++i_ref_face_list) { + const RefFaceList& ref_face_list = mesh.connectivity().refItemList<ItemType::face>(i_ref_face_list); + const RefId& ref = ref_face_list.refId(); + if (ref == sym_bc_descriptor.boundaryDescriptor()) { + SymmetryBoundaryCondition<MeshType::Dimension>* sym_bc + = new SymmetryBoundaryCondition<MeshType::Dimension>(MeshFlatNodeBoundary<MeshType::Dimension>(mesh, ref_face_list)); + std::shared_ptr<SymmetryBoundaryCondition<MeshType::Dimension>> bc(sym_bc); + bc_list.push_back(BoundaryConditionHandler(bc)); } - break; - } - default: { - perr() << "Unknown BCDescription\n"; - std::exit(1); } + break; + } + default: { + perr() << "Unknown BCDescription\n"; + std::exit(1); } } } + } - UnknownsType unknowns(mesh_data); - - unknowns.initializeSod(); - - AcousticSolver<MeshDataType> acoustic_solver(mesh_data, bc_list); + UnknownsType unknowns(mesh_data); - const CellValue<const double>& Vj = mesh_data.Vj(); + unknowns.initializeSod(); - const double tmax=0.2; - double t=0; + AcousticSolver<MeshDataType> acoustic_solver(mesh_data, bc_list); - int itermax=std::numeric_limits<int>::max(); - int iteration=0; + const CellValue<const double>& Vj = mesh_data.Vj(); - CellValue<double>& rhoj = unknowns.rhoj(); - CellValue<double>& ej = unknowns.ej(); - CellValue<double>& pj = unknowns.pj(); - CellValue<double>& gammaj = unknowns.gammaj(); - CellValue<double>& cj = unknowns.cj(); + const double tmax=0.2; + double t=0; - BlockPerfectGas block_eos(rhoj, ej, pj, gammaj, cj); + int itermax=std::numeric_limits<int>::max(); + int iteration=0; - VTKWriter vtk_writer("mesh", 0.01); + CellValue<double>& rhoj = unknowns.rhoj(); + CellValue<double>& ej = unknowns.ej(); + CellValue<double>& pj = unknowns.pj(); + CellValue<double>& gammaj = unknowns.gammaj(); + CellValue<double>& cj = unknowns.cj(); - while((t<tmax) and (iteration<itermax)) { - vtk_writer.write(mesh, {NamedItemValue{"density", rhoj}, - NamedItemValue{"velocity", unknowns.uj()}, - NamedItemValue{"coords", mesh.xr()}}, t); - double dt = 0.4*acoustic_solver.acoustic_dt(Vj, cj); - if (t+dt>tmax) { - dt=tmax-t; - } - acoustic_solver.computeNextStep(t,dt, unknowns); + BlockPerfectGas block_eos(rhoj, ej, pj, gammaj, cj); - block_eos.updatePandCFromRhoE(); + VTKWriter vtk_writer("mesh", 0.01); - t += dt; - ++iteration; - } + while((t<tmax) and (iteration<itermax)) { vtk_writer.write(mesh, {NamedItemValue{"density", rhoj}, NamedItemValue{"velocity", unknowns.uj()}, - NamedItemValue{"coords", mesh.xr()}}, t, true); // forces last output + NamedItemValue{"coords", mesh.xr()}, + NamedItemValue{"cell_owner", mesh.connectivity().cellOwner()}, + NamedItemValue{"node_owner", mesh.connectivity().nodeOwner()}}, t); + double dt = 0.4*acoustic_solver.acoustic_dt(Vj, cj); + if (t+dt>tmax) { + dt=tmax-t; + } + acoustic_solver.computeNextStep(t,dt, unknowns); - pout() << "* " << rang::style::underline << "Final time" << rang::style::reset - << ": " << rang::fgB::green << t << rang::fg::reset << " (" << iteration << " iterations)\n"; + block_eos.updatePandCFromRhoE(); - method_cost_map["AcousticSolverWithMesh"] = timer.seconds(); - break; + t += dt; + ++iteration; } - case 3: { - std::vector<std::string> sym_boundary_name_list = {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}; - std::vector<std::shared_ptr<BoundaryConditionDescriptor>> bc_descriptor_list; - for (const auto& sym_boundary_name : sym_boundary_name_list){ - std::shared_ptr<BoundaryDescriptor> boudary_descriptor - = std::shared_ptr<BoundaryDescriptor>(new NamedBoundaryDescriptor(sym_boundary_name)); - SymmetryBoundaryConditionDescriptor* sym_bc_descriptor - = new SymmetryBoundaryConditionDescriptor(boudary_descriptor); - - bc_descriptor_list.push_back(std::shared_ptr<BoundaryConditionDescriptor>(sym_bc_descriptor)); - } + vtk_writer.write(mesh, {NamedItemValue{"density", rhoj}, + NamedItemValue{"velocity", unknowns.uj()}, + NamedItemValue{"coords", mesh.xr()}, + NamedItemValue{"cell_owner", mesh.connectivity().cellOwner()}, + NamedItemValue{"node_owner", mesh.connectivity().nodeOwner()}}, t, true); // forces last output - using ConnectivityType = Connectivity3D; - using MeshType = Mesh<ConnectivityType>; - using MeshDataType = MeshData<MeshType>; - using UnknownsType = FiniteVolumesEulerUnknowns<MeshDataType>; - - const MeshType& mesh = dynamic_cast<const MeshType&>(*gmsh_reader.mesh()); - - Timer timer; - timer.reset(); - MeshDataType mesh_data(mesh); - - std::vector<BoundaryConditionHandler> bc_list; - { - for (const auto& bc_descriptor : bc_descriptor_list) { - switch (bc_descriptor->type()) { - case BoundaryConditionDescriptor::Type::symmetry: { - const SymmetryBoundaryConditionDescriptor& sym_bc_descriptor - = dynamic_cast<const SymmetryBoundaryConditionDescriptor&>(*bc_descriptor); - for (size_t i_ref_face_list=0; i_ref_face_list<mesh.connectivity().numberOfRefFaceList(); - ++i_ref_face_list) { - const RefFaceList& ref_face_list = mesh.connectivity().refFaceList(i_ref_face_list); - const RefId& ref = ref_face_list.refId(); - if (ref == sym_bc_descriptor.boundaryDescriptor()) { - SymmetryBoundaryCondition<MeshType::dimension>* sym_bc - = new SymmetryBoundaryCondition<MeshType::dimension>(MeshFlatNodeBoundary<MeshType::dimension>(mesh, ref_face_list)); - std::shared_ptr<SymmetryBoundaryCondition<MeshType::dimension>> bc(sym_bc); - bc_list.push_back(BoundaryConditionHandler(bc)); - } + pout() << "* " << rang::style::underline << "Final time" << rang::style::reset + << ": " << rang::fgB::green << t << rang::fg::reset << " (" << iteration << " iterations)\n"; + + method_cost_map["AcousticSolverWithMesh"] = timer.seconds(); + break; + } + case 3: { + std::vector<std::string> sym_boundary_name_list = {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}; + std::vector<std::shared_ptr<BoundaryConditionDescriptor>> bc_descriptor_list; + for (const auto& sym_boundary_name : sym_boundary_name_list){ + std::shared_ptr<BoundaryDescriptor> boudary_descriptor + = std::shared_ptr<BoundaryDescriptor>(new NamedBoundaryDescriptor(sym_boundary_name)); + SymmetryBoundaryConditionDescriptor* sym_bc_descriptor + = new SymmetryBoundaryConditionDescriptor(boudary_descriptor); + + bc_descriptor_list.push_back(std::shared_ptr<BoundaryConditionDescriptor>(sym_bc_descriptor)); + } + + using ConnectivityType = Connectivity3D; + using MeshType = Mesh<ConnectivityType>; + using MeshDataType = MeshData<MeshType>; + using UnknownsType = FiniteVolumesEulerUnknowns<MeshDataType>; + + const MeshType& mesh = dynamic_cast<const MeshType&>(*gmsh_reader.mesh()); + + Timer timer; + timer.reset(); + MeshDataType mesh_data(mesh); + + std::vector<BoundaryConditionHandler> bc_list; + { + for (const auto& bc_descriptor : bc_descriptor_list) { + switch (bc_descriptor->type()) { + case BoundaryConditionDescriptor::Type::symmetry: { + const SymmetryBoundaryConditionDescriptor& sym_bc_descriptor + = dynamic_cast<const SymmetryBoundaryConditionDescriptor&>(*bc_descriptor); + for (size_t i_ref_face_list=0; i_ref_face_list<mesh.connectivity().numberOfRefItemList<ItemType::face>(); + ++i_ref_face_list) { + const RefFaceList& ref_face_list = mesh.connectivity().refItemList<ItemType::face>(i_ref_face_list); + const RefId& ref = ref_face_list.refId(); + if (ref == sym_bc_descriptor.boundaryDescriptor()) { + SymmetryBoundaryCondition<MeshType::Dimension>* sym_bc + = new SymmetryBoundaryCondition<MeshType::Dimension>(MeshFlatNodeBoundary<MeshType::Dimension>(mesh, ref_face_list)); + std::shared_ptr<SymmetryBoundaryCondition<MeshType::Dimension>> bc(sym_bc); + bc_list.push_back(BoundaryConditionHandler(bc)); } - break; - } - default: { - perr() << "Unknown BCDescription\n"; - std::exit(1); } + break; + } + default: { + perr() << "Unknown BCDescription\n"; + std::exit(1); } } } + } - UnknownsType unknowns(mesh_data); - - unknowns.initializeSod(); + UnknownsType unknowns(mesh_data); - AcousticSolver<MeshDataType> acoustic_solver(mesh_data, bc_list); + unknowns.initializeSod(); - const CellValue<const double>& Vj = mesh_data.Vj(); + AcousticSolver<MeshDataType> acoustic_solver(mesh_data, bc_list); - const double tmax=0.2; - double t=0; + const CellValue<const double>& Vj = mesh_data.Vj(); - int itermax=std::numeric_limits<int>::max(); - int iteration=0; + const double tmax=0.2; + double t=0; - CellValue<double>& rhoj = unknowns.rhoj(); - CellValue<double>& ej = unknowns.ej(); - CellValue<double>& pj = unknowns.pj(); - CellValue<double>& gammaj = unknowns.gammaj(); - CellValue<double>& cj = unknowns.cj(); + int itermax=std::numeric_limits<int>::max(); + int iteration=0; - BlockPerfectGas block_eos(rhoj, ej, pj, gammaj, cj); + CellValue<double>& rhoj = unknowns.rhoj(); + CellValue<double>& ej = unknowns.ej(); + CellValue<double>& pj = unknowns.pj(); + CellValue<double>& gammaj = unknowns.gammaj(); + CellValue<double>& cj = unknowns.cj(); - VTKWriter vtk_writer("mesh", 0.01); + BlockPerfectGas block_eos(rhoj, ej, pj, gammaj, cj); - while((t<tmax) and (iteration<itermax)) { - vtk_writer.write(mesh, {NamedItemValue{"density", rhoj}, - NamedItemValue{"velocity", unknowns.uj()}, - NamedItemValue{"coords", mesh.xr()}}, t); - double dt = 0.4*acoustic_solver.acoustic_dt(Vj, cj); - if (t+dt>tmax) { - dt=tmax-t; - } - acoustic_solver.computeNextStep(t,dt, unknowns); - block_eos.updatePandCFromRhoE(); + VTKWriter vtk_writer("mesh", 0.01); - t += dt; - ++iteration; - } + while((t<tmax) and (iteration<itermax)) { vtk_writer.write(mesh, {NamedItemValue{"density", rhoj}, NamedItemValue{"velocity", unknowns.uj()}, - NamedItemValue{"coords", mesh.xr()}}, t, true); // forces last output - - pout() << "* " << rang::style::underline << "Final time" << rang::style::reset - << ": " << rang::fgB::green << t << rang::fg::reset << " (" << iteration << " iterations)\n"; + NamedItemValue{"coords", mesh.xr()}, + NamedItemValue{"cell_owner", mesh.connectivity().cellOwner()}, + NamedItemValue{"node_owner", mesh.connectivity().nodeOwner()}}, t); + double dt = 0.4*acoustic_solver.acoustic_dt(Vj, cj); + if (t+dt>tmax) { + dt=tmax-t; + } + acoustic_solver.computeNextStep(t,dt, unknowns); + block_eos.updatePandCFromRhoE(); - method_cost_map["AcousticSolverWithMesh"] = timer.seconds(); - break; + t += dt; + ++iteration; } - } + vtk_writer.write(mesh, {NamedItemValue{"density", rhoj}, + NamedItemValue{"velocity", unknowns.uj()}, + NamedItemValue{"coords", mesh.xr()}, + NamedItemValue{"cell_owner", mesh.connectivity().cellOwner()}, + NamedItemValue{"node_owner", mesh.connectivity().nodeOwner()}}, t, true); // forces last output - pout() << "* " << rang::fgB::red << "Could not be uglier!" << rang::fg::reset << " (" << __FILE__ << ':' << __LINE__ << ")\n"; + pout() << "* " << rang::style::underline << "Final time" << rang::style::reset + << ": " << rang::fgB::green << t << rang::fg::reset << " (" << iteration << " iterations)\n"; - } else { - perr() << "Connectivity1D defined by number of nodes no more implemented\n"; - std::exit(0); + method_cost_map["AcousticSolverWithMesh"] = timer.seconds(); + break; + } } + + pout() << "* " << rang::fgB::red << "Could not be uglier!" << rang::fg::reset << " (" << __FILE__ << ':' << __LINE__ << ")\n"; + + } else { + perr() << "Connectivity1D defined by number of nodes no more implemented\n"; + std::exit(0); } - catch (const AssertError& error) { - perr() << error << '\n'; - std::exit(1); - } + + SynchronizerManager::destroy(); finalize(); @@ -377,14 +388,14 @@ int main(int argc, char *argv[]) for (const auto& method_cost : method_cost_map) { pout() << "* [" - << rang::fgB::cyan - << std::setw(size) << std::left - << method_cost.first - << rang::fg::reset - << "] Execution time: " - << rang::style::bold - << method_cost.second - << rang::style::reset << '\n'; + << rang::fgB::cyan + << std::setw(size) << std::left + << method_cost.first + << rang::fg::reset + << "] Execution time: " + << rang::style::bold + << method_cost.second + << rang::style::reset << '\n'; } return 0; diff --git a/src/mesh/CMakeLists.txt b/src/mesh/CMakeLists.txt index c28b0643fd8bbf1b2677512423d2185d9a7ff970..ad245fb038c4a09e451591ff44098da80cb22106 100644 --- a/src/mesh/CMakeLists.txt +++ b/src/mesh/CMakeLists.txt @@ -7,9 +7,11 @@ add_library( PastisMesh Connectivity.cpp ConnectivityComputer.cpp - GmshReader.cpp) + GmshReader.cpp + ConnectivityDispatcher.cpp + SynchronizerManager.cpp) -#include_directories(${PASTIS_SOURCE_DIR}/utils) +include_directories(${PASTIS_BINARY_DIR}/src/utils) # Additional dependencies #add_dependencies(PastisMesh) diff --git a/src/mesh/CellType.hpp b/src/mesh/CellType.hpp index 1f444323b5f5073b9aa09d020cc1fc962b1182a5..d3aa4578f769972e06f9df976df16c53737af80f 100644 --- a/src/mesh/CellType.hpp +++ b/src/mesh/CellType.hpp @@ -1,9 +1,12 @@ #ifndef CELL_TYPE_HPP #define CELL_TYPE_HPP +#include <PastisMacros.hpp> +#include <string_view> + enum class CellType : unsigned short { - Line, + Line = 0, Triangle, Quadrangle, @@ -14,4 +17,19 @@ enum class CellType : unsigned short Hexahedron }; +PASTIS_INLINE +std::string_view name(const CellType& cell_type) +{ + switch (cell_type) { + case CellType::Line: return "line"; + case CellType::Triangle: return "triangle"; + case CellType::Quadrangle: return "quadrangle"; + case CellType::Tetrahedron: return "tetrahedron"; + case CellType::Pyramid: return "pyramid"; + case CellType::Prism: return "prism"; + case CellType::Hexahedron: return "hexahedron"; + default: return "unknown cell type"; + } +} + #endif // CELL_TYPE_HPP diff --git a/src/mesh/Connectivity.cpp b/src/mesh/Connectivity.cpp index 856a1452bf78ec323107142fc919b0bda4f79243..3a4b45766d36453e862aaeca2b5d8d855942e495 100644 --- a/src/mesh/Connectivity.cpp +++ b/src/mesh/Connectivity.cpp @@ -1,261 +1,178 @@ #include <Connectivity.hpp> #include <map> -template<> -void Connectivity<3>::_computeCellFaceAndFaceNodeConnectivities() -{ - using CellFaceInfo = std::tuple<CellId, unsigned short, bool>; - - const auto& cell_to_node_matrix - = this->_getMatrix(ItemType::cell, ItemType::node); - - CellValue<unsigned short> cell_nb_faces(*this); - std::map<Face, std::vector<CellFaceInfo>> face_cells_map; - for (CellId j=0; j<this->numberOfCells(); ++j) { - const auto& cell_nodes = cell_to_node_matrix.rowConst(j); - - switch (m_cell_type[j]) { - case CellType::Tetrahedron: { - cell_nb_faces[j] = 4; - // face 0 - Face f0({cell_nodes(1), - cell_nodes(2), - cell_nodes(3)}); - face_cells_map[f0].emplace_back(std::make_tuple(j, 0, f0.reversed())); - - // face 1 - Face f1({cell_nodes(0), - cell_nodes(3), - cell_nodes(2)}); - face_cells_map[f1].emplace_back(std::make_tuple(j, 1, f1.reversed())); +#include <Messenger.hpp> - // face 2 - Face f2({cell_nodes(0), - cell_nodes(1), - cell_nodes(3)}); - face_cells_map[f2].emplace_back(std::make_tuple(j, 2, f2.reversed())); +#include <ConnectivityDescriptor.hpp> - // face 3 - Face f3({cell_nodes(0), - cell_nodes(2), - cell_nodes(1)}); - face_cells_map[f3].emplace_back(std::make_tuple(j, 3, f3.reversed())); - break; - } - case CellType::Hexahedron: { - // face 0 - Face f0({cell_nodes(3), - cell_nodes(2), - cell_nodes(1), - cell_nodes(0)}); - face_cells_map[f0].emplace_back(std::make_tuple(j, 0, f0.reversed())); - - // face 1 - Face f1({cell_nodes(4), - cell_nodes(5), - cell_nodes(6), - cell_nodes(7)}); - face_cells_map[f1].emplace_back(std::make_tuple(j, 1, f1.reversed())); - - // face 2 - Face f2({cell_nodes(0), - cell_nodes(4), - cell_nodes(7), - cell_nodes(3)}); - face_cells_map[f2].emplace_back(std::make_tuple(j, 2, f2.reversed())); +template<size_t Dimension> +Connectivity<Dimension>::Connectivity() {} - // face 3 - Face f3({cell_nodes(1), - cell_nodes(2), - cell_nodes(6), - cell_nodes(5)}); - face_cells_map[f3].emplace_back(std::make_tuple(j, 3, f3.reversed())); +template<size_t Dimension> +void Connectivity<Dimension>:: +_buildFrom(const ConnectivityDescriptor& descriptor) +{ + Assert(descriptor.cell_to_node_vector.size() == descriptor.cell_type_vector.size()); + Assert(descriptor.cell_number_vector.size() == descriptor.cell_type_vector.size()); + if constexpr (Dimension>1) { + Assert(descriptor.cell_to_face_vector.size() == descriptor.cell_type_vector.size()); + Assert(descriptor.face_to_node_vector.size() == descriptor.face_number_vector.size()); + Assert(descriptor.face_owner_vector.size() == descriptor.face_number_vector.size()); + } - // face 4 - Face f4({cell_nodes(0), - cell_nodes(1), - cell_nodes(5), - cell_nodes(4)}); - face_cells_map[f4].emplace_back(std::make_tuple(j, 4, f4.reversed())); + auto& cell_to_node_matrix + = m_item_to_item_matrix[itemTId(ItemType::cell)][itemTId(ItemType::node)]; + cell_to_node_matrix = descriptor.cell_to_node_vector; - // face 5 - Face f5({cell_nodes(3), - cell_nodes(7), - cell_nodes(6), - cell_nodes(2)}); - face_cells_map[f5].emplace_back(std::make_tuple(j, 5, f5.reversed())); + { + WeakCellValue<CellType> cell_type(*this); + parallel_for(this->numberOfCells(), PASTIS_LAMBDA(const CellId& j){ + cell_type[j] = descriptor.cell_type_vector[j]; + }); + m_cell_type = cell_type; + } - cell_nb_faces[j] = 6; - break; - } - default: { - perr() << "unexpected cell type!\n"; - std::exit(0); - } - } + { + WeakCellValue<int> cell_number(*this); + cell_number = convert_to_array(descriptor.cell_number_vector); + m_cell_number = cell_number; } { - auto& cell_to_face_matrix - = m_item_to_item_matrix[itemTId(ItemType::cell)][itemTId(ItemType::face)]; - std::vector<std::vector<unsigned int>> cell_to_face_vector(this->numberOfCells()); - for (CellId j=0; j<cell_to_face_vector.size(); ++j) { - cell_to_face_vector[j].resize(cell_nb_faces[j]); - } - FaceId l=0; - for (const auto& face_cells_vector : face_cells_map) { - const auto& cells_vector = face_cells_vector.second; - for (unsigned short lj=0; lj<cells_vector.size(); ++lj) { - const auto& [cell_number, cell_local_face, reversed] = cells_vector[lj]; - cell_to_face_vector[cell_number][cell_local_face] = l; - } - ++l; - } - cell_to_face_matrix = cell_to_face_vector; + WeakNodeValue<int> node_number(*this); + node_number = convert_to_array(descriptor.node_number_vector); + m_node_number = node_number; } - FaceValuePerCell<bool> cell_face_is_reversed(*this); { - for (const auto& face_cells_vector : face_cells_map) { - const auto& cells_vector = face_cells_vector.second; - for (unsigned short lj=0; lj<cells_vector.size(); ++lj) { - const auto& [cell_number, cell_local_face, reversed] = cells_vector[lj]; - cell_face_is_reversed(cell_number, cell_local_face) = reversed; - } - } + WeakCellValue<int> cell_global_index(*this); + int first_index = 0; + parallel_for(this->numberOfCells(), PASTIS_LAMBDA(const CellId& j) { + cell_global_index[j] = first_index+j; + }); + m_cell_global_index = cell_global_index; + } - m_cell_face_is_reversed = cell_face_is_reversed; + { + WeakCellValue<int> cell_owner(*this); + cell_owner = convert_to_array(descriptor.cell_owner_vector); + m_cell_owner = cell_owner; } { - auto& face_to_node_matrix - = m_item_to_item_matrix[itemTId(ItemType::face)][itemTId(ItemType::node)]; + const int rank = parallel::rank(); + WeakCellValue<bool> cell_is_owned(*this); + parallel_for(this->numberOfCells(), PASTIS_LAMBDA(const CellId& j) { + cell_is_owned[j] = (m_cell_owner[j] == rank); + }); + m_cell_is_owned = cell_is_owned; + } - std::vector<std::vector<unsigned int>> face_to_node_vector(face_cells_map.size()); - int l=0; - for (const auto& face_info : face_cells_map) { - const Face& face = face_info.first; - face_to_node_vector[l] = face.nodeIdList(); - ++l; - } - face_to_node_matrix = face_to_node_vector; + { + WeakNodeValue<int> node_owner(*this); + node_owner = convert_to_array(descriptor.node_owner_vector); + m_node_owner = node_owner; } { - int l=0; - for (const auto& face_cells_vector : face_cells_map) { - const Face& face = face_cells_vector.first; - m_face_number_map[face] = l; - ++l; - } + const int rank = parallel::rank(); + WeakNodeValue<bool> node_is_owned(*this); + parallel_for(this->numberOfNodes(), PASTIS_LAMBDA(const NodeId& r) { + node_is_owned[r] = (m_node_owner[r] == rank); + }); + m_node_is_owned = node_is_owned; } -#warning check that the number of cell per faces is <=2 -} + m_ref_node_list_vector = descriptor.template refItemListVector<ItemType::node>(); + m_ref_cell_list_vector = descriptor.template refItemListVector<ItemType::cell>(); -template<> -void Connectivity<2>::_computeCellFaceAndFaceNodeConnectivities() -{ - const auto& cell_to_node_matrix - = this->_getMatrix(ItemType::cell, ItemType::node); + if constexpr (Dimension>1) { + m_item_to_item_matrix[itemTId(ItemType::face)][itemTId(ItemType::node)] = descriptor.face_to_node_vector; - // In 2D faces are simply define - using CellFaceId = std::pair<CellId, unsigned short>; - std::map<Face, std::vector<CellFaceId>> face_cells_map; - for (CellId j=0; j<this->numberOfCells(); ++j) { - const auto& cell_nodes = cell_to_node_matrix.rowConst(j); - for (unsigned short r=0; r<cell_nodes.length; ++r) { - NodeId node0_id = cell_nodes(r); - NodeId node1_id = cell_nodes((r+1)%cell_nodes.length); - if (node1_id<node0_id) { - std::swap(node0_id, node1_id); + m_item_to_item_matrix[itemTId(ItemType::cell)][itemTId(ItemType::face)] = descriptor.cell_to_face_vector; + + { + FaceValuePerCell<bool> cell_face_is_reversed(*this); + for (CellId j=0; j<descriptor.cell_face_is_reversed_vector.size(); ++j) { + const auto& face_cells_vector = descriptor.cell_face_is_reversed_vector[j]; + for (unsigned short lj=0; lj<face_cells_vector.size(); ++lj) { + cell_face_is_reversed(j,lj) = face_cells_vector[lj]; + } } - face_cells_map[Face({node0_id, node1_id})].push_back(std::make_pair(j, r)); + m_cell_face_is_reversed = cell_face_is_reversed; } - } - { - FaceId l=0; - for (const auto& face_cells_vector : face_cells_map) { - const Face& face = face_cells_vector.first; - m_face_number_map[face] = l; - ++l; + { + WeakFaceValue<int> face_number(*this); + face_number = convert_to_array(descriptor.face_number_vector); + m_face_number = face_number; } - } - { - std::vector<std::vector<unsigned int>> face_to_node_vector(face_cells_map.size()); - int l=0; - for (const auto& face_info : face_cells_map) { - const Face& face = face_info.first; - face_to_node_vector[l] = {face.m_node0_id, face.m_node1_id}; - ++l; + { + WeakFaceValue<int> face_owner(*this); + face_owner = convert_to_array(descriptor.face_owner_vector); + m_face_owner = face_owner; } - auto& face_to_node_matrix - = m_item_to_item_matrix[itemTId(ItemType::face)][itemTId(ItemType::node)]; - face_to_node_matrix = face_to_node_vector; - } - { - std::vector<std::vector<unsigned int>> face_to_cell_vector(face_cells_map.size()); - int l=0; - for (const auto& face_cells_vector : face_cells_map) { - const auto& [face, cell_info_vector] = face_cells_vector; - for (const auto& cell_info : cell_info_vector) { - face_to_cell_vector[l].push_back(cell_info.second); - } - ++l; + { + const int rank = parallel::rank(); + WeakFaceValue<bool> face_is_owned(*this); + parallel_for(this->numberOfFaces(), PASTIS_LAMBDA(const FaceId& l) { + face_is_owned[l] = (m_face_owner[l] == rank); + }); + m_face_is_owned = face_is_owned; } - auto& face_to_cell_matrix - = m_item_to_item_matrix[itemTId(ItemType::face)][itemTId(ItemType::cell)]; - face_to_cell_matrix = face_to_cell_vector; - } -} + m_ref_face_list_vector = descriptor.template refItemListVector<ItemType::face>(); -template<size_t Dimension> -Connectivity<Dimension>:: -Connectivity(const std::vector<std::vector<unsigned int>>& cell_by_node_vector, - const std::vector<CellType>& cell_type_vector) -{ - Assert(cell_by_node_vector.size() == cell_type_vector.size()); + if constexpr (Dimension>2) { + m_item_to_item_matrix[itemTId(ItemType::edge)][itemTId(ItemType::node)] = descriptor.edge_to_node_vector; - auto& cell_to_node_matrix - = m_item_to_item_matrix[itemTId(ItemType::cell)][itemTId(ItemType::node)]; - cell_to_node_matrix = cell_by_node_vector; + m_item_to_item_matrix[itemTId(ItemType::face)][itemTId(ItemType::edge)] = descriptor.face_to_edge_vector; - Assert(this->numberOfCells()>0); + m_item_to_item_matrix[itemTId(ItemType::cell)][itemTId(ItemType::edge)] = descriptor.cell_to_edge_vector; - { - CellValue<CellType> cell_type(*this); - parallel_for(this->numberOfCells(), PASTIS_LAMBDA(const CellId& j){ - cell_type[j] = cell_type_vector[j]; - }); - m_cell_type = cell_type; - } - { - CellValue<double> inv_cell_nb_nodes(*this); - parallel_for(this->numberOfCells(), PASTIS_LAMBDA(const CellId& j){ - const auto& cell_nodes = cell_to_node_matrix.rowConst(j); - inv_cell_nb_nodes[j] = 1./cell_nodes.length; - }); - m_inv_cell_nb_nodes = inv_cell_nb_nodes; - } + { + EdgeValuePerFace<bool> face_edge_is_reversed(*this); + for (FaceId l=0; l<descriptor.face_edge_is_reversed_vector.size(); ++l) { + const auto& edge_faces_vector = descriptor.face_edge_is_reversed_vector[l]; + for (unsigned short el=0; el<edge_faces_vector.size(); ++el) { + face_edge_is_reversed(l,el) = edge_faces_vector[el]; + } + } + m_face_edge_is_reversed = face_edge_is_reversed; + } - if constexpr (Dimension>1) { - this->_computeCellFaceAndFaceNodeConnectivities(); - } -} + { + WeakEdgeValue<int> edge_number(*this); + edge_number = convert_to_array(descriptor.edge_number_vector); + m_edge_number = edge_number; + } + { + WeakEdgeValue<int> edge_owner(*this); + edge_owner = convert_to_array(descriptor.edge_owner_vector); + m_edge_owner = edge_owner; + } -template Connectivity1D:: -Connectivity(const std::vector<std::vector<unsigned int>>& cell_by_node_vector, - const std::vector<CellType>& cell_type_vector); + { + const int rank = parallel::rank(); + WeakEdgeValue<bool> edge_is_owned(*this); + parallel_for(this->numberOfEdges(), PASTIS_LAMBDA(const EdgeId& e) { + edge_is_owned[e] = (m_edge_owner[e] == rank); + }); + m_edge_is_owned = edge_is_owned; + } + + m_ref_edge_list_vector = descriptor.template refItemListVector<ItemType::edge>(); + } + } +} -template Connectivity2D:: -Connectivity(const std::vector<std::vector<unsigned int>>& cell_by_node_vector, - const std::vector<CellType>& cell_type_vector); +template Connectivity1D::Connectivity(); +template Connectivity2D::Connectivity(); +template Connectivity3D::Connectivity(); -template Connectivity3D:: -Connectivity(const std::vector<std::vector<unsigned int>>& cell_by_node_vector, - const std::vector<CellType>& cell_type_vector); +template void Connectivity1D::_buildFrom(const ConnectivityDescriptor&); +template void Connectivity2D::_buildFrom(const ConnectivityDescriptor&); +template void Connectivity3D::_buildFrom(const ConnectivityDescriptor&); diff --git a/src/mesh/Connectivity.hpp b/src/mesh/Connectivity.hpp index 01c2da0ffff27d15f65b8a841522b37ad9285d7d..7e1ebd1198094a1f214e87e26106ea28cfb0a4f1 100644 --- a/src/mesh/Connectivity.hpp +++ b/src/mesh/Connectivity.hpp @@ -7,6 +7,8 @@ #include <PastisOStream.hpp> #include <PastisUtils.hpp> +#include <PastisTraits.hpp> + #include <TinyVector.hpp> #include <ItemValue.hpp> @@ -20,294 +22,261 @@ #include <ConnectivityComputer.hpp> #include <vector> -#include <unordered_map> #include <algorithm> #include <CellType.hpp> +#include <CSRGraph.hpp> + #include <RefId.hpp> #include <ItemType.hpp> -#include <RefNodeList.hpp> -#include <RefFaceList.hpp> +#include <RefItemList.hpp> -#include <tuple> -#include <algorithm> -template <size_t Dimension> -class Connectivity; +#include <SynchronizerManager.hpp> -template <size_t Dimension> -class ConnectivityFace; +#include <set> -template<> -class ConnectivityFace<1> -{ - public: - friend struct Hash; - struct Hash - { - size_t operator()(const ConnectivityFace& f) const; - }; -}; +class ConnectivityDescriptor; -template<> -class ConnectivityFace<2> +template <size_t Dim> +class Connectivity final + : public IConnectivity { public: - friend struct Hash; - struct Hash - { - size_t operator()(const ConnectivityFace& f) const { - size_t hash = 0; - hash ^= std::hash<unsigned int>()(f.m_node0_id); - hash ^= std::hash<unsigned int>()(f.m_node1_id) >> 1; - return hash; - } - }; + PASTIS_INLINE + static std::shared_ptr<Connectivity<Dim>> + build(const ConnectivityDescriptor&); - unsigned int m_node0_id; - unsigned int m_node1_id; + private: + constexpr static auto& itemTId = ItemTypeId<Dim>::itemTId; - friend std::ostream& operator<<(std::ostream& os, const ConnectivityFace& f) - { - os << f.m_node0_id << ' ' << f.m_node1_id << ' '; - return os; - } + public: + static constexpr size_t Dimension = Dim; PASTIS_INLINE - bool operator==(const ConnectivityFace& f) const + size_t dimension() const final { - return ((m_node0_id == f.m_node0_id) and - (m_node1_id == f.m_node1_id)); + return Dimension; } + private: + ConnectivityMatrix m_item_to_item_matrix[Dimension+1][Dimension+1]; + WeakCellValue<const CellType> m_cell_type; - PASTIS_INLINE - bool operator<(const ConnectivityFace& f) const - { - return ((m_node0_id<f.m_node0_id) or - ((m_node0_id == f.m_node0_id) and - (m_node1_id<f.m_node1_id))); - } + WeakCellValue<const int> m_cell_global_index; - PASTIS_INLINE - ConnectivityFace& operator=(const ConnectivityFace&) = default; + WeakCellValue<const int> m_cell_number; + WeakFaceValue<const int> m_face_number; + WeakEdgeValue<const int> m_edge_number; + WeakNodeValue<const int> m_node_number; - PASTIS_INLINE - ConnectivityFace& operator=(ConnectivityFace&&) = default; + WeakCellValue<const int> m_cell_owner; + WeakCellValue<const bool> m_cell_is_owned; - PASTIS_INLINE - ConnectivityFace(const std::vector<unsigned int>& given_node_id_list) - { - Assert(given_node_id_list.size()==2); -#warning rework this dirty constructor - const auto& [min, max] = std::minmax(given_node_id_list[0], given_node_id_list[1]); - m_node0_id = min; - m_node1_id = max; - } + WeakFaceValue<const int> m_face_owner; + WeakFaceValue<const bool> m_face_is_owned; - PASTIS_INLINE - ConnectivityFace(const ConnectivityFace&) = default; + WeakEdgeValue<const int> m_edge_owner; + WeakEdgeValue<const bool> m_edge_is_owned; - PASTIS_INLINE - ConnectivityFace(ConnectivityFace&&) = default; + WeakNodeValue<const int> m_node_owner; + WeakNodeValue<const bool> m_node_is_owned; - PASTIS_INLINE - ~ConnectivityFace() = default; -}; + WeakFaceValuePerCell<const bool> m_cell_face_is_reversed; + WeakEdgeValuePerFace<const bool> m_face_edge_is_reversed; -template <> -class ConnectivityFace<3> -{ - private: - friend class Connectivity<3>; - friend struct Hash; - struct Hash - { - size_t operator()(const ConnectivityFace& f) const { - size_t hash = 0; - for (size_t i=0; i<f.m_node_id_list.size(); ++i) { - hash ^= std::hash<unsigned int>()(f.m_node_id_list[i]) >> i; - } - return hash; - } - }; + WeakNodeValuePerCell<const unsigned short> m_cell_local_numbers_in_their_nodes; + WeakEdgeValuePerCell<const unsigned short> m_cell_local_numbers_in_their_edges; + WeakFaceValuePerCell<const unsigned short> m_cell_local_numbers_in_their_faces; + + WeakCellValuePerFace<const unsigned short> m_face_local_numbers_in_their_cells; + WeakEdgeValuePerFace<const unsigned short> m_face_local_numbers_in_their_edges; + WeakNodeValuePerFace<const unsigned short> m_face_local_numbers_in_their_nodes; + + WeakCellValuePerEdge<const unsigned short> m_edge_local_numbers_in_their_cells; + WeakFaceValuePerEdge<const unsigned short> m_edge_local_numbers_in_their_faces; + WeakNodeValuePerEdge<const unsigned short> m_edge_local_numbers_in_their_nodes; - bool m_reversed; - std::vector<NodeId::base_type> m_node_id_list; + WeakCellValuePerNode<const unsigned short> m_node_local_numbers_in_their_cells; + WeakEdgeValuePerNode<const unsigned short> m_node_local_numbers_in_their_edges; + WeakFaceValuePerNode<const unsigned short> m_node_local_numbers_in_their_faces; - friend std::ostream& operator<<(std::ostream& os, const ConnectivityFace& f) + ConnectivityComputer m_connectivity_computer; + + std::vector<RefCellList> m_ref_cell_list_vector; + std::vector<RefFaceList> m_ref_face_list_vector; + std::vector<RefEdgeList> m_ref_edge_list_vector; + std::vector<RefNodeList> m_ref_node_list_vector; + + WeakCellValue<const double> m_inv_cell_nb_nodes; + + void _computeCellFaceAndFaceNodeConnectivities(); + + template <typename SubItemValuePerItemType> + PASTIS_INLINE + const SubItemValuePerItemType& + _lazzyBuildItemNumberInTheirChild(const SubItemValuePerItemType& sub_item_value_per_item) const { - for (auto id : f.m_node_id_list) { - os << id << ' '; + using ReversedItemOfItem = typename SubItemValuePerItemType::ItemOfItemType::Reversed; + if (not sub_item_value_per_item.isBuilt()) { + const_cast<SubItemValuePerItemType&>(sub_item_value_per_item) + = m_connectivity_computer + . computeLocalItemNumberInChildItem<ReversedItemOfItem>(*this); } - return os; + return sub_item_value_per_item; } + friend class ConnectivityComputer; + PASTIS_INLINE - const bool& reversed() const + const ConnectivityMatrix& _getMatrix(const ItemType& item_type_0, + const ItemType& item_type_1) const { - return m_reversed; + const ConnectivityMatrix& connectivity_matrix + = m_item_to_item_matrix[itemTId(item_type_0)][itemTId(item_type_1)]; + if (not connectivity_matrix.isBuilt()) { + const_cast<ConnectivityMatrix&>(connectivity_matrix) + = m_connectivity_computer + . computeConnectivityMatrix(*this, item_type_0, item_type_1); + + } + return connectivity_matrix; } + public: PASTIS_INLINE - const std::vector<unsigned int>& nodeIdList() const + const auto& cellType() const { - return m_node_id_list; + return m_cell_type; } PASTIS_INLINE - std::vector<unsigned int> _sort(const std::vector<unsigned int>& node_list) + const auto& cellNumber() const { - const auto min_id = std::min_element(node_list.begin(), node_list.end()); - const int shift = std::distance(node_list.begin(), min_id); - - std::vector<unsigned int> rotated_node_list(node_list.size()); - if (node_list[(shift+1)%node_list.size()] > node_list[(shift+node_list.size()-1)%node_list.size()]) { - for (size_t i=0; i<node_list.size(); ++i) { - rotated_node_list[i] = node_list[(shift+node_list.size()-i)%node_list.size()]; - m_reversed = true; - } - } else { - for (size_t i=0; i<node_list.size(); ++i) { - rotated_node_list[i] = node_list[(shift+i)%node_list.size()]; - } - } + return m_cell_number; + } - return rotated_node_list; + PASTIS_INLINE + const auto& faceNumber() const + { + return m_face_number; } PASTIS_INLINE - ConnectivityFace(const std::vector<unsigned int>& given_node_id_list) - : m_reversed(false), - m_node_id_list(_sort(given_node_id_list)) + const auto& edgeNumber() const { - ; + return m_edge_number; } - public: - bool operator==(const ConnectivityFace& f) const + PASTIS_INLINE + const auto& nodeNumber() const { - if (m_node_id_list.size() == f.nodeIdList().size()) { - for (size_t j=0; j<m_node_id_list.size(); ++j) { - if (m_node_id_list[j] != f.nodeIdList()[j]) { - return false; - } - } - return true; - } - return false; + return m_node_number; } + template <ItemType item_type> PASTIS_INLINE - bool operator<(const ConnectivityFace& f) const + const auto& number() const { - const size_t min_nb_nodes = std::min(f.m_node_id_list.size(), m_node_id_list.size()); - for (size_t i=0; i<min_nb_nodes; ++i) { - if (m_node_id_list[i] < f.m_node_id_list[i]) return true; - if (m_node_id_list[i] != f.m_node_id_list[i]) return false; + if constexpr(item_type == ItemType::cell) { + return m_cell_number; + } else if constexpr(item_type == ItemType::face) { + return m_face_number; + } else if constexpr(item_type == ItemType::edge) { + return m_edge_number; + } else if constexpr(item_type == ItemType::node) { + return m_node_number; + } else { + static_assert(is_false_item_type_v<item_type>, "unknown ItemType"); } - return m_node_id_list.size() < f.m_node_id_list.size(); } PASTIS_INLINE - ConnectivityFace& operator=(const ConnectivityFace&) = default; + const auto& cellOwner() const + { + return m_cell_owner; + } PASTIS_INLINE - ConnectivityFace& operator=(ConnectivityFace&&) = default; + const auto& faceOwner() const + { + return m_face_owner; + } PASTIS_INLINE - ConnectivityFace(const ConnectivityFace&) = default; + const auto& edgeOwner() const + { + perr() << __FILE__ << ':' << __LINE__ << ": edge owner not built\n"; + std::terminate(); + return m_edge_owner; + } PASTIS_INLINE - ConnectivityFace(ConnectivityFace&&) = default; - + const auto& nodeOwner() const + { + return m_node_owner; + } + template <ItemType item_type> PASTIS_INLINE - ConnectivityFace() = delete; + const auto& owner() const + { + if constexpr(item_type == ItemType::cell) { + return m_cell_owner; + } else if constexpr(item_type == ItemType::face) { + return m_face_owner; + } else if constexpr(item_type == ItemType::edge) { + return m_edge_owner; + } else if constexpr(item_type == ItemType::node) { + return m_node_owner; + } else { + static_assert(is_false_item_type_v<item_type>, "unknown ItemType"); + } + } PASTIS_INLINE - ~ConnectivityFace() = default; -}; - - -template <size_t Dimension> -class Connectivity final - : public IConnectivity -{ - private: - constexpr static auto& itemTId = ItemTypeId<Dimension>::itemTId; - - public: - static constexpr size_t dimension = Dimension; - - private: - ConnectivityMatrix m_item_to_item_matrix[Dimension+1][Dimension+1]; - CellValue<const CellType> m_cell_type; - - FaceValuePerCell<const bool> m_cell_face_is_reversed; - - NodeValuePerCell<const unsigned short> m_cell_local_numbers_in_their_nodes; - EdgeValuePerCell<const unsigned short> m_cell_local_numbers_in_their_edges; - FaceValuePerCell<const unsigned short> m_cell_local_numbers_in_their_faces; - - CellValuePerFace<const unsigned short> m_face_local_numbers_in_their_cells; - EdgeValuePerFace<const unsigned short> m_face_local_numbers_in_their_edges; - NodeValuePerFace<const unsigned short> m_face_local_numbers_in_their_nodes; - - CellValuePerEdge<const unsigned short> m_edge_local_numbers_in_their_cells; - FaceValuePerEdge<const unsigned short> m_edge_local_numbers_in_their_faces; - NodeValuePerEdge<const unsigned short> m_edge_local_numbers_in_their_nodes; - - CellValuePerNode<const unsigned short> m_node_local_numbers_in_their_cells; - EdgeValuePerNode<const unsigned short> m_node_local_numbers_in_their_edges; - FaceValuePerNode<const unsigned short> m_node_local_numbers_in_their_faces; - - ConnectivityComputer m_connectivity_computer; - - std::vector<RefFaceList> m_ref_face_list; - std::vector<RefNodeList> m_ref_node_list; - - CellValue<const double> m_inv_cell_nb_nodes; - - using Face = ConnectivityFace<Dimension>; - - std::unordered_map<Face, FaceId, typename Face::Hash> m_face_number_map; - - void _computeCellFaceAndFaceNodeConnectivities(); + const auto& cellIsOwned() const + { + return m_cell_is_owned; + } - template <typename SubItemValuePerItemType> PASTIS_INLINE - const SubItemValuePerItemType& - _lazzyBuildItemNumberInTheirChild(const SubItemValuePerItemType& sub_item_value_per_item) const + const auto& faceIsOwned() const { - if (not sub_item_value_per_item.isBuilt()) { - const_cast<SubItemValuePerItemType&>(sub_item_value_per_item) - = m_connectivity_computer - . computeLocalItemNumberInChildItem<SubItemValuePerItemType::item_t, - SubItemValuePerItemType::sub_item_t>(*this); - } - return sub_item_value_per_item; + return m_face_is_owned; } - friend class ConnectivityComputer; + PASTIS_INLINE + const auto& edgeIsOwned() const + { + perr() << __FILE__ << ':' << __LINE__ << ": edge is owned not built\n"; + std::terminate(); + return m_edge_is_owned; + } PASTIS_INLINE - const ConnectivityMatrix& _getMatrix(const ItemType& item_type_0, - const ItemType& item_type_1) const + const auto& nodeIsOwned() const { - const ConnectivityMatrix& connectivity_matrix - = m_item_to_item_matrix[itemTId(item_type_0)][itemTId(item_type_1)]; - if (not connectivity_matrix.isBuilt()) { - const_cast<ConnectivityMatrix&>(connectivity_matrix) - = m_connectivity_computer - . computeConnectivityMatrix(*this, item_type_0, item_type_1); + return m_node_is_owned; + } + template <ItemType item_type> + PASTIS_INLINE + const auto& isOwned() const + { + if constexpr(item_type == ItemType::cell) { + return m_cell_is_owned; + } else if constexpr(item_type == ItemType::face) { + return m_face_is_owned; + } else if constexpr(item_type == ItemType::edge) { + return m_edge_is_owned; + } else if constexpr(item_type == ItemType::node) { + return m_node_is_owned; + } else { + static_assert(is_false_item_type_v<item_type>, "unknown ItemType"); } - return connectivity_matrix; } - public: - PASTIS_INLINE const bool& isConnectivityMatrixBuilt(const ItemType& item_type_0, const ItemType& item_type_1) const @@ -398,14 +367,20 @@ class Connectivity final return this->template getItemToItemMatrix<ItemType::node, ItemType::edge>(); } - PASTIS_INLINE const auto& cellFaceIsReversed() const { - static_assert(dimension>1, "reversed faces makes no sense in dimension 1"); + static_assert(Dimension>1, "reversed faces makes no sense in dimension 1"); return m_cell_face_is_reversed; } + PASTIS_INLINE + const auto& faceEdgeIsReversed() const + { + static_assert(Dimension>2, "reversed edges makes no sense in dimension 1 or 2"); + return m_face_edge_is_reversed; + } + PASTIS_INLINE const auto& cellLocalNumbersInTheirNodes() const { @@ -415,7 +390,7 @@ class Connectivity final PASTIS_INLINE const auto& cellLocalNumbersInTheirEdges() const { - if constexpr (dimension>2) { + if constexpr (Dimension>2) { return _lazzyBuildItemNumberInTheirChild(m_cell_local_numbers_in_their_edges); } else { return cellLocalNumbersInTheirFaces(); @@ -425,7 +400,7 @@ class Connectivity final PASTIS_INLINE const auto& cellLocalNumbersInTheirFaces() const { - if constexpr (dimension>1) { + if constexpr (Dimension>1) { return _lazzyBuildItemNumberInTheirChild(m_cell_local_numbers_in_their_faces); } else { return cellLocalNumbersInTheirNodes(); @@ -435,7 +410,7 @@ class Connectivity final PASTIS_INLINE const auto& faceLocalNumbersInTheirCells() const { - if constexpr(dimension>1) { + if constexpr(Dimension>1) { return _lazzyBuildItemNumberInTheirChild(m_face_local_numbers_in_their_cells); } else { return nodeLocalNumbersInTheirCells(); @@ -445,21 +420,21 @@ class Connectivity final PASTIS_INLINE const auto& faceLocalNumbersInTheirEdges() const { - static_assert(dimension>2,"this function has no meaning in 1d or 2d"); + static_assert(Dimension>2,"this function has no meaning in 1d or 2d"); return _lazzyBuildItemNumberInTheirChild(m_face_local_numbers_in_their_edges); } PASTIS_INLINE const auto& faceLocalNumbersInTheirNodes() const { - static_assert(dimension>1,"this function has no meaning in 1d"); + static_assert(Dimension>1,"this function has no meaning in 1d"); return _lazzyBuildItemNumberInTheirChild(m_face_local_numbers_in_their_nodes); } PASTIS_INLINE const auto& edgeLocalNumbersInTheirCells() const { - if constexpr (dimension>2) { + if constexpr (Dimension>2) { return _lazzyBuildItemNumberInTheirChild(m_edge_local_numbers_in_their_cells); } else { return faceLocalNumbersInTheirCells(); @@ -469,14 +444,14 @@ class Connectivity final PASTIS_INLINE const auto& edgeLocalNumbersInTheirFaces() const { - static_assert(dimension>2, "this function has no meaning in 1d or 2d"); + static_assert(Dimension>2, "this function has no meaning in 1d or 2d"); return _lazzyBuildItemNumberInTheirChild(m_edge_local_numbers_in_their_faces); } PASTIS_INLINE const auto& edgeLocalNumbersInTheirNodes() const { - static_assert(dimension>2, "this function has no meaning in 1d or 2d"); + static_assert(Dimension>2, "this function has no meaning in 1d or 2d"); return _lazzyBuildItemNumberInTheirChild(m_edge_local_numbers_in_their_nodes); } @@ -489,45 +464,117 @@ class Connectivity final PASTIS_INLINE const auto& nodeLocalNumbersInTheirEdges() const { - static_assert(dimension>2, "this function has no meaning in 1d or 2d"); + static_assert(Dimension>2, "this function has no meaning in 1d or 2d"); return _lazzyBuildItemNumberInTheirChild(m_node_local_numbers_in_their_edges); } PASTIS_INLINE const auto& nodeLocalNumbersInTheirFaces() const { - static_assert(dimension>1,"this function has no meaning in 1d"); + static_assert(Dimension>1,"this function has no meaning in 1d"); return _lazzyBuildItemNumberInTheirChild(m_node_local_numbers_in_their_faces); } - void addRefFaceList(const RefFaceList& ref_face_list) + template <ItemType item_type> + size_t numberOfRefItemList() const { - m_ref_face_list.push_back(ref_face_list); - } + if constexpr (item_type == ItemType::cell) { + return m_ref_cell_list_vector.size(); + } else if constexpr (item_type == ItemType::face) { + return m_ref_face_list_vector.size(); + } else if constexpr (item_type == ItemType::edge) { + return m_ref_edge_list_vector.size(); + } else if constexpr (item_type == ItemType::node) { + return m_ref_node_list_vector.size(); + } else { + static_assert(is_false_item_type_v<item_type>, "Unexpected item type"); + } - size_t numberOfRefFaceList() const - { - return m_ref_face_list.size(); } - const RefFaceList& refFaceList(const size_t& i) const + template <ItemType item_type> + const RefItemList<item_type>& refItemList(const size_t& i) const { - return m_ref_face_list[i]; + if constexpr (item_type == ItemType::cell) { + return m_ref_cell_list_vector[i]; + } else if constexpr (item_type == ItemType::face) { + return m_ref_face_list_vector[i]; + } else if constexpr (item_type == ItemType::edge) { + return m_ref_edge_list_vector[i]; + } else if constexpr (item_type == ItemType::node) { + return m_ref_node_list_vector[i]; + } else { + static_assert(is_false_item_type_v<item_type>, "Unexpected item type"); + } } - void addRefNodeList(const RefNodeList& ref_node_list) + template <ItemType item_type> + void addRefItemList(const RefItemList<item_type>& ref_item_list) { - m_ref_node_list.push_back(ref_node_list); + if constexpr (item_type == ItemType::cell) { + m_ref_cell_list_vector.push_back(ref_item_list); + } else if constexpr (item_type == ItemType::face) { + m_ref_face_list_vector.push_back(ref_item_list); + } else if constexpr (item_type == ItemType::edge) { + m_ref_edge_list_vector.push_back(ref_item_list); + } else if constexpr (item_type == ItemType::node) { + m_ref_node_list_vector.push_back(ref_item_list); + } else { + static_assert(is_false_item_type_v<item_type>, "Unexpected item type"); + } } - size_t numberOfRefNodeList() const + PASTIS_INLINE + CSRGraph cellToCellGraph() const { - return m_ref_node_list.size(); - } + std::vector<std::set<int>> cell_cells(this->numberOfCells()); + if constexpr (true) { + const auto& face_to_cell_matrix + = this->faceToCellMatrix(); - const RefNodeList& refNodeList(const size_t& i) const - { - return m_ref_node_list[i]; + for (FaceId l=0; l<this->numberOfFaces(); ++l) { + const auto& face_to_cell = face_to_cell_matrix[l]; + if (face_to_cell.size() > 1) { + const CellId cell_0 = face_to_cell[0]; + const CellId cell_1 = face_to_cell[1]; + + cell_cells[cell_0].insert(cell_1); + cell_cells[cell_1].insert(cell_0); + } + } + } else { + const auto& node_to_cell_matrix + = this->nodeToCellMatrix(); + + for (NodeId l=0; l<this->numberOfNodes(); ++l) { + const auto& node_to_cell = node_to_cell_matrix[l]; + for (size_t i_cell=0; i_cell<node_to_cell.size(); ++i_cell) { + const CellId cell_0 = node_to_cell[i_cell]; + for (size_t j_cell=0; j_cell<i_cell; ++j_cell) { + const CellId cell_1 = node_to_cell[j_cell]; + cell_cells[cell_0].insert(cell_1); + cell_cells[cell_1].insert(cell_0); + } + } + } + } + + Array<int> entries(this->numberOfCells()+1); + entries[0]=0; + for (size_t j=0; j<this->numberOfCells(); ++j) { + entries[j+1] = entries[j]+cell_cells[j].size(); + } + Array<int> neighbors(entries[this->numberOfCells()]); + { + size_t k=0; + for (size_t j=0; j<this->numberOfCells(); ++j) { + for (CellId cell_id : cell_cells[j]) { + neighbors[k] = m_cell_global_index[cell_id]; + ++k; + } + } + } + return CSRGraph(entries, neighbors); } PASTIS_INLINE @@ -562,35 +609,48 @@ class Connectivity final return cell_to_node_matrix.numRows(); } - const CellValue<const double>& invCellNbNodes() const + CellValue<const double> invCellNbNodes() const { -#warning add calculation on demand when variables will be defined - return m_inv_cell_nb_nodes; - } + if (not m_inv_cell_nb_nodes.isBuilt()) { + const auto& cell_to_node_matrix + = m_item_to_item_matrix[itemTId(ItemType::cell)][itemTId(ItemType::node)]; - unsigned int getFaceNumber(const std::vector<unsigned int>& face_nodes) const - { - const Face face(face_nodes); - auto i_face = m_face_number_map.find(face); - if (i_face == m_face_number_map.end()) { - perr() << "Face " << face << " not found!\n"; - throw std::exception(); - std::exit(0); + WeakCellValue<double> inv_cell_nb_nodes(*this); + parallel_for(this->numberOfCells(), PASTIS_LAMBDA(const CellId& j) { + const auto& cell_nodes = cell_to_node_matrix.rowConst(j); + inv_cell_nb_nodes[j] = 1./cell_nodes.length; + }); + const_cast<WeakCellValue<const double>&>(m_inv_cell_nb_nodes) = inv_cell_nb_nodes; } - return i_face->second; + + return m_inv_cell_nb_nodes; } Connectivity(const Connectivity&) = delete; - Connectivity(const std::vector<std::vector<unsigned int>>& cell_by_node_vector, - const std::vector<CellType>& cell_type_vector); + private: + Connectivity(); + void _buildFrom(const ConnectivityDescriptor& descriptor); + public: ~Connectivity() { - ; + auto& manager = SynchronizerManager::instance(); + manager.deleteConnectivitySynchronizer(this); } }; +template <size_t Dim> +PASTIS_INLINE +std::shared_ptr<Connectivity<Dim>> +Connectivity<Dim>::build(const ConnectivityDescriptor& descriptor) +{ + std::shared_ptr<Connectivity<Dim>> connectivity_ptr(new Connectivity<Dim>); + connectivity_ptr->_buildFrom(descriptor); + + return connectivity_ptr; +} + using Connectivity3D = Connectivity<3>; using Connectivity2D = Connectivity<2>; using Connectivity1D = Connectivity<1>; diff --git a/src/mesh/ConnectivityComputer.cpp b/src/mesh/ConnectivityComputer.cpp index d6a77b13e6f124b825c54cbf422567281784fb79..294b15591eac18ab8ac11951b74a0effd84c1827 100644 --- a/src/mesh/ConnectivityComputer.cpp +++ b/src/mesh/ConnectivityComputer.cpp @@ -24,7 +24,7 @@ computeConnectivityMatrix(const ConnectivityType& connectivity, } else { perr() << "unable to compute connectivity " << itemName(item_type) << " -> " << itemName(child_item_type) << '\n'; - std::exit(0); + std::terminate(); } return item_to_child_item_matrix; @@ -76,18 +76,22 @@ _computeInverse(const ConnectivityMatrix& item_to_child_matrix) const return ConnectivityMatrix(child_to_items_vector); } -template <ItemType item_type, - ItemType child_item_type, +template <typename ItemOfItem, typename ConnectivityType> -SubItemValuePerItem<const unsigned short, child_item_type, item_type> +WeakSubItemValuePerItem<const unsigned short, typename ItemOfItem::Reversed> ConnectivityComputer::computeLocalItemNumberInChildItem(const ConnectivityType& connectivity) const { + using ReversedItemOfItem = typename ItemOfItem::Reversed; + + constexpr ItemType item_type = ReversedItemOfItem::item_type; + constexpr ItemType child_item_type = ReversedItemOfItem::sub_item_type; + const ConnectivityMatrix& child_item_to_items_matrix = connectivity._getMatrix(child_item_type, item_type); const ConnectivityMatrix& item_to_child_items_matrix = connectivity._getMatrix(item_type, child_item_type); - SubItemValuePerItem<unsigned short, child_item_type, item_type> item_number_in_child_item(connectivity); + WeakSubItemValuePerItem<unsigned short, ReversedItemOfItem> item_number_in_child_item(connectivity); for (ItemIdT<item_type> r=0; r<item_to_child_items_matrix.numRows(); ++r) { const auto& item_to_child_items = item_to_child_items_matrix.rowConst(r); for (unsigned short J=0; J<item_to_child_items.length; ++J) { @@ -108,51 +112,89 @@ ConnectivityComputer::computeLocalItemNumberInChildItem(const ConnectivityType& // 1D -template SubItemValuePerItem<const unsigned short, ItemType::cell, ItemType::node> +template WeakSubItemValuePerItem<const unsigned short, CellOfNode> +ConnectivityComputer:: +computeLocalItemNumberInChildItem<NodeOfCell>(const Connectivity1D&) const; + +template WeakSubItemValuePerItem<const unsigned short, NodeOfCell> ConnectivityComputer:: -computeLocalItemNumberInChildItem<ItemType::node, - ItemType::cell>(const Connectivity1D&) const; +computeLocalItemNumberInChildItem<CellOfNode>(const Connectivity1D&) const; -template SubItemValuePerItem<const unsigned short, ItemType::cell, ItemType::face> +// 2D + +template WeakSubItemValuePerItem<const unsigned short, CellOfNode> ConnectivityComputer:: -computeLocalItemNumberInChildItem<ItemType::face, - ItemType::cell>(const Connectivity1D&) const; +computeLocalItemNumberInChildItem<NodeOfCell>(const Connectivity2D&) const; -template SubItemValuePerItem<const unsigned short, ItemType::node, ItemType::cell> +template WeakSubItemValuePerItem<const unsigned short, CellOfFace> ConnectivityComputer:: -computeLocalItemNumberInChildItem<ItemType::cell, - ItemType::node>(const Connectivity1D&) const; +computeLocalItemNumberInChildItem<FaceOfCell>(const Connectivity2D&) const; -// 2D +template WeakSubItemValuePerItem<const unsigned short, FaceOfNode> +ConnectivityComputer:: +computeLocalItemNumberInChildItem<NodeOfFace>(const Connectivity2D&) const; -template SubItemValuePerItem<const unsigned short, ItemType::cell, ItemType::node> +template WeakSubItemValuePerItem<const unsigned short, FaceOfCell> ConnectivityComputer:: -computeLocalItemNumberInChildItem<ItemType::node, - ItemType::cell>(const Connectivity2D&) const; +computeLocalItemNumberInChildItem<CellOfFace>(const Connectivity2D&) const; -template SubItemValuePerItem<const unsigned short, ItemType::cell, ItemType::face> +template WeakSubItemValuePerItem<const unsigned short, NodeOfFace> ConnectivityComputer:: -computeLocalItemNumberInChildItem<ItemType::face, - ItemType::cell>(const Connectivity2D&) const; +computeLocalItemNumberInChildItem<FaceOfNode>(const Connectivity2D&) const; -template SubItemValuePerItem<const unsigned short, ItemType::node, ItemType::cell> +template WeakSubItemValuePerItem<const unsigned short, NodeOfCell> ConnectivityComputer:: -computeLocalItemNumberInChildItem<ItemType::cell, - ItemType::node>(const Connectivity2D&) const; +computeLocalItemNumberInChildItem<CellOfNode>(const Connectivity2D&) const; // 3D -template SubItemValuePerItem<const unsigned short, ItemType::cell, ItemType::node> +template WeakSubItemValuePerItem<const unsigned short, CellOfNode> +ConnectivityComputer:: +computeLocalItemNumberInChildItem<NodeOfCell>(const Connectivity3D&) const; + +template WeakSubItemValuePerItem<const unsigned short, CellOfEdge> +ConnectivityComputer:: +computeLocalItemNumberInChildItem<EdgeOfCell>(const Connectivity3D&) const; + +template WeakSubItemValuePerItem<const unsigned short, CellOfFace> +ConnectivityComputer:: +computeLocalItemNumberInChildItem<FaceOfCell>(const Connectivity3D&) const; + + +template WeakSubItemValuePerItem<const unsigned short, FaceOfNode> +ConnectivityComputer:: +computeLocalItemNumberInChildItem<NodeOfFace>(const Connectivity3D&) const; + +template WeakSubItemValuePerItem<const unsigned short, FaceOfEdge> +ConnectivityComputer:: +computeLocalItemNumberInChildItem<EdgeOfFace>(const Connectivity3D&) const; + +template WeakSubItemValuePerItem<const unsigned short, FaceOfCell> +ConnectivityComputer:: +computeLocalItemNumberInChildItem<CellOfFace>(const Connectivity3D&) const; + + +template WeakSubItemValuePerItem<const unsigned short, EdgeOfNode> +ConnectivityComputer:: +computeLocalItemNumberInChildItem<NodeOfEdge>(const Connectivity3D&) const; + +template WeakSubItemValuePerItem<const unsigned short, EdgeOfFace> +ConnectivityComputer:: +computeLocalItemNumberInChildItem<FaceOfEdge>(const Connectivity3D&) const; + +template WeakSubItemValuePerItem<const unsigned short, EdgeOfCell> +ConnectivityComputer:: +computeLocalItemNumberInChildItem<CellOfEdge>(const Connectivity3D&) const; + + +template WeakSubItemValuePerItem<const unsigned short, NodeOfEdge> ConnectivityComputer:: -computeLocalItemNumberInChildItem<ItemType::node, - ItemType::cell>(const Connectivity3D&) const; +computeLocalItemNumberInChildItem<EdgeOfNode>(const Connectivity3D&) const; -template SubItemValuePerItem<const unsigned short, ItemType::cell, ItemType::face> +template WeakSubItemValuePerItem<const unsigned short, NodeOfFace> ConnectivityComputer:: -computeLocalItemNumberInChildItem<ItemType::face, - ItemType::cell>(const Connectivity3D&) const; +computeLocalItemNumberInChildItem<FaceOfNode>(const Connectivity3D&) const; -template SubItemValuePerItem<const unsigned short, ItemType::node, ItemType::cell> +template WeakSubItemValuePerItem<const unsigned short, NodeOfCell> ConnectivityComputer:: -computeLocalItemNumberInChildItem<ItemType::cell, - ItemType::node>(const Connectivity3D&) const; +computeLocalItemNumberInChildItem<CellOfNode>(const Connectivity3D&) const; diff --git a/src/mesh/ConnectivityComputer.hpp b/src/mesh/ConnectivityComputer.hpp index 8b4fdfc3ab20bf2d6290d234a13b833dc6451cb6..d5c6019d54b57d6f18ad1eab0af3797f2e0108cb 100644 --- a/src/mesh/ConnectivityComputer.hpp +++ b/src/mesh/ConnectivityComputer.hpp @@ -18,10 +18,9 @@ class ConnectivityComputer const ItemType& child_item_type) const; - template <ItemType item_type, - ItemType child_item_type, + template <typename ItemOfItem, typename ConnectivityType> - SubItemValuePerItem<const unsigned short, child_item_type, item_type> + WeakSubItemValuePerItem<const unsigned short, typename ItemOfItem::Reversed> computeLocalItemNumberInChildItem(const ConnectivityType& connectivity) const; ConnectivityComputer(const ConnectivityComputer&) = default; diff --git a/src/mesh/ConnectivityDescriptor.hpp b/src/mesh/ConnectivityDescriptor.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e20b05c11ac585b4293fc233bd0919efce48e5ea --- /dev/null +++ b/src/mesh/ConnectivityDescriptor.hpp @@ -0,0 +1,121 @@ +#ifndef CONNECTIVITY_DESCRIPTOR_HPP +#define CONNECTIVITY_DESCRIPTOR_HPP + +#include <RefItemList.hpp> +#include <PastisTraits.hpp> +#include <ItemOfItemType.hpp> + +#include <vector> + +class ConnectivityDescriptor +{ + private: + std::vector<RefCellList> m_ref_cell_list_vector; + std::vector<RefFaceList> m_ref_face_list_vector; + std::vector<RefEdgeList> m_ref_edge_list_vector; + std::vector<RefNodeList> m_ref_node_list_vector; + + public: + std::vector<std::vector<unsigned int>> cell_to_node_vector; + std::vector<std::vector<unsigned int>> cell_to_face_vector; + std::vector<std::vector<unsigned int>> cell_to_edge_vector; + + std::vector<std::vector<unsigned int>> face_to_node_vector; + std::vector<std::vector<unsigned int>> face_to_edge_vector; + + std::vector<std::vector<unsigned int>> edge_to_node_vector; + + template <typename ItemOfItemT> + auto& itemOfItemVector() + { + if constexpr (std::is_same_v<ItemOfItemT,NodeOfCell>) { + return cell_to_node_vector; + } else if constexpr (std::is_same_v<ItemOfItemT,FaceOfCell>) { + return cell_to_face_vector; + } else if constexpr (std::is_same_v<ItemOfItemT,EdgeOfCell>) { + return cell_to_edge_vector; + } else if constexpr (std::is_same_v<ItemOfItemT,EdgeOfFace>) { + return face_to_edge_vector; + } else if constexpr (std::is_same_v<ItemOfItemT,NodeOfFace>) { + return face_to_node_vector; + } else if constexpr (std::is_same_v<ItemOfItemT,NodeOfEdge>) { + return edge_to_node_vector; + } else { + static_assert(is_false_v<ItemOfItemT>, "Unexpected item of item type"); + } + } + + std::vector<Array<bool>> cell_face_is_reversed_vector; + std::vector<Array<bool>> face_edge_is_reversed_vector; + + std::vector<CellType> cell_type_vector; + + std::vector<int> cell_number_vector; + std::vector<int> face_number_vector; + std::vector<int> edge_number_vector; + std::vector<int> node_number_vector; + + template <ItemType item_type> + const std::vector<int>& itemNumberVector() const + { + if constexpr (item_type == ItemType::cell) { + return cell_number_vector; + } else if constexpr (item_type == ItemType::face) { + return face_number_vector; + } else if constexpr (item_type == ItemType::edge) { + return edge_number_vector; + } else if constexpr (item_type == ItemType::node) { + return node_number_vector; + } else { + static_assert(is_false_item_type_v<item_type>, "Unexpected item type"); + } + } + + std::vector<int> cell_owner_vector; + std::vector<int> face_owner_vector; + std::vector<int> edge_owner_vector; + std::vector<int> node_owner_vector; + + template <ItemType item_type> + const std::vector<RefItemList<item_type>>& refItemListVector() const + { + if constexpr (item_type == ItemType::cell) { + return m_ref_cell_list_vector; + } else if constexpr (item_type == ItemType::face) { + return m_ref_face_list_vector; + } else if constexpr (item_type == ItemType::edge) { + return m_ref_edge_list_vector; + } else if constexpr (item_type == ItemType::node) { + return m_ref_node_list_vector; + } else { + static_assert(is_false_item_type_v<item_type>, "Unexpected item type"); + } + } + + template <ItemType item_type> + void addRefItemList(const RefItemList<item_type>& ref_item_list) + { + if constexpr (item_type == ItemType::cell) { + m_ref_cell_list_vector.push_back(ref_item_list); + } else if constexpr (item_type == ItemType::face) { + m_ref_face_list_vector.push_back(ref_item_list); + } else if constexpr (item_type == ItemType::edge) { + m_ref_edge_list_vector.push_back(ref_item_list); + } else if constexpr (item_type == ItemType::node) { + m_ref_node_list_vector.push_back(ref_item_list); + } else { + static_assert(is_false_item_type_v<item_type>, "Unexpected item type"); + } + } + + ConnectivityDescriptor& operator=(const ConnectivityDescriptor&) = delete; + ConnectivityDescriptor& operator=(ConnectivityDescriptor&&) = delete; + + ConnectivityDescriptor() = default; + ConnectivityDescriptor(const ConnectivityDescriptor&) = default; + ConnectivityDescriptor(ConnectivityDescriptor&&) = delete; + ~ConnectivityDescriptor() = default; +}; + + +#endif // CONNECTIVITY_DESCRIPTOR_HPP diff --git a/src/mesh/ConnectivityDispatcher.cpp b/src/mesh/ConnectivityDispatcher.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aa26cb6f71422cfaf646f3d52ab22ff882e0f72a --- /dev/null +++ b/src/mesh/ConnectivityDispatcher.cpp @@ -0,0 +1,683 @@ +#include <ConnectivityDispatcher.hpp> +#include <Partitioner.hpp> + +#include <ItemOfItemType.hpp> + +#include <unordered_map> + +template <int Dimension> +template <ItemType item_type> +void +ConnectivityDispatcher<Dimension>::_buildNewOwner() +{ + if constexpr (item_type == ItemType::cell) { + CSRGraph connectivity_graph = m_connectivity.cellToCellGraph(); + Partitioner P; + + CellValue<int> cell_new_owner(m_connectivity); + cell_new_owner = P.partition(connectivity_graph); + + this->_dispatchedInfo<ItemType::cell>().m_new_owner = cell_new_owner; + } else { + const auto& item_to_cell_matrix + = m_connectivity.template getItemToItemMatrix<item_type,ItemType::cell>(); + + const auto& cell_number = m_connectivity.cellNumber(); + const auto& cell_new_owner = this->_dispatchedInfo<ItemType::cell>().m_new_owner; + + using ItemId = ItemIdT<item_type>; + ItemValue<int, item_type> item_new_owner(m_connectivity); + parallel_for(item_new_owner.size(), PASTIS_LAMBDA(const ItemId& l) { + const auto& item_to_cell = item_to_cell_matrix[l]; + CellId Jmin = item_to_cell[0]; + + for (size_t j=1; j<item_to_cell.size(); ++j) { + const CellId J = item_to_cell[j]; + if (cell_number[J] < cell_number[Jmin]) { + Jmin=J; + } + } + item_new_owner[l] = cell_new_owner[Jmin]; + }); + + synchronize(item_new_owner); + this->_dispatchedInfo<item_type>().m_new_owner = item_new_owner; + } +} + +template <int Dimension> +template <ItemType item_type> +void ConnectivityDispatcher<Dimension>:: +_buildItemToExchangeLists() +{ + this->_buildItemListToSend<item_type>(); + this->_buildNumberOfItemToExchange<item_type>(); + if constexpr (item_type == ItemType::cell) { + this->_buildCellNumberIdMap(); + } + this->_buildRecvItemIdCorrespondanceByProc<item_type>(); +} + +template <int Dimension> +template <ItemType item_type> +void +ConnectivityDispatcher<Dimension>::_buildItemListToSend() +{ + if constexpr (item_type == ItemType::cell) { + const auto& node_to_cell_matrix + = m_connectivity.nodeToCellMatrix(); + const auto& cell_to_node_matrix + = m_connectivity.cellToNodeMatrix(); + + const auto& cell_new_owner = this->_dispatchedInfo<ItemType::cell>().m_new_owner; + + std::vector<std::vector<CellId>> cell_vector_to_send_by_proc(parallel::size()); + Array<bool> send_to_rank(parallel::size()); + for (CellId j=0; j<m_connectivity.numberOfCells(); ++j) { + send_to_rank.fill(false); + const auto& cell_to_node = cell_to_node_matrix[j]; + + for (size_t R=0; R<cell_to_node.size(); ++R) { + const NodeId& r = cell_to_node[R]; + const auto& node_to_cell = node_to_cell_matrix[r]; + for (size_t K=0; K<node_to_cell.size(); ++K) { + const CellId& k = node_to_cell[K]; + send_to_rank[cell_new_owner[k]] = true; + } + } + + for (size_t k=0; k<send_to_rank.size(); ++k) { + if (send_to_rank[k]) { + cell_vector_to_send_by_proc[k].push_back(j); + } + } + } + + auto& cell_list_to_send_by_proc = this->_dispatchedInfo<ItemType::cell>().m_list_to_send_by_proc; + cell_list_to_send_by_proc.resize(parallel::size()); + for (size_t i=0; i<parallel::size(); ++i) { + cell_list_to_send_by_proc[i] = convert_to_array(cell_vector_to_send_by_proc[i]); + } + } else { + const auto& cell_list_to_send_by_proc = this->_dispatchedInfo<ItemType::cell>().m_list_to_send_by_proc; + + using ItemId = ItemIdT<item_type>; + const auto& cell_to_sub_item_matrix = m_connectivity.template getItemToItemMatrix<ItemType::cell,item_type>(); + + auto& item_list_to_send_by_proc = this->_dispatchedInfo<item_type>().m_list_to_send_by_proc; + item_list_to_send_by_proc.resize(parallel::size()); + + for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) { + Array<bool> tag(m_connectivity.template numberOf<item_type>()); + tag.fill(false); + std::vector<ItemId> item_id_vector; + for (size_t j=0; j<cell_list_to_send_by_proc[i_rank].size(); ++j) { + const CellId& cell_id = cell_list_to_send_by_proc[i_rank][j]; + const auto& cell_sub_item_list = cell_to_sub_item_matrix[cell_id]; + for (size_t r=0; r<cell_sub_item_list.size(); ++r) { + const ItemId& item_id = cell_sub_item_list[r]; + if (not tag[item_id]) { + item_id_vector.push_back(item_id); + tag[item_id] = true; + } + } + } + item_list_to_send_by_proc[i_rank] = convert_to_array(item_id_vector); + } + } +} + +template <int Dimension> +template <ItemType item_type> +void +ConnectivityDispatcher<Dimension>::_buildNumberOfItemToExchange() +{ + const auto& item_list_to_send_by_proc = this->_dispatchedInfo<item_type>().m_list_to_send_by_proc; + Array<unsigned int> nb_item_to_send_by_proc(parallel::size()); + for (size_t i=0; i<parallel::size(); ++i) { + nb_item_to_send_by_proc[i] = item_list_to_send_by_proc[i].size(); + } + this->_dispatchedInfo<item_type>().m_list_to_send_size_by_proc = nb_item_to_send_by_proc; + + this->_dispatchedInfo<item_type>().m_list_to_recv_size_by_proc + = parallel::allToAll(nb_item_to_send_by_proc); +} + + +template <int Dimension> +template<typename DataType, ItemType item_type, typename ConnectivityPtr> +void +ConnectivityDispatcher<Dimension>:: +_gatherFrom(const ItemValue<DataType, item_type, ConnectivityPtr>& data_to_gather, + std::vector<std::remove_const_t<DataType>>& gathered_vector) +{ + std::vector<Array<const DataType>> recv_item_data_by_proc = this->exchange(data_to_gather); + + const auto& recv_id_correspondance_by_proc = this->_dispatchedInfo<item_type>().m_recv_id_correspondance_by_proc; + Assert(recv_id_correspondance_by_proc.size()==parallel::size()); + + gathered_vector.resize(this->_dispatchedInfo<item_type>().m_number_to_id_map.size()); + for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) { + Assert(recv_id_correspondance_by_proc[i_rank].size()==recv_item_data_by_proc[i_rank].size()); + for (size_t r=0; r<recv_id_correspondance_by_proc[i_rank].size(); ++r) { + const auto& item_id = recv_id_correspondance_by_proc[i_rank][r]; + gathered_vector[item_id] = recv_item_data_by_proc[i_rank][r]; + } + } +} + +template <int Dimension> +template<typename DataType, typename ItemOfItem, typename ConnectivityPtr> +void ConnectivityDispatcher<Dimension>:: +_gatherFrom(const SubItemValuePerItem<DataType, ItemOfItem, ConnectivityPtr>& data_to_gather, + std::vector<Array<std::remove_const_t<DataType>>>& gathered_vector) +{ + using MutableDataType = std::remove_const_t<DataType>; + + constexpr ItemType item_type = ItemOfItem::item_type; + using ItemId = ItemIdT<item_type>; + + const auto& item_list_to_send_by_proc = this->_dispatchedInfo<item_type>().m_list_to_send_by_proc; + + std::vector<Array<MutableDataType>> data_to_send_by_proc(parallel::size()); + + for (size_t i_rank=0; i_rank < parallel::size(); ++i_rank) { + std::vector<MutableDataType> data_by_item_vector; + for (size_t j=0; j<item_list_to_send_by_proc[i_rank].size(); ++j) { + const ItemId& item_id = item_list_to_send_by_proc[i_rank][j]; + const auto& item_data = data_to_gather.itemValues(item_id); + for (size_t l=0; l<item_data.size(); ++l) { + data_by_item_vector.push_back(item_data[l]); + } + } + data_to_send_by_proc[i_rank] = convert_to_array(data_by_item_vector); + } + + const auto& number_of_sub_item_per_item_to_recv_by_proc = + this->_dispatchedInfo<ItemOfItem>().m_number_of_sub_item_per_item_to_recv_by_proc; + + std::vector<Array<MutableDataType>> recv_data_to_gather_by_proc(parallel::size()); + for (size_t i_rank=0; i_rank < parallel::size(); ++i_rank) { + recv_data_to_gather_by_proc[i_rank] + = Array<MutableDataType>(sum(number_of_sub_item_per_item_to_recv_by_proc[i_rank])); + } + + parallel::exchange(data_to_send_by_proc, recv_data_to_gather_by_proc); + + const auto& item_list_to_recv_size_by_proc = + this->_dispatchedInfo<item_type>().m_list_to_recv_size_by_proc; + + for (size_t i_rank=0; i_rank < parallel::size(); ++i_rank) { + int l=0; + for (size_t i=0; i<item_list_to_recv_size_by_proc[i_rank]; ++i) { + Array<MutableDataType> data_vector(number_of_sub_item_per_item_to_recv_by_proc[i_rank][i]); + for (size_t k=0; k<data_vector.size(); ++k) { + data_vector[k] = recv_data_to_gather_by_proc[i_rank][l++]; + } + gathered_vector.emplace_back(data_vector); + } + } +} + +template <int Dimension> +void +ConnectivityDispatcher<Dimension>:: +_buildCellNumberIdMap() +{ + const auto recv_cell_number_by_proc = this->exchange(m_connectivity.template number<ItemType::cell>()); + auto& cell_number_id_map = this->_dispatchedInfo<ItemType::cell>().m_number_to_id_map; + for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) { + CellId cell_id=0; + for (size_t i=0; i<recv_cell_number_by_proc[i_rank].size(); ++i) { + const int cell_number = recv_cell_number_by_proc[i_rank][i]; + auto [iterator, inserted] = cell_number_id_map.insert(std::make_pair(cell_number, cell_id)); + if (inserted) ++cell_id; + } + } +} + +template <int Dimension> +template <typename ItemOfItemT> +void +ConnectivityDispatcher<Dimension>::_buildSubItemNumberToIdMap() +{ + static_assert(ItemOfItemT::item_type == ItemType::cell, "Dispatcher requires to be built using cell as master entities"); + + const auto& cell_sub_item_number_to_recv_by_proc + = this->_dispatchedInfo<ItemOfItemT>().m_sub_item_numbers_to_recv_by_proc; + + auto& sub_item_number_id_map = this->_dispatchedInfo<ItemOfItemT::sub_item_type>().m_number_to_id_map; + for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) { + int sub_item_id=0; + for (size_t i=0; i<cell_sub_item_number_to_recv_by_proc[i_rank].size(); ++i) { + int sub_item_number = cell_sub_item_number_to_recv_by_proc[i_rank][i]; + auto [iterator, inserted] = sub_item_number_id_map.insert(std::make_pair(sub_item_number, sub_item_id)); + if (inserted) sub_item_id++; + } + } +} + +template <int Dimension> +template <typename SubItemOfItemT> +void +ConnectivityDispatcher<Dimension>::_buildNumberOfSubItemPerItemToRecvByProc() +{ + const auto& item_to_sub_item_matrix + = m_connectivity.template getItemToItemMatrix<SubItemOfItemT::item_type, + SubItemOfItemT::sub_item_type>(); + + ItemValue<int, SubItemOfItemT::item_type> number_of_sub_item_per_item(m_connectivity); + + using ItemId = ItemIdT<SubItemOfItemT::item_type>; + parallel_for(number_of_sub_item_per_item.size(), PASTIS_LAMBDA(const ItemId& j){ + number_of_sub_item_per_item[j] = item_to_sub_item_matrix[j].size(); + }); + + this->_dispatchedInfo<SubItemOfItemT>().m_number_of_sub_item_per_item_to_recv_by_proc = + this->exchange(number_of_sub_item_per_item); +} + +template <int Dimension> +template <typename SubItemOfItemT> +void +ConnectivityDispatcher<Dimension>::_buildSubItemNumbersToRecvByProc() +{ + const std::vector<Array<const int>> sub_item_numbers_to_send_by_proc = + [&] () { + const auto& item_to_sub_item_matrix + = m_connectivity.template getItemToItemMatrix<SubItemOfItemT::item_type, + SubItemOfItemT::sub_item_type>(); + + const auto& sub_item_number = m_connectivity.template number<SubItemOfItemT::sub_item_type>(); + + using ItemId = ItemIdT<SubItemOfItemT::item_type>; + using SubItemId = ItemIdT<SubItemOfItemT::sub_item_type>; + + std::vector<Array<const int>> sub_item_numbers_to_send_by_proc(parallel::size()); + for (size_t i_rank=0; i_rank < parallel::size(); ++i_rank) { + const auto& item_list_to_send_by_proc + = this->_dispatchedInfo<SubItemOfItemT::item_type>().m_list_to_send_by_proc; + + std::vector<int> sub_item_numbers_by_item_vector; + for (size_t j=0; j<item_list_to_send_by_proc[i_rank].size(); ++j) { + const ItemId& item_id = item_list_to_send_by_proc[i_rank][j]; + const auto& sub_item_list = item_to_sub_item_matrix[item_id]; + for (size_t r=0; r<sub_item_list.size(); ++r) { + const SubItemId& sub_item_id = sub_item_list[r]; + sub_item_numbers_by_item_vector.push_back(sub_item_number[sub_item_id]); + } + } + sub_item_numbers_to_send_by_proc[i_rank] = convert_to_array(sub_item_numbers_by_item_vector); + } + return sub_item_numbers_to_send_by_proc; + } (); + + const auto& number_of_sub_item_per_item_to_recv_by_proc = + this->_dispatchedInfo<SubItemOfItemT>().m_number_of_sub_item_per_item_to_recv_by_proc; + + std::vector<Array<int>> sub_item_numbers_to_recv_by_proc(parallel::size()); + for (size_t i_rank=0; i_rank < parallel::size(); ++i_rank) { + sub_item_numbers_to_recv_by_proc[i_rank] + = Array<int>(sum(number_of_sub_item_per_item_to_recv_by_proc[i_rank])); + } + parallel::exchange(sub_item_numbers_to_send_by_proc, sub_item_numbers_to_recv_by_proc); + + auto& const_sub_item_numbers_to_recv_by_proc = + this->_dispatchedInfo<SubItemOfItemT>().m_sub_item_numbers_to_recv_by_proc; + + const_sub_item_numbers_to_recv_by_proc.resize(parallel::size()); + for (size_t i_rank=0; i_rank < parallel::size(); ++i_rank) { + const_sub_item_numbers_to_recv_by_proc[i_rank] = sub_item_numbers_to_recv_by_proc[i_rank]; + } +} + +template <int Dimension> +template <typename ItemOfItemT> +void +ConnectivityDispatcher<Dimension>::_buildItemToSubItemDescriptor() +{ + constexpr ItemType item_type = ItemOfItemT::item_type; + constexpr ItemType sub_item_type = ItemOfItemT::sub_item_type; + + const auto& item_list_to_recv_size_by_proc = + this->_dispatchedInfo<item_type>().m_list_to_recv_size_by_proc; + + const auto& number_of_sub_item_per_item_to_recv_by_proc = + this->_dispatchedInfo<ItemOfItemT>().m_number_of_sub_item_per_item_to_recv_by_proc; + + const auto& sub_item_number_id_map = + this->_dispatchedInfo<sub_item_type>().m_number_to_id_map; + + const auto& recv_item_of_item_numbers_by_proc = + this->_dispatchedInfo<ItemOfItemT>().m_sub_item_numbers_to_recv_by_proc; + + for (size_t i_rank=0; i_rank < parallel::size(); ++i_rank) { + int l=0; + for (size_t i=0; i<item_list_to_recv_size_by_proc[i_rank]; ++i) { + std::vector<unsigned int> sub_item_vector; + for (int k=0; k<number_of_sub_item_per_item_to_recv_by_proc[i_rank][i]; ++k) { + const auto& searched_sub_item_id = + sub_item_number_id_map.find(recv_item_of_item_numbers_by_proc[i_rank][l++]); + Assert(searched_sub_item_id != sub_item_number_id_map.end()); + sub_item_vector.push_back(searched_sub_item_id->second); + } + m_new_descriptor.itemOfItemVector<ItemOfItemT>().emplace_back(sub_item_vector); + } + } +} + +template <int Dimension> +template <ItemType item_type> +void +ConnectivityDispatcher<Dimension>:: +_buildRecvItemIdCorrespondanceByProc() +{ + const auto& item_list_to_send_by_proc = this->_dispatchedInfo<item_type>().m_list_to_send_by_proc; + using ItemId = ItemIdT<item_type>; + + std::vector<Array<const ItemId>> recv_item_id_correspondance_by_proc(parallel::size()); + const ItemValue<const int,item_type>& item_number = + m_connectivity.template number<item_type>(); + + std::vector<Array<const int>> send_item_number_by_proc(parallel::size()); + for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) { + Array<int> send_item_number(item_list_to_send_by_proc[i_rank].size()); + const Array<const ItemId> send_item_id = item_list_to_send_by_proc[i_rank]; + parallel_for(send_item_number.size(), PASTIS_LAMBDA(const size_t& j){ + send_item_number[j] = item_number[send_item_id[j]]; + }); + send_item_number_by_proc[i_rank] = send_item_number; + } + + const auto& item_list_to_recv_size_by_proc = this->_dispatchedInfo<item_type>().m_list_to_recv_size_by_proc; + std::vector<Array<int>> recv_item_number_by_proc(parallel::size()); + for (size_t i_rank=0; i_rank < parallel::size(); ++i_rank) { + recv_item_number_by_proc[i_rank] = Array<int>(item_list_to_recv_size_by_proc[i_rank]); + } + parallel::exchange(send_item_number_by_proc, recv_item_number_by_proc); + + const auto& item_number_to_id_map = this->_dispatchedInfo<item_type>().m_number_to_id_map; + for (size_t i_rank=0; i_rank<item_list_to_recv_size_by_proc.size(); ++i_rank) { + Array<ItemId> item_id_correspondance(item_list_to_recv_size_by_proc[i_rank]); + for (size_t l=0; l<item_list_to_recv_size_by_proc[i_rank]; ++l) { + const int& item_number = recv_item_number_by_proc[i_rank][l]; + const auto& searched_item_id = item_number_to_id_map.find(item_number); + Assert(searched_item_id != item_number_to_id_map.end()); + item_id_correspondance[l] = searched_item_id->second; + } + recv_item_id_correspondance_by_proc[i_rank] = item_id_correspondance; + } + this->_dispatchedInfo<item_type>().m_recv_id_correspondance_by_proc = recv_item_id_correspondance_by_proc; +} + +template <int Dimension> +template <ItemType item_type> +void +ConnectivityDispatcher<Dimension>::_buildItemReferenceList() +{ + using ItemId = ItemIdT<item_type>; + + // Getting references + Array<const size_t> number_of_item_ref_list_per_proc + = parallel::allGather(m_connectivity.template numberOfRefItemList<item_type>()); + + const size_t number_of_item_list_sender + = [&] () { + size_t number_of_item_list_sender=0; + for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { + number_of_item_list_sender + += (number_of_item_ref_list_per_proc[i_rank] > 0); + } + return number_of_item_list_sender; + }(); + + if (number_of_item_list_sender > 0) { + if (number_of_item_list_sender > 1) { + perr() << __FILE__ << ':' << __LINE__ << ": " + << rang::fgB::red + <<"need to check that knowing procs know the same item_ref_lists!" + << rang::fg::reset << '\n'; + } + + if (number_of_item_list_sender < parallel::size()) { + const size_t sender_rank + = [&] () { + size_t i_rank = 0; + for (; i_rank < parallel::size(); ++i_rank) { + if (number_of_item_ref_list_per_proc[i_rank] > 0) { + break; + } + } + return i_rank; + }(); + + Assert(number_of_item_list_sender < parallel::size()); + + // sending references tags + Array<RefId::TagNumberType> ref_tag_list{number_of_item_ref_list_per_proc[sender_rank]}; + if (parallel::rank() == sender_rank){ + for (size_t i_item_ref_list=0; i_item_ref_list<m_connectivity.template numberOfRefItemList<item_type>(); + ++i_item_ref_list) { + auto item_ref_list = m_connectivity.template refItemList<item_type>(i_item_ref_list); + ref_tag_list[i_item_ref_list] = item_ref_list.refId().tagNumber(); + } + } + parallel::broadcast(ref_tag_list, sender_rank); + + // sending references name size + Array<size_t> ref_name_size_list{number_of_item_ref_list_per_proc[sender_rank]}; + if (parallel::rank() == sender_rank){ + for (size_t i_item_ref_list=0; i_item_ref_list<m_connectivity.template numberOfRefItemList<item_type>(); + ++i_item_ref_list) { + auto item_ref_list = m_connectivity.template refItemList<item_type>(i_item_ref_list); + ref_name_size_list[i_item_ref_list] = item_ref_list.refId().tagName().size(); + } + } + parallel::broadcast(ref_name_size_list, sender_rank); + + // sending references name size + Array<RefId::TagNameType::value_type> ref_name_cat{sum(ref_name_size_list)}; + if (parallel::rank() == sender_rank){ + size_t i_char=0; + for (size_t i_item_ref_list=0; i_item_ref_list<m_connectivity.template numberOfRefItemList<item_type>(); + ++i_item_ref_list) { + auto item_ref_list = m_connectivity.template refItemList<item_type>(i_item_ref_list); + for (auto c : item_ref_list.refId().tagName()) { + ref_name_cat[i_char++] = c; + } + } + } + parallel::broadcast(ref_name_cat, sender_rank); + + std::vector<RefId> ref_id_list + = [&] () { + std::vector<RefId> ref_id_list; + ref_id_list.reserve(ref_name_size_list.size()); + size_t begining=0; + for (size_t i_ref=0; i_ref < ref_name_size_list.size(); ++i_ref) { + const size_t size = ref_name_size_list[i_ref]; + ref_id_list.emplace_back(ref_tag_list[i_ref], + std::string{&(ref_name_cat[begining]), size}); + begining += size; + } + return ref_id_list; + } (); + + using block_type = int32_t; + constexpr size_t block_size = sizeof(block_type); + const size_t nb_block = ref_id_list.size()/block_size + (ref_id_list.size()%block_size != 0); + for (size_t i_block=0; i_block<nb_block; ++i_block) { + ItemValue<block_type, item_type> item_references(m_connectivity); + item_references.fill(0); + + if (m_connectivity.template numberOfRefItemList<item_type>() > 0) { + const size_t max_i_ref = std::min(ref_id_list.size(), block_size*(i_block+1)); + for (size_t i_ref=block_size*i_block, i=0; i_ref<max_i_ref; ++i_ref, ++i) { + block_type ref_bit{1<<i}; + auto item_ref_list = m_connectivity.template refItemList<item_type>(i_ref); + + const auto& item_list = item_ref_list.list(); + for (size_t i_item=0; i_item<item_list.size(); ++i_item) { + const ItemId& item_id = item_list[i_item]; + item_references[item_id] |= ref_bit; + } + } + } + + const auto& nb_item_to_send_by_proc = + this->_dispatchedInfo<item_type>().m_list_to_send_size_by_proc; + + const auto& send_item_id_by_proc = + this->_dispatchedInfo<item_type>().m_list_to_send_by_proc; + + std::vector<Array<const block_type>> send_item_refs_by_proc(parallel::size()); + + for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) { + Array<block_type> send_item_refs(nb_item_to_send_by_proc[i_rank]); + const Array<const ItemId> send_item_id = send_item_id_by_proc[i_rank]; + parallel_for(send_item_id.size(), PASTIS_LAMBDA(const size_t& l) { + const ItemId& item_id = send_item_id[l]; + send_item_refs[l] = item_references[item_id]; + }); + send_item_refs_by_proc[i_rank] = send_item_refs; + } + + std::vector<Array<block_type>> recv_item_refs_by_proc(parallel::size()); + for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) { + recv_item_refs_by_proc[i_rank] = Array<block_type>(this->_dispatchedInfo<item_type>().m_list_to_recv_size_by_proc[i_rank]); + } + parallel::exchange(send_item_refs_by_proc, recv_item_refs_by_proc); + + const auto& recv_item_id_correspondance_by_proc = + this->_dispatchedInfo<item_type>().m_recv_id_correspondance_by_proc; + std::vector<block_type> item_refs(m_new_descriptor.template itemNumberVector<item_type>().size()); + for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) { + for (size_t r=0; r<recv_item_refs_by_proc[i_rank].size(); ++r) { + const ItemId& item_id = recv_item_id_correspondance_by_proc[i_rank][r]; + item_refs[item_id] = recv_item_refs_by_proc[i_rank][r]; + } + } + + const size_t max_i_ref = std::min(ref_id_list.size(), block_size*(i_block+1)); + for (size_t i_ref=block_size*i_block, i=0; i_ref<max_i_ref; ++i_ref, ++i) { + block_type ref_bit{1<<i}; + + std::vector<ItemId> item_id_vector; + + for (uint32_t i_item=0; i_item<item_refs.size(); ++i_item) { + const ItemId item_id{i_item}; + if (item_refs[item_id] & ref_bit) { + item_id_vector.push_back(item_id); + } + } + + Array<const ItemId> item_id_array = convert_to_array(item_id_vector); + + m_new_descriptor.addRefItemList(RefItemList<item_type>(ref_id_list[i_ref], item_id_array)); + } + } + } + } +} + +template <int Dimension> +void +ConnectivityDispatcher<Dimension>::_dispatchEdges() +{ + if constexpr (Dimension>2) { + this->_buildNumberOfSubItemPerItemToRecvByProc<EdgeOfCell>(); + this->_buildSubItemNumbersToRecvByProc<EdgeOfCell>(); + this->_buildSubItemNumberToIdMap<EdgeOfCell>(); + this->_buildItemToExchangeLists<ItemType::edge>(); + + this->_gatherFrom(m_connectivity.template number<ItemType::edge>(), m_new_descriptor.edge_number_vector); + + this->_buildItemToSubItemDescriptor<EdgeOfCell>(); + + this->_buildNumberOfSubItemPerItemToRecvByProc<NodeOfEdge>(); + this->_buildSubItemNumbersToRecvByProc<NodeOfEdge>(); + this->_buildItemToSubItemDescriptor<NodeOfEdge>(); + + this->_buildNumberOfSubItemPerItemToRecvByProc<EdgeOfFace>(); + this->_buildSubItemNumbersToRecvByProc<EdgeOfFace>(); + this->_buildItemToSubItemDescriptor<EdgeOfFace>(); + + this->_gatherFrom(m_connectivity.faceEdgeIsReversed(), m_new_descriptor.face_edge_is_reversed_vector); + + this->_gatherFrom(this->_dispatchedInfo<ItemType::edge>().m_new_owner, m_new_descriptor.edge_owner_vector); + + this->_buildItemReferenceList<ItemType::edge>(); + } +} + +template <int Dimension> +void +ConnectivityDispatcher<Dimension>::_dispatchFaces() +{ + if constexpr (Dimension>1) { + this->_buildNumberOfSubItemPerItemToRecvByProc<FaceOfCell>(); + this->_buildSubItemNumbersToRecvByProc<FaceOfCell>(); + this->_buildSubItemNumberToIdMap<FaceOfCell>(); + this->_buildItemToExchangeLists<ItemType::face>(); + + this->_buildNumberOfSubItemPerItemToRecvByProc<NodeOfFace>(); + this->_buildSubItemNumbersToRecvByProc<NodeOfFace>(); + this->_buildItemToSubItemDescriptor<NodeOfFace>(); + + this->_gatherFrom(m_connectivity.template number<ItemType::face>(), m_new_descriptor.face_number_vector); + + this->_buildItemToSubItemDescriptor<FaceOfCell>(); + + this->_gatherFrom(m_connectivity.cellFaceIsReversed(), m_new_descriptor.cell_face_is_reversed_vector); + + this->_gatherFrom(this->_dispatchedInfo<ItemType::face>().m_new_owner, m_new_descriptor.face_owner_vector); + + this->_buildItemReferenceList<ItemType::face>(); + } +} + + +template <int Dimension> +ConnectivityDispatcher<Dimension>::ConnectivityDispatcher(const ConnectivityType& connectivity) + : m_connectivity(connectivity) +{ + this->_buildNewOwner<ItemType::cell>(); + this->_buildNewOwner<ItemType::face>(); + this->_buildNewOwner<ItemType::edge>(); + this->_buildNewOwner<ItemType::node>(); + + this->_buildItemToExchangeLists<ItemType::cell>(); + + this->_buildNumberOfSubItemPerItemToRecvByProc<NodeOfCell>(); + + this->_buildSubItemNumbersToRecvByProc<NodeOfCell>(); + + this->_gatherFrom(m_connectivity.template number<ItemType::cell>(), m_new_descriptor.cell_number_vector); + + this->_buildSubItemNumberToIdMap<NodeOfCell>(); + + this->_buildItemToExchangeLists<ItemType::node>(); + + // Fill new descriptor + this->_gatherFrom(m_connectivity.cellType(), m_new_descriptor.cell_type_vector); + this->_gatherFrom(this->_dispatchedInfo<ItemType::cell>().m_new_owner, m_new_descriptor.cell_owner_vector); + + this->_gatherFrom(m_connectivity.template number<ItemType::node>(), m_new_descriptor.node_number_vector); + this->_gatherFrom(this->_dispatchedInfo<ItemType::node>().m_new_owner, m_new_descriptor.node_owner_vector); + + this->_buildItemToSubItemDescriptor<NodeOfCell>(); + + this->_buildItemReferenceList<ItemType::cell>(); + + this->_dispatchFaces(); + + this->_dispatchEdges(); + + this->_buildItemReferenceList<ItemType::node>(); + + m_dispatched_connectivity = ConnectivityType::build(m_new_descriptor); +} + +template ConnectivityDispatcher<1>::ConnectivityDispatcher(const ConnectivityType&); +template ConnectivityDispatcher<2>::ConnectivityDispatcher(const ConnectivityType&); +template ConnectivityDispatcher<3>::ConnectivityDispatcher(const ConnectivityType&); diff --git a/src/mesh/ConnectivityDispatcher.hpp b/src/mesh/ConnectivityDispatcher.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e0ce2e88e2690093b6c8d4c16f7f2abc70346674 --- /dev/null +++ b/src/mesh/ConnectivityDispatcher.hpp @@ -0,0 +1,294 @@ +#ifndef CONNECTIVITY_DISPATCHER_HPP +#define CONNECTIVITY_DISPATCHER_HPP + +#include <Mesh.hpp> +#include <ItemValue.hpp> +#include <ItemValueUtils.hpp> + +#include <unordered_map> +#include <ConnectivityDescriptor.hpp> + +template <int Dimension> +class ConnectivityDispatcher +{ + public: + using ConnectivityType = Connectivity<Dimension>; + + private: + const ConnectivityType& m_connectivity; + ConnectivityDescriptor m_new_descriptor; + std::shared_ptr<ConnectivityType> m_dispatched_connectivity; + + template <ItemType item_type> + struct DispatchedItemInfo + { + using ItemId = ItemIdT<item_type>; + ItemValue<const int, item_type> m_new_owner; + Array<const unsigned int> m_list_to_send_size_by_proc; + std::vector<Array<const ItemId>> m_list_to_send_by_proc; + Array<const unsigned int> m_list_to_recv_size_by_proc; + std::unordered_map<int, int> m_number_to_id_map; + std::vector<Array<const ItemId>> m_recv_id_correspondance_by_proc; + }; + + DispatchedItemInfo<ItemType::cell> m_dispatched_cell_info; + DispatchedItemInfo<ItemType::face> m_dispatched_face_info; + DispatchedItemInfo<ItemType::edge> m_dispatched_edge_info; + DispatchedItemInfo<ItemType::node> m_dispatched_node_info; + + template <ItemType item_type> + PASTIS_INLINE + DispatchedItemInfo<item_type>& _dispatchedInfo() + { + if constexpr (item_type == ItemType::cell) { + return m_dispatched_cell_info; + } else if constexpr (item_type == ItemType::face) { + return m_dispatched_face_info; + } else if constexpr (item_type == ItemType::edge) { + return m_dispatched_edge_info; + } else { + return m_dispatched_node_info; + } + } + + template <ItemType item_type> + PASTIS_INLINE + const DispatchedItemInfo<item_type>& _dispatchedInfo() const + { + if constexpr (item_type == ItemType::cell) { + return m_dispatched_cell_info; + } else if constexpr (item_type == ItemType::face) { + return m_dispatched_face_info; + } else if constexpr (item_type == ItemType::edge) { + return m_dispatched_edge_info; + } else { + return m_dispatched_node_info; + } + } + + template <typename ItemToItem> + struct DispatchedItemOfItemInfo + { + std::vector<Array<const int>> m_number_of_sub_item_per_item_to_recv_by_proc; + std::vector<Array<const int>> m_sub_item_numbers_to_recv_by_proc; + }; + + DispatchedItemOfItemInfo<NodeOfCell> m_dispatched_node_of_cell_info; + DispatchedItemOfItemInfo<EdgeOfCell> m_dispatched_edge_of_cell_info; + DispatchedItemOfItemInfo<FaceOfCell> m_dispatched_face_of_cell_info; + + DispatchedItemOfItemInfo<NodeOfEdge> m_dispatched_node_of_edge_info; + DispatchedItemOfItemInfo<FaceOfEdge> m_dispatched_face_of_edge_info; + DispatchedItemOfItemInfo<CellOfEdge> m_dispatched_cell_of_edge_info; + + DispatchedItemOfItemInfo<NodeOfFace> m_dispatched_node_of_face_info; + DispatchedItemOfItemInfo<EdgeOfFace> m_dispatched_edge_of_face_info; + DispatchedItemOfItemInfo<CellOfFace> m_dispatched_cell_of_face_info; + + DispatchedItemOfItemInfo<EdgeOfNode> m_dispatched_edge_of_node_info; + DispatchedItemOfItemInfo<FaceOfNode> m_dispatched_face_of_node_info; + DispatchedItemOfItemInfo<CellOfNode> m_dispatched_cell_of_node_info; + + template <typename ItemOfItem> + PASTIS_INLINE + DispatchedItemOfItemInfo<ItemOfItem>& _dispatchedInfo() + { + if constexpr (std::is_same_v<NodeOfCell, ItemOfItem>) { + return m_dispatched_node_of_cell_info; + } else if constexpr (std::is_same_v<EdgeOfCell, ItemOfItem>) { + return m_dispatched_edge_of_cell_info; + } else if constexpr (std::is_same_v<FaceOfCell, ItemOfItem>) { + return m_dispatched_face_of_cell_info; + } else if constexpr (std::is_same_v<NodeOfEdge, ItemOfItem>) { + return m_dispatched_node_of_edge_info; + } else if constexpr (std::is_same_v<FaceOfEdge, ItemOfItem>) { + return m_dispatched_face_of_edge_info; + } else if constexpr (std::is_same_v<CellOfEdge, ItemOfItem>) { + return m_dispatched_cell_of_edge_info; + } else if constexpr (std::is_same_v<NodeOfFace, ItemOfItem>) { + return m_dispatched_node_of_face_info; + } else if constexpr (std::is_same_v<EdgeOfFace, ItemOfItem>) { + return m_dispatched_edge_of_face_info; + } else if constexpr (std::is_same_v<CellOfFace, ItemOfItem>) { + return m_dispatched_cell_of_face_info; + } else if constexpr (std::is_same_v<EdgeOfNode, ItemOfItem>) { + return m_dispatched_edge_of_node_info; + } else if constexpr (std::is_same_v<FaceOfNode, ItemOfItem>) { + return m_dispatched_face_of_node_info; + } else if constexpr (std::is_same_v<CellOfNode, ItemOfItem>) { + return m_dispatched_cell_of_node_info; + } else { + static_assert(is_false_v<ItemOfItem>, "Unexpected ItemOfItem type"); + } + } + + template <typename ItemOfItem> + PASTIS_INLINE + const DispatchedItemOfItemInfo<ItemOfItem>& _dispatchedInfo() const + { + if constexpr (std::is_same_v<NodeOfCell, ItemOfItem>) { + return m_dispatched_node_of_cell_info; + } else if constexpr (std::is_same_v<EdgeOfCell, ItemOfItem>) { + return m_dispatched_edge_of_cell_info; + } else if constexpr (std::is_same_v<FaceOfCell, ItemOfItem>) { + return m_dispatched_face_of_cell_info; + } else if constexpr (std::is_same_v<NodeOfEdge, ItemOfItem>) { + return m_dispatched_node_of_edge_info; + } else if constexpr (std::is_same_v<FaceOfEdge, ItemOfItem>) { + return m_dispatched_face_of_edge_info; + } else if constexpr (std::is_same_v<CellOfEdge, ItemOfItem>) { + return m_dispatched_cell_of_edge_info; + } else if constexpr (std::is_same_v<NodeOfFace, ItemOfItem>) { + return m_dispatched_node_of_face_info; + } else if constexpr (std::is_same_v<EdgeOfFace, ItemOfItem>) { + return m_dispatched_edge_of_face_info; + } else if constexpr (std::is_same_v<CellOfFace, ItemOfItem>) { + return m_dispatched_cell_of_face_info; + } else if constexpr (std::is_same_v<EdgeOfNode, ItemOfItem>) { + return m_dispatched_edge_of_node_info; + } else if constexpr (std::is_same_v<FaceOfNode, ItemOfItem>) { + return m_dispatched_face_of_node_info; + } else if constexpr (std::is_same_v<CellOfNode, ItemOfItem>) { + return m_dispatched_cell_of_node_info; + } else { + static_assert(is_false_v<ItemOfItem>, "Unexpected ItemOfItem type"); + } + } + + template <ItemType item_type> + void _buildNewOwner(); + + template <ItemType item_type> + void _buildItemListToSend(); + + void _buildCellNumberIdMap(); + + template <typename ItemOfItemT> + void _buildSubItemNumberToIdMap(); + + template <ItemType item_type> + void _buildItemToExchangeLists(); + + template <ItemType item_type> + void _buildNumberOfItemToExchange(); + + template <typename ItemOfItemT> + void _buildItemToSubItemDescriptor(); + + void _dispatchEdges(); + void _dispatchFaces(); + + template<typename DataType, ItemType item_type, typename ConnectivityPtr> + void _gatherFrom(const ItemValue<DataType, item_type, ConnectivityPtr>& data_to_gather, + std::vector<std::remove_const_t<DataType>>& gathered_vector); + + template<typename DataType, typename ItemOfItem, typename ConnectivityPtr> + void _gatherFrom(const SubItemValuePerItem<DataType, ItemOfItem, ConnectivityPtr>& data_to_gather, + std::vector<Array<std::remove_const_t<DataType>>>& gathered_vector); + + template <typename SubItemOfItemT> + void _buildNumberOfSubItemPerItemToRecvByProc(); + + template <typename SubItemOfItemT> + void _buildSubItemNumbersToRecvByProc(); + + template <ItemType item_type> + void _buildRecvItemIdCorrespondanceByProc(); + + template <ItemType item_type> + void _buildItemReferenceList(); + + public: + std::shared_ptr<const ConnectivityType> + dispatchedConnectivity() const + { + return m_dispatched_connectivity; + } + + template<typename DataType, ItemType item_type, typename ConnectivityPtr> + std::vector<Array<const DataType>> + exchange(ItemValue<DataType, item_type, ConnectivityPtr> item_value) const + { + using ItemId = ItemIdT<item_type>; + using MutableDataType = std::remove_const_t<DataType>; + std::vector<Array<const DataType>> item_value_to_send_by_proc(parallel::size()); + + const auto& item_list_to_send_by_proc = this->_dispatchedInfo<item_type>().m_list_to_send_by_proc; + + for (size_t i=0; i<parallel::size(); ++i) { + const Array<const ItemId>& item_list = item_list_to_send_by_proc[i]; + Array<MutableDataType> item_value_list(item_list.size()); + parallel_for (item_list.size(), PASTIS_LAMBDA(const ItemId& item_id) { + item_value_list[item_id] = item_value[item_list[item_id]]; + }); + item_value_to_send_by_proc[i] = item_value_list; + } + + std::vector<Array<MutableDataType>> recv_item_value_by_proc(parallel::size()); + { + const auto& list_to_recv_size_by_proc = this->_dispatchedInfo<item_type>().m_list_to_recv_size_by_proc; + for (size_t i=0; i<parallel::size(); ++i) { + recv_item_value_by_proc[i] = Array<MutableDataType>(list_to_recv_size_by_proc[i]); + } + } + parallel::exchange(item_value_to_send_by_proc, recv_item_value_by_proc); + + std::vector<Array<const DataType>> const_recv_item_value_by_proc(parallel::size()); + for (size_t i=0; i<parallel::size(); ++i) { + const_recv_item_value_by_proc[i] = recv_item_value_by_proc[i]; + } + + return const_recv_item_value_by_proc; + } + + template<typename DataType, ItemType item_type, typename ConnectivityPtr> + ItemValue<std::remove_const_t<DataType>, item_type, ConnectivityPtr> + dispatch(ItemValue<DataType, item_type, ConnectivityPtr> item_value) const + { + using ItemId = ItemIdT<item_type>; + + Assert(m_dispatched_connectivity.use_count()> 0, + "cannot dispatch quantity before connectivity"); + + const auto& item_list_to_send_by_proc = this->_dispatchedInfo<item_type>().m_list_to_send_by_proc; + + using MutableDataType = std::remove_const_t<DataType>; + std::vector<Array<DataType>> item_value_to_send_by_proc(parallel::size()); + for (size_t i=0; i<parallel::size(); ++i) { + const Array<const ItemId>& item_list = item_list_to_send_by_proc[i]; + Array<MutableDataType> item_value_list(item_list.size()); + parallel_for (item_list.size(), PASTIS_LAMBDA(const ItemId& item_id) { + item_value_list[item_id] = item_value[item_list[item_id]]; + }); + item_value_to_send_by_proc[i] = item_value_list; + } + + const auto& item_list_to_recv_size_by_proc = this->_dispatchedInfo<item_type>().m_list_to_recv_size_by_proc; + std::vector<Array<MutableDataType>> recv_item_value_by_proc(parallel::size()); + for (size_t i=0; i<parallel::size(); ++i) { + recv_item_value_by_proc[i] = Array<MutableDataType>(item_list_to_recv_size_by_proc[i]); + } + + parallel::exchange(item_value_to_send_by_proc, recv_item_value_by_proc); + + const auto& recv_item_id_correspondance_by_proc = + this->_dispatchedInfo<item_type>().m_recv_id_correspondance_by_proc; + ItemValue<MutableDataType, item_type> new_item_value(*m_dispatched_connectivity); + for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) { + const auto& recv_item_id_correspondance = recv_item_id_correspondance_by_proc[i_rank]; + const auto& recv_item_value = recv_item_value_by_proc[i_rank]; + parallel_for(recv_item_value.size(), PASTIS_LAMBDA(size_t r) { + const ItemId& item_id = recv_item_id_correspondance[r]; + new_item_value[item_id] = recv_item_value[r]; + }); + } + return new_item_value; + } + + ConnectivityDispatcher(const ConnectivityType& mesh); + ConnectivityDispatcher(const ConnectivityDispatcher&) = delete; + ~ConnectivityDispatcher() = default; +}; + + +#endif // CONNECTIVITY_DISPATCHER_HPP diff --git a/src/mesh/ConnectivityMatrix.hpp b/src/mesh/ConnectivityMatrix.hpp index 53feb3375a9de19370d03a002cf869fe4f039c0f..18b39a67a8839e1a893bb7bf2cec50aa60c407ee 100644 --- a/src/mesh/ConnectivityMatrix.hpp +++ b/src/mesh/ConnectivityMatrix.hpp @@ -2,6 +2,7 @@ #define CONNECTIVITY_MATRIX_HPP #include <PastisUtils.hpp> +#include <Array.hpp> #include <Kokkos_StaticCrsGraph.hpp> class ConnectivityMatrix @@ -20,9 +21,11 @@ class ConnectivityMatrix return m_is_built; } - const auto& entries() const + auto entries() const { - return m_host_matrix.entries; + return encapsulate(m_host_matrix.entries); + // using DataType = typename decltype(m_host_matrix.entries)::value_type; + // return Array<DataType>(m_host_matrix.entries); } const auto& rowsMap() const diff --git a/src/mesh/GmshReader.cpp b/src/mesh/GmshReader.cpp index b49328d77129789bcbefbd32ed29b34d40ba37e3..1881f99a4eb55e8f6de284ee2b91a05a412aba04 100644 --- a/src/mesh/GmshReader.cpp +++ b/src/mesh/GmshReader.cpp @@ -10,29 +10,21 @@ #include <Connectivity.hpp> #include <Mesh.hpp> +#include <MeshData.hpp> -#include <RefFaceList.hpp> +#include <RefItemList.hpp> +#include <Messenger.hpp> +#include <ArrayUtils.hpp> +#include <ItemValueUtils.hpp> + +#include <ConnectivityDispatcher.hpp> + +#include <unordered_map> #include <map> #include <regex> #include <iomanip> -template<typename T> -PASTIS_INLINE -std::string stringify(const T & t) -{ - std::ostringstream oss; - oss << t; - return oss.str(); -} - -template<> -PASTIS_INLINE -std::string stringify<std::string>(const std::string& t) -{ - return t; -} - class ErrorHandler { public: @@ -44,11 +36,11 @@ class ErrorHandler }; private: - const std::string __filename; /**< The source file name where the error occured */ - const size_t __lineNumber; /**< The line number where exception was raised */ + const std::string __filename; /**< The source file name where the error occured */ + const size_t __lineNumber; /**< The line number where exception was raised */ const std::string __errorMessage; /**< The reporting message */ - const Type __type; /**< the type of the error */ + const Type __type; /**< the type of the error */ public: /** * Prints the error message @@ -92,6 +84,7 @@ class ErrorHandler ; } }; + void ErrorHandler::writeErrorMessage() const { switch(__type) { @@ -138,167 +131,416 @@ ErrorHandler(const std::string& filename, ; } +template <int Dimension> +void GmshReader::_dispatch() +{ + using ConnectivityType = Connectivity<Dimension>; + using Rd = TinyVector<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + if (not m_mesh) { + ConnectivityDescriptor descriptor; + std::shared_ptr connectivity = ConnectivityType::build(descriptor); + NodeValue<Rd> xr; + m_mesh = std::make_shared<MeshType>(connectivity, xr); + } + const MeshType& mesh = static_cast<const MeshType&>(*m_mesh); -GmshReader::GmshReader(const std::string& filename) - : m_filename(filename) + ConnectivityDispatcher<Dimension> dispatcher(mesh.connectivity()); + + std::shared_ptr dispatched_connectivity = dispatcher.dispatchedConnectivity(); + NodeValue<Rd> dispatched_xr = dispatcher.dispatch(mesh.xr()); + + m_mesh = std::make_shared<MeshType>(dispatched_connectivity, dispatched_xr); +} + + +template <size_t Dimension> +class ConnectivityFace; + +template<> +class ConnectivityFace<2> { - try { - m_fin.open(m_filename); - if (not m_fin) { - perr() << "cannot read file '" << m_filename << "'\n"; - std::exit(0); + public: + friend struct Hash; + struct Hash + { + size_t operator()(const ConnectivityFace& f) const { + size_t hash = 0; + hash ^= std::hash<unsigned int>()(f.m_node0_id); + hash ^= std::hash<unsigned int>()(f.m_node1_id) >> 1; + return hash; } + }; - // gmsh 2.2 format keywords - __keywordList["$MeshFormat"] = MESHFORMAT; - __keywordList["$EndMeshFormat"] = ENDMESHFORMAT; - __keywordList["$Nodes"] = NODES; - __keywordList["$EndNodes"] = ENDNODES; - __keywordList["$Elements"] = ELEMENTS; - __keywordList["$EndElements"] = ENDELEMENTS; - __keywordList["$PhysicalNames"] = PHYSICALNAMES; - __keywordList["$EndPhysicalNames"] = ENDPHYSICALNAMES; - - __numberOfPrimitiveNodes.resize(16); - __numberOfPrimitiveNodes[ 0] = 2; // edge - __numberOfPrimitiveNodes[ 1] = 3; // triangle - __numberOfPrimitiveNodes[ 2] = 4; // quadrangle - __numberOfPrimitiveNodes[ 3] = 4; // Tetrahedron - __numberOfPrimitiveNodes[ 4] = 8; // Hexaredron - __numberOfPrimitiveNodes[ 5] = 6; // Prism - __numberOfPrimitiveNodes[ 6] = 5; // Pyramid - __numberOfPrimitiveNodes[ 7] = 3; // second order edge - __numberOfPrimitiveNodes[ 8] = 6; // second order triangle - __numberOfPrimitiveNodes[ 9] = 9; // second order quadrangle - __numberOfPrimitiveNodes[10] = 10; // second order tetrahedron - __numberOfPrimitiveNodes[11] = 27; // second order hexahedron - __numberOfPrimitiveNodes[12] = 18; // second order prism - __numberOfPrimitiveNodes[13] = 14; // second order pyramid - __numberOfPrimitiveNodes[14] = 1; // point - - __primitivesNames[0] = "edges"; - __supportedPrimitives[0] = true; - __primitivesNames[1] = "triangles"; - __supportedPrimitives[1] = true; - __primitivesNames[2] = "quadrangles"; - __supportedPrimitives[2] = true; - __primitivesNames[3] = "tetrahedra"; - __supportedPrimitives[3] = true; - __primitivesNames[4] = "hexahedra"; - __supportedPrimitives[4] = true; - __primitivesNames[5] = "prisms"; - __supportedPrimitives[5] = false; - __primitivesNames[6] = "pyramids"; - __supportedPrimitives[6] = false; - __primitivesNames[7] = "second order edges"; - __supportedPrimitives[7] = false; - __primitivesNames[8] = "second order triangles"; - __supportedPrimitives[8] = false; - __primitivesNames[9] = "second order quadrangles"; - __supportedPrimitives[9] = false; - __primitivesNames[10] = "second order tetrahedra"; - __supportedPrimitives[10]= false; - __primitivesNames[11] = "second order hexahedra"; - __supportedPrimitives[11]= false; - __primitivesNames[12] = "second order prisms"; - __supportedPrimitives[12]= false; - __primitivesNames[13] = "second order pyramids"; - __supportedPrimitives[13]= false; - __primitivesNames[14] = "point"; - __supportedPrimitives[14]= true; - - pout() << "Reading file '" << m_filename << "'\n"; - - // Getting vertices list - GmshReader::Keyword kw = this->__nextKeyword(); - switch(kw.second) { - // case NOD: { - // this->__readGmsh1(); - // break; - // } - case MESHFORMAT: { - double fileVersion = this->_getReal(); - if (fileVersion != 2.2) { - throw ErrorHandler(__FILE__,__LINE__, - "Cannot read Gmsh format '"+stringify(fileVersion)+"'", - ErrorHandler::normal); - } - int fileType = this->_getInteger(); - __binary = (fileType == 1); - if ((fileType < 0) or (fileType > 1)) { - throw ErrorHandler(__FILE__,__LINE__, - "Cannot read Gmsh file type '"+stringify(fileType)+"'", - ErrorHandler::normal); - } + private: + const std::vector<int>& m_node_number_vector; - int dataSize = this->_getInteger(); - if (dataSize != sizeof(double)) { - throw ErrorHandler(__FILE__,__LINE__, - "Data size not supported '"+stringify(dataSize)+"'", - ErrorHandler::normal); - } + unsigned int m_node0_id; + unsigned int m_node1_id; + + bool m_reversed; + + public: + + std::vector<unsigned int> nodeIdList() const + { + return {m_node0_id, m_node1_id}; + } + + bool reversed() const + { + return m_reversed; + } + + PASTIS_INLINE + bool operator==(const ConnectivityFace& f) const + { + return ((m_node0_id == f.m_node0_id) and + (m_node1_id == f.m_node1_id)); + } + + PASTIS_INLINE + bool operator<(const ConnectivityFace& f) const + { + return ((m_node_number_vector[m_node0_id] < m_node_number_vector[f.m_node0_id]) or + ((m_node_number_vector[m_node0_id] == m_node_number_vector[f.m_node0_id]) and + (m_node_number_vector[m_node1_id]<m_node_number_vector[f.m_node1_id]))); + } + + PASTIS_INLINE + ConnectivityFace(const std::vector<unsigned int>& node_id_list, + const std::vector<int>& node_number_vector) + : m_node_number_vector(node_number_vector) + { + Assert(node_id_list.size()==2); + + if (m_node_number_vector[node_id_list[0]] < m_node_number_vector[node_id_list[1]]) { + m_node0_id = node_id_list[0]; + m_node1_id = node_id_list[1]; + m_reversed = false; + } else { + m_node0_id = node_id_list[1]; + m_node1_id = node_id_list[0]; + m_reversed = true; + } + } + + PASTIS_INLINE + ConnectivityFace(const ConnectivityFace&) = default; + + PASTIS_INLINE + ~ConnectivityFace() = default; +}; + +template <> +class ConnectivityFace<3> +{ + private: + friend class GmshReader; + friend struct Hash; + struct Hash + { + size_t operator()(const ConnectivityFace& f) const { + size_t hash = 0; + for (size_t i=0; i<f.m_node_id_list.size(); ++i) { + hash ^= std::hash<unsigned int>()(f.m_node_id_list[i]) >> i; + } + return hash; + } + }; + + private: + bool m_reversed; + std::vector<NodeId::base_type> m_node_id_list; + const std::vector<int>& m_node_number_vector; + + PASTIS_INLINE + std::vector<unsigned int> _sort(const std::vector<unsigned int>& node_list) + { + const auto min_id = std::min_element(node_list.begin(), node_list.end()); + const int shift = std::distance(node_list.begin(), min_id); + + std::vector<unsigned int> rotated_node_list(node_list.size()); + if (node_list[(shift+1)%node_list.size()] > node_list[(shift+node_list.size()-1)%node_list.size()]) { + for (size_t i=0; i<node_list.size(); ++i) { + rotated_node_list[i] = node_list[(shift+node_list.size()-i)%node_list.size()]; + m_reversed = true; + } + } else { + for (size_t i=0; i<node_list.size(); ++i) { + rotated_node_list[i] = node_list[(shift+i)%node_list.size()]; + } + } + + return rotated_node_list; + } + + public: + PASTIS_INLINE + const bool& reversed() const + { + return m_reversed; + } + + PASTIS_INLINE + const std::vector<unsigned int>& nodeIdList() const + { + return m_node_id_list; + } + + PASTIS_INLINE + ConnectivityFace(const std::vector<unsigned int>& given_node_id_list, + const std::vector<int>& node_number_vector) + : m_reversed(false), + m_node_id_list(_sort(given_node_id_list)), + m_node_number_vector(node_number_vector) + { + ; + } - if (__binary) { - // int one=0; - - // fseek(__ifh,1L,SEEK_CUR); - // fread(reinterpret_cast<char*>(&one),sizeof(int),1,__ifh); - - // if (one == 1) { - // __convertEndian = false; - // } else { - // invertEndianess(one); - - // if (one == 1) { - // __convertEndian = true; - // } else { - // throw ErrorHandler(__FILE__,__LINE__, - // "Cannot determine endianess", - // ErrorHandler::normal); - // } - // } - - // pout() << "- Binary format: "; - // #ifdef WORDS_BIGENDIAN - // if (not __convertEndian) { - // pout() << "big endian\n"; - // } else { - // pout() << "little endian\n"; - // } - // #else // WORDS_BIGENDIAN - // if (not __convertEndian) { - // pout() << "little endian\n"; - // } else { - // pout() << "big endian\n"; - // } - // #endif // WORDS_BIGENDIAN + public: + bool operator==(const ConnectivityFace& f) const + { + if (m_node_id_list.size() == f.nodeIdList().size()) { + for (size_t j=0; j<m_node_id_list.size(); ++j) { + if (m_node_id_list[j] != f.nodeIdList()[j]) { + return false; } + } + return true; + } + return false; + } - kw = this->__nextKeyword(); - if (kw.second != ENDMESHFORMAT) { + PASTIS_INLINE + bool operator<(const ConnectivityFace& f) const + { + const size_t min_nb_nodes = std::min(f.m_node_id_list.size(), m_node_id_list.size()); + for (size_t i=0; i<min_nb_nodes; ++i) { + if (m_node_id_list[i] < f.m_node_id_list[i]) return true; + if (m_node_id_list[i] != f.m_node_id_list[i]) return false; + } + return m_node_id_list.size() < f.m_node_id_list.size(); + } + + PASTIS_INLINE + ConnectivityFace(const ConnectivityFace&) = default; + + PASTIS_INLINE + ConnectivityFace() = delete; + + PASTIS_INLINE + ~ConnectivityFace() = default; +}; + +GmshReader::GmshReader(const std::string& filename) + : m_filename(filename) +{ + if (parallel::rank() == 0) { + try { + m_fin.open(m_filename); + if (not m_fin) { + perr() << "cannot read file '" << m_filename << "'\n"; + std::exit(0); + } + + // gmsh 2.2 format keywords + __keywordList["$MeshFormat"] = MESHFORMAT; + __keywordList["$EndMeshFormat"] = ENDMESHFORMAT; + __keywordList["$Nodes"] = NODES; + __keywordList["$EndNodes"] = ENDNODES; + __keywordList["$Elements"] = ELEMENTS; + __keywordList["$EndElements"] = ENDELEMENTS; + __keywordList["$PhysicalNames"] = PHYSICALNAMES; + __keywordList["$EndPhysicalNames"] = ENDPHYSICALNAMES; + + __numberOfPrimitiveNodes.resize(16); + __numberOfPrimitiveNodes[ 0] = 2; // edge + __numberOfPrimitiveNodes[ 1] = 3; // triangle + __numberOfPrimitiveNodes[ 2] = 4; // quadrangle + __numberOfPrimitiveNodes[ 3] = 4; // Tetrahedron + __numberOfPrimitiveNodes[ 4] = 8; // Hexaredron + __numberOfPrimitiveNodes[ 5] = 6; // Prism + __numberOfPrimitiveNodes[ 6] = 5; // Pyramid + __numberOfPrimitiveNodes[ 7] = 3; // second order edge + __numberOfPrimitiveNodes[ 8] = 6; // second order triangle + __numberOfPrimitiveNodes[ 9] = 9; // second order quadrangle + __numberOfPrimitiveNodes[10] = 10; // second order tetrahedron + __numberOfPrimitiveNodes[11] = 27; // second order hexahedron + __numberOfPrimitiveNodes[12] = 18; // second order prism + __numberOfPrimitiveNodes[13] = 14; // second order pyramid + __numberOfPrimitiveNodes[14] = 1; // point + + __primitivesNames[0] = "edges"; + __supportedPrimitives[0] = true; + __primitivesNames[1] = "triangles"; + __supportedPrimitives[1] = true; + __primitivesNames[2] = "quadrangles"; + __supportedPrimitives[2] = true; + __primitivesNames[3] = "tetrahedra"; + __supportedPrimitives[3] = true; + __primitivesNames[4] = "hexahedra"; + __supportedPrimitives[4] = true; + __primitivesNames[5] = "prisms"; + __supportedPrimitives[5] = false; + __primitivesNames[6] = "pyramids"; + __supportedPrimitives[6] = false; + __primitivesNames[7] = "second order edges"; + __supportedPrimitives[7] = false; + __primitivesNames[8] = "second order triangles"; + __supportedPrimitives[8] = false; + __primitivesNames[9] = "second order quadrangles"; + __supportedPrimitives[9] = false; + __primitivesNames[10] = "second order tetrahedra"; + __supportedPrimitives[10]= false; + __primitivesNames[11] = "second order hexahedra"; + __supportedPrimitives[11]= false; + __primitivesNames[12] = "second order prisms"; + __supportedPrimitives[12]= false; + __primitivesNames[13] = "second order pyramids"; + __supportedPrimitives[13]= false; + __primitivesNames[14] = "point"; + __supportedPrimitives[14]= true; + + pout() << "Reading file '" << m_filename << "'\n"; + + // Getting vertices list + GmshReader::Keyword kw = this->__nextKeyword(); + switch(kw.second) { + // case NOD: { + // this->__readGmsh1(); + // break; + // } + case MESHFORMAT: { + double fileVersion = this->_getReal(); + if (fileVersion != 2.2) { + throw ErrorHandler(__FILE__,__LINE__, + "Cannot read Gmsh format '"+std::to_string(fileVersion)+"'", + ErrorHandler::normal); + } + int fileType = this->_getInteger(); + __binary = (fileType == 1); + if ((fileType < 0) or (fileType > 1)) { + throw ErrorHandler(__FILE__,__LINE__, + "Cannot read Gmsh file type '"+std::to_string(fileType)+"'", + ErrorHandler::normal); + } + + int dataSize = this->_getInteger(); + if (dataSize != sizeof(double)) { + throw ErrorHandler(__FILE__,__LINE__, + "Data size not supported '"+std::to_string(dataSize)+"'", + ErrorHandler::normal); + } + + if (__binary) { + // int one=0; + + // fseek(__ifh,1L,SEEK_CUR); + // fread(reinterpret_cast<char*>(&one),sizeof(int),1,__ifh); + + // if (one == 1) { + // __convertEndian = false; + // } else { + // invertEndianess(one); + + // if (one == 1) { + // __convertEndian = true; + // } else { + // throw ErrorHandler(__FILE__,__LINE__, + // "Cannot determine endianess", + // ErrorHandler::normal); + // } + // } + + // pout() << "- Binary format: "; + // #ifdef WORDS_BIGENDIAN + // if (not __convertEndian) { + // pout() << "big endian\n"; + // } else { + // pout() << "little endian\n"; + // } + // #else // WORDS_BIGENDIAN + // if (not __convertEndian) { + // pout() << "little endian\n"; + // } else { + // pout() << "big endian\n"; + // } + // #endif // WORDS_BIGENDIAN + } + + kw = this->__nextKeyword(); + if (kw.second != ENDMESHFORMAT) { + throw ErrorHandler(__FILE__,__LINE__, + "reading file '"+m_filename + +"': expecting $EndMeshFormat, '"+kw.first+"' was found", + ErrorHandler::normal); + } + + this->__readGmshFormat2_2(); + + break; + } + default: { throw ErrorHandler(__FILE__,__LINE__, - "reading file '"+m_filename - +"': expecting $EndMeshFormat, '"+kw.first+"' was found", + "cannot determine format version of '"+m_filename+"'", ErrorHandler::normal); } + } - this->__readGmshFormat2_2(); - + this->__proceedData(); + // this->__createMesh(); + } + catch(const ErrorHandler& e) { + e.writeErrorMessage(); + std::exit(0); + } + } + pout() << std::flush; + if (parallel::size() > 1) { + pout() << "Sequential mesh read! Need to be dispatched\n" << std::flush; + + const int mesh_dimension + = [&]() { + int mesh_dimension = -1; // unknown mesh dimension + if (m_mesh) { + mesh_dimension = m_mesh->dimension(); + } + + Array<int> dimensions = parallel::allGather(mesh_dimension); + std::set<int> dimension_set; + for (size_t i=0; i<dimensions.size(); ++i) { + const int i_dimension = dimensions[i]; + if (i_dimension != -1) { + dimension_set.insert(i_dimension); + } + } + if (dimension_set.size() != 1) { + std::cerr << "error dimensions of read mesh parts differ!\n"; + std::exit(1); + } + + return *begin(dimension_set); + }(); + switch (mesh_dimension) { + case 1: { + this->_dispatch<1>(); + break; + } + case 2: { + this->_dispatch<2>(); + break; + } + case 3: { + this->_dispatch<3>(); break; } default: { - throw ErrorHandler(__FILE__,__LINE__, - "cannot determine format version of '"+m_filename+"'", - ErrorHandler::normal); + perr() << "unexpected dimension " << mesh_dimension << '\n'; + std::exit(1); } } - - this->__proceedData(); - // this->__createMesh(); - } - catch(const ErrorHandler& e) { - e.writeErrorMessage(); - std::exit(0); } } @@ -371,7 +613,7 @@ void GmshReader::__readVertices() // if ((elementType < 0) or (elementType > 14)) { // throw ErrorHandler(__FILE__,__LINE__, // "reading file '"+m_filename -// +"': unknown element type '"+stringify(elementType)+"'", +// +"': unknown element type '"+std::to_string(elementType)+"'", // ErrorHandler::normal); // } // __elementType[i] = elementType; @@ -409,19 +651,20 @@ GmshReader::__readElements2_2() ErrorHandler::normal); } + __elementNumber.resize(numberOfElements); __elementType.resize(numberOfElements); __references.resize(numberOfElements); __elementVertices.resize(numberOfElements); if (not __binary) { for (int i=0; i<numberOfElements; ++i) { - this->_getInteger(); // drops element number + __elementNumber[i] = this->_getInteger(); const int elementType = this->_getInteger()-1; if ((elementType < 0) or (elementType > 14)) { throw ErrorHandler(__FILE__,__LINE__, "reading file '"+m_filename - +"': unknown element type '"+stringify(elementType)+"'", + +"': unknown element type '"+std::to_string(elementType)+"'", ErrorHandler::normal); } __elementType[i] = elementType; @@ -451,7 +694,7 @@ GmshReader::__readElements2_2() // if ((elementType < 0) or (elementType > 14)) { // throw ErrorHandler(__FILE__,__LINE__, // "reading file '"+m_filename - // +"': unknown element type '"+stringify(elementType)+"'", + // +"': unknown element type '"+std::to_string(elementType)+"'", // ErrorHandler::normal); // } @@ -525,6 +768,325 @@ __readPhysicalNames2_2() } } +template <size_t Dimension> +void +GmshReader::__computeCellFaceAndFaceNodeConnectivities(ConnectivityDescriptor& descriptor) +{ + static_assert((Dimension==2) or (Dimension==3), + "Invalid dimension to compute cell-face connectivities"); + using CellFaceInfo = std::tuple<CellId, unsigned short, bool>; + using Face = ConnectivityFace<Dimension>; + + const auto& node_number_vector = descriptor.node_number_vector; + Array<unsigned short> cell_nb_faces(descriptor.cell_to_node_vector.size()); + std::map<Face, std::vector<CellFaceInfo>> face_cells_map; + for (CellId j=0; j<descriptor.cell_to_node_vector.size(); ++j) { + const auto& cell_nodes = descriptor.cell_to_node_vector[j]; + + if constexpr (Dimension==2) { + switch (descriptor.cell_type_vector[j]) { + case CellType::Triangle: { + cell_nb_faces[j] = 3; + // face 0 + Face f0({cell_nodes[1], cell_nodes[2]}, node_number_vector); + face_cells_map[f0].emplace_back(std::make_tuple(j, 0, f0.reversed())); + + // face 1 + Face f1({cell_nodes[2], cell_nodes[0]}, node_number_vector); + face_cells_map[f1].emplace_back(std::make_tuple(j, 1, f1.reversed())); + + // face 2 + Face f2({cell_nodes[0], cell_nodes[1]}, node_number_vector); + face_cells_map[f2].emplace_back(std::make_tuple(j, 2, f2.reversed())); + break; + } + case CellType::Quadrangle: { + cell_nb_faces[j] = 4; + // face 0 + Face f0({cell_nodes[0], cell_nodes[1]}, node_number_vector); + face_cells_map[f0].emplace_back(std::make_tuple(j, 0, f0.reversed())); + + // face 1 + Face f1({cell_nodes[1], cell_nodes[2]}, node_number_vector); + face_cells_map[f1].emplace_back(std::make_tuple(j, 1, f1.reversed())); + + // face 2 + Face f2({cell_nodes[2], cell_nodes[3]}, node_number_vector); + face_cells_map[f2].emplace_back(std::make_tuple(j, 2, f2.reversed())); + + // face 3 + Face f3({cell_nodes[3], cell_nodes[0]}, node_number_vector); + face_cells_map[f3].emplace_back(std::make_tuple(j, 3, f3.reversed())); + break; + } + default: { + perr() << name(descriptor.cell_type_vector[j]) + << ": unexpected cell type in dimension 2!\n"; + std::terminate(); + } + } + } else if constexpr (Dimension==3) { + switch (descriptor.cell_type_vector[j]) { + case CellType::Tetrahedron: { + cell_nb_faces[j] = 4; + // face 0 + Face f0({cell_nodes[1], + cell_nodes[2], + cell_nodes[3]}, node_number_vector); + face_cells_map[f0].emplace_back(std::make_tuple(j, 0, f0.reversed())); + + // face 1 + Face f1({cell_nodes[0], + cell_nodes[3], + cell_nodes[2]}, node_number_vector); + face_cells_map[f1].emplace_back(std::make_tuple(j, 1, f1.reversed())); + + // face 2 + Face f2({cell_nodes[0], + cell_nodes[1], + cell_nodes[3]}, node_number_vector); + face_cells_map[f2].emplace_back(std::make_tuple(j, 2, f2.reversed())); + + // face 3 + Face f3({cell_nodes[0], + cell_nodes[2], + cell_nodes[1]}, node_number_vector); + face_cells_map[f3].emplace_back(std::make_tuple(j, 3, f3.reversed())); + break; + } + case CellType::Hexahedron: { + // face 0 + Face f0({cell_nodes[3], + cell_nodes[2], + cell_nodes[1], + cell_nodes[0]}, node_number_vector); + face_cells_map[f0].emplace_back(std::make_tuple(j, 0, f0.reversed())); + + // face 1 + Face f1({cell_nodes[4], + cell_nodes[5], + cell_nodes[6], + cell_nodes[7]}, node_number_vector); + face_cells_map[f1].emplace_back(std::make_tuple(j, 1, f1.reversed())); + + // face 2 + Face f2({cell_nodes[0], + cell_nodes[4], + cell_nodes[7], + cell_nodes[3]}, node_number_vector); + face_cells_map[f2].emplace_back(std::make_tuple(j, 2, f2.reversed())); + + // face 3 + Face f3({cell_nodes[1], + cell_nodes[2], + cell_nodes[6], + cell_nodes[5]}, node_number_vector); + face_cells_map[f3].emplace_back(std::make_tuple(j, 3, f3.reversed())); + + // face 4 + Face f4({cell_nodes[0], + cell_nodes[1], + cell_nodes[5], + cell_nodes[4]}, node_number_vector); + face_cells_map[f4].emplace_back(std::make_tuple(j, 4, f4.reversed())); + + // face 5 + Face f5({cell_nodes[3], + cell_nodes[7], + cell_nodes[6], + cell_nodes[2]}, node_number_vector); + face_cells_map[f5].emplace_back(std::make_tuple(j, 5, f5.reversed())); + + cell_nb_faces[j] = 6; + break; + } + default: { + perr() << name(descriptor.cell_type_vector[j]) + << ": unexpected cell type in dimension 3!\n"; + std::terminate(); + } + } + } + } + + { + descriptor.cell_to_face_vector.resize(descriptor.cell_to_node_vector.size()); + for (CellId j=0; j<descriptor.cell_to_face_vector.size(); ++j) { + descriptor.cell_to_face_vector[j].resize(cell_nb_faces[j]); + } + FaceId l=0; + for (const auto& face_cells_vector : face_cells_map) { + const auto& cells_vector = face_cells_vector.second; + for (unsigned short lj=0; lj<cells_vector.size(); ++lj) { + const auto& [cell_number, cell_local_face, reversed] = cells_vector[lj]; + descriptor.cell_to_face_vector[cell_number][cell_local_face] = l; + } + ++l; + } + } + + { + descriptor.cell_face_is_reversed_vector.resize(descriptor.cell_to_node_vector.size()); + for (CellId j=0; j<descriptor.cell_face_is_reversed_vector.size(); ++j) { + descriptor.cell_face_is_reversed_vector[j] = Array<bool>(cell_nb_faces[j]); + } + for (const auto& face_cells_vector : face_cells_map) { + const auto& cells_vector = face_cells_vector.second; + for (unsigned short lj=0; lj<cells_vector.size(); ++lj) { + const auto& [cell_number, cell_local_face, reversed] = cells_vector[lj]; + descriptor.cell_face_is_reversed_vector[cell_number][cell_local_face] = reversed; + } + } + } + + { + descriptor.face_to_node_vector.resize(face_cells_map.size()); + int l=0; + for (const auto& face_info : face_cells_map) { + const Face& face = face_info.first; + descriptor.face_to_node_vector[l] = face.nodeIdList(); + ++l; + } + } + + { + // Face numbers may change if numbers are provided in the file + descriptor.face_number_vector.resize(face_cells_map.size()); + for (size_t l=0; l<face_cells_map.size(); ++l) { + descriptor.face_number_vector[l] = l; + } + } +} + + +template <size_t Dimension> +void +GmshReader::__computeFaceEdgeAndEdgeNodeAndCellEdgeConnectivities(ConnectivityDescriptor& descriptor) +{ + static_assert(Dimension==3, "Invalid dimension to compute face-edge connectivities"); + using FaceEdgeInfo = std::tuple<FaceId, unsigned short, bool>; + using Edge = ConnectivityFace<2>; + + const auto& node_number_vector = descriptor.node_number_vector; + Array<unsigned short> face_nb_edges(descriptor.face_to_node_vector.size()); + std::map<Edge, std::vector<FaceEdgeInfo>> edge_faces_map; + for (FaceId l=0; l<descriptor.face_to_node_vector.size(); ++l) { + const auto& face_nodes = descriptor.face_to_node_vector[l]; + + face_nb_edges[l] = face_nodes.size(); + for (size_t r=0; r<face_nodes.size()-1; ++r) { + Edge e({face_nodes[r], face_nodes[r+1]}, node_number_vector); + edge_faces_map[e].emplace_back(std::make_tuple(l, r, e.reversed())); + } + { + Edge e({face_nodes[face_nodes.size()-1], face_nodes[0]}, node_number_vector); + edge_faces_map[e].emplace_back(std::make_tuple(l, face_nodes.size()-1, e.reversed())); + } + } + + std::unordered_map<Edge,EdgeId,typename Edge::Hash> edge_id_map; + { + descriptor.face_to_edge_vector.resize(descriptor.face_to_node_vector.size()); + for (FaceId l=0; l<descriptor.face_to_node_vector.size(); ++l) { + descriptor.face_to_edge_vector[l].resize(face_nb_edges[l]); + } + EdgeId e=0; + for (const auto& edge_faces_vector : edge_faces_map) { + const auto& faces_vector = edge_faces_vector.second; + for (unsigned short l=0; l<faces_vector.size(); ++l) { + const auto& [face_number, face_local_edge, reversed] = faces_vector[l]; + descriptor.face_to_edge_vector[face_number][face_local_edge] = e; + } + edge_id_map[edge_faces_vector.first] = e; + ++e; + } + } + + { + descriptor.face_edge_is_reversed_vector.resize(descriptor.face_to_node_vector.size()); + for (FaceId j=0; j<descriptor.face_edge_is_reversed_vector.size(); ++j) { + descriptor.face_edge_is_reversed_vector[j] = Array<bool>(face_nb_edges[j]); + } + for (const auto& edge_faces_vector : edge_faces_map) { + const auto& faces_vector = edge_faces_vector.second; + for (unsigned short lj=0; lj<faces_vector.size(); ++lj) { + const auto& [face_number, face_local_edge, reversed] = faces_vector[lj]; + descriptor.face_edge_is_reversed_vector[face_number][face_local_edge] = reversed; + } + } + } + + { + descriptor.edge_to_node_vector.resize(edge_faces_map.size()); + int e=0; + for (const auto& edge_info : edge_faces_map) { + const Edge& edge = edge_info.first; + descriptor.edge_to_node_vector[e] = edge.nodeIdList(); + ++e; + } + } + + { + // Edge numbers may change if numbers are provided in the file + descriptor.edge_number_vector.resize(edge_faces_map.size()); + for (size_t e=0; e<edge_faces_map.size(); ++e) { + descriptor.edge_number_vector[e] = e; + } + } + + { + descriptor.cell_to_node_vector.reserve(descriptor.cell_to_node_vector.size()); + for (CellId j=0; j<descriptor.cell_to_node_vector.size(); ++j) { + const auto& cell_nodes = descriptor.cell_to_node_vector[j]; + + switch (descriptor.cell_type_vector[j]) { + case CellType::Tetrahedron: { + constexpr int local_edge[6][2] = {{0,1}, {0,2}, {0,3}, {1,2}, {2,3}, {3,1}}; + std::vector<unsigned int> cell_edge_vector; + cell_edge_vector.reserve(6); + for (int i_edge=0; i_edge<6; ++i_edge) { + const auto e = local_edge[i_edge]; + Edge edge{{cell_nodes[e[0]],cell_nodes[e[1]]}, node_number_vector}; + auto i = edge_id_map.find(edge); + if (i == edge_id_map.end()) { + std::cerr << "could not find this edge!\n"; + std::terminate(); + } + cell_edge_vector.push_back(i->second); + } + descriptor.cell_to_edge_vector.emplace_back(cell_edge_vector); + break; + } + case CellType::Hexahedron: { + constexpr int local_edge[12][2] = {{0,1}, {1,2}, {2,3}, {3,0}, + {4,5}, {5,6}, {6,7}, {7,4}, + {0,4}, {1,5}, {2,6}, {3,7}}; + std::vector<unsigned int> cell_edge_vector; + cell_edge_vector.reserve(12); + for (int i_edge=0; i_edge<12; ++i_edge) { + const auto e = local_edge[i_edge]; + Edge edge{{cell_nodes[e[0]],cell_nodes[e[1]]}, node_number_vector}; + auto i = edge_id_map.find(edge); + if (i == edge_id_map.end()) { + std::cerr << "could not find this edge!\n"; + std::terminate(); + } + cell_edge_vector.push_back(i->second); + } + descriptor.cell_to_edge_vector.emplace_back(cell_edge_vector); + break; + } + default: { + perr() << name(descriptor.cell_type_vector[j]) + << ": unexpected cell type in dimension 3!\n"; + std::terminate(); + } + } + } + } +} + + void GmshReader::__proceedData() { @@ -558,32 +1120,37 @@ GmshReader::__proceedData() case 0: { // edges __edges = Array<Edge>(elementNumber[i]); __edges_ref.resize(elementNumber[i]); + __edges_number.resize(elementNumber[i]); break; } case 1: { // triangles __triangles = Array<Triangle>(elementNumber[i]); __triangles_ref.resize(elementNumber[i]); + __triangles_number.resize(elementNumber[i]); break; } case 2: { // quadrangles __quadrangles = Array<Quadrangle>(elementNumber[i]); __quadrangles_ref.resize(elementNumber[i]); + __quadrangles_number.resize(elementNumber[i]); break; } case 3: { // tetrahedra __tetrahedra = Array<Tetrahedron>(elementNumber[i]); __tetrahedra_ref.resize(elementNumber[i]); + __tetrahedra_number.resize(elementNumber[i]); break; } case 4: {// hexahedra __hexahedra = Array<Hexahedron>(elementNumber[i]); __hexahedra_ref.resize(elementNumber[i]); + __hexahedra_number.resize(elementNumber[i]); break; } - // Ignored entities case 14: {// point __points = Array<Point>(elementNumber[i]); __points_ref.resize(elementNumber[i]); + __points_number.resize(elementNumber[i]); break; } // Unsupported entities @@ -628,7 +1195,6 @@ GmshReader::__proceedData() __verticesCorrepondance.resize(maxNumber+1,-1); for (size_t i=0; i<__verticesNumbers.size(); ++i) { - __verticesCorrepondance[__verticesNumbers[i]] = i; } @@ -647,13 +1213,14 @@ GmshReader::__proceedData() if ((a<0) or (b<0)) { throw ErrorHandler(__FILE__,__LINE__, "reading file '"+m_filename - +"': error reading element "+stringify(i) + +"': error reading element "+std::to_string(i) +" [bad vertices definition]", ErrorHandler::normal); } __edges[edgeNumber] = Edge(a, b); __edges_ref[edgeNumber] = __references[i]; + __edges_number[edgeNumber] = __elementNumber[i]; edgeNumber++; // one more edge break; } @@ -666,13 +1233,14 @@ GmshReader::__proceedData() if ((a<0) or (b<0) or (c<0)) { throw ErrorHandler(__FILE__,__LINE__, "reading file '"+m_filename - +"': error reading element "+stringify(i) + +"': error reading element "+std::to_string(i) +" [bad vertices definition]", ErrorHandler::normal); } __triangles[triangleNumber] = Triangle(a, b, c); __triangles_ref[triangleNumber] = __references[i]; + __triangles_number[triangleNumber] = __elementNumber[i]; triangleNumber++; // one more triangle break; } @@ -686,12 +1254,13 @@ GmshReader::__proceedData() if ((a<0) or (b<0) or (c<0) or (d<0)) { throw ErrorHandler(__FILE__,__LINE__, "reading file '"+m_filename - +"': error reading element "+stringify(i) + +"': error reading element "+std::to_string(i) +" [bad vertices definition]", ErrorHandler::normal); } __quadrangles[quadrilateralNumber] = Quadrangle(a,b,c,d); __quadrangles_ref[quadrilateralNumber] = __references[i]; + __quadrangles_number[quadrilateralNumber] = __elementNumber[i]; quadrilateralNumber++; // one more quadrangle break; } @@ -705,12 +1274,13 @@ GmshReader::__proceedData() if ((a<0) or (b<0) or (c<0) or (d<0)) { throw ErrorHandler(__FILE__,__LINE__, "reading file '"+m_filename - +"': error reading element "+stringify(i) + +"': error reading element "+std::to_string(i) +" [bad vertices definition]", ErrorHandler::normal); } __tetrahedra[tetrahedronNumber] = Tetrahedron(a, b, c, d); __tetrahedra_ref[tetrahedronNumber] = __references[i]; + __tetrahedra_number[tetrahedronNumber] = __elementNumber[i]; tetrahedronNumber++; // one more tetrahedron break; } @@ -728,7 +1298,7 @@ GmshReader::__proceedData() if ((a<0) or (b<0) or (c<0) or (d<0) or (e<0) or (f<0) or (g<0) or (h<0)) { throw ErrorHandler(__FILE__,__LINE__, "reading file '"+m_filename - +"': error reading element "+stringify(i) + +"': error reading element "+std::to_string(i) +" [bad vertices definition]", ErrorHandler::normal); } @@ -736,6 +1306,7 @@ GmshReader::__proceedData() = Hexahedron(a, b, c, d, e, f, g, h); __hexahedra_ref[hexahedronNumber] = __references[i]; + __hexahedra_number[hexahedronNumber] = __elementNumber[i]; hexahedronNumber++; // one more hexahedron break; } @@ -745,6 +1316,7 @@ GmshReader::__proceedData() const int a = __verticesCorrepondance[elementVertices[0]]; __points[point_number] = a; __points_ref[point_number] = __references[i]; + __points_number[point_number] = __elementNumber[i]; point_number++; break; } @@ -794,54 +1366,270 @@ GmshReader::__proceedData() if ((dimension3_mask, elementNumber)>0) { const size_t nb_cells = (dimension3_mask, elementNumber); - std::vector<CellType> cell_type_vector(nb_cells); + ConnectivityDescriptor descriptor; + + descriptor.node_number_vector = __verticesNumbers; + descriptor.cell_type_vector.resize(nb_cells); + descriptor.cell_number_vector.resize(nb_cells); + descriptor.cell_to_node_vector.resize(nb_cells); - std::vector<std::vector<unsigned int>> cell_by_node_vector(nb_cells); const size_t nb_tetrahedra = __tetrahedra.size(); for (size_t j=0; j<nb_tetrahedra; ++j) { - cell_by_node_vector[j].resize(4); + descriptor.cell_to_node_vector[j].resize(4); for (int r=0; r<4; ++r) { - cell_by_node_vector[j][r] = __tetrahedra[j][r]; + descriptor.cell_to_node_vector[j][r] = __tetrahedra[j][r]; } - cell_type_vector[j] = CellType::Tetrahedron; + descriptor.cell_type_vector[j] = CellType::Tetrahedron; + descriptor.cell_number_vector[j] = __tetrahedra_number[j]; } const size_t nb_hexahedra = __hexahedra.size(); for (size_t j=0; j<nb_hexahedra; ++j) { const size_t jh = nb_tetrahedra+j; - cell_by_node_vector[jh].resize(8); + descriptor.cell_to_node_vector[jh].resize(8); for (int r=0; r<8; ++r) { - cell_by_node_vector[jh][r] = __hexahedra[j][r]; + descriptor.cell_to_node_vector[jh][r] = __hexahedra[j][r]; } - cell_type_vector[jh] = CellType::Hexahedron; + descriptor.cell_type_vector[jh] = CellType::Hexahedron; + descriptor.cell_number_vector[jh] = __hexahedra_number[j]; } - std::shared_ptr<Connectivity3D> p_connectivity(new Connectivity3D(cell_by_node_vector, - cell_type_vector)); - Connectivity3D& connectivity = *p_connectivity; + std::map<unsigned int, std::vector<unsigned int>> ref_cells_map; + for (unsigned int r=0; r<__tetrahedra_ref.size(); ++r) { + const unsigned int elem_number = __tetrahedra_ref[r]; + const unsigned int& ref = __tetrahedra_ref[r]; + ref_cells_map[ref].push_back(elem_number); + } - std::map<unsigned int, std::vector<unsigned int>> ref_faces_map; - for (unsigned int f=0; f<__triangles.size(); ++f) { - const unsigned int face_number - = connectivity.getFaceNumber({__triangles[f][0], __triangles[f][1], __triangles[f][2]}); - const unsigned int& ref = __triangles_ref[f]; - ref_faces_map[ref].push_back(face_number); + for (unsigned int j=0; j<__hexahedra_ref.size(); ++j) { + const size_t elem_number = nb_tetrahedra+j; + const unsigned int& ref = __hexahedra_ref[j]; + ref_cells_map[ref].push_back(elem_number); } - for (unsigned int f=0; f<__quadrangles.size(); ++f) { - const unsigned int face_number - = connectivity.getFaceNumber({__quadrangles[f][0], __quadrangles[f][1], - __quadrangles[f][2], __quadrangles[f][3]}); - const unsigned int& ref = __quadrangles_ref[f]; - ref_faces_map[ref].push_back(face_number); + + + for (const auto& ref_cell_list : ref_cells_map) { + Array<CellId> cell_list(ref_cell_list.second.size()); + for (size_t j=0; j<ref_cell_list.second.size(); ++j) { + cell_list[j]=ref_cell_list.second[j]; + } + const PhysicalRefId& physical_ref_id = m_physical_ref_map.at(ref_cell_list.first); + descriptor.addRefItemList(RefCellList(physical_ref_id.refId(), cell_list)); } - for (const auto& ref_face_list : ref_faces_map) { - Array<FaceId> face_list(ref_face_list.second.size()); - for (size_t j=0; j<ref_face_list.second.size(); ++j) { - face_list[j]=ref_face_list.second[j]; + + this->__computeCellFaceAndFaceNodeConnectivities<3>(descriptor); + + const auto& node_number_vector = descriptor.node_number_vector; + + { + using Face = ConnectivityFace<3>; + const std::unordered_map<Face, FaceId, typename Face::Hash> face_to_id_map + = [&] { + std::unordered_map<Face, FaceId, typename Face::Hash> face_to_id_map; + for (FaceId l=0; l<descriptor.face_to_node_vector.size(); ++l) { + const auto& node_vector = descriptor.face_to_node_vector[l]; + face_to_id_map[Face(node_vector, node_number_vector)] = l; + } + return face_to_id_map; + } (); + + std::unordered_map<int, FaceId> face_number_id_map + = [&] { + std::unordered_map<int, FaceId> face_number_id_map; + for (size_t l=0; l< descriptor.face_number_vector.size(); ++l) { + face_number_id_map[descriptor.face_number_vector[l]] = l; + } + Assert(face_number_id_map.size() == descriptor.face_number_vector.size()); + return face_number_id_map; + } (); + + std::map<unsigned int, std::vector<unsigned int>> ref_faces_map; + for (unsigned int f=0; f<__triangles.size(); ++f) { + const unsigned int face_id + = [&]{ + auto i = face_to_id_map.find(Face({__triangles[f][0], __triangles[f][1], __triangles[f][2]}, + node_number_vector)); + if (i == face_to_id_map.end()) { + std::cerr << "face not found!\n"; + std::terminate(); + } + return i->second; + }(); + + const unsigned int& ref = __triangles_ref[f]; + ref_faces_map[ref].push_back(face_id); + + if (descriptor.face_number_vector[face_id] != __quadrangles_number[f]) { + if (auto i_face = face_number_id_map.find(__quadrangles_number[f]); + i_face != face_number_id_map.end()) { + const int other_face_id = i_face->second; + std::swap(descriptor.face_number_vector[face_id], + descriptor.face_number_vector[other_face_id]); + + face_number_id_map.erase(descriptor.face_number_vector[face_id]); + face_number_id_map.erase(descriptor.face_number_vector[other_face_id]); + + face_number_id_map[descriptor.face_number_vector[face_id]]=face_id; + face_number_id_map[descriptor.face_number_vector[other_face_id]]=other_face_id; + } else { + face_number_id_map.erase(descriptor.face_number_vector[face_id]); + descriptor.face_number_vector[face_id] = __quadrangles_number[f]; + face_number_id_map[descriptor.face_number_vector[face_id]] = face_id; + } + } } - const PhysicalRefId& physical_ref_id = m_physical_ref_map.at(ref_face_list.first); - connectivity.addRefFaceList(RefFaceList(physical_ref_id.refId(), face_list)); + + for (unsigned int f=0; f<__quadrangles.size(); ++f) { + const unsigned int face_id + = [&]{ + auto i = face_to_id_map.find(Face({__quadrangles[f][0], __quadrangles[f][1], + __quadrangles[f][2], __quadrangles[f][3]}, node_number_vector)); + if (i == face_to_id_map.end()) { + std::cerr << "face not found!\n"; + std::terminate(); + } + return i->second; + }(); + + const unsigned int& ref = __quadrangles_ref[f]; + ref_faces_map[ref].push_back(face_id); + + if (descriptor.face_number_vector[face_id] != __quadrangles_number[f]) { + if (auto i_face = face_number_id_map.find(__quadrangles_number[f]); + i_face != face_number_id_map.end()) { + const int other_face_id = i_face->second; + std::swap(descriptor.face_number_vector[face_id], + descriptor.face_number_vector[other_face_id]); + + face_number_id_map.erase(descriptor.face_number_vector[face_id]); + face_number_id_map.erase(descriptor.face_number_vector[other_face_id]); + + face_number_id_map[descriptor.face_number_vector[face_id]]=face_id; + face_number_id_map[descriptor.face_number_vector[other_face_id]]=other_face_id; + } else { + face_number_id_map.erase(descriptor.face_number_vector[face_id]); + descriptor.face_number_vector[face_id] = __quadrangles_number[f]; + face_number_id_map[descriptor.face_number_vector[face_id]] = face_id; + } + } + } + + for (const auto& ref_face_list : ref_faces_map) { + Array<FaceId> face_list(ref_face_list.second.size()); + for (size_t j=0; j<ref_face_list.second.size(); ++j) { + face_list[j]=ref_face_list.second[j]; + } + const PhysicalRefId& physical_ref_id = m_physical_ref_map.at(ref_face_list.first); + descriptor.addRefItemList(RefFaceList{physical_ref_id.refId(), face_list}); + } + } + this->__computeFaceEdgeAndEdgeNodeAndCellEdgeConnectivities<3>(descriptor); + + { + using Edge = ConnectivityFace<2>; + const auto& node_number_vector = descriptor.node_number_vector; + const std::unordered_map<Edge, EdgeId, typename Edge::Hash> edge_to_id_map + = [&] { + std::unordered_map<Edge, EdgeId, typename Edge::Hash> edge_to_id_map; + for (EdgeId l=0; l<descriptor.edge_to_node_vector.size(); ++l) { + const auto& node_vector = descriptor.edge_to_node_vector[l]; + edge_to_id_map[Edge(node_vector, node_number_vector)] = l; + } + return edge_to_id_map; + } (); + + std::unordered_map<int, EdgeId> edge_number_id_map + = [&] { + std::unordered_map<int, EdgeId> edge_number_id_map; + for (size_t l=0; l< descriptor.edge_number_vector.size(); ++l) { + edge_number_id_map[descriptor.edge_number_vector[l]] = l; + } + Assert(edge_number_id_map.size() == descriptor.edge_number_vector.size()); + return edge_number_id_map; + } (); + + std::map<unsigned int, std::vector<unsigned int>> ref_edges_map; + for (unsigned int e=0; e<__edges.size(); ++e) { + const unsigned int edge_id + = [&]{ + auto i = edge_to_id_map.find(Edge({__edges[e][0], __edges[e][1]}, node_number_vector)); + if (i == edge_to_id_map.end()) { + std::cerr << "edge " << __edges[e][0] << " not found!\n"; + std::terminate(); + } + return i->second; + }(); + const unsigned int& ref = __edges_ref[e]; + ref_edges_map[ref].push_back(edge_id); + + if (descriptor.edge_number_vector[edge_id] != __edges_number[e]) { + if (auto i_edge = edge_number_id_map.find(__edges_number[e]); + i_edge != edge_number_id_map.end()) { + const int other_edge_id = i_edge->second; + std::swap(descriptor.edge_number_vector[edge_id], + descriptor.edge_number_vector[other_edge_id]); + + edge_number_id_map.erase(descriptor.edge_number_vector[edge_id]); + edge_number_id_map.erase(descriptor.edge_number_vector[other_edge_id]); + + edge_number_id_map[descriptor.edge_number_vector[edge_id]]=edge_id; + edge_number_id_map[descriptor.edge_number_vector[other_edge_id]]=other_edge_id; + } else { + edge_number_id_map.erase(descriptor.edge_number_vector[edge_id]); + descriptor.edge_number_vector[edge_id] = __edges_number[e]; + edge_number_id_map[descriptor.edge_number_vector[edge_id]] = edge_id; + } + } + } + + for (const auto& ref_edge_list : ref_edges_map) { + Array<EdgeId> edge_list(ref_edge_list.second.size()); + for (size_t j=0; j<ref_edge_list.second.size(); ++j) { + edge_list[j]=ref_edge_list.second[j]; + } + const PhysicalRefId& physical_ref_id = m_physical_ref_map.at(ref_edge_list.first); + descriptor.addRefItemList(RefEdgeList{physical_ref_id.refId(), edge_list}); + } + } + + std::map<unsigned int, std::vector<unsigned int>> ref_points_map; + for (unsigned int r=0; r<__points.size(); ++r) { + const unsigned int point_number = __points[r]; + const unsigned int& ref = __points_ref[r]; + ref_points_map[ref].push_back(point_number); + } + + for (const auto& ref_point_list : ref_points_map) { + Array<NodeId> point_list(ref_point_list.second.size()); + for (size_t j=0; j<ref_point_list.second.size(); ++j) { + point_list[j]=ref_point_list.second[j]; + } + const PhysicalRefId& physical_ref_id = m_physical_ref_map.at(ref_point_list.first); + descriptor.addRefItemList(RefNodeList(physical_ref_id.refId(), point_list)); } + descriptor.cell_owner_vector.resize(nb_cells); + std::fill(descriptor.cell_owner_vector.begin(), + descriptor.cell_owner_vector.end(), + parallel::rank()); + + descriptor.face_owner_vector.resize(descriptor.face_number_vector.size()); + std::fill(descriptor.face_owner_vector.begin(), + descriptor.face_owner_vector.end(), + parallel::rank()); + + descriptor.edge_owner_vector.resize(descriptor.edge_number_vector.size()); + std::fill(descriptor.edge_owner_vector.begin(), + descriptor.edge_owner_vector.end(), + parallel::rank()); + + descriptor.node_owner_vector.resize(descriptor.node_number_vector.size()); + std::fill(descriptor.node_owner_vector.begin(), + descriptor.node_owner_vector.end(), + parallel::rank()); + + std::shared_ptr p_connectivity = Connectivity3D::build(descriptor); + Connectivity3D& connectivity = *p_connectivity; + using MeshType = Mesh<Connectivity3D>; using Rd = TinyVector<3, double>; @@ -851,42 +1639,117 @@ GmshReader::__proceedData() xr[i][1] = __vertices[i][1]; xr[i][2] = __vertices[i][2]; } - m_mesh = std::shared_ptr<IMesh>(new MeshType(p_connectivity, xr)); + m_mesh = std::make_shared<MeshType>(p_connectivity, xr); } else if ((dimension2_mask, elementNumber)>0) { const size_t nb_cells = (dimension2_mask, elementNumber); - std::vector<CellType> cell_type_vector(nb_cells); + ConnectivityDescriptor descriptor; + + descriptor.node_number_vector = __verticesNumbers; + descriptor.cell_type_vector.resize(nb_cells); + descriptor.cell_number_vector.resize(nb_cells); + descriptor.cell_to_node_vector.resize(nb_cells); - std::vector<std::vector<unsigned int>> cell_by_node_vector(nb_cells); const size_t nb_triangles = __triangles.size(); for (size_t j=0; j<nb_triangles; ++j) { - cell_by_node_vector[j].resize(3); + descriptor.cell_to_node_vector[j].resize(3); for (int r=0; r<3; ++r) { - cell_by_node_vector[j][r] = __triangles[j][r]; + descriptor.cell_to_node_vector[j][r] = __triangles[j][r]; } - cell_type_vector[j] = CellType::Triangle; + descriptor.cell_type_vector[j] = CellType::Triangle; + descriptor.cell_number_vector[j] = __triangles_number[j]; } const size_t nb_quadrangles = __quadrangles.size(); for (size_t j=0; j<nb_quadrangles; ++j) { const size_t jq = j+nb_triangles; - cell_by_node_vector[jq].resize(4); + descriptor.cell_to_node_vector[jq].resize(4); for (int r=0; r<4; ++r) { - cell_by_node_vector[jq][r] = __quadrangles[j][r]; + descriptor.cell_to_node_vector[jq][r] = __quadrangles[j][r]; } - cell_type_vector[jq] = CellType::Quadrangle; + descriptor.cell_type_vector[jq] = CellType::Quadrangle; + descriptor.cell_number_vector[jq] = __quadrangles_number[j]; } - std::shared_ptr<Connectivity2D> p_connectivity(new Connectivity2D(cell_by_node_vector, - cell_type_vector)); - Connectivity2D& connectivity = *p_connectivity; + std::map<unsigned int, std::vector<unsigned int>> ref_cells_map; + for (unsigned int r=0; r<__triangles_ref.size(); ++r) { + const unsigned int elem_number = __triangles_ref[r]; + const unsigned int& ref = __triangles_ref[r]; + ref_cells_map[ref].push_back(elem_number); + } + + for (unsigned int j=0; j<__quadrangles_ref.size(); ++j) { + const size_t elem_number = nb_triangles+j; + const unsigned int& ref = __quadrangles_ref[j]; + ref_cells_map[ref].push_back(elem_number); + } + + for (const auto& ref_cell_list : ref_cells_map) { + Array<CellId> cell_list(ref_cell_list.second.size()); + for (size_t j=0; j<ref_cell_list.second.size(); ++j) { + cell_list[j]=ref_cell_list.second[j]; + } + const PhysicalRefId& physical_ref_id = m_physical_ref_map.at(ref_cell_list.first); + descriptor.addRefItemList(RefCellList(physical_ref_id.refId(), cell_list)); + } + + this->__computeCellFaceAndFaceNodeConnectivities<2>(descriptor); + + using Face = ConnectivityFace<2>; + const auto& node_number_vector = descriptor.node_number_vector; + const std::unordered_map<Face, FaceId, typename Face::Hash> face_to_id_map + = [&] { + std::unordered_map<Face, FaceId, typename Face::Hash> face_to_id_map; + for (FaceId l=0; l<descriptor.face_to_node_vector.size(); ++l) { + const auto& node_vector = descriptor.face_to_node_vector[l]; + face_to_id_map[Face(node_vector, node_number_vector)] = l; + } + return face_to_id_map; + } (); + + std::unordered_map<int, FaceId> face_number_id_map + = [&] { + std::unordered_map<int, FaceId> face_number_id_map; + for (size_t l=0; l< descriptor.face_number_vector.size(); ++l) { + face_number_id_map[descriptor.face_number_vector[l]] = l; + } + Assert(face_number_id_map.size() == descriptor.face_number_vector.size()); + return face_number_id_map; + } (); std::map<unsigned int, std::vector<unsigned int>> ref_faces_map; for (unsigned int e=0; e<__edges.size(); ++e) { - const unsigned int edge_number = connectivity.getFaceNumber({__edges[e][0], __edges[e][1]}); + const unsigned int edge_id + = [&]{ + auto i = face_to_id_map.find(Face({__edges[e][0], __edges[e][1]}, node_number_vector)); + if (i == face_to_id_map.end()) { + std::cerr << "face " << __edges[e][0] << " not found!\n"; + std::terminate(); + } + return i->second; + }(); const unsigned int& ref = __edges_ref[e]; - ref_faces_map[ref].push_back(edge_number); + ref_faces_map[ref].push_back(edge_id); + + if (descriptor.face_number_vector[edge_id] != __edges_number[e]) { + if (auto i_face = face_number_id_map.find(__edges_number[e]); + i_face != face_number_id_map.end()) { + const int other_edge_id = i_face->second; + std::swap(descriptor.face_number_vector[edge_id], + descriptor.face_number_vector[other_edge_id]); + + face_number_id_map.erase(descriptor.face_number_vector[edge_id]); + face_number_id_map.erase(descriptor.face_number_vector[other_edge_id]); + + face_number_id_map[descriptor.face_number_vector[edge_id]]=edge_id; + face_number_id_map[descriptor.face_number_vector[other_edge_id]]=other_edge_id; + } else { + face_number_id_map.erase(descriptor.face_number_vector[edge_id]); + descriptor.face_number_vector[edge_id] = __edges_number[e]; + face_number_id_map[descriptor.face_number_vector[edge_id]] = edge_id; + } + } } for (const auto& ref_face_list : ref_faces_map) { @@ -895,7 +1758,7 @@ GmshReader::__proceedData() face_list[j]=ref_face_list.second[j]; } const PhysicalRefId& physical_ref_id = m_physical_ref_map.at(ref_face_list.first); - connectivity.addRefFaceList(RefFaceList(physical_ref_id.refId(), face_list)); + descriptor.addRefItemList(RefFaceList{physical_ref_id.refId(), face_list}); } std::map<unsigned int, std::vector<unsigned int>> ref_points_map; @@ -911,9 +1774,27 @@ GmshReader::__proceedData() point_list[j]=ref_point_list.second[j]; } const PhysicalRefId& physical_ref_id = m_physical_ref_map.at(ref_point_list.first); - connectivity.addRefNodeList(RefNodeList(physical_ref_id.refId(), point_list)); + descriptor.addRefItemList(RefNodeList(physical_ref_id.refId(), point_list)); } + descriptor.cell_owner_vector.resize(nb_cells); + std::fill(descriptor.cell_owner_vector.begin(), + descriptor.cell_owner_vector.end(), + parallel::rank()); + + descriptor.face_owner_vector.resize(descriptor.face_number_vector.size()); + std::fill(descriptor.face_owner_vector.begin(), + descriptor.face_owner_vector.end(), + parallel::rank()); + + descriptor.node_owner_vector.resize(descriptor.node_number_vector.size()); + std::fill(descriptor.node_owner_vector.begin(), + descriptor.node_owner_vector.end(), + parallel::rank()); + + std::shared_ptr p_connectivity = Connectivity2D::build(descriptor); + Connectivity2D& connectivity = *p_connectivity; + using MeshType = Mesh<Connectivity2D>; using Rd = TinyVector<2, double>; @@ -922,25 +1803,27 @@ GmshReader::__proceedData() xr[i][0] = __vertices[i][0]; xr[i][1] = __vertices[i][1]; } - m_mesh = std::shared_ptr<IMesh>(new MeshType(p_connectivity, xr)); + m_mesh = std::make_shared<MeshType>(p_connectivity, xr); } else if ((dimension1_mask, elementNumber)>0) { const size_t nb_cells = (dimension1_mask, elementNumber); - std::vector<CellType> cell_type_vector(nb_cells); + ConnectivityDescriptor descriptor; + + descriptor.node_number_vector = __verticesNumbers; + descriptor.cell_type_vector.resize(nb_cells); + descriptor.cell_number_vector.resize(nb_cells); + descriptor.cell_to_node_vector.resize(nb_cells); - std::vector<std::vector<unsigned int>> cell_by_node_vector(nb_cells); for (size_t j=0; j<nb_cells; ++j) { - cell_by_node_vector[j].resize(2); + descriptor.cell_to_node_vector[j].resize(2); for (int r=0; r<2; ++r) { - cell_by_node_vector[j][r] = __edges[j][r]; + descriptor.cell_to_node_vector[j][r] = __edges[j][r]; } - cell_type_vector[j] = CellType::Line; + descriptor.cell_type_vector[j] = CellType::Line; + descriptor.cell_number_vector[j] = __edges_number[j]; } - std::shared_ptr<Connectivity1D> p_connectivity(new Connectivity1D(cell_by_node_vector, - cell_type_vector)); - Connectivity1D& connectivity = *p_connectivity; std::map<unsigned int, std::vector<unsigned int>> ref_points_map; for (unsigned int r=0; r<__points.size(); ++r) { @@ -955,9 +1838,22 @@ GmshReader::__proceedData() point_list[j]=ref_point_list.second[j]; } const PhysicalRefId& physical_ref_id = m_physical_ref_map.at(ref_point_list.first); - connectivity.addRefNodeList(RefNodeList(physical_ref_id.refId(), point_list)); + descriptor.addRefItemList(RefNodeList(physical_ref_id.refId(), point_list)); } + descriptor.cell_owner_vector.resize(nb_cells); + std::fill(descriptor.cell_owner_vector.begin(), + descriptor.cell_owner_vector.end(), + parallel::rank()); + + descriptor.node_owner_vector.resize(descriptor.node_number_vector.size()); + std::fill(descriptor.node_owner_vector.begin(), + descriptor.node_owner_vector.end(), + parallel::rank()); + + std::shared_ptr p_connectivity = Connectivity1D::build(descriptor); + Connectivity1D& connectivity = *p_connectivity; + using MeshType = Mesh<Connectivity1D>; using Rd = TinyVector<1, double>; @@ -966,7 +1862,7 @@ GmshReader::__proceedData() xr[i][0] = __vertices[i][0]; } - m_mesh = std::shared_ptr<IMesh>(new MeshType(p_connectivity, xr)); + m_mesh = std::make_shared<MeshType>(p_connectivity, xr); } else { perr() << "*** using a dimension 0 mesh is forbidden!\n"; diff --git a/src/mesh/GmshReader.hpp b/src/mesh/GmshReader.hpp index ba9ffe2502b4df46f59308f411747c7c2deaaac0..cd482e637c90a68c1bd208a51285db06d5f93741 100644 --- a/src/mesh/GmshReader.hpp +++ b/src/mesh/GmshReader.hpp @@ -14,6 +14,8 @@ #include <map> #include <fstream> +class ConnectivityDescriptor; + class GmshReader { public: @@ -83,26 +85,32 @@ private: using Point = unsigned int; Array<Point> __points; std::vector<int> __points_ref; + std::vector<int> __points_number; using Edge = TinyVector<2,unsigned int>; Array<Edge> __edges; std::vector<int> __edges_ref; + std::vector<int> __edges_number; using Triangle = TinyVector<3,unsigned int>; Array<Triangle> __triangles; std::vector<int> __triangles_ref; + std::vector<int> __triangles_number; using Quadrangle = TinyVector<4,unsigned int>; Array<Quadrangle> __quadrangles; std::vector<int> __quadrangles_ref; + std::vector<int> __quadrangles_number; using Tetrahedron = TinyVector<4,unsigned int>; Array<Tetrahedron> __tetrahedra; std::vector<int> __tetrahedra_ref; + std::vector<int> __tetrahedra_number; using Hexahedron = TinyVector<8,unsigned int>; Array<Hexahedron> __hexahedra; std::vector<int> __hexahedra_ref; + std::vector<int> __hexahedra_number; /** * Gmsh format provides a numbered, none ordrered and none dense @@ -110,6 +118,11 @@ private: */ std::vector<int> __verticesCorrepondance; + /** + * elements types + */ + std::vector<int> __elementNumber; + /** * elements types */ @@ -153,7 +166,7 @@ private: int i; m_fin >> i; - return std::move(i); + return i; } double _getReal() @@ -161,7 +174,7 @@ private: double d; m_fin >> d; - return std::move(d); + return d; } /** @@ -246,6 +259,15 @@ private: */ void __readPhysicalNames2_2(); + template <size_t Dimension> + void __computeCellFaceAndFaceNodeConnectivities(ConnectivityDescriptor& descriptor); + + template <size_t Dimension> + void __computeFaceEdgeAndEdgeNodeAndCellEdgeConnectivities(ConnectivityDescriptor& descriptor); + + template <int Dimension> + void _dispatch(); + std::shared_ptr<IMesh> m_mesh; public: @@ -256,8 +278,6 @@ public: GmshReader(const std::string& filename); ~GmshReader() = default; - - }; #endif // GMSH_READER_HPP diff --git a/src/mesh/IConnectivity.hpp b/src/mesh/IConnectivity.hpp index f60db961d80f939c64139f8746a674e4ce4de91e..63ae03f3aa9e30f477db20c044e90822dd8dd046 100644 --- a/src/mesh/IConnectivity.hpp +++ b/src/mesh/IConnectivity.hpp @@ -2,15 +2,16 @@ #define ICONNECTIVITY_HPP #include <ItemType.hpp> +#include <ItemOfItemType.hpp> #include <ConnectivityMatrix.hpp> +#include <memory> -class IConnectivity +class IConnectivity : public std::enable_shared_from_this<IConnectivity> { protected: template <typename DataType, - ItemType sub_item_type, - ItemType item_type, - typename Allowed> + typename ItemOfItem, + typename ConnectivityPtr> friend class SubItemValuePerItem; @@ -19,6 +20,13 @@ class IConnectivity const ItemType& item_type_1) const = 0; public: + virtual size_t dimension() const = 0; + + std::shared_ptr<const IConnectivity> shared_ptr() const + { + return this->shared_from_this(); + } + virtual size_t numberOfNodes() const = 0; virtual size_t numberOfEdges() const = 0; virtual size_t numberOfFaces() const = 0; diff --git a/src/mesh/ItemOfItemType.hpp b/src/mesh/ItemOfItemType.hpp new file mode 100644 index 0000000000000000000000000000000000000000..53a7d5ffe8d339d99e8be2e2fed0639542f83772 --- /dev/null +++ b/src/mesh/ItemOfItemType.hpp @@ -0,0 +1,34 @@ +#ifndef ITEM_OF_ITEM_TYPE_HPP +#define ITEM_OF_ITEM_TYPE_HPP + +#include <ItemType.hpp> + +template <ItemType sub_item_t, + ItemType item_t> +struct ItemOfItemType +{ + static_assert(sub_item_t != item_t, "item and its sub-item cannot be of same type"); + + constexpr static ItemType sub_item_type = sub_item_t; + constexpr static ItemType item_type = item_t; + + using Reversed = ItemOfItemType<item_type, sub_item_type>; +}; + +using FaceOfCell = ItemOfItemType<ItemType::face, ItemType::cell>; +using EdgeOfCell = ItemOfItemType<ItemType::edge, ItemType::cell>; +using NodeOfCell = ItemOfItemType<ItemType::node, ItemType::cell>; + +using CellOfFace = ItemOfItemType<ItemType::cell, ItemType::face>; +using EdgeOfFace = ItemOfItemType<ItemType::edge, ItemType::face>; +using NodeOfFace = ItemOfItemType<ItemType::node, ItemType::face>; + +using CellOfEdge = ItemOfItemType<ItemType::cell, ItemType::edge>; +using FaceOfEdge = ItemOfItemType<ItemType::face, ItemType::edge>; +using NodeOfEdge = ItemOfItemType<ItemType::node, ItemType::edge>; + +using CellOfNode = ItemOfItemType<ItemType::cell, ItemType::node>; +using FaceOfNode = ItemOfItemType<ItemType::face, ItemType::node>; +using EdgeOfNode = ItemOfItemType<ItemType::edge, ItemType::node>; + +#endif // ITEM_OF_ITEM_TYPE_HPP diff --git a/src/mesh/ItemToItemMatrix.hpp b/src/mesh/ItemToItemMatrix.hpp index bf7e86ebca7d279a03586a6ad183b24887457b23..bf62537d33b805aee07fddd9e886aa2ed48a922c 100644 --- a/src/mesh/ItemToItemMatrix.hpp +++ b/src/mesh/ItemToItemMatrix.hpp @@ -67,7 +67,7 @@ class ItemToItemMatrix return m_connectivity_matrix.numEntries(); } - const auto& entries() const + auto entries() const { return m_connectivity_matrix.entries(); } @@ -83,7 +83,7 @@ class ItemToItemMatrix PASTIS_INLINE const auto& operator[](const IndexType& source_id) const { - static_assert(std::is_same<IndexType, SourceItemId>(), + static_assert(std::is_same_v<IndexType, SourceItemId>, "ItemToItemMatrix must be indexed using correct ItemId"); using RowType = decltype(m_connectivity_matrix.rowConst(source_id)); return SubItemList<RowType>(m_connectivity_matrix.rowConst(source_id)); diff --git a/src/mesh/ItemType.hpp b/src/mesh/ItemType.hpp index 1b5f3cb452e898dc957ff7f96fb07c4955312f16..e9de6153098342eeaa0306e3754fd8b03e45644c 100644 --- a/src/mesh/ItemType.hpp +++ b/src/mesh/ItemType.hpp @@ -118,4 +118,8 @@ struct ItemTypeId<3> } }; +template <ItemType item_type> +PASTIS_INLINE +constexpr bool is_false_item_type_v = false; + #endif // ITEM_TYPE_HPP diff --git a/src/mesh/ItemValue.hpp b/src/mesh/ItemValue.hpp index bfea6dd3dbc294a02bb3ca8d060100bf9098f8bc..827daa32d0e912dd43c13f4113f5ceb2566f034d 100644 --- a/src/mesh/ItemValue.hpp +++ b/src/mesh/ItemValue.hpp @@ -2,6 +2,7 @@ #define ITEM_VALUE_HPP #include <PastisAssert.hpp> +#include <PastisOStream.hpp> #include <Array.hpp> @@ -9,101 +10,174 @@ #include <ItemId.hpp> #include <IConnectivity.hpp> +#include <memory> template <typename DataType, - ItemType item_type> + ItemType item_type, + typename ConnectivityPtr = std::shared_ptr<const IConnectivity>> class ItemValue { public: - static const ItemType item_t{item_type}; + static constexpr ItemType item_t{item_type}; using data_type = DataType; using ItemId = ItemIdT<item_type>; using index_type = ItemId; private: - bool m_is_built{false}; + using ConnectivitySharedPtr = std::shared_ptr<const IConnectivity>; + using ConnectivityWeakPtr = std::weak_ptr<const IConnectivity>; + + static_assert(std::is_same_v<ConnectivityPtr, ConnectivitySharedPtr> or + std::is_same_v<ConnectivityPtr, ConnectivityWeakPtr>); + + ConnectivityPtr m_connectivity_ptr; Array<DataType> m_values; - // Allows const version to access our data - friend ItemValue<std::add_const_t<DataType>, - item_type>; + // Allow const std:shared_ptr version to access our data + friend ItemValue<std::add_const_t<DataType>, item_type, + ConnectivitySharedPtr>; + + // Allow const std:weak_ptr version to access our data + friend ItemValue<std::add_const_t<DataType>, item_type, + ConnectivityWeakPtr>; + + friend PASTIS_INLINE + ItemValue<std::remove_const_t<DataType>,item_type, ConnectivityPtr> + copy(const ItemValue<DataType, item_type, ConnectivityPtr>& source) + { + ItemValue<std::remove_const_t<DataType>, item_type, ConnectivityPtr> image(source); + + image.m_values = copy(source.m_values); + return image; + } public: - PASTIS_FORCEINLINE - const bool& isBuilt() const + PASTIS_INLINE + bool isBuilt() const noexcept + { + return m_connectivity_ptr.use_count() != 0; + } + + PASTIS_INLINE + std::shared_ptr<const IConnectivity> connectivity_ptr() const noexcept { - return m_is_built; + if constexpr (std::is_same_v<ConnectivityPtr, ConnectivitySharedPtr>) { + return m_connectivity_ptr; + } else { + return m_connectivity_ptr.lock(); + } } PASTIS_INLINE - size_t size() const + size_t size() const noexcept(NO_ASSERT) { - Assert(m_is_built); + Assert(this->isBuilt()); return m_values.size(); } + PASTIS_INLINE + void fill(const DataType& data) const noexcept + { + static_assert(not std::is_const_v<DataType>, + "Cannot modify ItemValue of const"); + m_values.fill(data); + } + // Following Kokkos logic, these classes are view and const view does allow // changes in data PASTIS_FORCEINLINE DataType& operator[](const ItemId& i) const noexcept(NO_ASSERT) { - Assert(m_is_built); + Assert(this->isBuilt()); return m_values[i]; } template <typename IndexType> - DataType& operator[](const IndexType& i) const noexcept(NO_ASSERT) + DataType& operator[](const IndexType&) const noexcept(NO_ASSERT) { - static_assert(std::is_same<IndexType,ItemId>(), + static_assert(std::is_same_v<IndexType,ItemId>, "ItemValue must be indexed by ItemId"); - return m_values[i]; } PASTIS_INLINE - size_t numberOfItems() const + size_t numberOfItems() const noexcept(NO_ASSERT) { - Assert(m_is_built); + Assert(this->isBuilt()); return m_values.size(); } template <typename DataType2> PASTIS_INLINE ItemValue& - operator=(const ItemValue<DataType2, item_type>& value_per_item) + operator=(const Array<DataType2>& values) noexcept(NO_ASSERT) + { + // ensures that DataType is the same as source DataType2 + static_assert(std::is_same_v<std::remove_const_t<DataType>, std::remove_const_t<DataType2>>, + "Cannot assign ItemValue of different type"); + // ensures that const is not lost through copy + static_assert(((std::is_const_v<DataType2> and std::is_const_v<DataType>) + or not std::is_const_v<DataType2>), + "Cannot assign ItemValue of const to ItemValue of non-const"); + + Assert((m_values.size() == values.size()), + "Cannot assign an array of values of a different size\n"); + + Assert ((values.size() == 0) or this->isBuilt(), + "Cannot assign array of values to a non-built ItemValue\n"); + + m_values = values; + + return *this; + } + + template <typename DataType2, + typename ConnectivityPtr2> + PASTIS_INLINE + ItemValue& + operator=(const ItemValue<DataType2, item_type, ConnectivityPtr2>& value_per_item) noexcept { // ensures that DataType is the same as source DataType2 - static_assert(std::is_same<std::remove_const_t<DataType>, std::remove_const_t<DataType2>>(), + static_assert(std::is_same_v<std::remove_const_t<DataType>, std::remove_const_t<DataType2>>, "Cannot assign ItemValue of different type"); // ensures that const is not lost through copy - static_assert(((std::is_const<DataType2>() and std::is_const<DataType>()) - or not std::is_const<DataType2>()), - "Cannot assign ItemValue of const to ItemValue of non-const"); + static_assert(((std::is_const_v<DataType2> and std::is_const_v<DataType>) + or not std::is_const_v<DataType2>), + "Cannot assign ItemValue of const to ItemValue of non-const"); m_values = value_per_item.m_values; - m_is_built = value_per_item.m_is_built; + if constexpr (std::is_same_v<ConnectivityPtr, ConnectivitySharedPtr> and + std::is_same_v<ConnectivityPtr2, ConnectivityWeakPtr>) { + m_connectivity_ptr = value_per_item.m_connectivity_ptr.lock(); + } else { + m_connectivity_ptr = value_per_item.m_connectivity_ptr; + } return *this; } - template <typename DataType2> + template <typename DataType2, + typename ConnectivityPtr2> PASTIS_INLINE - ItemValue(const ItemValue<DataType2, item_type>& value_per_item) + ItemValue(const ItemValue<DataType2, item_type, ConnectivityPtr2>& value_per_item) noexcept { this->operator=(value_per_item); } + PASTIS_INLINE ItemValue() = default; - ItemValue(const IConnectivity& connectivity) - : m_is_built{true}, - m_values(connectivity.numberOf<item_type>()) + PASTIS_INLINE + ItemValue(const IConnectivity& connectivity) noexcept + : m_connectivity_ptr{connectivity.shared_ptr()}, + m_values{connectivity.numberOf<item_type>()} { - static_assert(not std::is_const<DataType>(), + static_assert(not std::is_const_v<DataType>, "Cannot allocate ItemValue of const data: only view is supported"); ; } + PASTIS_INLINE ~ItemValue() = default; }; @@ -119,4 +193,22 @@ using FaceValue = ItemValue<DataType, ItemType::face>; template <typename DataType> using CellValue = ItemValue<DataType, ItemType::cell>; +// Weak versions: should not be used outside of Connectivity + +template <typename DataType, + ItemType item_type> +using WeakItemValue = ItemValue<DataType, item_type, std::weak_ptr<const IConnectivity>>; + +template <typename DataType> +using WeakNodeValue = WeakItemValue<DataType, ItemType::node>; + +template <typename DataType> +using WeakEdgeValue = WeakItemValue<DataType, ItemType::edge>; + +template <typename DataType> +using WeakFaceValue = WeakItemValue<DataType, ItemType::face>; + +template <typename DataType> +using WeakCellValue = WeakItemValue<DataType, ItemType::cell>; + #endif // ITEM_VALUE_HPP diff --git a/src/mesh/ItemValueUtils.hpp b/src/mesh/ItemValueUtils.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6c45be4c283a69a729eb7d1cdda25480ba96d2d6 --- /dev/null +++ b/src/mesh/ItemValueUtils.hpp @@ -0,0 +1,311 @@ +#ifndef ITEM_VALUE_UTILS_HPP +#define ITEM_VALUE_UTILS_HPP + +#include <Messenger.hpp> +#include <ItemValue.hpp> + +#include <Connectivity.hpp> + +#include <SynchronizerManager.hpp> +#include <Synchronizer.hpp> + +template <typename DataType, + ItemType item_type> +std::remove_const_t<DataType> +min(const ItemValue<DataType, item_type>& item_value) +{ + using ItemValueType = ItemValue<DataType, item_type>; + using data_type = std::remove_const_t<typename ItemValueType::data_type>; + using index_type = typename ItemValueType::index_type; + + const auto& is_owned + = [&] (const IConnectivity& connectivity) { + Assert((connectivity.dimension()>0) and (connectivity.dimension()<=3), + "unexpected connectivity dimension"); + + switch (connectivity.dimension()) { + case 1: { + const auto& connectivity_1d = static_cast<const Connectivity1D&>(connectivity); + return connectivity_1d.isOwned<item_type>(); + break; + } + case 2: { + const auto& connectivity_2d = static_cast<const Connectivity2D&>(connectivity); + return connectivity_2d.isOwned<item_type>(); + break; + } + case 3: { + const auto& connectivity_3d = static_cast<const Connectivity3D&>(connectivity); + return connectivity_3d.isOwned<item_type>(); + break; + } + default: { + perr() << __FILE__ << ':' << __LINE__ << ": unexpected dimension\n"; + std::terminate(); + } + } + } (*item_value.connectivity_ptr()); + + using IsOwnedType = std::remove_reference_t<decltype(is_owned)>; + + class ItemValueMin + { + private: + const ItemValueType& m_item_value; + const IsOwnedType& m_is_owned; + + public: + PASTIS_INLINE + operator data_type() + { + data_type reduced_value; + parallel_reduce(m_item_value.size(), *this, reduced_value); + return reduced_value; + } + + PASTIS_INLINE + void operator()(const index_type& i, data_type& data) const + { + if ((m_is_owned[i]) and (m_item_value[i] < data)) { + data = m_item_value[i]; + } + } + + PASTIS_INLINE + void join(volatile data_type& dst, + const volatile data_type& src) const + { + if (src < dst) { + dst = src; + } + } + + PASTIS_INLINE + void init(data_type& value) const + { + value = std::numeric_limits<data_type>::max(); + } + + PASTIS_INLINE + ItemValueMin(const ItemValueType& item_value, + const IsOwnedType& is_owned) + : m_item_value(item_value), + m_is_owned(is_owned) + { + ; + } + + PASTIS_INLINE + ~ItemValueMin() = default; + }; + + const DataType local_min = ItemValueMin{item_value, is_owned}; + return parallel::allReduceMin(local_min); +} + +template <typename DataType, + ItemType item_type> +std::remove_const_t<DataType> +max(const ItemValue<DataType, item_type>& item_value) +{ + using ItemValueType = ItemValue<DataType, item_type>; + using data_type = std::remove_const_t<typename ItemValueType::data_type>; + using index_type = typename ItemValueType::index_type; + + const auto& is_owned + = [&] (const IConnectivity& connectivity) { + Assert((connectivity.dimension()>0) and (connectivity.dimension()<=3), + "unexpected connectivity dimension"); + + switch (connectivity.dimension()) { + case 1: { + const auto& connectivity_1d = static_cast<const Connectivity1D&>(connectivity); + return connectivity_1d.isOwned<item_type>(); + break; + } + case 2: { + const auto& connectivity_2d = static_cast<const Connectivity2D&>(connectivity); + return connectivity_2d.isOwned<item_type>(); + break; + } + case 3: { + const auto& connectivity_3d = static_cast<const Connectivity3D&>(connectivity); + return connectivity_3d.isOwned<item_type>(); + break; + } + default: { + perr() << __FILE__ << ':' << __LINE__ << ": unexpected dimension\n"; + std::terminate(); + } + } + } (*item_value.connectivity_ptr()); + + using IsOwnedType = std::remove_reference_t<decltype(is_owned)>; + + class ItemValueMax + { + private: + const ItemValueType& m_item_value; + const IsOwnedType& m_is_owned; + + public: + PASTIS_INLINE + operator data_type() + { + data_type reduced_value; + parallel_reduce(m_item_value.size(), *this, reduced_value); + return reduced_value; + } + + PASTIS_INLINE + void operator()(const index_type& i, data_type& data) const + { + if ((m_is_owned[i]) and (m_item_value[i] > data)) { + data = m_item_value[i]; + } + } + + PASTIS_INLINE + void join(volatile data_type& dst, + const volatile data_type& src) const + { + if (src > dst) { + dst = src; + } + } + + PASTIS_INLINE + void init(data_type& value) const + { + value = std::numeric_limits<data_type>::min(); + } + + PASTIS_INLINE + ItemValueMax(const ItemValueType& item_value, + const IsOwnedType& is_owned) + : m_item_value(item_value), + m_is_owned(is_owned) + { + ; + } + + PASTIS_INLINE + ~ItemValueMax() = default; + }; + + const DataType local_max = ItemValueMax{item_value}; + return parallel::allReduceMax(local_max); +} + + +template <typename DataType, + ItemType item_type> +std::remove_const_t<DataType> +sum(const ItemValue<DataType, item_type>& item_value) +{ + using ItemValueType = ItemValue<DataType, item_type>; + using data_type = std::remove_const_t<typename ItemValueType::data_type>; + using index_type = typename ItemValueType::index_type; + + const auto& is_owned + = [&] (const IConnectivity& connectivity) { + Assert((connectivity.dimension()>0) and (connectivity.dimension()<=3), + "unexpected connectivity dimension"); + + switch (connectivity.dimension()) { + case 1: { + const auto& connectivity_1d = static_cast<const Connectivity1D&>(connectivity); + return connectivity_1d.isOwned<item_type>(); + break; + } + case 2: { + const auto& connectivity_2d = static_cast<const Connectivity2D&>(connectivity); + return connectivity_2d.isOwned<item_type>(); + break; + } + case 3: { + const auto& connectivity_3d = static_cast<const Connectivity3D&>(connectivity); + return connectivity_3d.isOwned<item_type>(); + break; + } + default: { + perr() << __FILE__ << ':' << __LINE__ << ": unexpected dimension\n"; + std::terminate(); + } + } + } (*item_value.connectivity_ptr()); + + using IsOwnedType = std::remove_reference_t<decltype(is_owned)>; + + class ItemValueSum + { + private: + const ItemValueType& m_item_value; + const IsOwnedType& m_is_owned; + + public: + PASTIS_INLINE + operator data_type() + { + data_type reduced_value; + parallel_reduce(m_item_value.size(), *this, reduced_value); + return reduced_value; + } + + PASTIS_INLINE + void operator()(const index_type& i, data_type& data) const + { + if (m_is_owned[i]) { + data += m_item_value[i]; + } + } + + PASTIS_INLINE + void join(volatile data_type& dst, + const volatile data_type& src) const + { + dst += src; + } + + PASTIS_INLINE + void init(data_type& value) const + { + if constexpr (std::is_arithmetic_v<data_type>) { + value = 0; + } else { + value = zero; + } + } + + PASTIS_INLINE + ItemValueSum(const ItemValueType& item_value, + const IsOwnedType& is_owned) + : m_item_value(item_value), + m_is_owned(is_owned) + { + ; + } + + PASTIS_INLINE + ~ItemValueSum() = default; + }; + + const DataType local_sum = ItemValueSum{item_value, is_owned}; + return parallel::allReduceSum(local_sum); +} + +template <typename DataType, + ItemType item_type, + typename ConnectivityPtr> +void synchronize(ItemValue<DataType, item_type, ConnectivityPtr>& item_value) +{ + static_assert(not std::is_const_v<DataType>, "cannot synchronize ItemValue of const data"); + if (parallel::size() > 1) { + auto& manager = SynchronizerManager::instance(); + const IConnectivity* connectivity = item_value.connectivity_ptr().get(); + Synchronizer& synchronizer = manager.getConnectivitySynchronizer(connectivity); + synchronizer.synchronize(item_value); + } +} + +#endif // ITEM_VALUE_UTILS_HPP diff --git a/src/mesh/Mesh.hpp b/src/mesh/Mesh.hpp index 1c5924a12bdead13b68f773a16a8537a42234555..b103e22946019e8ff2c50a056e8c86a75fbf4c05 100644 --- a/src/mesh/Mesh.hpp +++ b/src/mesh/Mesh.hpp @@ -4,11 +4,13 @@ #include <ItemValue.hpp> #include <TinyVector.hpp> +#include <CSRGraph.hpp> + #include <memory> struct IMesh { - virtual size_t meshDimension() const = 0; + virtual size_t dimension() const = 0; ~IMesh() = default; }; @@ -18,19 +20,19 @@ class Mesh final : public IMesh public: using Connectivity = ConnectivityType; - static constexpr size_t dimension = ConnectivityType::dimension; - using Rd = TinyVector<dimension>; + static constexpr size_t Dimension = ConnectivityType::Dimension; + using Rd = TinyVector<Dimension>; private: - const std::shared_ptr<Connectivity> m_connectivity; + const std::shared_ptr<const Connectivity> m_connectivity; NodeValue<const Rd> m_xr; NodeValue<Rd> m_mutable_xr; public: PASTIS_INLINE - size_t meshDimension() const + size_t dimension() const { - return dimension; + return Dimension; } PASTIS_INLINE @@ -63,7 +65,6 @@ public: return m_xr; } - [[deprecated("should rework this class: quite ugly")]] PASTIS_INLINE NodeValue<Rd> mutableXr() const { @@ -71,7 +72,7 @@ public: } PASTIS_INLINE - Mesh(const std::shared_ptr<Connectivity>& connectivity, + Mesh(const std::shared_ptr<const Connectivity>& connectivity, NodeValue<Rd>& xr) : m_connectivity{connectivity}, m_xr{xr}, diff --git a/src/mesh/MeshData.hpp b/src/mesh/MeshData.hpp index f9a2aeb575ac5265073d47ce2c4025da39db7d51..3d5cc9b753ae4835e78f70b729b831a1cffc06ba 100644 --- a/src/mesh/MeshData.hpp +++ b/src/mesh/MeshData.hpp @@ -15,12 +15,12 @@ class MeshData public: using MeshType = M; - static constexpr size_t dimension = MeshType::dimension; - static_assert(dimension>0, "dimension must be strictly positive"); + static constexpr size_t Dimension = MeshType::Dimension; + static_assert(Dimension>0, "dimension must be strictly positive"); - using Rd = TinyVector<dimension>; + using Rd = TinyVector<Dimension>; - static constexpr double inv_dimension = 1./dimension; + static constexpr double inv_Dimension = 1./Dimension; private: const MeshType& m_mesh; @@ -33,7 +33,7 @@ class MeshData PASTIS_INLINE void _updateCenter() { // Computes vertices isobarycenter - if constexpr (dimension == 1) { + if constexpr (Dimension == 1) { const NodeValue<const Rd>& xr = m_mesh.xr(); const auto& cell_to_node_matrix @@ -81,17 +81,17 @@ class MeshData for (size_t R=0; R<cell_nodes.size(); ++R) { sum_cjr_xr += (xr[cell_nodes[R]], m_Cjr(j,R)); } - Vj[j] = inv_dimension * sum_cjr_xr; + Vj[j] = inv_Dimension * sum_cjr_xr; }); m_Vj = Vj; } PASTIS_INLINE void _updateCjr() { - if constexpr (dimension == 1) { + if constexpr (Dimension == 1) { // Cjr/njr/ljr are constant overtime } - else if constexpr (dimension == 2) { + else if constexpr (Dimension == 2) { const NodeValue<const Rd>& xr = m_mesh.xr(); const auto& cell_to_node_matrix = m_mesh.connectivity().cellToNodeMatrix(); @@ -125,7 +125,7 @@ class MeshData }); m_njr = njr; } - } else if (dimension ==3) { + } else if (Dimension ==3) { const NodeValue<const Rd>& xr = m_mesh.xr(); NodeValuePerFace<Rd> Nlr(m_mesh.connectivity()); @@ -178,13 +178,11 @@ class MeshData const FaceId& l = cell_faces[L]; const auto& face_nodes = face_to_node_matrix[l]; -#warning should this lambda be replaced by a precomputed correspondance? auto local_node_number_in_cell = [&](const NodeId& node_number) { for (size_t i_node=0; i_node<cell_nodes.size(); ++i_node) { if (node_number == cell_nodes[i_node]) { return i_node; - break; } } return std::numeric_limits<size_t>::max(); @@ -223,35 +221,41 @@ class MeshData m_njr = njr; } } - static_assert((dimension<=3), "only 1d, 2d and 3d are implemented"); + static_assert((Dimension<=3), "only 1d, 2d and 3d are implemented"); } public: + PASTIS_INLINE const MeshType& mesh() const { return m_mesh; } + PASTIS_INLINE const NodeValuePerCell<const Rd>& Cjr() const { return m_Cjr; } + PASTIS_INLINE const NodeValuePerCell<const double>& ljr() const { return m_ljr; } + PASTIS_INLINE const NodeValuePerCell<const Rd>& njr() const { return m_njr; } + PASTIS_INLINE const CellValue<const Rd>& xj() const { return m_xj; } + PASTIS_INLINE const CellValue<const double>& Vj() const { return m_Vj; @@ -267,7 +271,7 @@ class MeshData MeshData(const MeshType& mesh) : m_mesh(mesh) { - if constexpr (dimension==1) { + if constexpr (Dimension==1) { // in 1d Cjr are computed once for all { NodeValuePerCell<Rd> Cjr(m_mesh.connectivity()); diff --git a/src/mesh/MeshNodeBoundary.hpp b/src/mesh/MeshNodeBoundary.hpp index a52a91c99d61c028fdc9f634c896d0ce06aa15dd..ff8ef4a920d0bc2a767b1a0d677c2157a1fe8a9a 100644 --- a/src/mesh/MeshNodeBoundary.hpp +++ b/src/mesh/MeshNodeBoundary.hpp @@ -7,14 +7,15 @@ #include <Kokkos_Vector.hpp> #include <TinyVector.hpp> -#include <RefNodeList.hpp> -#include <RefFaceList.hpp> +#include <RefItemList.hpp> #include <ConnectivityMatrix.hpp> #include <IConnectivity.hpp> #include <iostream> -template <size_t dimension> +#include <Messenger.hpp> + +template <size_t Dimension> class MeshNodeBoundary { protected: @@ -32,11 +33,11 @@ class MeshNodeBoundary MeshNodeBoundary(const MeshType& mesh, const RefFaceList& ref_face_list) { - static_assert(dimension == MeshType::dimension); + static_assert(Dimension == MeshType::Dimension); const auto& face_to_cell_matrix = mesh.connectivity().faceToCellMatrix(); - const Array<const FaceId>& face_list = ref_face_list.faceList(); + const Array<const FaceId>& face_list = ref_face_list.list(); parallel_for(face_list.size(), PASTIS_LAMBDA(const int& l){ const auto& face_cells = face_to_cell_matrix[face_list[l]]; if (face_cells.size()>1) { @@ -47,7 +48,7 @@ class MeshNodeBoundary Kokkos::vector<unsigned int> node_ids; // not enough but should reduce significantly the number of resizing - node_ids.reserve(dimension*face_list.size()); + node_ids.reserve(Dimension*face_list.size()); const auto& face_to_node_matrix = mesh.connectivity().faceToNodeMatrix(); @@ -72,9 +73,9 @@ class MeshNodeBoundary template <typename MeshType> MeshNodeBoundary(const MeshType&, const RefNodeList& ref_node_list) - : m_node_list(ref_node_list.nodeList()) + : m_node_list(ref_node_list.list()) { - static_assert(dimension == MeshType::dimension); + static_assert(Dimension == MeshType::Dimension); } MeshNodeBoundary() = default; @@ -84,12 +85,12 @@ class MeshNodeBoundary }; -template <size_t dimension> +template <size_t Dimension> class MeshFlatNodeBoundary - : public MeshNodeBoundary<dimension> + : public MeshNodeBoundary<Dimension> { public: - using Rd = TinyVector<dimension, double>; + using Rd = TinyVector<Dimension, double>; private: const Rd m_outgoing_normal; @@ -120,7 +121,7 @@ class MeshFlatNodeBoundary template <typename MeshType> MeshFlatNodeBoundary(const MeshType& mesh, const RefFaceList& ref_face_list) - : MeshNodeBoundary<dimension>(mesh, ref_face_list), + : MeshNodeBoundary<Dimension>(mesh, ref_face_list), m_outgoing_normal(_getOutgoingNormal(mesh)) { ; @@ -129,7 +130,7 @@ class MeshFlatNodeBoundary template <typename MeshType> MeshFlatNodeBoundary(const MeshType& mesh, const RefNodeList& ref_node_list) - : MeshNodeBoundary<dimension>(mesh, ref_node_list), + : MeshNodeBoundary<Dimension>(mesh, ref_node_list), m_outgoing_normal(_getOutgoingNormal(mesh)) { ; @@ -149,7 +150,7 @@ _checkBoundaryIsFlat(const TinyVector<2,double>& normal, const TinyVector<2,double>& xmax, const MeshType& mesh) const { - static_assert(MeshType::dimension == 2); + static_assert(MeshType::Dimension == 2); using R2 = TinyVector<2,double>; const R2 origin = 0.5*(xmin+xmax); @@ -173,7 +174,7 @@ TinyVector<1,double> MeshFlatNodeBoundary<1>:: _getNormal(const MeshType&) { - static_assert(MeshType::dimension == 1); + static_assert(MeshType::Dimension == 1); using R = TinyVector<1,double>; if (m_node_list.size() != 1) { @@ -181,7 +182,7 @@ _getNormal(const MeshType&) std::exit(1); } - return R(1); + return R{1}; } template <> @@ -191,7 +192,7 @@ TinyVector<2,double> MeshFlatNodeBoundary<2>:: _getNormal(const MeshType& mesh) { - static_assert(MeshType::dimension == 2); + static_assert(MeshType::Dimension == 2); using R2 = TinyVector<2,double>; const NodeValue<const R2>& xr = mesh.xr(); @@ -212,6 +213,21 @@ _getNormal(const MeshType& mesh) } } + Array<R2> xmin_array = parallel::allGather(xmin); + Array<R2> xmax_array = parallel::allGather(xmax); + for (size_t i=0; i<xmin_array.size(); ++i) { + const R2& x = xmin_array[i]; + if ((x[0]<xmin[0]) or ((x[0] == xmin[0]) and (x[1] < xmin[1]))) { + xmin = x; + } + } + for (size_t i=0; i<xmax_array.size(); ++i) { + const R2& x = xmax_array[i]; + if ((x[0]>xmax[0]) or ((x[0] == xmax[0]) and (x[1] > xmax[1]))) { + xmax = x; + } + } + if (xmin == xmax) { perr() << "xmin==xmax (" << xmin << "==" << xmax << ") unable to compute normal"; std::exit(1); @@ -234,7 +250,7 @@ TinyVector<3,double> MeshFlatNodeBoundary<3>:: _getNormal(const MeshType& mesh) { - static_assert(MeshType::dimension == 3); + static_assert(MeshType::Dimension == 3); using R3 = TinyVector<3,double>; @@ -271,6 +287,37 @@ _getNormal(const MeshType& mesh) zmax = x; } } + Array<R3> xmin_array = parallel::allGather(xmin); + Array<R3> xmax_array = parallel::allGather(xmax); + Array<R3> ymin_array = parallel::allGather(ymin); + Array<R3> ymax_array = parallel::allGather(ymax); + Array<R3> zmin_array = parallel::allGather(zmin); + Array<R3> zmax_array = parallel::allGather(zmax); + + for (size_t i=0; i<xmin_array.size(); ++i) { + const R3& x = xmin_array[i]; + if (x[0] < xmin[0]) { xmin = x; } + } + for (size_t i=0; i<ymin_array.size(); ++i) { + const R3& x = ymin_array[i]; + if (x[1] < ymin[1]) { ymin = x; } + } + for (size_t i=0; i<zmin_array.size(); ++i) { + const R3& x = zmin_array[i]; + if (x[2] < zmin[2]) { zmin = x; } + } + for (size_t i=0; i<xmax_array.size(); ++i) { + const R3& x = xmax_array[i]; + if (x[0] > xmax[0]) { xmax = x; } + } + for (size_t i=0; i<ymax_array.size(); ++i) { + const R3& x = ymax_array[i]; + if (x[1] > ymax[1]) { ymax = x; } + } + for (size_t i=0; i<zmax_array.size(); ++i) { + const R3& x = zmax_array[i]; + if (x[2] > zmax[2]) { zmax = x; } + } const R3 u = xmax-xmin; const R3 v = ymax-ymin; @@ -306,7 +353,6 @@ _getNormal(const MeshType& mesh) normal *= 1./sqrt(normal_l2); -#warning Add flatness test // this->_checkBoundaryIsFlat(normal, xmin, xmax, mesh); return normal; @@ -319,28 +365,41 @@ TinyVector<1,double> MeshFlatNodeBoundary<1>:: _getOutgoingNormal(const MeshType& mesh) { - static_assert(MeshType::dimension == 1); + static_assert(MeshType::Dimension == 1); using R = TinyVector<1,double>; const R normal = this->_getNormal(mesh); - const NodeValue<const R>& xr = mesh.xr(); - const auto& cell_to_node_matrix - = mesh.connectivity().cellToNodeMatrix(); + double max_height = 0; - const auto& node_to_cell_matrix - = mesh.connectivity().nodeToCellMatrix(); + if (m_node_list.size()>0) { + const NodeValue<const R>& xr = mesh.xr(); + const auto& cell_to_node_matrix + = mesh.connectivity().cellToNodeMatrix(); - const NodeId r0 = m_node_list[0]; - const CellId j0 = node_to_cell_matrix[r0][0]; - const auto& j0_nodes = cell_to_node_matrix[j0]; - double max_height = 0; - for (size_t r=0; r<j0_nodes.size(); ++r) { - const double height = (xr[j0_nodes[r]]-xr[r0], normal); + const auto& node_to_cell_matrix + = mesh.connectivity().nodeToCellMatrix(); + + const NodeId r0 = m_node_list[0]; + const CellId j0 = node_to_cell_matrix[r0][0]; + const auto& j0_nodes = cell_to_node_matrix[j0]; + + for (size_t r=0; r<j0_nodes.size(); ++r) { + const double height = (xr[j0_nodes[r]]-xr[r0], normal); + if (std::abs(height) > std::abs(max_height)) { + max_height = height; + } + } + } + + Array<double> max_height_array = parallel::allGather(max_height); + for (size_t i=0; i<max_height_array.size(); ++i) { + const double height = max_height_array[i]; if (std::abs(height) > std::abs(max_height)) { max_height = height; } } + if (max_height > 0) { return -normal; } else { @@ -355,28 +414,40 @@ TinyVector<2,double> MeshFlatNodeBoundary<2>:: _getOutgoingNormal(const MeshType& mesh) { - static_assert(MeshType::dimension == 2); + static_assert(MeshType::Dimension == 2); using R2 = TinyVector<2,double>; const R2 normal = this->_getNormal(mesh); - const NodeValue<const R2>& xr = mesh.xr(); - const auto& cell_to_node_matrix - = mesh.connectivity().cellToNodeMatrix(); + double max_height = 0; - const auto& node_to_cell_matrix - = mesh.connectivity().nodeToCellMatrix(); + if (m_node_list.size()>0) { + const NodeValue<const R2>& xr = mesh.xr(); + const auto& cell_to_node_matrix + = mesh.connectivity().cellToNodeMatrix(); + + const auto& node_to_cell_matrix + = mesh.connectivity().nodeToCellMatrix(); + + const NodeId r0 = m_node_list[0]; + const CellId j0 = node_to_cell_matrix[r0][0]; + const auto& j0_nodes = cell_to_node_matrix[j0]; + for (size_t r=0; r<j0_nodes.size(); ++r) { + const double height = (xr[j0_nodes[r]]-xr[r0], normal); + if (std::abs(height) > std::abs(max_height)) { + max_height = height; + } + } + } - const NodeId r0 = m_node_list[0]; - const CellId j0 = node_to_cell_matrix[r0][0]; - const auto& j0_nodes = cell_to_node_matrix[j0]; - double max_height = 0; - for (size_t r=0; r<j0_nodes.size(); ++r) { - const double height = (xr[j0_nodes[r]]-xr[r0], normal); + Array<double> max_height_array = parallel::allGather(max_height); + for (size_t i=0; i<max_height_array.size(); ++i) { + const double height = max_height_array[i]; if (std::abs(height) > std::abs(max_height)) { max_height = height; } } + if (max_height > 0) { return -normal; } else { @@ -391,28 +462,42 @@ TinyVector<3,double> MeshFlatNodeBoundary<3>:: _getOutgoingNormal(const MeshType& mesh) { - static_assert(MeshType::dimension == 3); + static_assert(MeshType::Dimension == 3); using R3 = TinyVector<3,double>; const R3 normal = this->_getNormal(mesh); - const NodeValue<const R3>& xr = mesh.xr(); - const auto& cell_to_node_matrix - = mesh.connectivity().cellToNodeMatrix(); + double max_height = 0; - const auto& node_to_cell_matrix - = mesh.connectivity().nodeToCellMatrix(); + if (m_node_list.size()>0) { + const NodeValue<const R3>& xr = mesh.xr(); + const auto& cell_to_node_matrix + = mesh.connectivity().cellToNodeMatrix(); - const NodeId r0 = m_node_list[0]; - const CellId j0 = node_to_cell_matrix[r0][0]; - const auto& j0_nodes = cell_to_node_matrix[j0]; - double max_height = 0; - for (size_t r=0; r<j0_nodes.size(); ++r) { - const double height = (xr[j0_nodes[r]]-xr[r0], normal); + const auto& node_to_cell_matrix + = mesh.connectivity().nodeToCellMatrix(); + + const NodeId r0 = m_node_list[0]; + const CellId j0 = node_to_cell_matrix[r0][0]; + const auto& j0_nodes = cell_to_node_matrix[j0]; + + for (size_t r=0; r<j0_nodes.size(); ++r) { + const double height = (xr[j0_nodes[r]]-xr[r0], normal); + if (std::abs(height) > std::abs(max_height)) { + max_height = height; + } + } + + } + + Array<double> max_height_array = parallel::allGather(max_height); + for (size_t i=0; i<max_height_array.size(); ++i) { + const double height = max_height_array[i]; if (std::abs(height) > std::abs(max_height)) { max_height = height; } } + if (max_height > 0) { return -normal; } else { diff --git a/src/mesh/RefFaceList.hpp b/src/mesh/RefFaceList.hpp deleted file mode 100644 index ab66943936c091735c404b287d587a15d81a1a9f..0000000000000000000000000000000000000000 --- a/src/mesh/RefFaceList.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef REF_FACE_LIST_HPP -#define REF_FACE_LIST_HPP - -#include <Array.hpp> -#include <RefId.hpp> - -class RefFaceList -{ - private: - const RefId m_ref_id; - const Array<const FaceId> m_face_id_list; - - public: - const RefId& refId() const - { - return m_ref_id; - } - - const Array<const FaceId>& faceList() const - { - return m_face_id_list; - } - - RefFaceList(const RefId& ref_id, - const Array<const FaceId>& face_id_list) - : m_ref_id(ref_id), - m_face_id_list(face_id_list) - { - ; - } - - RefFaceList& operator=(const RefFaceList&) = default; - RefFaceList& operator=(RefFaceList&&) = default; - - RefFaceList() = default; - RefFaceList(const RefFaceList&) = default; - ~RefFaceList() = default; -}; - -#endif // REF_FACE_LIST_HPP diff --git a/src/mesh/RefId.hpp b/src/mesh/RefId.hpp index 775b1a7132d822c52dc6b48fb6bbc528216d58cb..dbd4f1a1b113b885b1040fdb3a0372f881e56a0c 100644 --- a/src/mesh/RefId.hpp +++ b/src/mesh/RefId.hpp @@ -6,14 +6,18 @@ class RefId { + public: + using TagNumberType = unsigned int; + using TagNameType = std::string; + private: - unsigned int m_tag_number; - std::string m_tag_name; + TagNumberType m_tag_number; + TagNameType m_tag_name; public: friend std::ostream& operator<<(std::ostream& os, const RefId& ref_id) { - if (ref_id.m_tag_name.size()>0) { + if (std::to_string(ref_id.m_tag_number) != ref_id.m_tag_name) { os << ref_id.m_tag_name << '(' << ref_id.m_tag_number << ')'; } else { os << ref_id.m_tag_number; @@ -34,12 +38,12 @@ class RefId (m_tag_name<ref_id.m_tag_name))); } - const unsigned int& tagNumber() const + TagNumberType tagNumber() const { return m_tag_number; } - const std::string& tagName() const + const TagNameType& tagName() const { return m_tag_name; } @@ -49,16 +53,18 @@ class RefId RefId() = default; RefId(const RefId&) = default; RefId(RefId&&) = default; - explicit RefId(const unsigned int& tag_number, - const std::string& tag_name) + + explicit RefId(const TagNumberType& tag_number, + const TagNameType& tag_name) : m_tag_number(tag_number), m_tag_name(tag_name) { ; } - explicit RefId(const unsigned int& tag_number) - : m_tag_number(tag_number) + explicit RefId(const TagNumberType& tag_number) + : m_tag_number(tag_number), + m_tag_name(std::to_string(tag_number)) { ; } diff --git a/src/mesh/RefItemList.hpp b/src/mesh/RefItemList.hpp new file mode 100644 index 0000000000000000000000000000000000000000..dea69f7ec79e2ca139eeb9f01600e687f6bea3d4 --- /dev/null +++ b/src/mesh/RefItemList.hpp @@ -0,0 +1,50 @@ +#ifndef REF_ITEM_LIST_HPP +#define REF_ITEM_LIST_HPP + +#include <Array.hpp> +#include <RefId.hpp> +#include <ItemId.hpp> + +template <ItemType item_type> +class RefItemList +{ + public: + using ItemId = ItemIdT<item_type>; + + private: + RefId m_ref_id; + Array<const ItemId> m_item_id_list; + + public: + const RefId& refId() const + { + return m_ref_id; + } + + const Array<const ItemId>& list() const + { + return m_item_id_list; + } + + RefItemList(const RefId& ref_id, + const Array<const ItemId>& item_id_list) + : m_ref_id(ref_id), + m_item_id_list(item_id_list) + { + ; + } + + RefItemList& operator=(const RefItemList&) = default; + RefItemList& operator=(RefItemList&&) = default; + + RefItemList() = default; + RefItemList(const RefItemList&) = default; + ~RefItemList() = default; +}; + +using RefNodeList = RefItemList<ItemType::node>; +using RefEdgeList = RefItemList<ItemType::edge>; +using RefFaceList = RefItemList<ItemType::face>; +using RefCellList = RefItemList<ItemType::cell>; + +#endif // REF_ITEM_LIST_HPP diff --git a/src/mesh/RefNodeList.hpp b/src/mesh/RefNodeList.hpp deleted file mode 100644 index 462d23bb3d35a6384fcd5176d0584419a586abc3..0000000000000000000000000000000000000000 --- a/src/mesh/RefNodeList.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef REF_NODE_LIST_HPP -#define REF_NODE_LIST_HPP - -#include <Array.hpp> -#include <RefId.hpp> - -class RefNodeList -{ - private: - RefId m_ref_id; - Array<const NodeId> m_node_id_list; - - public: - const RefId& refId() const - { - return m_ref_id; - } - - const Array<const NodeId>& nodeList() const - { - return m_node_id_list; - } - - RefNodeList(const RefId& ref_id, - const Array<const NodeId>& node_id_list) - : m_ref_id(ref_id), - m_node_id_list(node_id_list) - { - ; - } - - RefNodeList& operator=(const RefNodeList&) = default; - RefNodeList& operator=(RefNodeList&&) = default; - - RefNodeList() = default; - RefNodeList(const RefNodeList&) = default; - ~RefNodeList() = default; -}; - -#endif // REF_NODE_LIST_HPP diff --git a/src/mesh/SubItemValuePerItem.hpp b/src/mesh/SubItemValuePerItem.hpp index edb73dd5cb216c1b5bf9df339204447cd062cb17..450554edfc2fc0d3729f9300bb0d5dc516f91f72 100644 --- a/src/mesh/SubItemValuePerItem.hpp +++ b/src/mesh/SubItemValuePerItem.hpp @@ -6,47 +6,58 @@ #include <PastisAssert.hpp> #include <Array.hpp> -#include <ItemType.hpp> #include <ItemId.hpp> #include <ConnectivityMatrix.hpp> #include <IConnectivity.hpp> -template <typename DataType, - ItemType sub_item_type, - ItemType item_type, - typename Allowed=void> -class SubItemValuePerItem; +#include <ItemType.hpp> +#include <ItemOfItemType.hpp> + +#include <memory> template <typename DataType, - ItemType sub_item_type, - ItemType item_type> -class SubItemValuePerItem<DataType, - sub_item_type, - item_type, - std::enable_if_t<sub_item_type != item_type>> + typename ItemOfItem, + typename ConnectivityPtr = std::shared_ptr<const IConnectivity>> +class SubItemValuePerItem { public: - static const ItemType item_t{item_type}; - static const ItemType sub_item_t{sub_item_type}; + static constexpr ItemType item_type{ItemOfItem::item_type}; + static constexpr ItemType sub_item_type{ItemOfItem::sub_item_type}; + using ItemOfItemType = ItemOfItem; using data_type = DataType; using ItemId = ItemIdT<item_type>; using index_type = ItemId; private: - bool m_is_built{false}; + using ConnectivitySharedPtr = std::shared_ptr<const IConnectivity>; + using ConnectivityWeakPtr = std::weak_ptr<const IConnectivity>; + + static_assert(std::is_same_v<ConnectivityPtr, ConnectivitySharedPtr> or + std::is_same_v<ConnectivityPtr, ConnectivityWeakPtr>); + + ConnectivityPtr m_connectivity_ptr; typename ConnectivityMatrix::HostRowType m_host_row_map; Array<DataType> m_values; - // Allows const version to access our data + // Allow const std:shared_ptr version to access our data friend SubItemValuePerItem<std::add_const_t<DataType>, - sub_item_type, - item_type>; + ItemOfItem, + ConnectivitySharedPtr>; + + // Allow const std:weak_ptr version to access our data + friend SubItemValuePerItem<std::add_const_t<DataType>, + ItemOfItem, + ConnectivityWeakPtr>; public: + using ToShared = SubItemValuePerItem<DataType, + ItemOfItem, + ConnectivitySharedPtr>; + class SubView { public: @@ -72,7 +83,7 @@ class SubItemValuePerItem<DataType, } PASTIS_INLINE - const size_t& size() const + size_t size() const noexcept { return m_size; } @@ -80,7 +91,7 @@ class SubItemValuePerItem<DataType, SubView(const SubView&) = delete; PASTIS_INLINE - SubView(SubView&&) = default; + SubView(SubView&&) noexcept = default; PASTIS_INLINE SubView(const Array<DataType>& values, @@ -94,10 +105,10 @@ class SubItemValuePerItem<DataType, } }; - PASTIS_FORCEINLINE - const bool& isBuilt() const + PASTIS_INLINE + bool isBuilt() const noexcept { - return m_is_built; + return m_connectivity_ptr.use_count() != 0; } // Following Kokkos logic, these classes are view and const view does allow @@ -105,7 +116,7 @@ class SubItemValuePerItem<DataType, PASTIS_FORCEINLINE DataType& operator()(const ItemId& j, const size_t& r) const noexcept(NO_ASSERT) { - Assert(m_is_built); + Assert(this->isBuilt()); return m_values[m_host_row_map(size_t{j})+r]; } @@ -113,15 +124,16 @@ class SubItemValuePerItem<DataType, PASTIS_FORCEINLINE DataType& operator()(const IndexType& j, const size_t& r) const noexcept(NO_ASSERT) { - static_assert(std::is_same<IndexType, size_t>(), + static_assert(std::is_same_v<IndexType, ItemId>, "SubItemValuePerItem indexed by ItemId"); + Assert(this->isBuilt()); return m_values[m_host_row_map(size_t{j})+r]; } PASTIS_INLINE size_t numberOfValues() const noexcept(NO_ASSERT) { - Assert(m_is_built); + Assert(this->isBuilt()); return m_values.size(); } @@ -130,22 +142,23 @@ class SubItemValuePerItem<DataType, PASTIS_FORCEINLINE DataType& operator[](const size_t& i) const noexcept(NO_ASSERT) { - Assert(m_is_built); + Assert(this->isBuilt()); return m_values[i]; } template <typename IndexType> DataType& operator[](const IndexType& i) const noexcept(NO_ASSERT) { - static_assert(std::is_same<IndexType, size_t>(), + static_assert(std::is_same_v<IndexType, size_t>, "Access to SubItemValuePerItem's array must be indexed by size_t"); + Assert(this->isBuilt()); return m_values[i]; } PASTIS_INLINE size_t numberOfItems() const noexcept(NO_ASSERT) { - Assert(m_is_built); + Assert(this->isBuilt()); Assert(m_host_row_map.extent(0) != 0); return m_host_row_map.extent(0); } @@ -153,14 +166,14 @@ class SubItemValuePerItem<DataType, PASTIS_INLINE size_t numberOfSubValues(const size_t& i_cell) const noexcept(NO_ASSERT) { - Assert(m_is_built); + Assert(this->isBuilt()); return m_host_row_map(i_cell+1)-m_host_row_map(i_cell); } PASTIS_INLINE SubView itemValues(const size_t& i_cell) noexcept(NO_ASSERT) { - Assert(m_is_built); + Assert(this->isBuilt()); const auto& cell_begin = m_host_row_map(i_cell); const auto& cell_end = m_host_row_map(i_cell+1); return SubView(m_values, cell_begin, cell_end); @@ -171,45 +184,52 @@ class SubItemValuePerItem<DataType, PASTIS_INLINE SubView itemValues(const size_t& i_cell) const noexcept(NO_ASSERT) { - Assert(m_is_built); + Assert(this->isBuilt()); const auto& cell_begin = m_host_row_map(i_cell); const auto& cell_end = m_host_row_map(i_cell+1); return SubView(m_values, cell_begin, cell_end); } - template <typename DataType2> + template <typename DataType2, + typename ConnectivityPtr2> PASTIS_INLINE SubItemValuePerItem& - operator=(const SubItemValuePerItem<DataType2, sub_item_type, item_type>& sub_item_value_per_item) + operator=(const SubItemValuePerItem<DataType2, ItemOfItem, ConnectivityPtr2>& sub_item_value_per_item) noexcept { // ensures that DataType is the same as source DataType2 - static_assert(std::is_same<std::remove_const_t<DataType>, std::remove_const_t<DataType2>>(), + static_assert(std::is_same_v<std::remove_const_t<DataType>, std::remove_const_t<DataType2>>, "Cannot assign SubItemValuePerItem of different type"); // ensures that const is not lost through copy - static_assert(((std::is_const<DataType2>() and std::is_const<DataType>()) - or not std::is_const<DataType2>()), + static_assert(((std::is_const_v<DataType2> and std::is_const_v<DataType>) + or not std::is_const_v<DataType2>), "Cannot assign SubItemValuePerItem of const data to SubItemValuePerItem of non-const data"); m_host_row_map = sub_item_value_per_item.m_host_row_map; m_values = sub_item_value_per_item.m_values; - m_is_built = sub_item_value_per_item.m_is_built; + if constexpr (std::is_same_v<ConnectivityPtr, ConnectivitySharedPtr> and + std::is_same_v<ConnectivityPtr2, ConnectivityWeakPtr>) { + m_connectivity_ptr = sub_item_value_per_item.m_connectivity_ptr.lock(); + } else { + m_connectivity_ptr = sub_item_value_per_item.m_connectivity_ptr; + } return *this; } - template <typename DataType2> + template <typename DataType2, + typename ConnectivityPtr2> PASTIS_INLINE - SubItemValuePerItem(const SubItemValuePerItem<DataType2, sub_item_type, item_type>& sub_item_value_per_item) + SubItemValuePerItem(const SubItemValuePerItem<DataType2, ItemOfItem, ConnectivityPtr2>& sub_item_value_per_item) noexcept { this->operator=(sub_item_value_per_item); } SubItemValuePerItem() = default; - SubItemValuePerItem(const IConnectivity& connectivity) - : m_is_built{true} + SubItemValuePerItem(const IConnectivity& connectivity) noexcept + : m_connectivity_ptr{connectivity.shared_ptr()} { - static_assert(not std::is_const<DataType>(), + static_assert(not std::is_const_v<DataType>, "Cannot allocate SubItemValuePerItem of const data: only view is supported"); ; ConnectivityMatrix connectivity_matrix @@ -225,45 +245,94 @@ class SubItemValuePerItem<DataType, // Item values at nodes template <typename DataType> -using NodeValuePerEdge = SubItemValuePerItem<DataType, ItemType::node, ItemType::edge>; +using NodeValuePerEdge = SubItemValuePerItem<DataType, NodeOfEdge>; + +template <typename DataType> +using NodeValuePerFace = SubItemValuePerItem<DataType, NodeOfFace>; + +template <typename DataType> +using NodeValuePerCell = SubItemValuePerItem<DataType, NodeOfCell>; + +// Item values at edges + +template <typename DataType> +using EdgeValuePerNode = SubItemValuePerItem<DataType, EdgeOfNode>; + +template <typename DataType> +using EdgeValuePerFace = SubItemValuePerItem<DataType, EdgeOfFace>; + +template <typename DataType> +using EdgeValuePerCell = SubItemValuePerItem<DataType, EdgeOfCell>; + +// Item values at faces + +template <typename DataType> +using FaceValuePerNode = SubItemValuePerItem<DataType, FaceOfNode>; + +template <typename DataType> +using FaceValuePerEdge = SubItemValuePerItem<DataType, FaceOfEdge>; + +template <typename DataType> +using FaceValuePerCell = SubItemValuePerItem<DataType, FaceOfCell>; + +// Item values at cells + +template <typename DataType> +using CellValuePerNode = SubItemValuePerItem<DataType, CellOfNode>; + +template <typename DataType> +using CellValuePerEdge = SubItemValuePerItem<DataType, CellOfEdge>; + +template <typename DataType> +using CellValuePerFace = SubItemValuePerItem<DataType, CellOfFace>; + +// Weak versions: should not be used outside of Connectivity +// Item values at nodes + +template <typename DataType, + typename ItemOfItem> +using WeakSubItemValuePerItem = SubItemValuePerItem<DataType, ItemOfItem, std::weak_ptr<const IConnectivity>>; + +template <typename DataType> +using WeakNodeValuePerEdge = WeakSubItemValuePerItem<DataType, NodeOfEdge>; template <typename DataType> -using NodeValuePerFace = SubItemValuePerItem<DataType, ItemType::node, ItemType::face>; +using WeakNodeValuePerFace = WeakSubItemValuePerItem<DataType, NodeOfFace>; template <typename DataType> -using NodeValuePerCell = SubItemValuePerItem<DataType, ItemType::node, ItemType::cell>; +using WeakNodeValuePerCell = WeakSubItemValuePerItem<DataType, NodeOfCell>; // Item values at edges template <typename DataType> -using EdgeValuePerNode = SubItemValuePerItem<DataType, ItemType::edge, ItemType::node>; +using WeakEdgeValuePerNode = WeakSubItemValuePerItem<DataType, EdgeOfNode>; template <typename DataType> -using EdgeValuePerFace = SubItemValuePerItem<DataType, ItemType::edge, ItemType::face>; +using WeakEdgeValuePerFace = WeakSubItemValuePerItem<DataType, EdgeOfFace>; template <typename DataType> -using EdgeValuePerCell = SubItemValuePerItem<DataType, ItemType::edge, ItemType::cell>; +using WeakEdgeValuePerCell = WeakSubItemValuePerItem<DataType, EdgeOfCell>; // Item values at faces template <typename DataType> -using FaceValuePerNode = SubItemValuePerItem<DataType, ItemType::face, ItemType::node>; +using WeakFaceValuePerNode = WeakSubItemValuePerItem<DataType, FaceOfNode>; template <typename DataType> -using FaceValuePerEdge = SubItemValuePerItem<DataType, ItemType::face, ItemType::edge>; +using WeakFaceValuePerEdge = WeakSubItemValuePerItem<DataType, FaceOfEdge>; template <typename DataType> -using FaceValuePerCell = SubItemValuePerItem<DataType, ItemType::face, ItemType::cell>; +using WeakFaceValuePerCell = WeakSubItemValuePerItem<DataType, FaceOfCell>; // Item values at cells template <typename DataType> -using CellValuePerNode = SubItemValuePerItem<DataType, ItemType::cell, ItemType::node>; +using WeakCellValuePerNode = WeakSubItemValuePerItem<DataType, CellOfNode>; template <typename DataType> -using CellValuePerEdge = SubItemValuePerItem<DataType, ItemType::cell, ItemType::edge>; +using WeakCellValuePerEdge = WeakSubItemValuePerItem<DataType, CellOfEdge>; template <typename DataType> -using CellValuePerFace = SubItemValuePerItem<DataType, ItemType::cell, ItemType::face>; +using WeakCellValuePerFace = WeakSubItemValuePerItem<DataType, CellOfFace>; #endif // SUBITEM_VALUE_PER_ITEM_HPP diff --git a/src/mesh/Synchronizer.hpp b/src/mesh/Synchronizer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a44a17ed3738625d7cfbef3c30c1fb45a3d62621 --- /dev/null +++ b/src/mesh/Synchronizer.hpp @@ -0,0 +1,215 @@ +#ifndef SYNCHRONIZER_HPP +#define SYNCHRONIZER_HPP + +#include <ItemValue.hpp> +#include <Connectivity.hpp> + +#include <map> + +class Synchronizer +{ + template <ItemType item_type> + using ExchangeItemTypeInfo = std::vector<Array<const ItemIdT<item_type>>>; + + ExchangeItemTypeInfo<ItemType::cell> m_requested_cell_info; + ExchangeItemTypeInfo<ItemType::cell> m_provided_cell_info; + + ExchangeItemTypeInfo<ItemType::face> m_requested_face_info; + ExchangeItemTypeInfo<ItemType::face> m_provided_face_info; + + ExchangeItemTypeInfo<ItemType::edge> m_requested_edge_info; + ExchangeItemTypeInfo<ItemType::edge> m_provided_edge_info; + + ExchangeItemTypeInfo<ItemType::node> m_requested_node_info; + ExchangeItemTypeInfo<ItemType::node> m_provided_node_info; + + template <ItemType item_type> + PASTIS_INLINE + constexpr auto& _getRequestedItemInfo() + { + if constexpr (item_type == ItemType::cell) { + return m_requested_cell_info; + } else if constexpr (item_type == ItemType::face) { + return m_requested_face_info; + } else if constexpr (item_type == ItemType::edge) { + return m_requested_edge_info; + } else if constexpr (item_type == ItemType::node) { + return m_requested_node_info; + } + } + + template <ItemType item_type> + PASTIS_INLINE + constexpr auto& _getProvidedItemInfo() + { + if constexpr (item_type == ItemType::cell) { + return m_provided_cell_info; + } else if constexpr (item_type == ItemType::face) { + return m_provided_face_info; + } else if constexpr (item_type == ItemType::edge) { + return m_provided_edge_info; + } else if constexpr (item_type == ItemType::node) { + return m_provided_node_info; + } + } + + template <typename ConnectivityType, + ItemType item_type> + void _buildSynchronizeInfo(const ConnectivityType& connectivity) + { + const auto& item_owner = connectivity.template owner<item_type>(); + using ItemId = ItemIdT<item_type>; + + auto& requested_item_info = this->_getRequestedItemInfo<item_type>(); + requested_item_info + = [&] () { + std::vector<std::vector<ItemId>> requested_item_vector_info(parallel::size()); + for (ItemId item_id=0; item_id<item_owner.size(); ++item_id) { + if (const size_t owner = item_owner[item_id]; owner != parallel::rank()) { + requested_item_vector_info[owner].emplace_back(item_id); + } + } + std::vector<Array<const ItemId>> requested_item_info(parallel::size()); + for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) { + const auto& requested_item_vector = requested_item_vector_info[i_rank]; + requested_item_info[i_rank] = convert_to_array(requested_item_vector); + } + return requested_item_info; + }(); + + Array<unsigned int> local_number_of_requested_values(parallel::size()); + for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) { + local_number_of_requested_values[i_rank] = requested_item_info[i_rank].size(); + } + + Array<unsigned int> local_number_of_values_to_send + = parallel::allToAll(local_number_of_requested_values); + + std::vector<Array<const int>> requested_item_number_list_by_proc(parallel::size()); + const auto& item_number = connectivity.template number<item_type>(); + for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) { + const auto& requested_item_info_from_rank = requested_item_info[i_rank]; + Array<int> item_number_list{requested_item_info_from_rank.size()}; + parallel_for (requested_item_info_from_rank.size(), PASTIS_LAMBDA(size_t i_item) { + item_number_list[i_item] = item_number[requested_item_info_from_rank[i_item]]; + }); + requested_item_number_list_by_proc[i_rank] = item_number_list; + } + + + std::vector<Array<int>> provided_item_number_list_by_rank(parallel::size()); + for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) { + provided_item_number_list_by_rank[i_rank] = Array<int>{local_number_of_values_to_send[i_rank]}; + } + + parallel::exchange(requested_item_number_list_by_proc, provided_item_number_list_by_rank); + + std::map<int, ItemId> item_number_to_id_correspondance; + for (ItemId item_id=0; item_id<item_number.size(); ++item_id) { + item_number_to_id_correspondance[item_number[item_id]] = item_id; + } + + auto& provided_item_info = this->_getProvidedItemInfo<item_type>(); + provided_item_info + = [&] () { + std::vector<Array<const ItemId>> provided_item_info(parallel::size()); + for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) { + Array<ItemId> provided_item_id_to_rank{local_number_of_values_to_send[i_rank]}; + const Array<int>& provided_item_number_to_rank = provided_item_number_list_by_rank[i_rank]; + for (size_t i=0; i<provided_item_number_to_rank.size(); ++i) { + provided_item_id_to_rank[i] + = item_number_to_id_correspondance.find(provided_item_number_to_rank[i])->second; + } + provided_item_info[i_rank] = provided_item_id_to_rank; + } + return provided_item_info; + } (); + } + + template <typename ConnectivityType, + typename DataType, + ItemType item_type, + typename ConnectivityPtr> + PASTIS_INLINE + void _synchronize(const ConnectivityType& connectivity, + ItemValue<DataType, item_type, ConnectivityPtr>& item_value) + { + static_assert(not std::is_abstract_v<ConnectivityType>, + "_synchronize must be called on a concrete connectivity"); + + using ItemId = ItemIdT<item_type>; + + const auto& provided_item_info = this->_getProvidedItemInfo<item_type>(); + const auto& requested_item_info = this->_getRequestedItemInfo<item_type>(); + + Assert(requested_item_info.size() == provided_item_info.size()); + + if (provided_item_info.size() == 0) { + this->_buildSynchronizeInfo<ConnectivityType, item_type>(connectivity); + } + + std::vector<Array<const DataType>> provided_data_list(parallel::size()); + for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) { + const Array<const ItemId>& provided_item_info_to_rank = provided_item_info[i_rank]; + Array<DataType> provided_data{provided_item_info_to_rank.size()}; + parallel_for(provided_item_info_to_rank.size(), PASTIS_LAMBDA(size_t i) { + provided_data[i] = item_value[provided_item_info_to_rank[i]]; + }); + provided_data_list[i_rank] = provided_data; + } + + std::vector<Array<DataType>> requested_data_list(parallel::size()); + for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) { + const auto& requested_item_info_from_rank = requested_item_info[i_rank]; + requested_data_list[i_rank] = Array<DataType>{requested_item_info_from_rank.size()}; + } + + parallel::exchange(provided_data_list, requested_data_list); + + for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) { + const auto& requested_item_info_from_rank = requested_item_info[i_rank]; + const auto& requested_data = requested_data_list[i_rank]; + parallel_for(requested_item_info_from_rank.size(), PASTIS_LAMBDA(size_t i) { + item_value[requested_item_info_from_rank[i]] = requested_data[i]; + }); + } + } + + public: + template <typename DataType, + ItemType item_type, + typename ConnectivityPtr> + PASTIS_INLINE + void synchronize(ItemValue<DataType, item_type, ConnectivityPtr>& item_value) + { + Assert(item_value.connectivity_ptr().use_count()>0, "No connectivity is associated to this ItemValue"); + const IConnectivity& connectivity = *item_value.connectivity_ptr(); + + switch (connectivity.dimension()) { + case 1: { + this->_synchronize(static_cast<const Connectivity1D&>(connectivity), item_value); + break; + } + case 2: { + this->_synchronize(static_cast<const Connectivity2D&>(connectivity), item_value); + break; + } + case 3: { + this->_synchronize(static_cast<const Connectivity3D&>(connectivity), item_value); + break; + } + default: { + perr() << __FILE__ << ':' << __LINE__ << ": unexpected dimension\n"; + std::terminate(); + } + } + } + + PASTIS_INLINE + Synchronizer() + { + ; + } +}; + +#endif // SYNCHRONIZER_HPP diff --git a/src/mesh/SynchronizerManager.cpp b/src/mesh/SynchronizerManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1db720e396a40e0d15c952c5fefbbfd7f5b7597e --- /dev/null +++ b/src/mesh/SynchronizerManager.cpp @@ -0,0 +1,53 @@ +#include <SynchronizerManager.hpp> +#include <PastisAssert.hpp> + +#include <Messenger.hpp> +#include <Synchronizer.hpp> + +SynchronizerManager* +SynchronizerManager::m_instance{nullptr}; + +SynchronizerManager:: +~SynchronizerManager() +{ + if (m_connectivity_synchronizer_map.size() > 0) + { + perr() << __FILE__ << ':' << __LINE__ + << ": warning: some connectivities are still registered\n";; + } +} + +void SynchronizerManager::create() +{ + Assert(m_instance == nullptr, "SynchronizerManager is already created"); + m_instance = new SynchronizerManager; +} + +void SynchronizerManager::destroy() +{ + Assert(m_instance != nullptr, "SynchronizerManager was not created!"); + delete m_instance; + m_instance = nullptr; +} + + +void +SynchronizerManager:: +deleteConnectivitySynchronizer(const IConnectivity* connectivity) +{ + m_connectivity_synchronizer_map.erase(connectivity); +} + +Synchronizer& +SynchronizerManager:: +getConnectivitySynchronizer(const IConnectivity* connectivity) +{ + if (auto connectivity_synchronizer = m_connectivity_synchronizer_map.find(connectivity); + connectivity_synchronizer != m_connectivity_synchronizer_map.end()) { + return (*connectivity_synchronizer->second); + } else { + std::shared_ptr synchronizer = std::make_shared<Synchronizer>(); + m_connectivity_synchronizer_map[connectivity] = synchronizer; + return *synchronizer; + } +} diff --git a/src/mesh/SynchronizerManager.hpp b/src/mesh/SynchronizerManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3fdf57d9b97cebb782a0c7c4ab1cbf5ec1fc4904 --- /dev/null +++ b/src/mesh/SynchronizerManager.hpp @@ -0,0 +1,37 @@ +#ifndef SYNCHRONIZER_MANAGER_HPP +#define SYNCHRONIZER_MANAGER_HPP + +#include <PastisMacros.hpp> +#include <PastisAssert.hpp> + +#include <memory> +#include <map> + +class IConnectivity; +class Synchronizer; + +class SynchronizerManager +{ + private: + std::map<const IConnectivity*, std::shared_ptr<Synchronizer>> m_connectivity_synchronizer_map; + + static SynchronizerManager* m_instance; + SynchronizerManager() = default; + ~SynchronizerManager(); + + public: + static void create(); + static void destroy(); + + PASTIS_INLINE + static SynchronizerManager& instance() + { + Assert(m_instance != nullptr, "SynchronizerManager was not created!"); + return *m_instance; + } + + void deleteConnectivitySynchronizer(const IConnectivity*); + Synchronizer& getConnectivitySynchronizer(const IConnectivity*); +}; + +#endif // SYNCHRONIZER_MANAGER_HPP diff --git a/src/output/OutputNamedItemValueSet.hpp b/src/output/OutputNamedItemValueSet.hpp index 4f056098840f338f6afd869acad83f2f567d7d2f..c7ef0eeb9f24b738647c9aa3320fd6f396d4bbbf 100644 --- a/src/output/OutputNamedItemValueSet.hpp +++ b/src/output/OutputNamedItemValueSet.hpp @@ -31,7 +31,8 @@ class NamedItemValue NamedItemValue& operator=(const NamedItemValue&) = default; NamedItemValue& operator=(NamedItemValue&&) = default; - NamedItemValue(const std::string& name, const ItemValue<DataType,item_type>& item_value) + template <typename ConnectivityPtr> + NamedItemValue(const std::string& name, const ItemValue<DataType,item_type, ConnectivityPtr>& item_value) : m_name(name), m_item_value(item_value) { diff --git a/src/output/VTKWriter.hpp b/src/output/VTKWriter.hpp index ffed42738c967dde7b799cd7607eece281ce1ed5..16f49cc414693b0a6610e7adea861322f419912e 100644 --- a/src/output/VTKWriter.hpp +++ b/src/output/VTKWriter.hpp @@ -9,6 +9,7 @@ #include <IConnectivity.hpp> #include <ItemValue.hpp> +#include <Messenger.hpp> #include <OutputNamedItemValueSet.hpp> class VTKWriter @@ -19,6 +20,72 @@ class VTKWriter double m_last_time; const double m_time_period; + std::string _getFilenamePVTU() + { + std::ostringstream sout; + sout << m_base_filename; + sout << '.' << std::setfill('0') << std::setw(4) << m_file_number << ".pvtu"; + return sout.str(); + } + std::string _getFilenameVTU(const int& rank_number) const + { + std::ostringstream sout; + sout << m_base_filename; + if (parallel::size() > 1) { + sout << '-' << std::setfill('0') << std::setw(4) << rank_number; + } + sout << '.' << std::setfill('0') << std::setw(4) << m_file_number << ".vtu"; + return sout.str(); + } + + template <typename DataType> + void _write_node_pvtu(std::ofstream& os, + const std::string& name, + const NodeValue<const DataType>&) + { + os << "<PDataArray type=\"" << VTKType<DataType>::name + << "\" Name=\"" << name << "\"/>\n"; + } + + template <size_t N, + typename DataType> + void _write_node_pvtu(std::ofstream& os, + const std::string& name, + const NodeValue<const TinyVector<N, DataType>>&) + { + os << "<PDataArray type=\"" << VTKType<DataType>::name + << "\" Name=\"" << name << "\" NumberOfComponents=\"" << N << "\"/>\n"; + } + + template <typename DataType> + void _write_node_pvtu(std::ofstream&, + const std::string&, + const CellValue<const DataType>&) {} + + template <typename DataType> + void _write_cell_pvtu(std::ofstream& os, + const std::string& name, + const CellValue<const DataType>&) + { + os << "<PDataArray type=\"" << VTKType<DataType>::name + << "\" Name=\"" << name << "\"/>\n"; + } + + template <size_t N, + typename DataType> + void _write_cell_pvtu(std::ofstream& os, + const std::string& name, + const CellValue<const TinyVector<N, DataType>>&) + { + os << "<PDataArray type=\"" << VTKType<DataType>::name + << "\" Name=\"" << name << "\" NumberOfComponents=\"" << N << "\"/>\n"; + } + + template <typename DataType> + void _write_cell_pvtu(std::ofstream&, + const std::string&, + const NodeValue<const DataType>&) {} + template <typename DataType> struct VTKType {}; template <typename DataType> @@ -139,112 +206,160 @@ class VTKWriter } else { return; } - std::ostringstream sout; - sout << m_base_filename << '.' << std::setfill('0') << std::setw(4) << m_file_number << ".vtu" << std::ends; - std::ofstream fout(sout.str()); - fout << "<?xml version=\"1.0\"?>\n"; - fout << "<VTKFile type=\"UnstructuredGrid\">\n"; - fout << "<UnstructuredGrid>\n"; - fout << "<Piece NumberOfPoints=\""<< mesh.numberOfNodes() - << "\" NumberOfCells=\"" << mesh.numberOfCells() << "\">\n"; - fout << "<CellData>\n"; - for(const auto& [name, item_value_variant] : output_named_item_value_set) { - std::visit([&, name=name](auto&& item_value) { - return this->_write_cell_value(fout, name, item_value); - }, item_value_variant); - } - fout << "</CellData>\n"; - fout << "<PointData>\n"; - for(const auto& [name, item_value_variant] : output_named_item_value_set) { - std::visit([&, name=name](auto&& item_value) { - return this->_write_node_value(fout, name, item_value); - }, item_value_variant); - } - fout << "</PointData>\n"; - fout << "<Points>\n"; - { - using Rd = TinyVector<MeshType::dimension>; - const NodeValue<const Rd>& xr = mesh.xr(); - Array<TinyVector<3>> positions(mesh.numberOfNodes()); - for (NodeId r=0; r<mesh.numberOfNodes(); ++r) { - for (unsigned short i=0; i<MeshType::dimension; ++i) { - positions[r][i] = xr[r][i]; - } - for (unsigned short i=MeshType::dimension; i<3; ++i) { - positions[r][i] = 0; - } - } - _write_array(fout, "Positions", positions); - } - fout << "</Points>\n"; - fout << "<Cells>\n"; + if (parallel::rank() == 0) + { // write PVTK file + std::ofstream fout(_getFilenamePVTU()); + fout << "<?xml version=\"1.0\"?>\n"; + fout << "<VTKFile type=\"PUnstructuredGrid\">\n"; + fout << "<PUnstructuredGrid GhostLevel=\"0\">\n"; + + fout << "<PPoints>\n"; + fout << "<PDataArray Name=\"Positions\" NumberOfComponents=\"3\" type=\"Float64\"/>\n"; + fout << "</PPoints>\n"; + + fout << "<PCells>\n"; + fout << "<PDataArray type=\"Int32\" Name=\"connectivity\" NumberOfComponents=\"1\"/>\n"; + fout << "<PDataArray type=\"UInt32\" Name=\"offsets\" NumberOfComponents=\"1\"/>\n"; + fout << "<PDataArray type=\"Int8\" Name=\"types\" NumberOfComponents=\"1\"/>\n"; + for(const auto& [name, item_value_variant] : output_named_item_value_set) { + std::visit([&, name=name](auto&& item_value) { + return this->_write_cell_pvtu(fout, name, item_value); + }, item_value_variant); + } + fout << "</PCells>\n"; - { - const auto& cell_to_node_matrix - = mesh.connectivity().cellToNodeMatrix(); - Array<int> connectivity(cell_to_node_matrix.numberOfEntries()); + fout << "<PPointData>\n"; + for(const auto& [name, item_value_variant] : output_named_item_value_set) { + std::visit([&, name=name](auto&& item_value) { + return this->_write_node_pvtu(fout, name, item_value); + }, item_value_variant); + } + fout << "</PPointData>\n"; - for (size_t i=0; i<cell_to_node_matrix.numberOfEntries(); ++i) { - connectivity[i] = cell_to_node_matrix.entries()[i]; + fout << "<PCellData>\n"; + for(const auto& [name, item_value_variant] : output_named_item_value_set) { + std::visit([&, name=name](auto&& item_value) { + return this->_write_cell_pvtu(fout, name, item_value); + }, item_value_variant); } - _write_array(fout, "connectivity", connectivity); - } + fout << "</PCellData>\n"; - { - const auto& cell_to_node_matrix - = mesh.connectivity().cellToNodeMatrix(); - Array<unsigned int> offsets(mesh.numberOfCells()); - unsigned int offset=0; - for (CellId j=0; j<mesh.numberOfCells(); ++j) { - const auto& cell_nodes = cell_to_node_matrix[j]; - offset += cell_nodes.size(); - offsets[j]=offset; + for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) { + fout << "<Piece Source=\""<< _getFilenameVTU(i_rank) << "\"/>\n"; } - _write_array(fout, "offsets", offsets); + fout << "</PUnstructuredGrid>\n"; + fout << "</VTKFile>\n"; + } - { - const auto& cell_to_node_matrix - = mesh.connectivity().cellToNodeMatrix(); - Array<int8_t> types(mesh.numberOfCells()); - for (CellId j=0; j<mesh.numberOfCells(); ++j) { - const auto& cell_nodes = cell_to_node_matrix[j]; - switch (cell_nodes.size()) { - case 2: { - types[j]=3; - break; - } - case 3: { - types[j]=5; - break; - } - case 4: { - if (mesh.meshDimension() == 3) { - types[j]=10; - } else { - types[j]=9; + { // write VTK files + std::ofstream fout(_getFilenameVTU(parallel::rank())); + fout << "<?xml version=\"1.0\"?>\n"; + fout << "<VTKFile type=\"UnstructuredGrid\">\n"; + fout << "<UnstructuredGrid>\n"; + fout << "<Piece NumberOfPoints=\""<< mesh.numberOfNodes() + << "\" NumberOfCells=\"" << mesh.numberOfCells() << "\">\n"; + fout << "<CellData>\n"; + for(const auto& [name, item_value_variant] : output_named_item_value_set) { + std::visit([&, name=name](auto&& item_value) { + return this->_write_cell_value(fout, name, item_value); + }, item_value_variant); + } + fout << "</CellData>\n"; + fout << "<PointData>\n"; + for(const auto& [name, item_value_variant] : output_named_item_value_set) { + std::visit([&, name=name](auto&& item_value) { + return this->_write_node_value(fout, name, item_value); + }, item_value_variant); + } + fout << "</PointData>\n"; + fout << "<Points>\n"; + { + using Rd = TinyVector<MeshType::Dimension>; + const NodeValue<const Rd>& xr = mesh.xr(); + Array<TinyVector<3>> positions(mesh.numberOfNodes()); + parallel_for(mesh.numberOfNodes(), PASTIS_LAMBDA(NodeId r) { + for (unsigned short i=0; i<MeshType::Dimension; ++i) { + positions[r][i] = xr[r][i]; + } + for (unsigned short i=MeshType::Dimension; i<3; ++i) { + positions[r][i] = 0; } - break; - } - case 8: { - types[j]=12; - break; - } - default: { - types[j]=7; - break; - } + }); + _write_array(fout, "Positions", positions); + } + fout << "</Points>\n"; + + fout << "<Cells>\n"; + { + const auto& cell_to_node_matrix + = mesh.connectivity().cellToNodeMatrix(); + + _write_array(fout, "connectivity", cell_to_node_matrix.entries()); + } + + { + const auto& cell_to_node_matrix + = mesh.connectivity().cellToNodeMatrix(); + Array<unsigned int> offsets(mesh.numberOfCells()); + unsigned int offset=0; + for (CellId j=0; j<mesh.numberOfCells(); ++j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + offset += cell_nodes.size(); + offsets[j]=offset; } + _write_array(fout, "offsets", offsets); } - _write_array(fout, "types", types); - } - fout << "</Cells>\n"; - fout << "</Piece>\n"; - fout << "</UnstructuredGrid>\n"; - fout << "</VTKFile>\n"; + { + Array<int8_t> types(mesh.numberOfCells()); + const auto& cell_type + = mesh.connectivity().cellType(); + parallel_for(mesh.numberOfCells(), PASTIS_LAMBDA(const CellId& j) { + switch (cell_type[j]) { + case CellType::Line: { + types[j]=3; + break; + } + case CellType::Triangle: { + types[j]=5; + break; + } + case CellType::Quadrangle: { + types[j]=9; + break; + } + case CellType::Tetrahedron: { + types[j]=10; + break; + } + case CellType::Pyramid: { + types[j]=14; + break; + } + case CellType::Prism: { + types[j]=13; + break; + } + case CellType::Hexahedron: { + types[j]=12; + break; + } + default: { + std::cerr << __FILE__ << ':' << __LINE__ << ": unknown cell type\n"; + std::exit(1); + } + } + }); + _write_array(fout, "types", types); + } + fout << "</Cells>\n"; + fout << "</Piece>\n"; + fout << "</UnstructuredGrid>\n"; + fout << "</VTKFile>\n"; + } m_file_number++; } diff --git a/src/scheme/AcousticSolver.hpp b/src/scheme/AcousticSolver.hpp index 4d09a8ede6c6774d2cd0415f7b477730a9ff3507..cb83a583bc6fb4ec064dace3a75964816b6d7b20 100644 --- a/src/scheme/AcousticSolver.hpp +++ b/src/scheme/AcousticSolver.hpp @@ -16,6 +16,8 @@ #include <BoundaryCondition.hpp> #include <SubItemValuePerItem.hpp> +#include <Messenger.hpp> +#include <ItemValueUtils.hpp> template<typename MeshData> class AcousticSolver @@ -28,10 +30,10 @@ class AcousticSolver const typename MeshType::Connectivity& m_connectivity; const std::vector<BoundaryConditionHandler>& m_boundary_condition_list; - constexpr static size_t dimension = MeshType::dimension; + constexpr static size_t Dimension = MeshType::Dimension; - using Rd = TinyVector<dimension>; - using Rdd = TinyMatrix<dimension>; + using Rd = TinyVector<Dimension>; + using Rdd = TinyMatrix<Dimension>; private: PASTIS_INLINE @@ -134,8 +136,8 @@ class AcousticSolver break; } case BoundaryCondition::symmetry: { - const SymmetryBoundaryCondition<dimension>& symmetry_bc - = dynamic_cast<const SymmetryBoundaryCondition<dimension>&>(handler.boundaryCondition()); + const SymmetryBoundaryCondition<Dimension>& symmetry_bc + = dynamic_cast<const SymmetryBoundaryCondition<Dimension>&>(handler.boundaryCondition()); const Rd& n = symmetry_bc.outgoingNormal(); const Rdd I = identity; @@ -208,13 +210,16 @@ class AcousticSolver computeAjr(rhocj, Cjr, ljr, njr); NodeValuePerCell<const Rdd> Ajr = m_Ajr; - const NodeValue<const Rdd> Ar = computeAr(Ajr); - const NodeValue<const Rd> br = computeBr(m_Ajr, Cjr, uj, pj); + this->computeAr(Ajr); + this->computeBr(m_Ajr, Cjr, uj, pj); this->applyBoundaryConditions(); + synchronize(m_Ar); + synchronize(m_br); + NodeValue<Rd>& ur = m_ur; - ur = computeUr(Ar, br); + ur = computeUr(m_Ar, m_br); computeFjr(m_Ajr, ur, Cjr, uj, pj); } @@ -264,7 +269,7 @@ class AcousticSolver m_Vj_over_cj[j] = 2*Vj[j]/(S*cj[j]); }); - return ReduceMin(m_Vj_over_cj); + return min(m_Vj_over_cj); } void computeNextStep(const double&, const double& dt, diff --git a/src/scheme/BoundaryCondition.hpp b/src/scheme/BoundaryCondition.hpp index 0380f97c021b637a623146cd1be7f18524913e2d..669f546c19faf2e0cf059acef0f90ee2083682a4 100644 --- a/src/scheme/BoundaryCondition.hpp +++ b/src/scheme/BoundaryCondition.hpp @@ -6,7 +6,7 @@ #include <Array.hpp> -#include <RefNodeList.hpp> +#include <RefItemList.hpp> #include <MeshNodeBoundary.hpp> class BoundaryCondition diff --git a/src/scheme/FiniteVolumesEulerUnknowns.hpp b/src/scheme/FiniteVolumesEulerUnknowns.hpp index 109e93d5a13acd2ce5c23b2b83576af21665c3c7..390e1c91c95beec72d96ec1389d564971a25026b 100644 --- a/src/scheme/FiniteVolumesEulerUnknowns.hpp +++ b/src/scheme/FiniteVolumesEulerUnknowns.hpp @@ -11,8 +11,8 @@ public: using MeshDataType = TMeshData; using MeshType = typename MeshDataType::MeshType; - static constexpr size_t dimension = MeshType::dimension; - using Rd = TinyVector<dimension>; + static constexpr size_t Dimension = MeshType::Dimension; + using Rd = TinyVector<Dimension>; private: const MeshDataType& m_mesh_data; diff --git a/src/utils/Array.hpp b/src/utils/Array.hpp index 33d8ba7ac1b2546e26b91dafd6d969cb1870b784..a563c6462ecb71bbdc3b3aecf8f9a4c605b56a71 100644 --- a/src/utils/Array.hpp +++ b/src/utils/Array.hpp @@ -6,6 +6,9 @@ #include <PastisAssert.hpp> +#include <Kokkos_CopyViews.hpp> +#include <algorithm> + template <typename DataType> class Array { @@ -26,6 +29,19 @@ class Array return m_values.extent(0); } + friend PASTIS_INLINE + Array<std::remove_const_t<DataType>> copy(const Array<DataType>& source) + { + Array<std::remove_const_t<DataType>> image(source.size()); + Kokkos::deep_copy(image.m_values, source.m_values); + + return image; + } + + template <typename DataType2, typename ... RT> + friend PASTIS_INLINE + Array<DataType2> encapsulate(const Kokkos::View<DataType2*, RT...>& values); + PASTIS_INLINE DataType& operator[](const index_type& i) const noexcept(NO_ASSERT) { @@ -34,11 +50,15 @@ class Array } PASTIS_INLINE - Array(const size_t& size) - : m_values("anonymous", size) + void fill(const DataType& data) const { static_assert(not std::is_const<DataType>(), - "Cannot allocate Array of const data: only view is supported"); + "Cannot modify Array of const"); + + // could consider to use std::fill + parallel_for(this->size(), PASTIS_LAMBDA(const index_type& i){ + m_values[i] = data; + }); } template <typename DataType2> @@ -51,7 +71,7 @@ class Array // ensures that const is not lost through copy static_assert(((std::is_const<DataType2>() and std::is_const<DataType>()) or not std::is_const<DataType2>()), - "Cannot assign Array of const to Array of non-const"); + "Cannot assign Array of const to Array of non-const"); m_values = array.m_values; return *this; } @@ -62,9 +82,20 @@ class Array PASTIS_INLINE Array& operator=(Array&&) = default; + PASTIS_INLINE + Array(const size_t& size) + : m_values("anonymous", size) + { + static_assert(not std::is_const<DataType>(), + "Cannot allocate Array of const data: only view is supported"); + } + PASTIS_INLINE Array() = default; + PASTIS_INLINE + Array(const Array&) = default; + template <typename DataType2> PASTIS_INLINE Array(const Array<DataType2>& array) noexcept @@ -75,11 +106,31 @@ class Array PASTIS_INLINE Array(Array&&) = default; - PASTIS_INLINE - Array(const Array&) = default; - PASTIS_INLINE ~Array() = default; }; +template <typename DataType, typename ... RT> +PASTIS_INLINE +Array<DataType> encapsulate(const Kokkos::View<DataType*, RT...>& values) +{ + Array<DataType> array; + array.m_values = values; + return array; +} + +// map, multimap, unordered_map and stack cannot be copied this way +template <typename Container> +PASTIS_INLINE +Array<std::remove_const_t<typename Container::value_type>> +convert_to_array(const Container& given_container) +{ + using DataType = typename Container::value_type; + Array<std::remove_const_t<DataType>> array(given_container.size()); + if (given_container.size()>0) { + std::copy(begin(given_container), end(given_container), &(array[0])); + } + return array; +} + #endif // ARRAY_HPP diff --git a/src/utils/ArrayUtils.hpp b/src/utils/ArrayUtils.hpp index b64452d992b9728dfc2e3a117f790d8893ec155c..51e42309473cd069f0a8946277ae0630665ae686 100644 --- a/src/utils/ArrayUtils.hpp +++ b/src/utils/ArrayUtils.hpp @@ -4,107 +4,207 @@ #include <PastisMacros.hpp> #include <PastisUtils.hpp> -template <typename ArrayType> -class ReduceMin +#include <Types.hpp> + +template <typename DataType, + template <typename> typename ArrayT> +std::remove_const_t<DataType> +min(const ArrayT<DataType>& array) { - private: - const ArrayType& m_array; + using ArrayType = ArrayT<DataType>; using data_type = std::remove_const_t<typename ArrayType::data_type>; using index_type = typename ArrayType::index_type; - public: - PASTIS_INLINE - operator data_type() + class ArrayMin { - data_type reduced_value; - parallel_reduce(m_array.size(), *this, reduced_value); - return reduced_value; - } - PASTIS_INLINE - void operator()(const index_type& i, data_type& data) const - { - if (m_array[i] < data) { - data = m_array[i]; + private: + const ArrayType m_array; + + public: + PASTIS_INLINE + operator data_type() + { + data_type reduced_value; + parallel_reduce(m_array.size(), *this, reduced_value); + return reduced_value; } - } - PASTIS_INLINE - void join(volatile data_type& dst, - const volatile data_type& src) const - { - if (src < dst) { - dst = src; + PASTIS_INLINE + void operator()(const index_type& i, data_type& data) const + { + if (m_array[i] < data) { + data = m_array[i]; + } } - } - PASTIS_INLINE - void init(data_type& value) const - { - value = std::numeric_limits<data_type>::max(); - } + PASTIS_INLINE + void join(volatile data_type& dst, + const volatile data_type& src) const + { + if (src < dst) { + dst = src; + } + } - PASTIS_INLINE - ReduceMin(const ArrayType& array) - : m_array(array) - { - ; - } + PASTIS_INLINE + void init(data_type& value) const + { + value = std::numeric_limits<data_type>::max(); + } + + PASTIS_INLINE + ArrayMin(const ArrayType& array) + : m_array(array) + { + ; + } - PASTIS_INLINE - ~ReduceMin() = default; -}; + PASTIS_INLINE + ~ArrayMin() = default; + }; -template <typename ArrayType> -class ReduceMax + return ArrayMin(array); +} + +template <typename DataType, + template <typename> typename ArrayT> +std::remove_const_t<DataType> +max(const ArrayT<DataType>& array) { - private: - const ArrayType& m_array; + using ArrayType = ArrayT<DataType>; using data_type = std::remove_const_t<typename ArrayType::data_type>; using index_type = typename ArrayType::index_type; - public: - PASTIS_INLINE - operator data_type() + class ArrayMax { - data_type reduced_value; - parallel_reduce(m_array.size(), *this, reduced_value); - return reduced_value; - } + private: + const ArrayType m_array; + + public: + PASTIS_INLINE + operator data_type() + { + data_type reduced_value; + parallel_reduce(m_array.size(), *this, reduced_value); + return reduced_value; + } - PASTIS_INLINE - void operator()(const index_type& i, data_type& data) const - { - if (m_array[i] > data) { - data = m_array[i]; + PASTIS_INLINE + void operator()(const index_type& i, data_type& data) const + { + if (m_array[i] > data) { + data = m_array[i]; + } } - } - PASTIS_INLINE - void join(volatile data_type& dst, - const volatile data_type& src) const - { - if (src > dst) { - dst = src; + PASTIS_INLINE + void join(volatile data_type& dst, + const volatile data_type& src) const + { + if (src > dst) { + dst = src; + } } - } - PASTIS_INLINE - void init(data_type& value) const - { - value = -std::numeric_limits<data_type>::max(); - } + PASTIS_INLINE + void init(data_type& value) const + { + value = std::numeric_limits<data_type>::min(); + } + + PASTIS_INLINE + ArrayMax(const ArrayType& array) + : m_array(array) + { + ; + } + + PASTIS_INLINE + ~ArrayMax() = default; + }; - PASTIS_INLINE - ReduceMax(const ArrayType& array) - : m_array(array) + return ArrayMax(array); +} + +template <typename DataType, + template <typename> typename ArrayT> +std::remove_const_t<DataType> +sum(const ArrayT<DataType>& array) +{ + using ArrayType = ArrayT<DataType>; + using data_type = std::remove_const_t<DataType>; + using index_type = typename ArrayType::index_type; + + class ArraySum { - ; - } + private: + const ArrayType& m_array; + + public: + PASTIS_INLINE + operator data_type() + { + data_type reduced_value; + parallel_reduce(m_array.size(), *this, reduced_value); + return reduced_value; + } + + PASTIS_INLINE + void operator()(const index_type& i, data_type& data) const + { + data += m_array[i]; + } + + PASTIS_INLINE + void join(volatile data_type& dst, + const volatile data_type& src) const + { + dst += src; + } + + PASTIS_INLINE + void init(data_type& value) const + { + if constexpr (std::is_arithmetic_v<data_type>) { + value = 0; + } else { + value = zero; + } + } + + PASTIS_INLINE + ArraySum(const ArrayType& array) + : m_array(array) + { + ; + } + + PASTIS_INLINE + ~ArraySum() = default; + }; + + return ArraySum(array); +} + +template <template <typename ...SourceT> typename SourceArray, + template <typename ...ImageT> typename ImageArray, + typename ...SourceT, typename ...ImageT> +void value_copy(const SourceArray<SourceT...>& source_array, + ImageArray<ImageT...>& image_array) +{ + using SourceDataType = typename SourceArray<SourceT...>::data_type; + using ImageDataType = typename ImageArray<ImageT...>::data_type; + + static_assert(std::is_same_v<std::remove_const_t<SourceDataType>,ImageDataType>); + static_assert(not std::is_const_v<ImageDataType>); + + Assert(source_array.size() == image_array.size()); - PASTIS_INLINE - ~ReduceMax() = default; -}; + parallel_for(source_array.size(), PASTIS_LAMBDA(const size_t& i) { + image_array[i] = source_array[i]; + }); +} #endif //ARRAY_UTILS_HPP diff --git a/src/utils/BuildInfo.cpp b/src/utils/BuildInfo.cpp index 377e4af868fe9f4507e10c48de8fbf6272932433..4df473bd2a9407c99804081bcf6e51a7c6e5eb15 100644 --- a/src/utils/BuildInfo.cpp +++ b/src/utils/BuildInfo.cpp @@ -1,6 +1,13 @@ #include <BuildInfo.hpp> +#include <pastis_config.hpp> #include <pastis_build_info.hpp> +#include <sstream> + +#ifdef PASTIS_HAS_MPI +#include <mpi.h> +#endif // PASTIS_HAS_MPI + std::string BuildInfo::type() { return PASTIS_BUILD_TYPE; @@ -8,10 +15,28 @@ std::string BuildInfo::type() std::string BuildInfo::compiler() { - return PASTIS_BUILD_COMPILER; + std::stringstream compiler_info; + compiler_info << PASTIS_BUILD_COMPILER + << " (" << PASTIS_BUILD_COMPILER_VERSION + << ")" << std::ends; + return compiler_info.str(); } std::string BuildInfo::kokkosDevices() { return PASTIS_BUILD_KOKKOS_DEVICES; } + +std::string BuildInfo::mpiLibrary() +{ +#ifdef PASTIS_HAS_MPI + return [](){ + int length; + char mpi_version[MPI_MAX_LIBRARY_VERSION_STRING]; + MPI_Get_library_version(mpi_version, &length); + return std::string(mpi_version); + }(); +#else + return "none"; +#endif // PASTIS_HAS_MPI +} diff --git a/src/utils/BuildInfo.hpp b/src/utils/BuildInfo.hpp index 4fce84540f71f4d7556b6552146f0e6eaaecdf2d..86bcd1d262f58b9b77ea78375fbadd2684aaa267 100644 --- a/src/utils/BuildInfo.hpp +++ b/src/utils/BuildInfo.hpp @@ -8,6 +8,7 @@ struct BuildInfo static std::string type(); static std::string compiler(); static std::string kokkosDevices(); + static std::string mpiLibrary(); }; #endif // BUILD_INFO_HPP diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index 045c6d879715b130dedf6b8135e3fb4ed9421483..0c89bb3efabb8e68d77b820fcd512d759c28a29a 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -9,6 +9,8 @@ add_library( BacktraceManager.cpp ConsoleManager.cpp FPEManager.cpp + Messenger.cpp + Partitioner.cpp PastisOStream.cpp PastisUtils.cpp RevisionInfo.cpp diff --git a/src/utils/CSRGraph.hpp b/src/utils/CSRGraph.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2ae461eb082b0822895fdf7fa835a27489557afa --- /dev/null +++ b/src/utils/CSRGraph.hpp @@ -0,0 +1,46 @@ +#ifndef CSR_GRAPH_HPP +#define CSR_GRAPH_HPP + +#include <Array.hpp> + +class CSRGraph +{ + private: + Array<int> m_entries; + Array<int> m_neighbors; + + public: + size_t numberOfNodes() const + { + Assert(m_entries.size()>0); + return m_entries.size()-1; + } + + const Array<int>& entries() const + { + return m_entries; + } + + const Array<int>& neighbors() const + { + return m_neighbors; + } + + CSRGraph& operator=(CSRGraph&&) = default; + CSRGraph& operator=(const CSRGraph&) = default; + + CSRGraph(Array<int> entries, + Array<int> neighbors) + : m_entries(entries), + m_neighbors(neighbors) + { + Assert(m_entries.size()>0); + } + + CSRGraph() = default; + CSRGraph(CSRGraph&&) = default; + CSRGraph(const CSRGraph&) = default; + ~CSRGraph() = default; +}; + +#endif // CSR_GRAPH_HPP diff --git a/src/utils/CastArray.hpp b/src/utils/CastArray.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e6288b686c65f025847d82e95da43fc76ec49b37 --- /dev/null +++ b/src/utils/CastArray.hpp @@ -0,0 +1,108 @@ +#ifndef CAST_ARRAY_HPP +#define CAST_ARRAY_HPP + +#include <Array.hpp> +#include <PastisTraits.hpp> + +template <typename DataType, + typename CastDataType> +class CastArray +{ + public: + using data_type = CastDataType; + private: + const Array<DataType> m_array; + const size_t m_size; + CastDataType* const m_values; + + public: + PASTIS_INLINE + const size_t& size() const + { + return m_size; + } + + PASTIS_INLINE + CastDataType& operator[](const size_t& i) const + { + Assert(i<m_size); + return m_values[i]; + } + + PASTIS_INLINE + CastArray& operator=(const CastArray&) = default; + + PASTIS_INLINE + CastArray& operator=(CastArray&&) = default; + + PASTIS_INLINE + CastArray() + : m_size(0), + m_values(nullptr) + { + ; + } + + PASTIS_INLINE + CastArray(const Array<DataType>& array) + : m_array (array), + m_size (sizeof(DataType)*array.size()/sizeof(CastDataType)), + m_values((array.size() == 0) ? nullptr : reinterpret_cast<CastDataType*>(&(array[0]))) + { + static_assert((std::is_const_v<CastDataType> and std::is_const_v<DataType>) or + (not std::is_const_v<DataType>), "CastArray cannot remove const attribute"); + + if (sizeof(DataType)*array.size() % sizeof(CastDataType)) { + std::cerr << "cannot cast array to the chosen data type\n"; + std::exit(1); + } + } + + PASTIS_INLINE + CastArray(DataType& value) + : m_size (sizeof(DataType)/sizeof(CastDataType)), + m_values(reinterpret_cast<CastDataType*>(&(value))) + { + static_assert((std::is_const_v<CastDataType> and std::is_const_v<DataType>) or + (not std::is_const_v<DataType>), "CastArray cannot remove const attribute"); + static_assert(is_trivially_castable<DataType>, "Defining CastArray from non trivially castable type is not allowed"); + } + + PASTIS_INLINE + CastArray(DataType&& value) = delete; + + PASTIS_INLINE + CastArray(const CastArray&) = default; + + PASTIS_INLINE + CastArray(CastArray&&) = default; + + PASTIS_INLINE + ~CastArray() = default; +}; + +template <typename CastDataType> +struct cast_array_to +{ + template <typename DataType> + PASTIS_INLINE + static CastArray<DataType, CastDataType> + from(const Array<DataType>& array) + { + return CastArray<DataType, CastDataType>(array); + } +}; + +template <typename CastDataType> +struct cast_value_to +{ + template <typename DataType> + PASTIS_INLINE + static CastArray<DataType, CastDataType> + from(DataType& value) + { + return CastArray<DataType, CastDataType>(value); + } +}; + +#endif // CAST_ARRAY_HPP diff --git a/src/utils/Messenger.cpp b/src/utils/Messenger.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6dc4056efd81957f3a5a89fefc328d3d86a1d118 --- /dev/null +++ b/src/utils/Messenger.cpp @@ -0,0 +1,71 @@ +#include <Messenger.hpp> +#include <PastisOStream.hpp> + +namespace parallel +{ + +Messenger* Messenger::m_instance = nullptr; + +void Messenger::create(int& argc, char* argv[]) +{ + if (Messenger::m_instance == nullptr) { + Messenger::m_instance = new Messenger(argc, argv); + } else { + std::cerr << "Messenger already created\n"; + std::exit(1); + } +} + +void Messenger::destroy() +{ + // One allows multiple destruction to handle unexpected code exit + if (Messenger::m_instance != nullptr) { + delete Messenger::m_instance; + Messenger::m_instance = nullptr; + } +} + +Messenger:: +Messenger([[maybe_unused]] int& argc, + [[maybe_unused]] char* argv[]) +{ +#ifdef PASTIS_HAS_MPI + MPI_Init(&argc, &argv); + + m_rank = [] () { + int rank; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + return rank; + } (); + + m_size = [] () { + int size=0; + MPI_Comm_size(MPI_COMM_WORLD, &size); + return size; + } (); + + if (m_rank != 0) { + // LCOV_EXCL_START + pout.setOutput(null_stream); + perr.setOutput(null_stream); + // LCOV_EXCL_STOP + } +#endif // PASTIS_HAS_MPI +} + +Messenger:: +~Messenger() +{ +#ifdef PASTIS_HAS_MPI + MPI_Finalize(); +#endif // PASTIS_HAS_MPI +} + +void Messenger::barrier() const +{ +#ifdef PASTIS_HAS_MPI + MPI_Barrier(MPI_COMM_WORLD); +#endif // PASTIS_HAS_MPI +} + +} // namespace parallel diff --git a/src/utils/Messenger.hpp b/src/utils/Messenger.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4a54994a273b55e74c253135951a59a95c186909 --- /dev/null +++ b/src/utils/Messenger.hpp @@ -0,0 +1,685 @@ +#ifndef MESSENGER_HPP +#define MESSENGER_HPP + +#include <PastisMacros.hpp> +#include <PastisAssert.hpp> +#include <PastisOStream.hpp> + +#include <Array.hpp> +#include <CastArray.hpp> +#include <ArrayUtils.hpp> + +#include <type_traits> +#include <vector> + +#include <pastis_config.hpp> +#ifdef PASTIS_HAS_MPI +#include <mpi.h> +#endif // PASTIS_HAS_MPI + +#include <PastisTraits.hpp> + +namespace parallel +{ + +class Messenger +{ + private: + struct helper + { +#ifdef PASTIS_HAS_MPI + template<typename DataType> + static PASTIS_INLINE + MPI_Datatype mpiType() + { + if constexpr (std::is_const_v<DataType>) { + return mpiType<std::remove_const_t<DataType>>(); + } else { + static_assert(std::is_arithmetic_v<DataType>, + "Unexpected arithmetic type! Should not occur!"); + static_assert(is_false_v<DataType>, + "MPI_Datatype are only defined for arithmetic types!"); + return MPI_Datatype(); + } + } +#endif // PASTIS_HAS_MPI + + private: + template <typename T, + typename Allowed = void> + struct split_cast {}; + + template <typename T> + struct split_cast<T,std::enable_if_t<not(sizeof(T) % sizeof(int64_t))>> { + using type = int64_t; + static_assert(not(sizeof(T) % sizeof(int64_t))); + }; + + template <typename T> + struct split_cast<T,std::enable_if_t<not(sizeof(T) % sizeof(int32_t)) + and(sizeof(T) % sizeof(int64_t))>> { + using type = int32_t; + static_assert(not(sizeof(T) % sizeof(int32_t))); + }; + + template <typename T> + struct split_cast<T,std::enable_if_t<not(sizeof(T) % sizeof(int16_t)) + and(sizeof(T) % sizeof(int32_t)) + and(sizeof(T) % sizeof(int64_t))>> { + using type = int16_t; + static_assert(not(sizeof(T) % sizeof(int16_t))); + }; + + template <typename T> + struct split_cast<T,std::enable_if_t<not(sizeof(T) % sizeof(int8_t)) + and(sizeof(T) % sizeof(int16_t)) + and(sizeof(T) % sizeof(int32_t)) + and(sizeof(T) % sizeof(int64_t))>> { + using type = int8_t; + static_assert(not(sizeof(T) % sizeof(int8_t))); + }; + + public: + template <typename T> + using split_cast_t = typename split_cast<T>::type; + }; + + static Messenger* m_instance; + Messenger(int& argc, char* argv[]); + + size_t m_rank{0}; + size_t m_size{1}; + + template <typename DataType> + void _allGather(const DataType& data, + Array<DataType> gather) const + { + static_assert(std::is_arithmetic_v<DataType>); + Assert(gather.size() == m_size); // LCOV_EXCL_LINE + +#ifdef PASTIS_HAS_MPI + MPI_Datatype mpi_datatype + = Messenger::helper::mpiType<DataType>(); + + MPI_Allgather(&data, 1, mpi_datatype, + &(gather[0]), 1, mpi_datatype, + MPI_COMM_WORLD); +#else // PASTIS_HAS_MPI + gather[0] = data; +#endif // PASTIS_HAS_MPI + } + + + + template <template <typename ...SendT> typename SendArrayType, + template <typename ...RecvT> typename RecvArrayType, + typename ...SendT, typename ...RecvT> + void _allGather(const SendArrayType<SendT...>& data_array, + RecvArrayType<RecvT...> gather_array) const + { + Assert(gather_array.size() == data_array.size()*m_size); // LCOV_EXCL_LINE + + using SendDataType = typename SendArrayType<SendT...>::data_type; + using RecvDataType = typename RecvArrayType<RecvT...>::data_type; + + static_assert(std::is_same_v<std::remove_const_t<SendDataType>,RecvDataType>); + static_assert(std::is_arithmetic_v<SendDataType>); + +#ifdef PASTIS_HAS_MPI + MPI_Datatype mpi_datatype + = Messenger::helper::mpiType<RecvDataType>(); + + MPI_Allgather(&(data_array[0]), data_array.size(), mpi_datatype, + &(gather_array[0]), data_array.size(), mpi_datatype, + MPI_COMM_WORLD); +#else // PASTIS_HAS_MPI + value_copy(data_array, gather_array); +#endif // PASTIS_HAS_MPI + } + + template <typename DataType> + void _broadcast_value([[maybe_unused]] DataType& data, + [[maybe_unused]] const size_t& root_rank) const + { + static_assert(not std::is_const_v<DataType>); + static_assert(std::is_arithmetic_v<DataType>); + +#ifdef PASTIS_HAS_MPI + MPI_Datatype mpi_datatype + = Messenger::helper::mpiType<DataType>(); + + MPI_Bcast(&data, 1, mpi_datatype, root_rank, MPI_COMM_WORLD); +#endif // PASTIS_HAS_MPI + } + + template <typename ArrayType> + void _broadcast_array([[maybe_unused]] ArrayType& array, + [[maybe_unused]] const size_t& root_rank) const + { + using DataType = typename ArrayType::data_type; + static_assert(not std::is_const_v<DataType>); + static_assert(std::is_arithmetic_v<DataType>); + +#ifdef PASTIS_HAS_MPI + MPI_Datatype mpi_datatype + = Messenger::helper::mpiType<DataType>(); + MPI_Bcast(&(array[0]), array.size(), mpi_datatype, root_rank, MPI_COMM_WORLD); +#endif // PASTIS_HAS_MPI + } + + template <template <typename ...SendT> typename SendArrayType, + template <typename ...RecvT> typename RecvArrayType, + typename ...SendT, typename ...RecvT> + RecvArrayType<RecvT...> _allToAll(const SendArrayType<SendT...>& sent_array, + RecvArrayType<RecvT...>& recv_array) const + { +#ifdef PASTIS_HAS_MPI + using SendDataType = typename SendArrayType<SendT...>::data_type; + using RecvDataType = typename RecvArrayType<RecvT...>::data_type; + + static_assert(std::is_same_v<std::remove_const_t<SendDataType>,RecvDataType>); + static_assert(std::is_arithmetic_v<SendDataType>); + + Assert((sent_array.size() % m_size) == 0); // LCOV_EXCL_LINE + Assert(sent_array.size() == recv_array.size()); // LCOV_EXCL_LINE + + const size_t count = sent_array.size()/m_size; + + MPI_Datatype mpi_datatype + = Messenger::helper::mpiType<SendDataType>(); + + MPI_Alltoall(&(sent_array[0]), count, mpi_datatype, + &(recv_array[0]), count, mpi_datatype, + MPI_COMM_WORLD); +#else // PASTIS_HAS_MPI + value_copy(sent_array, recv_array); +#endif // PASTIS_HAS_MPI + return recv_array; + } + + template <template <typename ...SendT> typename SendArrayType, + template <typename ...RecvT> typename RecvArrayType, + typename ...SendT, typename ...RecvT> + void _exchange(const std::vector<SendArrayType<SendT...>>& sent_array_list, + std::vector<RecvArrayType<RecvT...>>& recv_array_list) const + { + using SendDataType = typename SendArrayType<SendT...>::data_type; + using RecvDataType = typename RecvArrayType<RecvT...>::data_type; + + static_assert(std::is_same_v<std::remove_const_t<SendDataType>,RecvDataType>); + static_assert(std::is_arithmetic_v<SendDataType>); + +#ifdef PASTIS_HAS_MPI + std::vector<MPI_Request> request_list; + + MPI_Datatype mpi_datatype + = Messenger::helper::mpiType<SendDataType>(); + + for (size_t i_send=0; i_send<sent_array_list.size(); ++i_send) { + const auto sent_array = sent_array_list[i_send]; + if (sent_array.size()>0) { + MPI_Request request; + MPI_Isend(&(sent_array[0]), sent_array.size(), mpi_datatype, i_send, 0, MPI_COMM_WORLD, &request); + request_list.push_back(request); + } + } + + for (size_t i_recv=0; i_recv<recv_array_list.size(); ++i_recv) { + auto recv_array = recv_array_list[i_recv]; + if (recv_array.size()>0) { + MPI_Request request; + MPI_Irecv(&(recv_array[0]), recv_array.size(), mpi_datatype, i_recv, 0, MPI_COMM_WORLD, &request); + request_list.push_back(request); + } + } + + if (request_list.size()>0) { + std::vector<MPI_Status> status_list(request_list.size()); + if (MPI_SUCCESS != MPI_Waitall(request_list.size(), &(request_list[0]), &(status_list[0]))) { + // LCOV_EXCL_START + std::cerr << "Communication error!\n"; + std::exit(1); + // LCOV_EXCL_STOP + } + } + +#else // PASTIS_HAS_MPI + Assert(sent_array_list.size() == 1); + Assert(recv_array_list.size() == 1); + + value_copy(sent_array_list[0], recv_array_list[0]); +#endif // PASTIS_HAS_MPI + } + + template <typename DataType, + typename CastDataType> + void _exchange_through_cast(const std::vector<Array<DataType>>& sent_array_list, + std::vector<Array<std::remove_const_t<DataType>>>& recv_array_list) const + { + std::vector<CastArray<DataType, const CastDataType>> sent_cast_array_list; + for (size_t i=0; i<sent_array_list.size(); ++i) { + sent_cast_array_list.emplace_back(cast_array_to<const CastDataType>::from(sent_array_list[i])); + } + + using MutableDataType = std::remove_const_t<DataType>; + std::vector<CastArray<MutableDataType, CastDataType>> recv_cast_array_list; + for (size_t i=0; i<sent_array_list.size(); ++i) { + recv_cast_array_list.emplace_back(recv_array_list[i]); + } + + _exchange(sent_cast_array_list, recv_cast_array_list); + } + + public: + static void create(int& argc, char* argv[]); + static void destroy(); + + PASTIS_INLINE + static Messenger& getInstance() + { + Assert(m_instance != nullptr); // LCOV_EXCL_LINE + return *m_instance; + } + + PASTIS_INLINE + const size_t& rank() const + { + return m_rank; + } + + PASTIS_INLINE + const size_t& size() const + { + return m_size; + } + + void barrier() const; + + template <typename DataType> + DataType allReduceMin(const DataType& data) const + { +#ifdef PASTIS_HAS_MPI + static_assert(not std::is_const_v<DataType>); + static_assert(std::is_arithmetic_v<DataType>); + + MPI_Datatype mpi_datatype + = Messenger::helper::mpiType<DataType>(); + + DataType min_data = data; + MPI_Allreduce(&data, &min_data, 1, mpi_datatype, MPI_MIN, MPI_COMM_WORLD); + + return min_data; +#else // PASTIS_HAS_MPI + return data; +#endif // PASTIS_HAS_MPI + } + + template <typename DataType> + DataType allReduceMax(const DataType& data) const + { +#ifdef PASTIS_HAS_MPI + static_assert(not std::is_const_v<DataType>); + static_assert(std::is_arithmetic_v<DataType>); + + MPI_Datatype mpi_datatype + = Messenger::helper::mpiType<DataType>(); + + DataType max_data = data; + MPI_Allreduce(&data, &max_data, 1, mpi_datatype, MPI_MAX, MPI_COMM_WORLD); + + return max_data; +#else // PASTIS_HAS_MPI + return data; +#endif // PASTIS_HAS_MPI + } + + template <typename DataType> + DataType allReduceSum(const DataType& data) const + { +#ifdef PASTIS_HAS_MPI + static_assert(not std::is_const_v<DataType>); + if constexpr(std::is_arithmetic_v<DataType>) { + MPI_Datatype mpi_datatype + = Messenger::helper::mpiType<DataType>(); + + DataType data_sum = data; + MPI_Allreduce(&data, &data_sum, 1, mpi_datatype, MPI_SUM, MPI_COMM_WORLD); + + return data_sum; + } else if (is_trivially_castable<DataType>){ + using InnerDataType = typename DataType::data_type; + + MPI_Datatype mpi_datatype + = Messenger::helper::mpiType<InnerDataType>(); + const int size = sizeof(DataType)/sizeof(InnerDataType); + DataType data_sum = data; + MPI_Allreduce(&data, &data_sum, size, mpi_datatype, MPI_SUM, MPI_COMM_WORLD); + + return data_sum; + } +#else // PASTIS_HAS_MPI + return data; +#endif // PASTIS_HAS_MPI + } + + template <typename DataType> + PASTIS_INLINE + Array<DataType> + allGather(const DataType& data) const + { + static_assert(not std::is_const_v<DataType>); + + Array<DataType> gather_array(m_size); + + if constexpr(std::is_arithmetic_v<DataType>) { + _allGather(data, gather_array); + } else if constexpr(is_trivially_castable<DataType>) { + using CastType = helper::split_cast_t<DataType>; + + CastArray cast_value_array = cast_value_to<const CastType>::from(data); + CastArray cast_gather_array = cast_array_to<CastType>::from(gather_array); + + _allGather(cast_value_array, cast_gather_array); + } else { + static_assert(is_false_v<DataType>, "unexpected type of data"); + } + return gather_array; + } + + template <typename DataType> + PASTIS_INLINE + Array<std::remove_const_t<DataType>> + allGather(const Array<DataType>& array) const + { + using MutableDataType = std::remove_const_t<DataType>; + Array<MutableDataType> gather_array(m_size*array.size()); + + if constexpr(std::is_arithmetic_v<DataType>) { + _allGather(array, gather_array); + } else if constexpr(is_trivially_castable<DataType>) { + using CastType = helper::split_cast_t<DataType>; + using MutableCastType = helper::split_cast_t<MutableDataType>; + + CastArray cast_array = cast_array_to<CastType>::from(array); + CastArray cast_gather_array = cast_array_to<MutableCastType>::from(gather_array); + + _allGather(cast_array, cast_gather_array); + } else { + static_assert(is_false_v<DataType>, "unexpected type of data"); + } + return gather_array; + } + + template <typename SendDataType> + PASTIS_INLINE + Array<std::remove_const_t<SendDataType>> + allToAll(const Array<SendDataType>& sent_array) const + { +#ifndef NDEBUG + const size_t min_size = allReduceMin(sent_array.size()); + const size_t max_size = allReduceMax(sent_array.size()); + Assert(max_size == min_size); // LCOV_EXCL_LINE +#endif // NDEBUG + Assert((sent_array.size() % m_size) == 0); // LCOV_EXCL_LINE + + using DataType = std::remove_const_t<SendDataType>; + Array<DataType> recv_array(sent_array.size()); + + if constexpr(std::is_arithmetic_v<DataType>) { + _allToAll(sent_array, recv_array); + } else if constexpr(is_trivially_castable<DataType>) { + using CastType = helper::split_cast_t<DataType>; + + auto send_cast_array = cast_array_to<const CastType>::from(sent_array); + auto recv_cast_array = cast_array_to<CastType>::from(recv_array); + _allToAll(send_cast_array, recv_cast_array); + } else { + static_assert(is_false_v<DataType>, "unexpected type of data"); + } + return recv_array; + } + + template <typename DataType> + PASTIS_INLINE + void broadcast(DataType& data, const size_t& root_rank) const + { + static_assert(not std::is_const_v<DataType>, + "cannot broadcast const data"); + if constexpr(std::is_arithmetic_v<DataType>) { + _broadcast_value(data, root_rank); + } else if constexpr(is_trivially_castable<DataType>) { + using CastType = helper::split_cast_t<DataType>; + if constexpr(sizeof(CastType) == sizeof(DataType)) { + CastType& cast_data = reinterpret_cast<CastType&>(data); + _broadcast_value(cast_data, root_rank); + } else { + CastArray cast_array = cast_value_to<CastType>::from(data); + _broadcast_array(cast_array, root_rank); + } + } else { + static_assert(is_false_v<DataType>, "unexpected type of data"); + } + } + + template <typename DataType> + PASTIS_INLINE + void broadcast(Array<DataType>& array, + const size_t& root_rank) const + { + static_assert(not std::is_const_v<DataType>, + "cannot broadcast array of const"); + if constexpr(std::is_arithmetic_v<DataType>) { + size_t size = array.size(); + _broadcast_value(size, root_rank); + if (m_rank != root_rank) { + array = Array<DataType>(size); // LCOV_EXCL_LINE + } + _broadcast_array(array, root_rank); + } else if constexpr(is_trivially_castable<DataType>) { + size_t size = array.size(); + _broadcast_value(size, root_rank); + if (m_rank != root_rank) { + array = Array<DataType>(size); // LCOV_EXCL_LINE + } + + using CastType = helper::split_cast_t<DataType>; + auto cast_array = cast_array_to<CastType>::from(array); + _broadcast_array(cast_array, root_rank); + } else{ + static_assert(is_false_v<DataType>, "unexpected type of data"); + } + } + + template <typename SendDataType, + typename RecvDataType> + PASTIS_INLINE + void exchange(const std::vector<Array<SendDataType>>& send_array_list, + std::vector<Array<RecvDataType>>& recv_array_list) const + { + static_assert(std::is_same_v<std::remove_const_t<SendDataType>,RecvDataType>, + "send and receive data type do not match"); + static_assert(not std::is_const_v<RecvDataType>, + "receive data type cannot be const"); + using DataType = std::remove_const_t<SendDataType>; + + Assert(send_array_list.size() == m_size); // LCOV_EXCL_LINE + Assert(recv_array_list.size() == m_size); // LCOV_EXCL_LINE +#ifndef NDEBUG + Array<size_t> send_size(m_size); + for (size_t i=0; i<m_size; ++i) { + send_size[i] = send_array_list[i].size(); + } + Array<size_t> recv_size = allToAll(send_size); + bool correct_sizes = true; + for (size_t i=0; i<m_size; ++i) { + correct_sizes &= (recv_size[i] == recv_array_list[i].size()); + } + Assert(correct_sizes); // LCOV_EXCL_LINE +#endif // NDEBUG + + if constexpr(std::is_arithmetic_v<DataType>) { + _exchange(send_array_list, recv_array_list); + } else if constexpr(is_trivially_castable<DataType>) { + using CastType = helper::split_cast_t<DataType>; + _exchange_through_cast<SendDataType, CastType>(send_array_list, recv_array_list); + } else { + static_assert(is_false_v<RecvDataType>, "unexpected type of data"); + } + } + + Messenger(const Messenger&) = delete; + ~Messenger(); +}; + +PASTIS_INLINE +const Messenger& messenger() +{ + return Messenger::getInstance(); +} + +PASTIS_INLINE +const size_t& rank() +{ + return messenger().rank(); +} + +PASTIS_INLINE +const size_t& size() +{ + return messenger().size(); +} + +PASTIS_INLINE +void barrier() +{ + return messenger().barrier(); +} + +template <typename DataType> +PASTIS_INLINE +DataType allReduceMax(const DataType& data) +{ + return messenger().allReduceMax(data); +} + +template <typename DataType> +PASTIS_INLINE +DataType allReduceMin(const DataType& data) +{ + return messenger().allReduceMin(data); +} + +template <typename DataType> +PASTIS_INLINE +DataType allReduceSum(const DataType& data) +{ + return messenger().allReduceSum(data); +} + +template <typename DataType> +PASTIS_INLINE +Array<DataType> +allGather(const DataType& data) +{ + return messenger().allGather(data); +} + +template <typename DataType> +PASTIS_INLINE +Array<std::remove_const_t<DataType>> +allGather(const Array<DataType>& array) +{ + return messenger().allGather(array); +} + +template <typename DataType> +PASTIS_INLINE +Array<std::remove_const_t<DataType>> +allToAll(const Array<DataType>& array) +{ + return messenger().allToAll(array); +} + +template <typename DataType> +PASTIS_INLINE +void broadcast(DataType& data, const size_t& root_rank) +{ + messenger().broadcast(data, root_rank); +} + +template <typename DataType> +PASTIS_INLINE +void broadcast(Array<DataType>& array, const size_t& root_rank) +{ + messenger().broadcast(array, root_rank); +} + +template <typename SendDataType, + typename RecvDataType> +PASTIS_INLINE +void exchange(const std::vector<Array<SendDataType>>& sent_array_list, + std::vector<Array<RecvDataType>>& recv_array_list) +{ + static_assert(std::is_same_v<std::remove_const_t<SendDataType>,RecvDataType>, + "send and receive data type do not match"); + static_assert(not std::is_const_v<RecvDataType>, + "receive data type cannot be const"); + + messenger().exchange(sent_array_list, recv_array_list); +} + +#ifdef PASTIS_HAS_MPI + +template<> PASTIS_INLINE MPI_Datatype +Messenger::helper::mpiType<char>() {return MPI_CHAR; } + +template<> PASTIS_INLINE MPI_Datatype +Messenger::helper::mpiType<int8_t>() {return MPI_INT8_T; } + +template<> PASTIS_INLINE MPI_Datatype +Messenger::helper::mpiType<int16_t>() {return MPI_INT16_T; } + +template<> PASTIS_INLINE MPI_Datatype +Messenger::helper::mpiType<int32_t>() {return MPI_INT32_T; } + +template<> PASTIS_INLINE MPI_Datatype +Messenger::helper::mpiType<int64_t>() {return MPI_INT64_T; } + +template<> PASTIS_INLINE MPI_Datatype +Messenger::helper::mpiType<uint8_t>() {return MPI_UINT8_T; } + +template<> PASTIS_INLINE MPI_Datatype +Messenger::helper::mpiType<uint16_t>() {return MPI_UINT16_T; } + +template<> PASTIS_INLINE MPI_Datatype +Messenger::helper::mpiType<uint32_t>() {return MPI_UINT32_T; } + +template<> PASTIS_INLINE MPI_Datatype +Messenger::helper::mpiType<uint64_t>() {return MPI_UINT64_T; } + +template<> PASTIS_INLINE MPI_Datatype +Messenger::helper::mpiType<signed long long int>() {return MPI_LONG_LONG_INT; } + +template<> PASTIS_INLINE MPI_Datatype +Messenger::helper::mpiType<unsigned long long int>() {return MPI_UNSIGNED_LONG_LONG; } + +template<> PASTIS_INLINE MPI_Datatype +Messenger::helper::mpiType<float>() {return MPI_FLOAT; } + +template<> PASTIS_INLINE MPI_Datatype +Messenger::helper::mpiType<double>() {return MPI_DOUBLE; } + +template<> PASTIS_INLINE MPI_Datatype +Messenger::helper::mpiType<long double>() {return MPI_LONG_DOUBLE; } + +template<> PASTIS_INLINE MPI_Datatype +Messenger::helper::mpiType<wchar_t>() {return MPI_WCHAR; } + +template<> PASTIS_INLINE MPI_Datatype +Messenger::helper::mpiType<bool>() {return MPI_CXX_BOOL; } + +#endif // PASTIS_HAS_MPI + +} // namespace parallel + +#endif // MESSENGER_HPP diff --git a/src/utils/Partitioner.cpp b/src/utils/Partitioner.cpp new file mode 100644 index 0000000000000000000000000000000000000000..709038090a5454794bbff49ef9240f2ebd17a780 --- /dev/null +++ b/src/utils/Partitioner.cpp @@ -0,0 +1,91 @@ +#include <Partitioner.hpp> +#include <Messenger.hpp> +#include <pastis_config.hpp> + +#include <PastisOStream.hpp> + +#ifdef PASTIS_HAS_MPI + +#define IDXTYPEWIDTH 64 +#define REALTYPEWIDTH 64 +#include <parmetis.h> + + +#include <vector> + + +Array<int> Partitioner::partition(const CSRGraph& graph) +{ + pout() << "Partitioning graph into " + << rang::style::bold << parallel::size() << rang::style::reset + << " parts\n"; + + int wgtflag = 0; + int numflag = 0; + int ncon = 1; + int npart= parallel::size(); + std::vector<float> tpwgts(npart, 1./npart); + + std::vector<float> ubvec{1.05}; + std::vector<int> options{1,1,0}; + int edgecut = 0; + Array<int> part(0); + + MPI_Group world_group; + MPI_Comm_group(MPI_COMM_WORLD, &world_group); + + MPI_Group mesh_group; + std::vector<int> group_ranks + = [&]() { + Array<int> graph_node_owners + = parallel::allGather(static_cast<int>(graph.numberOfNodes())); + std::vector<int> group_ranks; + group_ranks.reserve(graph_node_owners.size()); + for (size_t i=0; i<graph_node_owners.size(); ++i) { + if (graph_node_owners[i] > 0) { + group_ranks.push_back(i); + } + } + return group_ranks; + } (); + + MPI_Group_incl(world_group, group_ranks.size(), &(group_ranks[0]), &mesh_group); + + MPI_Comm parmetis_comm; + MPI_Comm_create_group(MPI_COMM_WORLD, mesh_group, 1, &parmetis_comm); + + int local_number_of_nodes = graph.numberOfNodes(); + + if (graph.numberOfNodes() > 0) { + part = Array<int>(local_number_of_nodes); + std::vector<int> vtxdist{0,local_number_of_nodes}; + + const Array<int>& entries = graph.entries(); + const Array<int>& neighbors = graph.neighbors(); + + int result + = ParMETIS_V3_PartKway(&(vtxdist[0]), &(entries[0]), &(neighbors[0]), + NULL, NULL, &wgtflag, &numflag, + &ncon, &npart, &(tpwgts[0]), &(ubvec[0]), + &(options[0]), &edgecut, &(part[0]), &parmetis_comm); + if (result == METIS_ERROR) { + perr() << "Metis Error\n"; + std::exit(1); + } + + MPI_Comm_free(&parmetis_comm); + } + + MPI_Group_free(&mesh_group); + + return part; +} + +#else // PASTIS_HAS_MPI + +Array<int> Partitioner::partition(const CSRGraph&) +{ + return Array<int>(0); +} + +#endif // PASTIS_HAS_MPI diff --git a/src/utils/Partitioner.hpp b/src/utils/Partitioner.hpp new file mode 100644 index 0000000000000000000000000000000000000000..427d67f30d4af57f2fe096ffdb8b91736906ee0c --- /dev/null +++ b/src/utils/Partitioner.hpp @@ -0,0 +1,17 @@ +#ifndef PARTITIONER_HPP +#define PARTITIONER_HPP + +#include <CSRGraph.hpp> + +class Partitioner +{ + public: + Partitioner() = default; + Partitioner(const Partitioner&) = default; + ~Partitioner() = default; + + Array<int> partition(const CSRGraph& graph); +}; + + +#endif // PARTITIONER_HPP diff --git a/src/utils/PastisAssert.hpp b/src/utils/PastisAssert.hpp index d260ce25f293e955c0bbfbaa66861345658f9995..5093a44c21547709dce73cdea1e48015afb90e0a 100644 --- a/src/utils/PastisAssert.hpp +++ b/src/utils/PastisAssert.hpp @@ -1,16 +1,25 @@ #ifndef PASTIS_ASSERT_HPP #define PASTIS_ASSERT_HPP +#include <PastisMacros.hpp> + #include <rang.hpp> #include <iostream> #include <string> +template <typename ErrorType> +void printAndThrow(const ErrorType& error) +{ + throw error; +} + class AssertError { private: const std::string m_file; const int m_line; const std::string m_function; + const std::string m_test; const std::string m_message; public: @@ -20,11 +29,16 @@ class AssertError { os << '\n' << rang::style::bold - << "*** Assertion error ***\n" + << "---------- Assertion error -----------\n" << " at " << assert_error.m_file << ':' << assert_error.m_line << '\n' << " in " << assert_error.m_function << '\n' - << "*** " << rang::fgB::red << assert_error.m_message << rang::fg::reset - << rang::style::reset << '\n'; + << " assertion (" << rang::fgB::red << assert_error.m_test << rang::fg::reset + << ") failed!\n"; + if (not assert_error.m_message.empty()) { + os << ' ' << rang::fgB::yellow << assert_error.m_message + << rang::fg::reset << '\n'; + } + os << "--------------------------------------" << rang::style::reset << '\n'; return os; } @@ -33,10 +47,12 @@ class AssertError AssertError(std::string file, int line, std::string function, - std::string message) + std::string test, + std::string message="") : m_file(file), m_line(line), m_function(function), + m_test(test), m_message(message) { ; @@ -45,29 +61,44 @@ class AssertError ~AssertError() = default; }; -#pragma GCC diagnostic ignored "-Wattributes" +PRAGMA_DIAGNOSTIC_IGNORED_WATTRIBUTES inline bool __attribute__((analyzer_noreturn)) _pastis_assert(const bool& assert) { return assert; } -#pragma GCC diagnostic pop +PRAGMA_DIAGNOSTIC_POP #ifdef NDEBUG // Useless test is there to check syntax even in optimized mode. Costs nothing. -#define Assert(assertion) \ - if (not _pastis_assert(assertion)) {} +#define Assert(assertion,...) \ + if (not _pastis_assert(assertion)) { \ + using vargs_t = decltype(std::make_tuple(__VA_ARGS__)); \ + static_assert(std::tuple_size_v<vargs_t> <= 1, \ + "too many arguments"); \ + } #else // NDEBUG -#define Assert(assertion) \ - if (not _pastis_assert(assertion)) { \ - throw AssertError(__FILE__, \ - __LINE__, \ - __PRETTY_FUNCTION__, \ - (#assertion)); \ +#define Assert(assertion,...) \ + if (not _pastis_assert(assertion)) { \ + using vargs_t = decltype(std::make_tuple(__VA_ARGS__)); \ + static_assert(std::tuple_size_v<vargs_t> <= 1, \ + "too many arguments"); \ + if constexpr(std::tuple_size_v<vargs_t> == 0) { \ + printAndThrow(AssertError(__FILE__, \ + __LINE__, \ + __PRETTY_FUNCTION__, \ + #assertion)); \ + } else { \ + printAndThrow(AssertError(__FILE__, \ + __LINE__, \ + __PRETTY_FUNCTION__, \ + #assertion, \ + #__VA_ARGS__)); \ + } \ } #endif // NDEBUG diff --git a/src/utils/PastisMacros.hpp b/src/utils/PastisMacros.hpp index 59d2a579512a67a637f8c47f5745db89efea0a24..27f686688d90fd9ac640e2f73fba25258e56cd4c 100644 --- a/src/utils/PastisMacros.hpp +++ b/src/utils/PastisMacros.hpp @@ -10,4 +10,20 @@ #define PASTIS_LAMBDA KOKKOS_LAMBDA +// Sets macro to ignore unknown pragma + +#if !defined(__clang__) and defined(__GNUC__) + +#define PRAGMA_DIAGNOSTIC_IGNORED_WATTRIBUTES \ + _Pragma ("GCC diagnostic ignored \"-Wattributes\"") +#define PRAGMA_DIAGNOSTIC_POP \ + _Pragma ("GCC diagnostic pop") + +#else // !defined(__clang__) and defined(__GNUC__) + +#define PRAGMA_DIAGNOSTIC_IGNORED_WATTRIBUTES +#define PRAGMA_DIAGNOSTIC_POP + +#endif + #endif // PASTIS_MACROS_HPP diff --git a/src/utils/PastisOStream.cpp b/src/utils/PastisOStream.cpp index 19277e562e1274e537ffdee71b8642f145a806fc..af188ac6b8132c613f24443726c2eaf976af7d00 100644 --- a/src/utils/PastisOStream.cpp +++ b/src/utils/PastisOStream.cpp @@ -7,6 +7,8 @@ PastisOStream pout(std::cout); PastisOStream perr(std::cerr); std::stringstream null_stream; -const PastisOStream _null_stream_initializer(*[](std::stringstream& null_stream){ - null_stream.setstate(std::ios::badbit); - return &null_stream;}(null_stream)); +const PastisOStream _null_stream_initializer([]()-> std::stringstream& + { + null_stream.setstate(std::ios::badbit); + return null_stream; + }()); diff --git a/src/utils/PastisTraits.hpp b/src/utils/PastisTraits.hpp new file mode 100644 index 0000000000000000000000000000000000000000..871e30fd1c471ec67841b156ab89a624a41194b6 --- /dev/null +++ b/src/utils/PastisTraits.hpp @@ -0,0 +1,25 @@ +#ifndef PASTIS_TRAITS_HPP +#define PASTIS_TRAITS_HPP + +#include <type_traits> + +template <size_t N, typename T> class TinyVector; +template <size_t N, typename T> class TinyMatrix; + +template <typename T> +inline constexpr bool is_trivially_castable = std::is_trivial_v<T>; + +template <size_t N, typename T> +inline constexpr bool is_trivially_castable<TinyVector<N,T>> = is_trivially_castable<T>; +template <size_t N, typename T> +inline constexpr bool is_trivially_castable<const TinyVector<N,T>> = is_trivially_castable<T>; + +template <size_t N, typename T> +inline constexpr bool is_trivially_castable<TinyMatrix<N,T>> = is_trivially_castable<T>; +template <size_t N, typename T> +inline constexpr bool is_trivially_castable<const TinyMatrix<N,T>> = is_trivially_castable<T>; + +template <typename T> +inline constexpr bool is_false_v = false; + +#endif // PASTIS_TRAITS_HPP diff --git a/src/utils/PastisUtils.cpp b/src/utils/PastisUtils.cpp index b7250c194bd17c1a9915cd41323a7b3dd587fc11..4a22dec2bf165a73259f7aa519e7f8b3f18a9eb1 100644 --- a/src/utils/PastisUtils.cpp +++ b/src/utils/PastisUtils.cpp @@ -6,6 +6,8 @@ #include <RevisionInfo.hpp> #include <BuildInfo.hpp> +#include <Messenger.hpp> + #include <rang.hpp> #include <FPEManager.hpp> @@ -16,6 +18,8 @@ std::string initialize(int& argc, char* argv[]) { + parallel::Messenger::create(argc, argv); + long unsigned number = 10; std::string filename; @@ -46,7 +50,8 @@ std::string initialize(int& argc, char* argv[]) << '\n'; pout() << "type: " << rang::style::bold << BuildInfo::type() << rang::style::reset << '\n'; pout() << "compiler: " << rang::style::bold << BuildInfo::compiler() << rang::style::reset << '\n'; - pout() << "devices: " << rang::style::bold << BuildInfo::kokkosDevices() << rang::style::reset << '\n'; + pout() << "kokkos: " << rang::style::bold << BuildInfo::kokkosDevices() << rang::style::reset << '\n'; + pout() << "mpi: " << rang::style::bold << BuildInfo::mpiLibrary() << rang::style::reset << '\n'; pout() << "-------------------------------------------------------\n"; { CLI::App app{"Pastis help"}; @@ -73,7 +78,8 @@ std::string initialize(int& argc, char* argv[]) try { app.parse(argc, argv); } catch (const CLI::ParseError &e) { - std::exit(app.exit(e)); + parallel::Messenger::destroy(); + std::exit(app.exit(e, pout(), perr())); } ConsoleManager::init(colorize); @@ -101,4 +107,5 @@ std::string initialize(int& argc, char* argv[]) void finalize() { Kokkos::finalize(); + parallel::Messenger::destroy(); } diff --git a/src/utils/SignalManager.cpp b/src/utils/SignalManager.cpp index 09a1c5ab8f9a6b56f58e61cd645ba3da86db8a5d..525db43c8d14a0191f8c9d18790def38051c9761 100644 --- a/src/utils/SignalManager.cpp +++ b/src/utils/SignalManager.cpp @@ -1,5 +1,6 @@ #include <SignalManager.hpp> +#include <PastisAssert.hpp> #include <PastisOStream.hpp> #include <BacktraceManager.hpp> @@ -11,6 +12,8 @@ #include <pastis_config.hpp> #include <rang.hpp> +#include <Messenger.hpp> + std::string SignalManager::s_pause_on_error = "auto"; void SignalManager::setPauseForDebug(const std::string& pause_on_error) { @@ -32,23 +35,25 @@ std::string SignalManager::signalName(const int& signal) void SignalManager::pauseForDebug(const int& signal) { - - pout() << "\n======================================\n"; - pout() << rang::style::reset - << rang::fg::reset - << rang::style::bold; - pout() << "to attach gdb to this process run\n"; - pout() << "\tgdb -pid " - << rang::fg::red - << getpid() - << rang::fg::reset - << '\n'; - pout() << "else press Control-C to exit\n"; - pout() << rang::style::reset; - pout() << "======================================\n"; - pout() << std::flush; - - pause(); + if (std::string(PASTIS_BUILD_TYPE) != "Release") { + if (s_pause_on_error == "yes") { + std::cerr << "\n======================================\n" + << rang::style::reset + << rang::fg::reset + << rang::style::bold + << "to attach gdb to this process run\n" + << "\tgdb -pid " + << rang::fg::red + << getpid() + << rang::fg::reset + << '\n' + << "else press Control-C to exit\n" + << rang::style::reset + << "======================================\n" + << std::flush; + pause(); + } + } std::exit(signal); } @@ -60,11 +65,27 @@ void SignalManager::handler(int signal) std::signal(SIGINT, SIG_DFL); std::signal(SIGABRT, SIG_DFL); - perr() << "\n *** " + BacktraceManager bm; + std::cerr << bm << '\n'; + + std::exception_ptr eptr = std::current_exception(); + try { + if (eptr) { + std::rethrow_exception(eptr); + } + } + catch(const AssertError& assert_error) { + std::cerr << assert_error << '\n'; + } + catch(...) { + std::cerr << "Unknown exception!\n"; + } + + std::cerr << "\n *** " << rang::style::reset << rang::fg::reset - << rang::style::bold; - perr() << "Signal " + << rang::style::bold + << "Signal " << rang::fgB::red << signalName(signal) << rang::fg::reset @@ -72,16 +93,7 @@ void SignalManager::handler(int signal) << rang::style::reset << " ***\n"; - BacktraceManager bm; - perr() << bm << '\n'; - - if ((ConsoleManager::isTerminal(pout()) and - (s_pause_on_error == "auto")) or - (s_pause_on_error == "yes")) { - SignalManager::pauseForDebug(signal); - } else { - std::exit(signal); - } + SignalManager::pauseForDebug(signal); } void SignalManager::init(const bool& enable) diff --git a/src/utils/pastis_build_info.hpp.in b/src/utils/pastis_build_info.hpp.in index cd8756fc6bebedb8277b4d05bbbae280343bc686..86a2eff3dfe794b34a24c8722239c23bf4c21dce 100644 --- a/src/utils/pastis_build_info.hpp.in +++ b/src/utils/pastis_build_info.hpp.in @@ -3,6 +3,7 @@ #define PASTIS_BUILD_TYPE "@CMAKE_BUILD_TYPE@" #define PASTIS_BUILD_COMPILER "@CMAKE_CXX_COMPILER@" +#define PASTIS_BUILD_COMPILER_VERSION "@CMAKE_CXX_COMPILER_VERSION@" #define PASTIS_BUILD_KOKKOS_DEVICES "@KOKKOS_GMAKE_DEVICES@" #endif // PASTIS_BUILD_INFO_HPP diff --git a/src/utils/pastis_config.hpp.in b/src/utils/pastis_config.hpp.in index b27b12f70b2a5c397bb029d6f793dd8389ac78ad..9cd9e145b0bbf7b9a0075f8ec5a22fdf9cf61fb7 100644 --- a/src/utils/pastis_config.hpp.in +++ b/src/utils/pastis_config.hpp.in @@ -2,6 +2,8 @@ #define PASTIS_CONFIG_HPP #cmakedefine PASTIS_HAS_FENV_H +#cmakedefine PASTIS_HAS_MPI + #cmakedefine SYSTEM_IS_LINUX #cmakedefine SYSTEM_IS_DARWIN #cmakedefine SYSTEM_IS_WINDOWS diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 94118a24d6104092883f9e6280cfd08c5c4ba5d9..394e2c339c952c745e90aead7587631b2d3404f3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -13,6 +13,11 @@ add_executable (unit_tests test_TinyVector.cpp ) +add_executable (mpi_unit_tests + mpi_test_main.cpp + mpi_test_Messenger.cpp + ) + target_include_directories(Catch2 INTERFACE ${CATCH_INCLUDE_DIR}) target_link_libraries (unit_tests @@ -21,7 +26,21 @@ target_link_libraries (unit_tests Catch2 ) +target_link_libraries (mpi_unit_tests + PastisUtils + kokkos + ${PARMETIS_LIBRARIES} + ${MPI_CXX_LINK_FLAGS} ${MPI_CXX_LIBRARIES} + Catch2 + ) + enable_testing() #parse catch tests ParseAndAddCatchTests(unit_tests) + +if(${PASTIS_HAS_MPI}) +set(OptionalCatchTestLauncher ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} 3 --oversubscribe --path ${PASTIS_BINARY_DIR}) +endif() +ParseAndAddCatchTests(mpi_unit_tests) +unset(OptionalCatchTestLauncher) diff --git a/tests/mpi_test_Messenger.cpp b/tests/mpi_test_Messenger.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f6ad79c210efd1f541f75f4332989e0a34d87162 --- /dev/null +++ b/tests/mpi_test_Messenger.cpp @@ -0,0 +1,437 @@ +#include <catch.hpp> + +#include <Messenger.hpp> +#include <Array.hpp> + +#include <pastis_config.hpp> +#include <fstream> + +#ifdef PASTIS_HAS_MPI +#include <mpi.h> +#define IF_MPI(INSTRUCTION) INSTRUCTION +#else +#define IF_MPI(INSTRUCTION) +#endif // PASTIS_HAS_MPI + +namespace mpi_check +{ +struct integer +{ + int m_int; + operator int&() {return m_int;} + operator const int&() const {return m_int;} + integer& operator=(const int& i) {m_int = i; return *this;} +}; + +struct tri_int +{ + int m_int_0; + int m_int_1; + int m_int_2; + bool operator==(const tri_int& t) const { + return ((m_int_0 == t.m_int_0) and + (m_int_1 == t.m_int_1) and + (m_int_2 == t.m_int_2)); + } +}; + + +template <typename T> +void test_allToAll() +{ + Array<T> data_array(parallel::size()); + for (size_t i=0; i< data_array.size(); ++i) { + data_array[i] = parallel::rank(); + } + auto exchanged_array = parallel::allToAll(data_array); + + for (size_t i=0; i< data_array.size(); ++i) { + const size_t value = exchanged_array[i]; + REQUIRE(value == i); + } +} + +template <> +void test_allToAll<bool>() +{ + Array<bool> data_array(parallel::size()); + for (size_t i=0; i< data_array.size(); ++i) { + data_array[i] = ((parallel::rank()%2)==0); + } + auto exchanged_array = parallel::allToAll(data_array); + + for (size_t i=0; i< data_array.size(); ++i) { + REQUIRE(exchanged_array[i] == ((i%2)==0)); + } +} + +template <> +void test_allToAll<tri_int>() +{ + Array<tri_int> data_array(parallel::size()); + for (size_t i=0; i< data_array.size(); ++i) { + const int val = 1+parallel::rank(); + data_array[i] = tri_int{val, 2*val, val+3 }; + } + auto exchanged_array = parallel::allToAll(data_array); + + for (size_t i=0; i< data_array.size(); ++i) { + const int val = 1+i; + REQUIRE(exchanged_array[i] == tri_int{val, 2*val, val+3 }); + } +} + +} + +TEST_CASE("Messenger", "[mpi]") { + + SECTION("communication info") { + int rank=0; + IF_MPI(MPI_Comm_rank(MPI_COMM_WORLD, &rank)); + REQUIRE(rank == parallel::rank()); + + int size=1; + IF_MPI(MPI_Comm_size(MPI_COMM_WORLD, &size)); + REQUIRE(size == parallel::size()); + } + + SECTION("reduction") { + const int min_value = parallel::allReduceMin(parallel::rank()+3); + REQUIRE(min_value ==3); + + const int max_value = parallel::allReduceMax(parallel::rank()+3); + REQUIRE(max_value == ((parallel::size()-1) + 3)); + } + + SECTION("all to all") { + // chars + mpi_check::test_allToAll<char>(); + mpi_check::test_allToAll<wchar_t>(); + + // integers + mpi_check::test_allToAll<int8_t>(); + mpi_check::test_allToAll<int16_t>(); + mpi_check::test_allToAll<int32_t>(); + mpi_check::test_allToAll<int64_t>(); + mpi_check::test_allToAll<uint8_t>(); + mpi_check::test_allToAll<uint16_t>(); + mpi_check::test_allToAll<uint32_t>(); + mpi_check::test_allToAll<uint64_t>(); + mpi_check::test_allToAll<signed long long int>(); + mpi_check::test_allToAll<unsigned long long int>(); + + // floats + mpi_check::test_allToAll<float>(); + mpi_check::test_allToAll<double>(); + mpi_check::test_allToAll<long double>(); + + // bools + mpi_check::test_allToAll<bool>(); + + // trivial simple type + mpi_check::test_allToAll<mpi_check::integer>(); + + // compound trivial type + mpi_check::test_allToAll<mpi_check::tri_int>(); + +#ifndef NDEBUG + SECTION("checking invalid all to all") { + if (parallel::size() > 1) { + Array<int> invalid_all_to_all(parallel::size()+1); + REQUIRE_THROWS_AS(parallel::allToAll(invalid_all_to_all), AssertError); + + Array<int> different_size_all_to_all(parallel::size()*(parallel::rank()+1)); + REQUIRE_THROWS_AS(parallel::allToAll(different_size_all_to_all), AssertError); + } + } +#endif // NDEBUG + } + + SECTION("broadcast value") { + { + // simple type + size_t value{(3+parallel::rank())*2}; + parallel::broadcast(value, 0); + REQUIRE(value == 6); + } + + { + // trivial simple type + mpi_check::integer value{static_cast<int>((3+parallel::rank())*2)}; + parallel::broadcast(value, 0); + REQUIRE((value == 6)); + } + + { + // compound trivial type + mpi_check::tri_int value{static_cast<int>((3+parallel::rank())*2), + static_cast<int>(2+parallel::rank()), + static_cast<int>(4-parallel::rank())}; + parallel::broadcast(value, 0); + REQUIRE((value == mpi_check::tri_int{6,2,4})); + } + } + + SECTION("broadcast array") { + { + // simple type + Array<size_t> array(3); + array[0] = (3+parallel::rank())*2; + array[1] = 2+parallel::rank(); + array[2] = 4-parallel::rank(); + parallel::broadcast(array, 0); + REQUIRE(((array[0]==6) and (array[1]==2) and (array[2]==4))); + } + + { + // trivial simple type + Array<mpi_check::integer> array(3); + array[0] = static_cast<int>((3+parallel::rank())*2); + array[1] = static_cast<int>(2+parallel::rank()); + array[2] = static_cast<int>(4-parallel::rank()); + parallel::broadcast(array, 0); + REQUIRE(((array[0]==6) and (array[1]==2) and (array[2]==4))); + } + + { + // compound trivial type + Array<mpi_check::tri_int> array(3); + array[0] = mpi_check::tri_int{static_cast<int>((3+parallel::rank())*2), + static_cast<int>(2+parallel::rank()), + static_cast<int>(4-parallel::rank())}; + array[1] = mpi_check::tri_int{static_cast<int>((2+parallel::rank())*4), + static_cast<int>(3+parallel::rank()), + static_cast<int>(1-parallel::rank())}; + array[2] = mpi_check::tri_int{static_cast<int>((5+parallel::rank())), + static_cast<int>(-3+parallel::rank()), + static_cast<int>(parallel::rank())}; + parallel::broadcast(array, 0); + REQUIRE(((array[0] == mpi_check::tri_int{6, 2,4}) and + (array[1] == mpi_check::tri_int{8, 3,1}) and + (array[2] == mpi_check::tri_int{5,-3,0}))); + } + } + + SECTION("all gather value") { + { + // simple type + size_t value{(3+parallel::rank())*2}; + Array<size_t> gather_array = parallel::allGather(value); + REQUIRE(gather_array.size() == parallel::size()); + + for (size_t i=0; i<gather_array.size(); ++i) { + REQUIRE((gather_array[i] == (3+i)*2)); + } + } + + { + // trivial simple type + mpi_check::integer value{static_cast<int>((3+parallel::rank())*2)}; + Array<mpi_check::integer> gather_array = parallel::allGather(value); + REQUIRE(gather_array.size() == parallel::size()); + + for (size_t i=0; i<gather_array.size(); ++i) { + const int expected_value = (3+i)*2; + REQUIRE(gather_array[i] == expected_value); + } + } + + { + // compound trivial type + mpi_check::tri_int value{static_cast<int>((3+parallel::rank())*2), + static_cast<int>(2+parallel::rank()), + static_cast<int>(4-parallel::rank())}; + Array<mpi_check::tri_int> gather_array + = parallel::allGather(value); + + REQUIRE(gather_array.size() == parallel::size()); + for (size_t i=0; i<gather_array.size(); ++i) { + mpi_check::tri_int expected_value{static_cast<int>((3+i)*2), + static_cast<int>(2+i), + static_cast<int>(4-i)}; + REQUIRE((gather_array[i] == expected_value)); + } + } + } + + SECTION("all gather array") { + { + // simple type + Array<int> array(3); + for (size_t i=0; i<array.size(); ++i) { + array[i] = (3+parallel::rank())*2+i; + } + Array<int> gather_array = parallel::allGather(array); + REQUIRE(gather_array.size() == array.size()*parallel::size()); + + for (size_t i=0; i<gather_array.size(); ++i) { + const int expected_value = (3+i/array.size())*2+(i%array.size()); + REQUIRE((gather_array[i] == expected_value)); + } + } + + { + // trivial simple type + Array<mpi_check::integer> array(3); + for (size_t i=0; i<array.size(); ++i) { + array[i] = (3+parallel::rank())*2+i; + } + Array<mpi_check::integer> gather_array = parallel::allGather(array); + REQUIRE(gather_array.size() == array.size()*parallel::size()); + + for (size_t i=0; i<gather_array.size(); ++i) { + const int expected_value = (3+i/array.size())*2+(i%array.size()); + REQUIRE((gather_array[i] == expected_value)); + } + } + + { + // compound trivial type + Array<mpi_check::tri_int> array(3); + for (size_t i=0; i<array.size(); ++i) { + array[i] = mpi_check::tri_int{static_cast<int>((3+parallel::rank())*2), + static_cast<int>(2+parallel::rank()+i), + static_cast<int>(4-parallel::rank()-i)}; + } + Array<mpi_check::tri_int> gather_array + = parallel::allGather(array); + + REQUIRE(gather_array.size() == array.size()*parallel::size()); + for (size_t i=0; i<gather_array.size(); ++i) { + mpi_check::tri_int expected_value{static_cast<int>((3+i/array.size())*2), + static_cast<int>(2+i/array.size()+(i%array.size())), + static_cast<int>(4-i/array.size()-(i%array.size()))}; + REQUIRE((gather_array[i] == expected_value)); + } + } + } + + SECTION("all array exchanges") { + { // simple type + std::vector<Array<const int>> send_array_list(parallel::size()); + for (size_t i=0; i<send_array_list.size(); ++i) { + Array<int> send_array(i+1); + for (size_t j=0; j<send_array.size(); ++j) { + send_array[j] = (parallel::rank()+1)*j; + } + send_array_list[i] = send_array; + } + + std::vector<Array<int>> recv_array_list(parallel::size()); + for (size_t i=0; i<recv_array_list.size(); ++i) { + recv_array_list[i] = Array<int>(parallel::rank()+1); + } + parallel::exchange(send_array_list, recv_array_list); + + for (size_t i=0; i<parallel::size(); ++i) { + const Array<const int> recv_array = recv_array_list[i]; + for (size_t j=0; j<recv_array.size(); ++j) { + REQUIRE(recv_array[j] == (i+1)*j); + } + } + } + + { // trivial simple type + std::vector<Array<mpi_check::integer>> send_array_list(parallel::size()); + for (size_t i=0; i<send_array_list.size(); ++i) { + Array<mpi_check::integer> send_array(i+1); + for (size_t j=0; j<send_array.size(); ++j) { + send_array[j] = static_cast<int>((parallel::rank()+1)*j); + } + send_array_list[i] = send_array; + } + + std::vector<Array<mpi_check::integer>> recv_array_list(parallel::size()); + for (size_t i=0; i<recv_array_list.size(); ++i) { + recv_array_list[i] = Array<mpi_check::integer>(parallel::rank()+1); + } + parallel::exchange(send_array_list, recv_array_list); + + for (size_t i=0; i<parallel::size(); ++i) { + const Array<const mpi_check::integer> recv_array = recv_array_list[i]; + for (size_t j=0; j<recv_array.size(); ++j) { + REQUIRE(recv_array[j] == (i+1)*j); + } + } + } + + { + // compound trivial type + std::vector<Array<mpi_check::tri_int>> send_array_list(parallel::size()); + for (size_t i=0; i<send_array_list.size(); ++i) { + Array<mpi_check::tri_int> send_array(i+1); + for (size_t j=0; j<send_array.size(); ++j) { + send_array[j] = mpi_check::tri_int{static_cast<int>((parallel::rank()+1)*j), + static_cast<int>(parallel::rank()), + static_cast<int>(j)}; + } + send_array_list[i] = send_array; + } + + std::vector<Array<mpi_check::tri_int>> recv_array_list(parallel::size()); + for (size_t i=0; i<recv_array_list.size(); ++i) { + recv_array_list[i] = Array<mpi_check::tri_int>(parallel::rank()+1); + } + parallel::exchange(send_array_list, recv_array_list); + + for (size_t i=0; i<parallel::size(); ++i) { + const Array<const mpi_check::tri_int> recv_array = recv_array_list[i]; + for (size_t j=0; j<recv_array.size(); ++j) { + mpi_check::tri_int expected_value{static_cast<int>((i+1)*j), + static_cast<int>(i), + static_cast<int>(j)}; + REQUIRE((recv_array[j] == expected_value)); + } + } + } + } + +#ifndef NDEBUG + SECTION("checking all array exchange invalid sizes") { + std::vector<Array<const int>> send_array_list(parallel::size()); + for (size_t i=0; i<send_array_list.size(); ++i) { + Array<int> send_array(i+1); + send_array.fill(parallel::rank()); + send_array_list[i] = send_array; + } + + std::vector<Array<int>> recv_array_list(parallel::size()); + REQUIRE_THROWS_AS(parallel::exchange(send_array_list, recv_array_list), AssertError); + } +#endif // NDEBUG + + + SECTION("checking barrier") { + for (size_t i=0; i<parallel::size(); ++i) { + if (i==parallel::rank()) { + std::ofstream file; + if (i==0) { + file.open("barrier_test", std::ios_base::out); + } else { + file.open("barrier_test", std::ios_base::app); + } + file << i << "\n" << std::flush; + } + parallel::barrier(); + } + + { // reading produced file + std::ifstream file("barrier_test"); + std::vector<size_t> number_list; + while (file) { + size_t value; + file >> value; + if (file) { + number_list.push_back(value); + } + } + REQUIRE(number_list.size() == parallel::size()); + for (size_t i=0; i<number_list.size(); ++i) { + REQUIRE(number_list[i] == i); + } + } + parallel::barrier(); + + std::remove("barrier_test"); + } +} diff --git a/tests/mpi_test_main.cpp b/tests/mpi_test_main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c3b8593f365b621da12155bc1c2f717d93a90a19 --- /dev/null +++ b/tests/mpi_test_main.cpp @@ -0,0 +1,24 @@ +#define CATCH_CONFIG_RUNNER +#include <catch.hpp> + +#include <Kokkos_Core.hpp> +#include <Messenger.hpp> + +#include <cstdlib> + +int main( int argc, char* argv[] ) +{ + parallel::Messenger::create(argc, argv); + Kokkos::initialize({4,-1,-1,true}); + + if (parallel::rank() != 0) { + setenv("GCOV_PREFIX", "/dev/null", 1); + } + + int result = Catch::Session().run( argc, argv ); + + Kokkos::finalize(); + parallel::Messenger::destroy(); + + return result; +} diff --git a/tests/test_Array.cpp b/tests/test_Array.cpp index 6cd02146c8775dca7a061672b8efed1fbdcad236..8bac68679ce4bd245fc1ff846adbfaf4a691e82a 100644 --- a/tests/test_Array.cpp +++ b/tests/test_Array.cpp @@ -4,6 +4,13 @@ #include <Array.hpp> #include <Types.hpp> +#include <vector> +#include <set> +#include <list> +#include <deque> +#include <valarray> +#include <unordered_set> + // Instantiate to ensure full coverage is performed template class Array<int>; @@ -50,7 +57,19 @@ TEST_CASE("Array", "[utils]") { } - SECTION("checking for affectations") { + SECTION("checking for fill") { + Array<int> b(10); + b.fill(3); + + REQUIRE(((b[0] == 3) and (b[1] == 3) and + (b[2] == 3) and (b[3] == 3) and + (b[4] == 3) and (b[5] == 3) and + (b[6] == 3) and (b[7] == 3) and + (b[8] == 3) and (b[9] == 3))); + + } + + SECTION("checking for affectations (shallow copy)") { Array<const int> b; b = a; @@ -78,7 +97,154 @@ TEST_CASE("Array", "[utils]") { (d[4] == 8) and (d[5] ==10) and (d[6] ==12) and (d[7] ==14) and (d[8] ==16) and (d[9] ==18))); + + } + + SECTION("checking for affectations (deep copy)") { + Array<int> b(copy(a)); + + REQUIRE(((b[0] == 0) and (b[1] == 2) and + (b[2] == 4) and (b[3] == 6) and + (b[4] == 8) and (b[5] ==10) and + (b[6] ==12) and (b[7] ==14) and + (b[8] ==16) and (b[9] ==18))); + + b.fill(2); + + REQUIRE(((a[0] == 0) and (a[1] == 2) and + (a[2] == 4) and (a[3] == 6) and + (a[4] == 8) and (a[5] ==10) and + (a[6] ==12) and (a[7] ==14) and + (a[8] ==16) and (a[9] ==18))); + + REQUIRE(((b[0] == 2) and (b[1] == 2) and + (b[2] == 2) and (b[3] == 2) and + (b[4] == 2) and (b[5] == 2) and + (b[6] == 2) and (b[7] == 2) and + (b[8] == 2) and (b[9] == 2))); + + Array<int> c; + c = a; + + REQUIRE(((c[0] == 0) and (c[1] == 2) and + (c[2] == 4) and (c[3] == 6) and + (c[4] == 8) and (c[5] ==10) and + (c[6] ==12) and (c[7] ==14) and + (c[8] ==16) and (c[9] ==18))); + + c = copy(b); + + REQUIRE(((a[0] == 0) and (a[1] == 2) and + (a[2] == 4) and (a[3] == 6) and + (a[4] == 8) and (a[5] ==10) and + (a[6] ==12) and (a[7] ==14) and + (a[8] ==16) and (a[9] ==18))); + + REQUIRE(((c[0] == 2) and (c[1] == 2) and + (c[2] == 2) and (c[3] == 2) and + (c[4] == 2) and (c[5] == 2) and + (c[6] == 2) and (c[7] == 2) and + (c[8] == 2) and (c[9] == 2))); + } + + SECTION("checking for std container conversion") { + { + std::vector<int> v{1,2,5,3}; + { + Array<int> v_array = convert_to_array(v); + + REQUIRE(v_array.size() == v.size()); + REQUIRE(((v_array[0] == 1) and (v_array[1] == 2) and + (v_array[2] == 5) and (v_array[3] == 3))); + } + + { + Array<const int> v_array = convert_to_array(v); + + REQUIRE(v_array.size() == v.size()); + REQUIRE(((v_array[0] == 1) and (v_array[1] == 2) and + (v_array[2] == 5) and (v_array[3] == 3))); + } + } + + { + std::vector<int> w; + { + Array<int> w_array = convert_to_array(w); + REQUIRE(w_array.size() == 0); + } + { + Array<const int> w_array = convert_to_array(w); + REQUIRE(w_array.size() == 0); + } + } + + { + std::valarray<int> v{1,2,5,3}; + Array<int> v_array = convert_to_array(v); + + REQUIRE(v_array.size() == v.size()); + REQUIRE(((v_array[0] == 1) and (v_array[1] == 2) and + (v_array[2] == 5) and (v_array[3] == 3))); + } + + { + std::set<int> s{4,2,5,3,1,3,2}; + Array<int> s_array = convert_to_array(s); + + REQUIRE(s_array.size() == s.size()); + REQUIRE(((s_array[0] == 1) and (s_array[1] == 2) and + (s_array[2] == 3) and (s_array[3] == 4) and + (s_array[4] == 5))); + } + + { + std::unordered_set<int> us{4,2,5,3,1,3,2}; + Array<int> us_array = convert_to_array(us); + + REQUIRE(us_array.size() == us.size()); + + std::set<int> s; + for (size_t i=0; i<us_array.size(); ++i) { + REQUIRE((us.find(us_array[i]) != us.end())); + s.insert(us_array[i]); + } + REQUIRE(s.size() == us_array.size()); + } + + { + std::multiset<int> ms{4,2,5,3,1,3,2}; + Array<int> ms_array = convert_to_array(ms); + + REQUIRE(ms_array.size() == ms.size()); + REQUIRE(((ms_array[0] == 1) and (ms_array[1] == 2) and + (ms_array[2] == 2) and (ms_array[3] == 3) and + (ms_array[4] == 3) and (ms_array[5] == 4) and + (ms_array[6] == 5))); + } + + { + std::list<int> l{1,3,5,6,2}; + Array<int> l_array = convert_to_array(l); + + REQUIRE(l_array.size() == l.size()); + REQUIRE(((l_array[0] == 1) and (l_array[1] == 3) and + (l_array[2] == 5) and (l_array[3] == 6) and + (l_array[4] == 2))); + } + + { + std::deque<int> q{1,3,5,6,2}; + q.push_front(2); + Array<int> q_array = convert_to_array(q); + + REQUIRE(q_array.size() == q.size()); + REQUIRE(((q_array[0] == 2) and (q_array[1] == 1) and + (q_array[2] == 3) and (q_array[3] == 5) and + (q_array[4] == 6) and (q_array[5] == 2))); + } } + #ifndef NDEBUG SECTION("checking for bounds violation") { REQUIRE_THROWS_AS(a[10], AssertError); diff --git a/tests/test_ArrayUtils.cpp b/tests/test_ArrayUtils.cpp index 8183e7157a2c39d3f27b3fbd703917fcdf567ff1..9d56667850c6ca727a154c6ef20fa40b6ba01ac1 100644 --- a/tests/test_ArrayUtils.cpp +++ b/tests/test_ArrayUtils.cpp @@ -4,15 +4,15 @@ #include <Array.hpp> #include <ArrayUtils.hpp> +#include <TinyVector.hpp> +#include <TinyMatrix.hpp> + // Instantiate to ensure full coverage is performed template class Array<int>; TEST_CASE("ArrayUtils", "[utils]") { - SECTION("checking for reductions") { - using ArrayType = Array<int>; - using data_type = ArrayType::data_type; - + SECTION("checking for Array reductions") { Array<int> a(10); a[0] =13; a[1] = 1; @@ -25,72 +25,51 @@ TEST_CASE("ArrayUtils", "[utils]") { a[8] =12; a[9] = 9; - SECTION("ReduceMin") { - ReduceMin r_min(a); - data_type data; - - r_min.init(data); - REQUIRE(data == std::numeric_limits<data_type>::max()); - - r_min(2,data); - REQUIRE(data == 8); - - r_min(9,data); - REQUIRE(data == 8); - - r_min(5,data); - REQUIRE(data == -1); - - { - data = 0; - data_type r_value{3}; - r_min.join(data, r_value); - REQUIRE(data == 0); - } - - { - data = 0; - data_type r_value{-5}; - r_min.join(data, r_value); - REQUIRE(data == -5); - } - - REQUIRE((r_min == -3)); - - REQUIRE((ReduceMin(a) == -3)); + SECTION("Min") { + REQUIRE((min(a) == -3)); } - SECTION("ReduceMax") { - ReduceMax r_max(a); - data_type data; - - r_max.init(data); - REQUIRE(data == -std::numeric_limits<data_type>::max()); - - r_max(3,data); - REQUIRE(data == -3); - r_max(5,data); - REQUIRE(data == -1); - r_max(8,data); - REQUIRE(data == 12); - - { - data = 0; - data_type r_value{3}; - r_max.join(data, r_value); - REQUIRE(data == 3); - } + SECTION("Max") { + REQUIRE((max(a) == 23)); + } - { - data = 0; - data_type r_value{-5}; - r_max.join(data, r_value); - REQUIRE(data == 0); - } + SECTION("Sum") { + REQUIRE((sum(a) == 75)); + } - REQUIRE((r_max == 23)); + SECTION("TinyVector Sum") { + using N2 = TinyVector<2,int>; + Array<N2> b(10); + b[0] ={13, 2}; + b[1] = {1, 3}; + b[2] = {8, -2}; + b[3] ={-3, 2}; + b[4] ={23, 4}; + b[5] ={-1, -3}; + b[6] ={13, 17}; + b[7] = {0, 9}; + b[8] ={12, 13}; + b[9] = {9, -17}; + + REQUIRE((sum(b) == N2{75, 28})); + } - REQUIRE((ReduceMax(a) == 23)); + SECTION("TinyMatrix Sum") { + using N22 = TinyMatrix<2,int>; + Array<N22> b(10); + b[0] ={13, 2, 0, 1}; + b[1] = {1, 3, 6, 3}; + b[2] = {8, -2, -1, 21}; + b[3] ={-3, 2, 5, 12}; + b[4] ={23, 4, 7, 1}; + b[5] ={-1, -3, 33, 11}; + b[6] ={13, 17, 12, 13}; + b[7] = {0, 9, 1, 14}; + b[8] ={12, 13, -3,-71}; + b[9] = {9,-17, 0, 16}; + + REQUIRE((sum(b) == N22{75, 28, 60, 21})); } + } } diff --git a/tests/test_PastisAssert.cpp b/tests/test_PastisAssert.cpp index d55ad8d826d75ac10e81b5b110495e74d131e921..90995c30f9782baa8f2c0468909983ac166e3a9c 100644 --- a/tests/test_PastisAssert.cpp +++ b/tests/test_PastisAssert.cpp @@ -8,10 +8,22 @@ TEST_CASE("PastisAssert", "[utils]") { const std::string filename = "filename"; const int line = 10; const std::string function = "function"; + const std::string test = "test"; + + AssertError assert_error(filename, line, function, test); + + REQUIRE(Catch::Detail::stringify(assert_error) == "\n---------- Assertion error -----------\n at filename:10\n in function\n assertion (test) failed!\n--------------------------------------\n"); + } + + SECTION("checking for assert error with message") { + const std::string filename = "filename"; + const int line = 10; + const std::string function = "function"; + const std::string test = "test"; const std::string message = "message"; - AssertError assert_error(filename, line, function, message); + AssertError assert_error(filename, line, function, test, message); - REQUIRE(Catch::Detail::stringify(assert_error) == "\n*** Assertion error ***\n at filename:10\n in function\n*** message\n"); + REQUIRE(Catch::Detail::stringify(assert_error) == "\n---------- Assertion error -----------\n at filename:10\n in function\n assertion (test) failed!\n message\n--------------------------------------\n"); } }