diff --git a/CMakeLists.txt b/CMakeLists.txt index 8047520391cb75d3f46acad5c87a720414f3c040..7c0f69fcf6395a96f88e74e9143e8e85730ba66e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,12 +22,20 @@ if("${PUGS_SHORT_VERSION}" STREQUAL "") message(FATAL_ERROR "Unable to compute short version from PUGS_VERSION=${PUGS_VERSION}") endif() - -set(CMAKE_CONFIGURATION_TYPES "Release;Debug;Coverage" CACHE STRING INTERNAL FORCE ) - # set project version as PUGS_SHORT_VERSION project (Pugs VERSION ${PUGS_SHORT_VERSION}) +# ----------------------------------------------------- +# dynamic libraries + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +link_libraries("-rdynamic") +set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") + +#------------------------------------------------------ + +set(CMAKE_CONFIGURATION_TYPES "Release;Debug;Coverage" CACHE STRING INTERNAL FORCE ) + #------------------------------------------------------ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -133,9 +141,42 @@ endif() #------------------------------------------------------ # Search for ParMETIS +find_package(ParMETIS) +if(PARMETIS_LIBRARIES) + add_library(PkgConfig::ParMETIS STATIC IMPORTED) + set_property(TARGET PkgConfig::ParMETIS PROPERTY + IMPORTED_LOCATION "${PARMETIS_LIBRARIES}") + + set(PUGS_HAS_PARMETIS TRUE) + set(PARMETIS_TARGET PkgConfig::ParMETIS) +else() + unset(PUGS_HAS_PARMETIS) +endif() + +#------------------------------------------------------ +# Search for PTScotch + +find_package(PTScotch) + +set(PUGS_HAS_PTSCOTCH ${PTScotch_FOUND}) + +if (PTScotch_FOUND) + add_library(PkgConfig::PTScotch STATIC IMPORTED) + set_property(TARGET PkgConfig::PTScotch PROPERTY + IMPORTED_LOCATION "${PTSCOTCH_LIBRARY}") + set_property(TARGET PkgConfig::PTScotch PROPERTY + INTERFACE_INCLUDE_DIRECTORIES "${PTSCOTCH_INCLUDE_DIR}") + + set(PTSCOTCH_TARGET PkgConfig::PTScotch) + include_directories(SYSTEM "${PTSCOTCH_INCLUDE_DIR}") +else() + set(PTSCOTCH_LIBRARY "") +endif() + +#------------------------------------------------------ + if(${MPI_FOUND}) - find_package(ParMETIS) - if (NOT PARMETIS_LIBRARIES) + if ((NOT PARMETIS_LIBRARIES) AND (NOT PTSCOTCH_LIBRARIES)) if(PUGS_ENABLE_MPI MATCHES "^AUTO$") message(STATUS "MPI support deactivated: ParMETIS cannot be found!") unset(MPI_FOUND) @@ -160,7 +201,18 @@ set(PUGS_ENABLE_PETSC AUTO CACHE STRING if (PUGS_ENABLE_PETSC MATCHES "^(AUTO|ON)$") if (MPI_FOUND) # PETSc support is deactivated if MPI is not found - pkg_check_modules(PETSC PETSc) + pkg_check_modules(PETSC IMPORTED_TARGET GLOBAL PETSc) + + if (${PETSC_FOUND}) + set_property(TARGET PkgConfig::PETSC PROPERTY + IMPORTED_LOCATION "${PETSC_LIBRARIES}" + ) + set_property(TARGET PkgConfig::PETSC PROPERTY + INTERFACE_INCLUDE_DIRECTORIES "${PETSC_INCLUDE_DIRS}" + ) + + set(PETSC_TARGET PkgConfig::PETSC) + endif() else() message(STATUS "PETSc support is deactivated since pugs will not be build with MPI support") set(PETSC_FOUND FALSE) @@ -188,7 +240,18 @@ set(PUGS_ENABLE_SLEPC AUTO CACHE STRING if (PUGS_ENABLE_SLEPC MATCHES "^(AUTO|ON)$") if (PETSC_FOUND) # SLEPc support is deactivated if PETSc is not found - pkg_check_modules(SLEPC SLEPc) + pkg_check_modules(SLEPC IMPORTED_TARGET GLOBAL SLEPc) + + if (${SLEPC_FOUND}) + set_property(TARGET PkgConfig::SLEPC PROPERTY + IMPORTED_LOCATION "${SLEPC_LIBRARIES}" + ) + set_property(TARGET PkgConfig::SLEPC PROPERTY + INTERFACE_INCLUDE_DIRECTORIES "${SLEPC_INCLUDE_DIRS}" + ) + + set(SLEPC_TARGET PkgConfig::SLEPC) + endif() else() message(STATUS "SLEPc support is deactivated since pugs will not be build with PETSc support") set(SLEPC_FOUND FALSE) @@ -207,6 +270,26 @@ else() endif() endif() +# ----------------------------------------------------- +# Check for Eigen3 +# defaults use Eigen3 +set(PUGS_ENABLE_EIGEN3 AUTO CACHE STRING + "Choose one of: AUTO ON OFF") + +if (PUGS_ENABLE_EIGEN3 MATCHES "^(AUTO|ON)$") + find_package (Eigen3 NO_MODULE) + + if (TARGET Eigen3::Eigen) + set(EIGEN3_TARGET Eigen3::Eigen) + set(EIGEN3_FOUND TRUE) + else() + set(EIGEN3_FOUND FALSE) + endif (TARGET Eigen3::Eigen) + set(PUGS_HAS_EIGEN3 ${EIGEN3_FOUND}) +else() + unset(PUGS_HAS_EIGEN3) +endif() + # ----------------------------------------------------- if (${MPI_FOUND}) @@ -225,22 +308,37 @@ set(PUGS_ENABLE_HDF5 AUTO CACHE STRING "Choose one of: AUTO ON OFF") if (PUGS_ENABLE_HDF5 MATCHES "^(AUTO|ON)$") - # May be risky. (make to show pugs build options) - set(HDF5_PREFER_PARALLEL TRUE) - find_package(HDF5) - if (HDF5_FOUND) - # HighFive - set(HIGHFIVE_USE_BOOST OFF) # no Boost - set(HIGHFIVE_BUILD_DOCS OFF) # no doc - set(HIGHFIVE_UNIT_TESTS OFF) # no unit tests - set(HIGHFIVE_UNIT_TESTS OFF) # no unit tests - set(HIGHFIVE_EXAMPLES OFF) # no examples - add_subdirectory(${PUGS_SOURCE_DIR}/packages/HighFive/) - set(HIGHFIVE_TARGET HighFive) + if (MPI_FOUND) + # May be risky. (make to show pugs build options) + set(HDF5_PREFER_PARALLEL TRUE) + find_package(HDF5) + if (HDF5_FOUND) + # HighFive + set(HIGHFIVE_USE_BOOST OFF) # no Boost + set(HIGHFIVE_BUILD_DOCS OFF) # no doc + set(HIGHFIVE_UNIT_TESTS OFF) # no unit tests + set(HIGHFIVE_UNIT_TESTS OFF) # no unit tests + set(HIGHFIVE_EXAMPLES OFF) # no examples + add_subdirectory(${PUGS_SOURCE_DIR}/packages/HighFive/) + set(HIGHFIVE_TARGET HighFive::HighFive) + + add_library(PkgConfig::HDF5 STATIC IMPORTED) + set_property(TARGET PkgConfig::HDF5 PROPERTY + IMPORTED_LOCATION "${HDF5_LIBRARIES}") + + set_property(TARGET PkgConfig::HDF5 PROPERTY + INTERFACE_INCLUDE_DIRECTORIES "${HDF5_INCLUDE_DIRS}") + + set(HDF5_TARGET PkgConfig::HDF5) + endif() + else() + message(STATUS "HDF5 support is deactivated since pugs will not be build with MPI support") + set(HDF5_FOUND FALSE) endif() set(PUGS_HAS_HDF5 ${HDF5_FOUND}) else() unset(HIGHFIVE_TARGET) + unset(HDF5_TARGET) unset(PUGS_HAS_HDF5) endif() @@ -251,7 +349,14 @@ find_package(Slurm) set(PUGS_HAS_SLURM ${SLURM_FOUND}) -if (${SLURM_FOUND}) +if (SLURM_FOUND) + add_library(PkgConfig::Slurm STATIC IMPORTED) + set_property(TARGET PkgConfig::Slurm PROPERTY + IMPORTED_LOCATION "${SLURM_LIBRARY}") + set_property(TARGET PkgConfig::Slurm PROPERTY + INTERFACE_INCLUDE_DIRECTORIES "${SLURM_INCLUDE_DIR}") + + set(SLURM_TARGET PkgConfig::Slurm) include_directories(SYSTEM "${SLURM_INCLUDE_DIR}") else() set(SLURM_LIBRARY "") @@ -364,6 +469,10 @@ endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${PUGS_CXX_FLAGS}") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${PUGS_CXX_FLAGS}") + +message(STATUS "CMAKE_CXX_FLAGS = ${CMAKE_CXX_FLAGS}") + + # Add debug mode for Standard C++ library (not for AppleClang since it is broken) if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_DEBUG -D_LIBCPP_DEBUG=1") @@ -372,12 +481,14 @@ endif() #------------------------------------------------------ # Rang (colors? Useless thus necessary!) +add_subdirectory(${PUGS_SOURCE_DIR}/packages/rang/) include_directories(SYSTEM "${PUGS_SOURCE_DIR}/packages/rang/include") # CLI11 include_directories(SYSTEM "${PUGS_SOURCE_DIR}/packages/CLI11/include") # PEGTL +add_subdirectory(${PUGS_SOURCE_DIR}/packages/PEGTL/) include_directories(SYSTEM "${PUGS_SOURCE_DIR}/packages/PEGTL/include/tao") # Pugs src @@ -400,7 +511,7 @@ if(${PUGS_HAS_MPI}) endif() set(MPIEXEC_NUMPROC 3) set(MPIEXEC_PATH_FLAG --path) - set(MPI_LAUNCHER ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MPIEXEC_NUMPROC} ${MPIEXEC_PATH_FLAG} ${PUGS_BINARY_DIR} ${MPIEXEC_OPTION_FLAGS}) + set(MPI_LAUNCHER ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MPIEXEC_NUMPROC} ${MPIEXEC_OPTION_FLAGS}) endif() add_custom_target(all_unit_tests @@ -427,11 +538,11 @@ endif() add_custom_target(run_mpi_unit_tests COMMAND ${MPI_LAUNCHER} "${PUGS_BINARY_DIR}/mpi_unit_tests" + WORKING_DIRECTORY ${PUGS_BINARY_DIR} DEPENDS run_unit_tests COMMENT ${RUN_MPI_UNIT_TESTS_COMMENT} ) - add_custom_target(run_unit_tests COMMAND "${PUGS_BINARY_DIR}/unit_tests" DEPENDS all_unit_tests @@ -594,10 +705,6 @@ if("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage") endif() endif() -# ----------------------------------------------------- - -link_libraries("-rdynamic") - # ------------------- Source files -------------------- # Pugs binary add_executable( @@ -625,17 +732,20 @@ target_link_libraries( PugsLanguageUtils PugsCheckpointing Kokkos::kokkos - ${PETSC_LIBRARIES} - ${SLEPC_LIBRARIES} - ${PARMETIS_LIBRARIES} + ${PETSC_TARGET} + ${SLEPC_TARGET} + ${PARMETIS_TARGET} ${MPI_CXX_LINK_FLAGS} ${MPI_CXX_LIBRARIES} ${KOKKOS_CXX_FLAGS} ${OPENMP_LINK_FLAGS} ${PUGS_STD_LINK_FLAGS} ${HIGHFIVE_TARGET} - ${SLURM_LIBRARY} + ${SLURM_TARGET} stdc++fs - ) +) + +target_include_directories(pugs PUBLIC ${PETSC_INCLUDE_DIRS}) +target_include_directories(pugs PUBLIC ${HDF5_INCLUDE_DIRS}) # Checkpoint management tool add_executable( @@ -660,19 +770,18 @@ target_link_libraries( PugsMesh PugsOutput Kokkos::kokkos - ${PETSC_LIBRARIES} - ${SLEPC_LIBRARIES} - ${PARMETIS_LIBRARIES} + ${PETSC_TARGET} + ${SLEPC_TARGET} + ${PARMETIS_TARGET} ${MPI_CXX_LINK_FLAGS} ${MPI_CXX_LIBRARIES} ${KOKKOS_CXX_FLAGS} ${OPENMP_LINK_FLAGS} ${PUGS_STD_LINK_FLAGS} ${HIGHFIVE_TARGET} - ${SLURM_LIBRARY} + ${SLURM_TARGET} stdc++fs ) - # -------------------- Documentation -------------------- include(PugsDoc) @@ -685,7 +794,6 @@ include(PugsDoxygen) install(TARGETS pugs pugs_checkpoint - PugsMesh PugsAlgebra PugsAnalysis PugsCheckpointing @@ -694,18 +802,33 @@ install(TARGETS PugsLanguage PugsLanguageAST PugsLanguageModules - PugsLanguageAlgorithms + PugsLanguageUtils PugsMesh - PugsAlgebra - PugsScheme - PugsUtils PugsOutput - PugsLanguageUtils + PugsScheme kokkos + Catch2 + + EXPORT "${PROJECT_NAME}Targets" RUNTIME DESTINATION bin LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib) + ARCHIVE DESTINATION lib +) + +include(CMakePackageConfigHelpers) + +write_basic_package_version_file( + PugsConfigVersion.cmake + VERSION ${PACKAGE_VERSION} + COMPATIBILITY AnyNewerVersion +) + +install(EXPORT PugsTargets + FILE PugsTargets.cmake + NAMESPACE Pugs:: + DESTINATION lib/cmake/pugs +) # ------------------- Build options ------------------- message("") @@ -751,6 +874,16 @@ else() endif() endif() +if (EIGEN3_FOUND) + message(" Eigen3: ${Eigen3_VERSION}") +else() + if (PUGS_ENABLE_EIGEN3 MATCHES "^(AUTO|ON)$") + message(" Eigen3: not found!") + else() + message(" Eigen3: explicitly deactivated!") + endif() +endif() + if (HDF5_FOUND) message(" HDF5: ${HDF5_VERSION} parallel: ${HDF5_IS_PARALLEL}") else() @@ -840,3 +973,24 @@ endif() message("================================") message("") + +configure_file( + ${PUGS_SOURCE_DIR}/cmake/PugsCompileFlags.cmake.in + ${PUGS_BINARY_DIR}/cmake/PugsCompileFlags.cmake + @ONLY +) + +install( + FILES ${PUGS_BINARY_DIR}/cmake/PugsCompileFlags.cmake + DESTINATION lib/cmake/pugs +) + +# Ugly patch to install user headers for Catch2 (for plugins) +install( + DIRECTORY + "${PUGS_BINARY_DIR}/packages/Catch2/generated-includes/catch2" # Also install the generated header + DESTINATION + "include" + FILES_MATCHING + PATTERN "*.hpp" + ) diff --git a/README.md b/README.md index 36e460a851a53d40b4ab442460594091fa22b20f..473b27b07ed129357a206fa59c4af7d74471de52 100644 --- a/README.md +++ b/README.md @@ -11,10 +11,10 @@ that are assembled together by a user friendly language. ## Requirements For the most basic build, `pugs` only requires -- a C++-17 compiler. +- a C++-20 compiler. - g++-10 or higher versions - - clang-10 or higher versions -- `CMake` (at least 3.16) + - clang-11 or higher versions +- `CMake` (at least 3.19) > **Warning**:<br> > Building `pugs` in its source directory is **forbidden**. Trying to @@ -82,6 +82,15 @@ To install `SLEPc` on Debian-like systems apt install slepc-dev ``` +#### `Eigen3` + +`Eigen3` is linear system solver and an eigenvalue problem solver. + +To install `Eigen3` on Debian-like systems +```shell +apt install libeigen3-dev +``` + ## Documentation ### User documentation @@ -234,6 +243,7 @@ the way `pugs` is built. | Description | Variable | Values | |:----------------|:--------------------|:----------------------------:| | MPI support | `PUGS_ENABLE_MPI` | `AUTO`(default), `ON`, `OFF` | +| `Eigen3` support| `PUGS_ENABLE_EIGEN3`| `AUTO`(default), `ON`, `OFF` | | `PETSc` support | `PUGS_ENABLE_PETSC` | `AUTO`(default), `ON`, `OFF` | | `SLEPc` support | `PUGS_ENABLE_SLEPC` | `AUTO`(default), `ON`, `OFF` | diff --git a/cmake/FindPTScotch.cmake b/cmake/FindPTScotch.cmake new file mode 100644 index 0000000000000000000000000000000000000000..da6286e3e8ea110460a068f8e40839b15080319c --- /dev/null +++ b/cmake/FindPTScotch.cmake @@ -0,0 +1,29 @@ +# Looking for PTScotch + +find_package(PkgConfig) +pkg_check_modules(PC_PTSCOTCH QUIET PTSCOTCH) + +find_path(PTSCOTCH_INCLUDE_DIR + NAMES ptscotch.h + PATH_SUFFIXES "include" "include/scotch" + HINTS "$ENV{PTSCOTCH_INCDIR}") + +if(EXISTS "${PTSCOTCH_INCLUDE_DIR}/ptscotch.h") + message(STATUS "Found ptscotch.h in ${PTSCOTCH_INCLUDE_DIR}") + find_library(LIB_PTSCOTCH ptscotch $ENV{PTSCOTCH_LIBDIR}) + if("${LIB_PTSCOTCH}" STREQUAL "LIB_PTSCOTCH-NOTFOUND") + message(WARNING "** Could not find ptscotch library.\n** Is PTSCOTCH_LIBDIR correctly set (Actual: \"$ENV{PTSCOTCH_LIBDIR}\")?") + endif() + set(PTSCOTCH_LIBRARIES ${LIB_PTSCOTCH}) + message(STATUS "Found ptscotch/scotch libraries ${PTSCOTCH_LIBRARIES}") +else() + message(WARNING "** Could not find ptscotch.h.\n** Is PTSCOTCH_INCDIR correctly set (Actual: \"$ENV{PTSCOTCH_INCDIR}\")?") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(PTScotch + FOUND_VAR + PTSCOTCH_FOUND + REQUIRED_VARS + PTSCOTCH_LIBRARIES + PTSCOTCH_INCLUDE_DIR) diff --git a/cmake/FindParMETIS.cmake b/cmake/FindParMETIS.cmake index d9b91d33d92617f3282d4eac2184549b62f62418..5fb1bb961c53897ab8f9cd33a2bf6bedfa41a783 100644 --- a/cmake/FindParMETIS.cmake +++ b/cmake/FindParMETIS.cmake @@ -1,6 +1,5 @@ # Looking for ParMETIS - find_path(PARMETIS_INCLUDE_DIR parmetis.h PATH_SUFFIX include parmetis $ENV{PARMETIS_INCDIR}) @@ -17,7 +16,8 @@ if(EXISTS "${PARMETIS_INCLUDE_DIR}/parmetis.h") find_path(METIS_INCLUDE_DIR metis.h $ENV{METIS_INCDIR}) if(EXISTS "${METIS_INCLUDE_DIR}/metis.h") message(STATUS "Found metis.h in ${METIS_INCLUDE_DIR}") - set(PARMETIS_LIBRARIES ${LIB_PARMETIS} ${LIB_METIS}) + set(PARMETIS_LIBRARIES ${LIB_PARMETIS}) + set(METIS_LIBRARIES ${LIB_METIS}) message(STATUS "Found parmetis/metis libraries ${PARMETIS_LIBRARIES}") else() message(WARNING "** Could not find metis.h.\n** Is METIS_INCDIR correctly set (Actual: \"$ENV{METIS_INCDIR}\")?") @@ -27,3 +27,4 @@ else() endif() mark_as_advanced(PARMETIS_INCLUDE_DIR PARMETIS_LIBRARIES) +mark_as_advanced(METIS_INCLUDE_DIR METIS_LIBRARIES) diff --git a/cmake/PugsCompileFlags.cmake.in b/cmake/PugsCompileFlags.cmake.in new file mode 100644 index 0000000000000000000000000000000000000000..83ecd3e6036a82c3e8696d2b75de2f35d724051c --- /dev/null +++ b/cmake/PugsCompileFlags.cmake.in @@ -0,0 +1,10 @@ +@PACKAGE_INIT@ + +set(PUGS_CMAKE_CXX_FLAGS "@CMAKE_CXX_FLAGS@") +set(PUGS_CMAKE_CXX_STANDARD "@CMAKE_CXX_STANDARD@") + +set(PUGS_CMAKE_BUILD_TYPE "@CMAKE_BUILD_TYPE@") +set(PUGS_CMAKE_CXX_COMPILER "@CMAKE_CXX_COMPILER@") +set(PUGS_CMAKE_C_COMPILER "@CMAKE_C_COMPILER@") + +set(PUGS_HAS_MPI "@PUGS_HAS_MPI@") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index eebccac12c3c1c7b80b6b1e13ff81d0e802a8def..30d920bb0c111049ae655f67e3951424134dbb01 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,6 +3,21 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}) #------------------------------------------------------ +install( + DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" + DESTINATION "include" + FILES_MATCHING + PATTERN "*.hpp" +) + +install( + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + DESTINATION "include" + FILES_MATCHING + PATTERN "*.hpp" + PATTERN "CMakeFiles" EXCLUDE +) + # Pugs utils add_subdirectory(utils) diff --git a/src/algebra/CMakeLists.txt b/src/algebra/CMakeLists.txt index 74f40290a2f9250cc2296ec62358b44c366ffc85..78c041c5cee2e297258d299152d5cc3a4880f599 100644 --- a/src/algebra/CMakeLists.txt +++ b/src/algebra/CMakeLists.txt @@ -3,13 +3,17 @@ add_library( PugsAlgebra EigenvalueSolver.cpp + EigenvalueSolverOptions.cpp LinearSolver.cpp LinearSolverOptions.cpp PETScUtils.cpp) target_link_libraries( PugsAlgebra - ${PETSC_LIBRARIES} - ${SLEPC_LIBRARIES} + ${PETSC_TARGET} + ${SLEPC_TARGET} ${HIGHFIVE_TARGET} ) + +target_include_directories(PugsAlgebra PUBLIC ${PETSC_INCLUDE_DIRS}) +target_include_directories(PugsAlgebra PUBLIC ${SLEPC_INCLUDE_DIRS}) diff --git a/src/algebra/DenseMatrixWrapper.hpp b/src/algebra/DenseMatrixWrapper.hpp new file mode 100644 index 0000000000000000000000000000000000000000..dee57f27eff3d4c906cb256754d86ca6eeafbdfa --- /dev/null +++ b/src/algebra/DenseMatrixWrapper.hpp @@ -0,0 +1,59 @@ +#ifndef DENSE_MATRIX_WRAPPER_HPP +#define DENSE_MATRIX_WRAPPER_HPP + +#include <algebra/SmallMatrix.hpp> +#include <algebra/TinyMatrix.hpp> + +template <typename DataType> +class DenseMatrixWrapper +{ + private: + const size_t m_number_of_rows; + const size_t m_number_of_columns; + const DataType* const m_matrix_ptr; + + public: + PUGS_INLINE + bool + isSquare() const + { + return (m_number_of_rows == m_number_of_columns); + } + + PUGS_INLINE + size_t + numberOfRows() const + { + return m_number_of_rows; + } + + PUGS_INLINE + size_t + numberOfColumns() const + { + return m_number_of_columns; + } + + PUGS_INLINE + const DataType* const& + ptr() const + { + return m_matrix_ptr; + } + + DenseMatrixWrapper(const SmallMatrix<DataType>& A) + : m_number_of_rows{A.numberOfRows()}, + m_number_of_columns{A.numberOfColumns()}, + m_matrix_ptr{(m_number_of_columns * m_number_of_rows > 0) ? (&A(0, 0)) : nullptr} + {} + + template <size_t M, size_t N> + DenseMatrixWrapper(const TinyMatrix<M, N, DataType>& A) + : m_number_of_rows{M}, m_number_of_columns{N}, m_matrix_ptr{(M * N > 0) ? (&A(0, 0)) : nullptr} + {} + + DenseMatrixWrapper(const DenseMatrixWrapper&) = delete; + DenseMatrixWrapper(DenseMatrixWrapper&&) = delete; +}; + +#endif // DENSE_MATRIX_WRAPPER_HPP diff --git a/src/algebra/Eigen3Utils.hpp b/src/algebra/Eigen3Utils.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d9d61e02746c77e22002fb1c1ecfb93db6c1a9bb --- /dev/null +++ b/src/algebra/Eigen3Utils.hpp @@ -0,0 +1,145 @@ +#ifndef EIGEN3_UTILS_HPP +#define EIGEN3_UTILS_HPP + +#include <utils/pugs_config.hpp> + +#ifdef PUGS_HAS_EIGEN3 + +#include <algebra/CRSMatrix.hpp> +#include <algebra/DenseMatrixWrapper.hpp> + +#include <eigen3/Eigen/Eigen> + +class Eigen3DenseMatrixEmbedder +{ + public: + using Eigen3MatrixType = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>; + using Eigen3MapMatrixType = Eigen::Map<const Eigen3MatrixType>; + + private: + Eigen3MapMatrixType m_eigen_matrix; + + Eigen3DenseMatrixEmbedder(const size_t nb_rows, const size_t nb_columns, const double* A) + : m_eigen_matrix{A, int(nb_rows), int(nb_columns)} + {} + + public: + PUGS_INLINE + size_t + isSquare() const + { + return (m_eigen_matrix.rows() == m_eigen_matrix.cols()); + } + + PUGS_INLINE + size_t + numberOfRows() const + { + return m_eigen_matrix.rows(); + } + + PUGS_INLINE + size_t + numberOfColumns() const + { + return m_eigen_matrix.cols(); + } + + PUGS_INLINE + Eigen3MapMatrixType& + matrix() + { + return m_eigen_matrix; + } + + PUGS_INLINE + const Eigen3MapMatrixType& + matrix() const + { + return m_eigen_matrix; + } + + Eigen3DenseMatrixEmbedder(const DenseMatrixWrapper<double>& A) + : Eigen3DenseMatrixEmbedder{A.numberOfRows(), A.numberOfColumns(), A.ptr()} + {} + + ~Eigen3DenseMatrixEmbedder() = default; +}; + +class Eigen3SparseMatrixEmbedder +{ + public: + using Eigen3MatrixType = Eigen::SparseMatrix<double, Eigen::RowMajor>; + using Eigen3MapMatrixType = Eigen::Map<const Eigen3MatrixType>; + + private: + Eigen3MapMatrixType m_eigen_matrix; + + public: + PUGS_INLINE + size_t + numberOfRows() const + { + return m_eigen_matrix.rows(); + } + + PUGS_INLINE + size_t + numberOfColumns() const + { + return m_eigen_matrix.cols(); + } + + PUGS_INLINE + size_t + isSquare() const + { + return (m_eigen_matrix.rows() == m_eigen_matrix.cols()); + } + + PUGS_INLINE + Eigen3MapMatrixType& + matrix() + { + return m_eigen_matrix; + } + + PUGS_INLINE + const Eigen3MapMatrixType& + matrix() const + { + return m_eigen_matrix; + } + + Eigen3SparseMatrixEmbedder(const CRSMatrix<double>& A) + : m_eigen_matrix{A.numberOfRows(), + A.numberOfColumns(), + static_cast<int>(A.values().size()), // + (A.rowMap().size() > 0) ? &(A.rowMap()[0]) : nullptr, // + (A.columnIndices().size() > 0) ? &(A.columnIndices()[0]) : nullptr, // + (A.values().size() > 0) ? &(A.values()[0]) : nullptr} + {} + ~Eigen3SparseMatrixEmbedder() = default; +}; + +#else // PUGS_HAS_EIGEN3 + +class Eigen3DenseMatrixEmbedder +{ + public: + Eigen3DenseMatrixEmbedder(const DenseMatrixWrapper<double>&) {} + + ~Eigen3DenseMatrixEmbedder() = default; +}; + +class Eigen3SparseMatrixEmbedder +{ + public: + Eigen3SparseMatrixEmbedder(const CRSMatrix<double>&) {} + + ~Eigen3SparseMatrixEmbedder() = default; +}; + +#endif // PUGS_HAS_EIGEN3 + +#endif // EIGEN3_UTILS_HPP diff --git a/src/algebra/EigenvalueSolver.cpp b/src/algebra/EigenvalueSolver.cpp index c71be58ddcb1c37498a172f30a1eb39b89f43aab..b17e650af775530d3e61f4561b7676dccf1efdd4 100644 --- a/src/algebra/EigenvalueSolver.cpp +++ b/src/algebra/EigenvalueSolver.cpp @@ -2,10 +2,17 @@ #include <utils/pugs_config.hpp> #ifdef PUGS_HAS_SLEPC +#include <algebra/PETScUtils.hpp> #include <slepc.h> +#endif // PUGS_HAS_SLEPC + +#ifdef PUGS_HAS_EIGEN3 +#include <algebra/Eigen3Utils.hpp> +#endif // PUGS_HAS_EIGEN3 struct EigenvalueSolver::Internals { +#ifdef PUGS_HAS_SLEPC static PetscReal computeSmallestRealEigenvalueOfSymmetricMatrix(EPS& eps) { @@ -65,93 +72,357 @@ struct EigenvalueSolver::Internals computeAllEigenvaluesOfSymmetricMatrixInInterval(eps, left_bound - 0.01 * std::abs(left_bound), right_bound + 0.01 * std::abs(right_bound)); } + + static void + slepscComputeForSymmetricMatrix(const PETScAijMatrixEmbedder& A, SmallArray<double>& eigenvalues) + { + EPS eps; + + EPSCreate(PETSC_COMM_SELF, &eps); + EPSSetOperators(eps, A, nullptr); + EPSSetProblemType(eps, EPS_HEP); + + Internals::computeAllEigenvaluesOfSymmetricMatrix(eps); + + PetscInt nb_eigenvalues; + EPSGetDimensions(eps, &nb_eigenvalues, nullptr, nullptr); + + eigenvalues = SmallArray<double>(nb_eigenvalues); + for (PetscInt i = 0; i < nb_eigenvalues; ++i) { + EPSGetEigenpair(eps, i, &(eigenvalues[i]), nullptr, nullptr, nullptr); + } + + EPSDestroy(&eps); + } + + static void + slepscComputeForSymmetricMatrix(const PETScAijMatrixEmbedder& A, + SmallArray<double>& eigenvalues, + std::vector<SmallVector<double>>& eigenvector_list) + { + EPS eps; + + EPSCreate(PETSC_COMM_SELF, &eps); + EPSSetOperators(eps, A, nullptr); + EPSSetProblemType(eps, EPS_HEP); + + Internals::computeAllEigenvaluesOfSymmetricMatrix(eps); + + PetscInt nb_eigenvalues; + EPSGetDimensions(eps, &nb_eigenvalues, nullptr, nullptr); + + eigenvalues = SmallArray<double>(nb_eigenvalues); + eigenvector_list.reserve(nb_eigenvalues); + for (PetscInt i = 0; i < nb_eigenvalues; ++i) { + Vec Vr; + SmallVector<double> eigenvector{A.numberOfRows()}; + VecCreateSeqWithArray(PETSC_COMM_SELF, 1, A.numberOfRows(), &(eigenvector[0]), &Vr); + EPSGetEigenpair(eps, i, &(eigenvalues[i]), nullptr, Vr, nullptr); + VecDestroy(&Vr); + eigenvector_list.push_back(eigenvector); + } + + EPSDestroy(&eps); + } + + static void + slepscComputeForSymmetricMatrix(const PETScAijMatrixEmbedder& A, + SmallArray<double>& eigenvalues, + SmallMatrix<double>& P) + { + EPS eps; + + EPSCreate(PETSC_COMM_SELF, &eps); + EPSSetOperators(eps, A, nullptr); + EPSSetProblemType(eps, EPS_HEP); + + Internals::computeAllEigenvaluesOfSymmetricMatrix(eps); + + PetscInt nb_eigenvalues; + EPSGetDimensions(eps, &nb_eigenvalues, nullptr, nullptr); + + eigenvalues = SmallArray<double>(nb_eigenvalues); + P = SmallMatrix<double>(nb_eigenvalues, nb_eigenvalues); + + Array<double> eigenvector(nb_eigenvalues); + for (PetscInt i = 0; i < nb_eigenvalues; ++i) { + Vec Vr; + VecCreateSeqWithArray(PETSC_COMM_SELF, 1, A.numberOfRows(), &(eigenvector[0]), &Vr); + EPSGetEigenpair(eps, i, &(eigenvalues[i]), nullptr, Vr, nullptr); + VecDestroy(&Vr); + for (size_t j = 0; j < eigenvector.size(); ++j) { + P(j, i) = eigenvector[j]; + } + } + + EPSDestroy(&eps); + } +#endif // PUGS_HAS_SLEPC + +#ifdef PUGS_HAS_EIGEN3 + + template <typename Eigen3MatrixEmbedderType> + static void + eigen3ComputeForSymmetricMatrix(const Eigen3MatrixEmbedderType& A, SmallArray<double>& eigenvalues) + { + using Eigen3MatrixType = typename Eigen3MatrixEmbedderType::Eigen3MatrixType; + Eigen::SelfAdjointEigenSolver<Eigen3MatrixType> eigen_solver; + + eigen_solver.compute(A.matrix(), Eigen::EigenvaluesOnly); + + eigenvalues = SmallArray<double>(A.numberOfRows()); + for (size_t i = 0; i < eigenvalues.size(); ++i) { + eigenvalues[i] = eigen_solver.eigenvalues()[i]; + } + } + + template <typename Eigen3MatrixEmbedderType> + static void + eigen3ComputeForSymmetricMatrix(const Eigen3MatrixEmbedderType& A, + SmallArray<double>& eigenvalues, + std::vector<SmallVector<double>>& eigenvectors) + { + using Eigen3MatrixType = typename Eigen3MatrixEmbedderType::Eigen3MatrixType; + Eigen::SelfAdjointEigenSolver<Eigen3MatrixType> eigen_solver; + + eigen_solver.compute(A.matrix(), Eigen::ComputeEigenvectors); + + eigenvalues = SmallArray<double>(A.numberOfRows()); + for (size_t i = 0; i < eigenvalues.size(); ++i) { + eigenvalues[i] = eigen_solver.eigenvalues()[i]; + } + + eigenvectors.resize(eigenvalues.size()); + for (size_t i = 0; i < eigenvalues.size(); ++i) { + eigenvectors[i] = SmallVector<double>{eigenvalues.size()}; + for (size_t j = 0; j < eigenvalues.size(); ++j) { + eigenvectors[i][j] = eigen_solver.eigenvectors().coeff(j, i); + } + } + } + + template <typename Eigen3MatrixEmbedderType> + static void + eigen3ComputeForSymmetricMatrix(const Eigen3MatrixEmbedderType& A, + SmallArray<double>& eigenvalues, + SmallMatrix<double>& P) + { + using Eigen3MatrixType = typename Eigen3MatrixEmbedderType::Eigen3MatrixType; + Eigen::SelfAdjointEigenSolver<Eigen3MatrixType> eigen_solver; + + eigen_solver.compute(A.matrix(), Eigen::ComputeEigenvectors); + + eigenvalues = SmallArray<double>(A.numberOfRows()); + for (size_t i = 0; i < eigenvalues.size(); ++i) { + eigenvalues[i] = eigen_solver.eigenvalues()[i]; + } + + P = SmallMatrix<double>(eigenvalues.size(), eigenvalues.size()); + for (size_t i = 0; i < eigenvalues.size(); ++i) { + for (size_t j = 0; j < eigenvalues.size(); ++j) { + P(i, j) = eigen_solver.eigenvectors().coeff(i, j); + } + } + } +#endif // PUGS_HAS_EIGEN3 }; +#ifdef PUGS_HAS_SLEPC void -EigenvalueSolver::computeForSymmetricMatrix(const PETScAijMatrixEmbedder& A, SmallArray<double>& eigenvalues) +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const CRSMatrix<double>& A, SmallArray<double>& eigenvalues) { - EPS eps; - - EPSCreate(PETSC_COMM_SELF, &eps); - EPSSetOperators(eps, A, nullptr); - EPSSetProblemType(eps, EPS_HEP); + Internals::slepscComputeForSymmetricMatrix(A, eigenvalues); +} - Internals::computeAllEigenvaluesOfSymmetricMatrix(eps); +void +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, SmallArray<double>& eigenvalues) +{ + Internals::slepscComputeForSymmetricMatrix(A, eigenvalues); +} - PetscInt nb_eigenvalues; - EPSGetDimensions(eps, &nb_eigenvalues, nullptr, nullptr); +void +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const CRSMatrix<double>& A, + SmallArray<double>& eigenvalues, + std::vector<SmallVector<double>>& eigenvectors) +{ + Internals::slepscComputeForSymmetricMatrix(A, eigenvalues, eigenvectors); +} - eigenvalues = SmallArray<double>(nb_eigenvalues); - for (PetscInt i = 0; i < nb_eigenvalues; ++i) { - EPSGetEigenpair(eps, i, &(eigenvalues[i]), nullptr, nullptr, nullptr); - } +void +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, + SmallArray<double>& eigenvalues, + std::vector<SmallVector<double>>& eigenvectors) +{ + Internals::slepscComputeForSymmetricMatrix(A, eigenvalues, eigenvectors); +} - EPSDestroy(&eps); +void +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const CRSMatrix<double>& A, + SmallArray<double>& eigenvalues, + SmallMatrix<double>& P) +{ + Internals::slepscComputeForSymmetricMatrix(A, eigenvalues, P); } void -EigenvalueSolver::computeForSymmetricMatrix(const PETScAijMatrixEmbedder& A, - SmallArray<double>& eigenvalues, - std::vector<SmallVector<double>>& eigenvector_list) +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, + SmallArray<double>& eigenvalues, + SmallMatrix<double>& P) { - EPS eps; + Internals::slepscComputeForSymmetricMatrix(A, eigenvalues, P); +} - EPSCreate(PETSC_COMM_SELF, &eps); - EPSSetOperators(eps, A, nullptr); - EPSSetProblemType(eps, EPS_HEP); +#else // PUGS_HAS_SLEPC - Internals::computeAllEigenvaluesOfSymmetricMatrix(eps); +void +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const CRSMatrix<double>&, SmallArray<double>&) +{} - PetscInt nb_eigenvalues; - EPSGetDimensions(eps, &nb_eigenvalues, nullptr, nullptr); +void +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const DenseMatrixWrapper<double>&, SmallArray<double>&) +{} - eigenvalues = SmallArray<double>(nb_eigenvalues); - eigenvector_list.reserve(nb_eigenvalues); - for (PetscInt i = 0; i < nb_eigenvalues; ++i) { - Vec Vr; - SmallVector<double> eigenvector{A.numberOfRows()}; - VecCreateSeqWithArray(PETSC_COMM_SELF, 1, A.numberOfRows(), &(eigenvector[0]), &Vr); - EPSGetEigenpair(eps, i, &(eigenvalues[i]), nullptr, Vr, nullptr); - VecDestroy(&Vr); - eigenvector_list.push_back(eigenvector); - } +void +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const CRSMatrix<double>&, + SmallArray<double>&, + std::vector<SmallVector<double>>&) +{} + +void +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const DenseMatrixWrapper<double>&, + SmallArray<double>&, + std::vector<SmallVector<double>>&) +{} + +void +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const CRSMatrix<double>&, SmallArray<double>&, SmallMatrix<double>&) +{} + +void +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const DenseMatrixWrapper<double>&, + SmallArray<double>&, + SmallMatrix<double>&) +{} + +#endif // PUGS_HAS_SLEPC + +#ifdef PUGS_HAS_EIGEN3 +void +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const CRSMatrix<double>& A, SmallArray<double>& eigenvalues) +{ + Eigen3SparseMatrixEmbedder embedded_A{A}; + Internals::eigen3ComputeForSymmetricMatrix(embedded_A, eigenvalues); +} + +void +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, SmallArray<double>& eigenvalues) +{ + Internals::eigen3ComputeForSymmetricMatrix(Eigen3DenseMatrixEmbedder{A}, eigenvalues); +} + +void +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const CRSMatrix<double>& A, + SmallArray<double>& eigenvalues, + std::vector<SmallVector<double>>& eigenvectors) +{ + Internals::eigen3ComputeForSymmetricMatrix(Eigen3SparseMatrixEmbedder{A}, eigenvalues, eigenvectors); +} + +void +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, + SmallArray<double>& eigenvalues, + std::vector<SmallVector<double>>& eigenvectors) +{ + Internals::eigen3ComputeForSymmetricMatrix(Eigen3DenseMatrixEmbedder{A}, eigenvalues, eigenvectors); +} - EPSDestroy(&eps); +void +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const CRSMatrix<double>& A, + SmallArray<double>& eigenvalues, + SmallMatrix<double>& P) +{ + Internals::eigen3ComputeForSymmetricMatrix(Eigen3SparseMatrixEmbedder{A}, eigenvalues, P); } void -EigenvalueSolver::computeForSymmetricMatrix(const PETScAijMatrixEmbedder& A, - SmallArray<double>& eigenvalues, - SmallMatrix<double>& P) +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, + SmallArray<double>& eigenvalues, + SmallMatrix<double>& P) { - EPS eps; + Internals::eigen3ComputeForSymmetricMatrix(Eigen3DenseMatrixEmbedder{A}, eigenvalues, P); +} - EPSCreate(PETSC_COMM_SELF, &eps); - EPSSetOperators(eps, A, nullptr); - EPSSetProblemType(eps, EPS_HEP); +#else // PUGS_HAS_EIGEN3 - Internals::computeAllEigenvaluesOfSymmetricMatrix(eps); +void +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const CRSMatrix<double>&, SmallArray<double>&) +{} - PetscInt nb_eigenvalues; - EPSGetDimensions(eps, &nb_eigenvalues, nullptr, nullptr); +void +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const DenseMatrixWrapper<double>&, SmallArray<double>&) +{} - eigenvalues = SmallArray<double>(nb_eigenvalues); - P = SmallMatrix<double>(nb_eigenvalues, nb_eigenvalues); +void +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const CRSMatrix<double>&, + SmallArray<double>&, + std::vector<SmallVector<double>>&) +{} - Array<double> eigenvector(nb_eigenvalues); - for (PetscInt i = 0; i < nb_eigenvalues; ++i) { - Vec Vr; - VecCreateSeqWithArray(PETSC_COMM_SELF, 1, A.numberOfRows(), &(eigenvector[0]), &Vr); - EPSGetEigenpair(eps, i, &(eigenvalues[i]), nullptr, Vr, nullptr); - VecDestroy(&Vr); - for (size_t j = 0; j < eigenvector.size(); ++j) { - P(j, i) = eigenvector[j]; - } - } +void +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const DenseMatrixWrapper<double>&, + SmallArray<double>&, + std::vector<SmallVector<double>>&) +{} + +void +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const CRSMatrix<double>&, SmallArray<double>&, SmallMatrix<double>&) +{} - EPSDestroy(&eps); +void +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const DenseMatrixWrapper<double>&, + SmallArray<double>&, + SmallMatrix<double>&) +{} + +#endif // PUGS_HAS_EIGEN3 + +bool +EigenvalueSolver::hasLibrary(const ESLibrary library) +{ + switch (library) { + case ESLibrary::eigen3: { +#ifdef PUGS_HAS_EIGEN3 + return true; +#else + return false; +#endif + } + case ESLibrary::slepsc: { +#ifdef PUGS_HAS_SLEPC + return true; +#else + return false; +#endif + } + // LCOV_EXCL_START + default: { + throw UnexpectedError("Eigenvalue library (" + ::name(library) + ") was not set!"); + } + // LCOV_EXCL_STOP + } } -#endif // PUGS_HAS_SLEPC +void +EigenvalueSolver::checkHasLibrary(const ESLibrary library) +{ + if (not hasLibrary(library)) { + // LCOV_EXCL_START + throw NormalError(::name(library) + " is not linked to pugs. Cannot use it!"); + // LCOV_EXCL_STOP + } +} -EigenvalueSolver::EigenvalueSolver() {} +EigenvalueSolver::EigenvalueSolver(const EigenvalueSolverOptions& options) : m_options{options} +{ + checkHasLibrary(options.library()); +} diff --git a/src/algebra/EigenvalueSolver.hpp b/src/algebra/EigenvalueSolver.hpp index a9b462509514ace4b736424c65f7c9abe4572f26..6567adcdc12caabae69baede83e74166696baaf0 100644 --- a/src/algebra/EigenvalueSolver.hpp +++ b/src/algebra/EigenvalueSolver.hpp @@ -1,10 +1,13 @@ #ifndef EIGENVALUE_SOLVER_HPP #define EIGENVALUE_SOLVER_HPP -#include <algebra/PETScUtils.hpp> +#include <algebra/EigenvalueSolverOptions.hpp> +#include <algebra/CRSMatrix.hpp> +#include <algebra/DenseMatrixWrapper.hpp> #include <algebra/SmallMatrix.hpp> #include <algebra/SmallVector.hpp> +#include <algebra/TinyMatrix.hpp> #include <utils/Exceptions.hpp> #include <utils/SmallArray.hpp> @@ -13,57 +16,118 @@ class EigenvalueSolver private: struct Internals; -#ifdef PUGS_HAS_SLEPC - void computeForSymmetricMatrix(const PETScAijMatrixEmbedder& A, SmallArray<double>& eigenvalues); + const EigenvalueSolverOptions m_options; - void computeForSymmetricMatrix(const PETScAijMatrixEmbedder& A, - SmallArray<double>& eigenvalues, - std::vector<SmallVector<double>>& eigenvectors); + void _slepscComputeForSymmetricMatrix(const CRSMatrix<double>& A, SmallArray<double>& eigenvalues); - void computeForSymmetricMatrix(const PETScAijMatrixEmbedder& A, - SmallArray<double>& eigenvalues, - SmallMatrix<double>& P); -#endif // PUGS_HAS_SLEPC + void _slepscComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, SmallArray<double>& eigenvalues); + + void _slepscComputeForSymmetricMatrix(const CRSMatrix<double>& A, + SmallArray<double>& eigenvalues, + std::vector<SmallVector<double>>& eigenvectors); + + void _slepscComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, + SmallArray<double>& eigenvalues, + std::vector<SmallVector<double>>& eigenvectors); + + void _slepscComputeForSymmetricMatrix(const CRSMatrix<double>& A, + SmallArray<double>& eigenvalues, + SmallMatrix<double>& P); + + void _slepscComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, + SmallArray<double>& eigenvalues, + SmallMatrix<double>& P); + + void _eigen3ComputeForSymmetricMatrix(const CRSMatrix<double>& A, SmallArray<double>& eigenvalues); + + void _eigen3ComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, SmallArray<double>& eigenvalues); + + void _eigen3ComputeForSymmetricMatrix(const CRSMatrix<double>& A, + SmallArray<double>& eigenvalues, + std::vector<SmallVector<double>>& eigenvectors); + + void _eigen3ComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, + SmallArray<double>& eigenvalues, + std::vector<SmallVector<double>>& eigenvectors); + + void _eigen3ComputeForSymmetricMatrix(const CRSMatrix<double>& A, + SmallArray<double>& eigenvalues, + SmallMatrix<double>& P); + + void _eigen3ComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, + SmallArray<double>& eigenvalues, + SmallMatrix<double>& P); public: + static bool hasLibrary(ESLibrary library); + static void checkHasLibrary(const ESLibrary library); + template <typename MatrixType> void - computeForSymmetricMatrix([[maybe_unused]] const MatrixType& A, [[maybe_unused]] SmallArray<double>& eigenvalues) + computeForSymmetricMatrix(const MatrixType& A, SmallArray<double>& eigenvalues) { -#ifdef PUGS_HAS_SLEPC - this->computeForSymmetricMatrix(PETScAijMatrixEmbedder{A}, eigenvalues); -#else // PUGS_HAS_SLEPC - throw NotImplementedError("SLEPc is required to solve eigenvalue problems"); -#endif // PUGS_HAS_SLEPC + Assert(A.isSquare()); + + switch (m_options.library()) { + case ESLibrary::eigen3: { + this->_eigen3ComputeForSymmetricMatrix(A, eigenvalues); + break; + } + case ESLibrary::slepsc: { + this->_slepscComputeForSymmetricMatrix(A, eigenvalues); + break; + } + default: { + throw UnexpectedError(::name(m_options.library()) + " cannot compute for symmetric matrices"); + } + } } template <typename MatrixType> void - computeForSymmetricMatrix([[maybe_unused]] const MatrixType& A, - [[maybe_unused]] SmallArray<double>& eigenvalues, - [[maybe_unused]] std::vector<SmallVector<double>>& eigenvectors) + computeForSymmetricMatrix(const MatrixType& A, + SmallArray<double>& eigenvalues, + std::vector<SmallVector<double>>& eigenvectors) { -#ifdef PUGS_HAS_SLEPC - this->computeForSymmetricMatrix(PETScAijMatrixEmbedder{A}, eigenvalues, eigenvectors); -#else // PUGS_HAS_SLEPC - throw NotImplementedError("SLEPc is required to solve eigenvalue problems"); -#endif // PUGS_HAS_SLEPC + Assert(A.isSquare()); + + switch (m_options.library()) { + case ESLibrary::eigen3: { + this->_eigen3ComputeForSymmetricMatrix(A, eigenvalues, eigenvectors); + break; + } + case ESLibrary::slepsc: { + this->_slepscComputeForSymmetricMatrix(A, eigenvalues, eigenvectors); + break; + } + default: { + throw UnexpectedError(::name(m_options.library()) + " cannot compute for symmetric matrices"); + } + } } template <typename MatrixType> void - computeForSymmetricMatrix([[maybe_unused]] const MatrixType& A, - [[maybe_unused]] SmallArray<double>& eigenvalues, - [[maybe_unused]] SmallMatrix<double>& P) + computeForSymmetricMatrix(const MatrixType& A, SmallArray<double>& eigenvalues, SmallMatrix<double>& P) { -#ifdef PUGS_HAS_SLEPC - this->computeForSymmetricMatrix(PETScAijMatrixEmbedder{A}, eigenvalues, P); -#else // PUGS_HAS_SLEPC - throw NotImplementedError("SLEPc is required to solve eigenvalue problems"); -#endif // PUGS_HAS_SLEPC + Assert(A.isSquare()); + + switch (m_options.library()) { + case ESLibrary::eigen3: { + this->_eigen3ComputeForSymmetricMatrix(A, eigenvalues, P); + break; + } + case ESLibrary::slepsc: { + this->_slepscComputeForSymmetricMatrix(A, eigenvalues, P); + break; + } + default: { + throw UnexpectedError(::name(m_options.library()) + " cannot compute for symmetric matrices"); + } + } } - EigenvalueSolver(); + EigenvalueSolver(const EigenvalueSolverOptions& options = EigenvalueSolverOptions::default_options); ~EigenvalueSolver() = default; }; diff --git a/src/algebra/EigenvalueSolverOptions.cpp b/src/algebra/EigenvalueSolverOptions.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2a4ef9549789da993760d72a5d7f289a76d9f2d6 --- /dev/null +++ b/src/algebra/EigenvalueSolverOptions.cpp @@ -0,0 +1,10 @@ +#include <algebra/EigenvalueSolverOptions.hpp> + +#include <rang.hpp> + +std::ostream& +operator<<(std::ostream& os, const EigenvalueSolverOptions& options) +{ + os << " library: " << rang::style::bold << name(options.library()) << rang::style::reset << '\n'; + return os; +} diff --git a/src/algebra/EigenvalueSolverOptions.hpp b/src/algebra/EigenvalueSolverOptions.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1c047ef3cb63df23cfb7f8725edf68ffa8ad26a6 --- /dev/null +++ b/src/algebra/EigenvalueSolverOptions.hpp @@ -0,0 +1,90 @@ +#ifndef EIGENVALUE_SOLVER_OPTIONS_HPP +#define EIGENVALUE_SOLVER_OPTIONS_HPP + +#include <utils/Exceptions.hpp> + +#include <iostream> + +enum class ESLibrary : int8_t +{ + ES__begin = 0, + // + eigen3 = ES__begin, + slepsc, + // + ES__end +}; + +inline std::string +name(const ESLibrary library) +{ + switch (library) { + case ESLibrary::eigen3: { + return "Eigen3"; + } + case ESLibrary::slepsc: { + return "SLEPSc"; + } + case ESLibrary::ES__end: { + } + } + throw UnexpectedError("Eigenvalue solver library name is not defined!"); +} + +template <typename ESEnumType> +inline ESEnumType +getESEnumFromName(const std::string& enum_name) +{ + using BaseT = std::underlying_type_t<ESEnumType>; + for (BaseT enum_value = static_cast<BaseT>(ESEnumType::ES__begin); + enum_value < static_cast<BaseT>(ESEnumType::ES__end); ++enum_value) { + if (name(ESEnumType{enum_value}) == enum_name) { + return ESEnumType{enum_value}; + } + } + throw NormalError(std::string{"could not find '"} + enum_name + "' associate type!"); +} + +template <typename ESEnumType> +inline void +printESEnumListNames(std::ostream& os) +{ + using BaseT = std::underlying_type_t<ESEnumType>; + for (BaseT enum_value = static_cast<BaseT>(ESEnumType::ES__begin); + enum_value < static_cast<BaseT>(ESEnumType::ES__end); ++enum_value) { + os << " - " << name(ESEnumType{enum_value}) << '\n'; + } +} + +class EigenvalueSolverOptions +{ + private: + ESLibrary m_library = ESLibrary::slepsc; + + public: + static EigenvalueSolverOptions default_options; + + friend std::ostream& operator<<(std::ostream& os, const EigenvalueSolverOptions& options); + + ESLibrary& + library() + { + return m_library; + } + + ESLibrary + library() const + { + return m_library; + } + + EigenvalueSolverOptions(const EigenvalueSolverOptions&) = default; + EigenvalueSolverOptions(EigenvalueSolverOptions&&) = default; + + EigenvalueSolverOptions() = default; + ~EigenvalueSolverOptions() = default; +}; + +inline EigenvalueSolverOptions EigenvalueSolverOptions::default_options; + +#endif // EIGENVALUE_SOLVER_OPTIONS_HPP diff --git a/src/algebra/LinearSolver.cpp b/src/algebra/LinearSolver.cpp index 99bff927c744c9ba216e575dcc914a2bdba1d4da..52bf8fc3969d2001685c5548f31c30ce7af20dd0 100644 --- a/src/algebra/LinearSolver.cpp +++ b/src/algebra/LinearSolver.cpp @@ -3,8 +3,13 @@ #include <algebra/BiCGStab.hpp> #include <algebra/CG.hpp> +#include <algebra/Eigen3Utils.hpp> #include <algebra/PETScUtils.hpp> +#ifdef PUGS_HAS_EIGEN3 +#include <eigen3/unsupported/Eigen/IterativeSolvers> +#endif // PUGS_HAS_EIGEN3 + #ifdef PUGS_HAS_PETSC #include <petsc.h> #endif // PUGS_HAS_PETSC @@ -18,6 +23,13 @@ struct LinearSolver::Internals case LSLibrary::builtin: { return true; } + case LSLibrary::eigen3: { +#ifdef PUGS_HAS_EIGEN3 + return true; +#else + return false; +#endif + } case LSLibrary::petsc: { #ifdef PUGS_HAS_PETSC return true; @@ -57,6 +69,25 @@ struct LinearSolver::Internals } } + static void + checkEigen3Method(const LSMethod method) + { + switch (method) { + case LSMethod::cg: + case LSMethod::bicgstab: + case LSMethod::gmres: + case LSMethod::lu: + case LSMethod::cholesky: { + break; + } + // LCOV_EXCL_START + default: { + throw NormalError(name(method) + " is not an Eigen3 linear solver!"); + } + // LCOV_EXCL_STOP + } + } + static void checkPETScMethod(const LSMethod method) { @@ -90,6 +121,24 @@ struct LinearSolver::Internals } } + static void + checkEigen3Precond(const LSPrecond precond) + { + switch (precond) { + case LSPrecond::none: + case LSPrecond::diagonal: + case LSPrecond::incomplete_cholesky: + case LSPrecond::incomplete_LU: { + break; + } + // LCOV_EXCL_START + default: { + throw NormalError(name(precond) + " is not an Eigen3 preconditioner!"); + } + // LCOV_EXCL_STOP + } + } + static void checkPETScPrecond(const LSPrecond precond) { @@ -118,6 +167,11 @@ struct LinearSolver::Internals checkBuiltinPrecond(options.precond()); break; } + case LSLibrary::eigen3: { + checkEigen3Method(options.method()); + checkEigen3Precond(options.precond()); + break; + } case LSLibrary::petsc: { checkPETScMethod(options.method()); checkPETScPrecond(options.precond()); @@ -133,10 +187,10 @@ struct LinearSolver::Internals template <typename MatrixType, typename SolutionVectorType, typename RHSVectorType> static void - builtinSolveLocalSystem(const MatrixType& A, - SolutionVectorType& x, - const RHSVectorType& b, - const LinearSolverOptions& options) + _builtinSolveLocalSystem(const MatrixType& A, + SolutionVectorType& x, + const RHSVectorType& b, + const LinearSolverOptions& options) { if (options.precond() != LSPrecond::none) { // LCOV_EXCL_START @@ -160,6 +214,134 @@ struct LinearSolver::Internals } } +#ifdef PUGS_HAS_EIGEN3 + template <typename PreconditionerType, typename Eigen3MatrixEmbedderType> + static void + _preconditionedEigen3SolveLocalSystem(const Eigen3MatrixEmbedderType& eigen3_A, + VectorWrapper<double>& x, + const VectorWrapper<const double>& b, + const LinearSolverOptions& options) + { + Eigen::Map<Eigen::VectorX<double>> eigen3_x{x.ptr(), static_cast<int>(x.size())}; + Eigen::Map<const Eigen::VectorX<double>> eigen3_b{b.ptr(), static_cast<int>(b.size())}; + + using Eigen3MatrixType = typename Eigen3MatrixEmbedderType::Eigen3MatrixType; + + switch (options.method()) { + case LSMethod::bicgstab: { + Eigen::BiCGSTAB<Eigen3MatrixType, PreconditionerType> solver; + solver.compute(eigen3_A.matrix()); + solver.setMaxIterations(options.maximumIteration()); + solver.setTolerance(options.epsilon()); + eigen3_x = solver.solve(eigen3_b); + break; + } + case LSMethod::cg: { + Eigen::ConjugateGradient<Eigen3MatrixType, Eigen::Lower | Eigen::Upper, PreconditionerType> solver; + solver.compute(eigen3_A.matrix()); + solver.setMaxIterations(options.maximumIteration()); + solver.setTolerance(options.epsilon()); + eigen3_x = solver.solve(eigen3_b); + break; + } + case LSMethod::gmres: { + Eigen::GMRES<Eigen3MatrixType, PreconditionerType> solver; + solver.compute(eigen3_A.matrix()); + solver.setMaxIterations(options.maximumIteration()); + solver.setTolerance(options.epsilon()); + eigen3_x = solver.solve(eigen3_b); + break; + } + case LSMethod::lu: { + if constexpr (std::is_same_v<Eigen3MatrixEmbedderType, Eigen3DenseMatrixEmbedder>) { + Eigen::FullPivLU<Eigen3MatrixType> solver; + solver.compute(eigen3_A.matrix()); + eigen3_x = solver.solve(eigen3_b); + } else { + Eigen::SparseLU<Eigen3MatrixType> solver; + solver.compute(eigen3_A.matrix()); + eigen3_x = solver.solve(eigen3_b); + } + break; + } + case LSMethod::cholesky: { + if constexpr (std::is_same_v<Eigen3MatrixEmbedderType, Eigen3DenseMatrixEmbedder>) { + Eigen::LLT<Eigen3MatrixType> solver; + solver.compute(eigen3_A.matrix()); + eigen3_x = solver.solve(eigen3_b); + } else { + Eigen::SimplicialLLT<Eigen3MatrixType> solver; + solver.compute(eigen3_A.matrix()); + eigen3_x = solver.solve(eigen3_b); + } + break; + } + // LCOV_EXCL_START + default: { + throw UnexpectedError("unexpected method: " + name(options.method())); + } + // LCOV_EXCL_STOP + } + } + + template <typename Eigen3MatrixEmbedderType> + static void + _eigen3SolveLocalSystem(const Eigen3MatrixEmbedderType& eigen3_A, + VectorWrapper<double>& x, + const VectorWrapper<const double>& b, + const LinearSolverOptions& options) + { + switch (options.precond()) { + case LSPrecond::none: { + _preconditionedEigen3SolveLocalSystem<Eigen::IdentityPreconditioner, Eigen3MatrixEmbedderType>(eigen3_A, x, b, + options); + break; + } + case LSPrecond::diagonal: { + _preconditionedEigen3SolveLocalSystem<Eigen::DiagonalPreconditioner<double>, Eigen3MatrixEmbedderType>(eigen3_A, + x, b, + options); + break; + } + case LSPrecond::incomplete_cholesky: { + if constexpr (std::is_same_v<Eigen3SparseMatrixEmbedder, Eigen3MatrixEmbedderType>) { + _preconditionedEigen3SolveLocalSystem<Eigen::IncompleteCholesky<double>, Eigen3MatrixEmbedderType>(eigen3_A, x, + b, options); + } else { + throw NormalError("incomplete cholesky is not available for dense matrices in Eigen3"); + } + break; + } + case LSPrecond::incomplete_LU: { + if constexpr (std::is_same_v<Eigen3SparseMatrixEmbedder, Eigen3MatrixEmbedderType>) { + _preconditionedEigen3SolveLocalSystem<Eigen::IncompleteLUT<double>, Eigen3MatrixEmbedderType>(eigen3_A, x, b, + options); + } else { + throw NormalError("incomplete LU is not available for dense matrices in Eigen3"); + } + break; + } + // LCOV_EXCL_START + default: { + throw UnexpectedError("unexpected preconditioner: " + name(options.precond())); + } + // LCOV_EXCL_STOP + } + } +#else // PUGS_HAS_EIGEN3 + + // LCOV_EXCL_START + template <typename MatrixType, typename SolutionVectorType, typename RHSVectorType> + static void + _eigen3SolveLocalSystem(const MatrixType&, SolutionVectorType&, const RHSVectorType&, const LinearSolverOptions&) + { + checkHasLibrary(LSLibrary::eigen3); + throw UnexpectedError("unexpected situation should not reach this point!"); + } + // LCOV_EXCL_STOP + +#endif // PUGS_HAS_EIGEN3 + #ifdef PUGS_HAS_PETSC static int petscMonitor(KSP, int i, double residu, void*) @@ -168,19 +350,17 @@ struct LinearSolver::Internals return 0; } - template <typename MatrixType, typename SolutionVectorType, typename RHSVectorType> + template <typename MatrixType> static void - petscSolveLocalSystem(const MatrixType& A, - SolutionVectorType& x, - const RHSVectorType& b, - const LinearSolverOptions& options) + _petscSolveLocalSystem(const MatrixType& A, + VectorWrapper<double>& x, + const VectorWrapper<const double>& b, + const LinearSolverOptions& options) { - Assert((x.size() == b.size()) and (x.size() - A.numberOfColumns() == 0) and A.isSquare()); - Vec petscB; - VecCreateMPIWithArray(PETSC_COMM_SELF, 1, b.size(), b.size(), &b[0], &petscB); + VecCreateMPIWithArray(PETSC_COMM_SELF, 1, b.size(), b.size(), b.ptr(), &petscB); Vec petscX; - VecCreateMPIWithArray(PETSC_COMM_SELF, 1, x.size(), x.size(), &x[0], &petscX); + VecCreateMPIWithArray(PETSC_COMM_SELF, 1, x.size(), x.size(), x.ptr(), &petscX); PETScAijMatrixEmbedder petscA(A); @@ -280,7 +460,7 @@ struct LinearSolver::Internals // LCOV_EXCL_START template <typename MatrixType, typename SolutionVectorType, typename RHSVectorType> static void - petscSolveLocalSystem(const MatrixType&, SolutionVectorType&, const RHSVectorType&, const LinearSolverOptions&) + _petscSolveLocalSystem(const MatrixType&, SolutionVectorType&, const RHSVectorType&, const LinearSolverOptions&) { checkHasLibrary(LSLibrary::petsc); throw UnexpectedError("unexpected situation should not reach this point!"); @@ -288,33 +468,6 @@ struct LinearSolver::Internals // LCOV_EXCL_STOP #endif // PUGS_HAS_PETSC - - template <typename MatrixType, typename SolutionVectorType, typename RHSVectorType> - static void - solveLocalSystem(const MatrixType& A, - SolutionVectorType& x, - const RHSVectorType& b, - const LinearSolverOptions& options) - { - switch (options.library()) { - case LSLibrary::builtin: { - builtinSolveLocalSystem(A, x, b, options); - break; - } - // LCOV_EXCL_START - case LSLibrary::petsc: { - // not covered since if PETSc is not linked this point is - // unreachable: LinearSolver throws an exception at construction - // in this case. - petscSolveLocalSystem(A, x, b, options); - break; - } - default: { - throw UnexpectedError(::name(options.library()) + " cannot solve local systems"); - } - // LCOV_EXCL_STOP - } - } }; bool @@ -330,15 +483,52 @@ LinearSolver::checkOptions(const LinearSolverOptions& options) const } void -LinearSolver::solveLocalSystem(const CRSMatrix<double, int>& A, Vector<double>& x, const Vector<const double>& b) +LinearSolver::_builtinSolveLocalSystem(const CRSMatrix<double, int>& A, + Vector<double>& x, + const Vector<const double>& b) +{ + Internals::_builtinSolveLocalSystem(A, x, b, m_options); +} + +void +LinearSolver::_builtinSolveLocalSystem(const SmallMatrix<double>& A, + SmallVector<double>& x, + const SmallVector<const double>& b) +{ + Internals::_builtinSolveLocalSystem(A, x, b, m_options); +} + +void +LinearSolver::_eigen3SolveLocalSystem(const Eigen3SparseMatrixEmbedder& A, + VectorWrapper<double> x, + const VectorWrapper<const double>& b) +{ + Internals::_eigen3SolveLocalSystem(A, x, b, m_options); +} + +void +LinearSolver::_eigen3SolveLocalSystem(const DenseMatrixWrapper<double>& A, + VectorWrapper<double> x, + const VectorWrapper<const double>& b) +{ + Eigen3DenseMatrixEmbedder A_wrapper{A}; + Internals::_eigen3SolveLocalSystem(A_wrapper, x, b, m_options); +} + +void +LinearSolver::_petscSolveLocalSystem(const CRSMatrix<double, int>& A, + VectorWrapper<double> x, + const VectorWrapper<const double>& b) { - Internals::solveLocalSystem(A, x, b, m_options); + Internals::_petscSolveLocalSystem(A, x, b, m_options); } void -LinearSolver::solveLocalSystem(const SmallMatrix<double>& A, SmallVector<double>& x, const SmallVector<const double>& b) +LinearSolver::_petscSolveLocalSystem(const DenseMatrixWrapper<double>& A, + VectorWrapper<double> x, + const VectorWrapper<const double>& b) { - Internals::solveLocalSystem(A, x, b, m_options); + Internals::_petscSolveLocalSystem(A, x, b, m_options); } LinearSolver::LinearSolver(const LinearSolverOptions& options) : m_options{options} diff --git a/src/algebra/LinearSolver.hpp b/src/algebra/LinearSolver.hpp index e90402c8ddc78c9426f97300ab6b10730b4c503b..2d03a9cf5e7cced7f99b6707c042395f48054199 100644 --- a/src/algebra/LinearSolver.hpp +++ b/src/algebra/LinearSolver.hpp @@ -2,11 +2,14 @@ #define LINEAR_SOLVER_HPP #include <algebra/CRSMatrix.hpp> +#include <algebra/DenseMatrixWrapper.hpp> +#include <algebra/Eigen3Utils.hpp> #include <algebra/LinearSolverOptions.hpp> #include <algebra/SmallMatrix.hpp> #include <algebra/TinyMatrix.hpp> #include <algebra/TinyVector.hpp> #include <algebra/Vector.hpp> +#include <algebra/VectorWrapper.hpp> #include <utils/Exceptions.hpp> class LinearSolver @@ -16,29 +19,139 @@ class LinearSolver const LinearSolverOptions m_options; + void _builtinSolveLocalSystem(const CRSMatrix<double, int>& A, Vector<double>& x, const Vector<const double>& b); + + void _builtinSolveLocalSystem(const SmallMatrix<double>& A, + SmallVector<double>& x, + const SmallVector<const double>& b); + + void _eigen3SolveLocalSystem(const Eigen3SparseMatrixEmbedder& A, + VectorWrapper<double> x, + const VectorWrapper<const double>& b); + + void _eigen3SolveLocalSystem(const DenseMatrixWrapper<double>& A, + VectorWrapper<double> x, + const VectorWrapper<const double>& b); + + void _petscSolveLocalSystem(const CRSMatrix<double, int>& A, + VectorWrapper<double> x, + const VectorWrapper<const double>& b); + + void _petscSolveLocalSystem(const DenseMatrixWrapper<double>& A, + VectorWrapper<double> x, + const VectorWrapper<const double>& b); + public: bool hasLibrary(LSLibrary library) const; void checkOptions(const LinearSolverOptions& options) const; + void + solveLocalSystem(const CRSMatrix<double, int>& A, Vector<double>& x, const Vector<const double>& b) + { + Assert((x.size() == b.size()) and (x.size() - A.numberOfColumns() == 0) and A.isSquare()); + switch (m_options.library()) { + case LSLibrary::builtin: { + _builtinSolveLocalSystem(A, x, b); + break; + } + // LCOV_EXCL_START + case LSLibrary::eigen3: { + // not covered since if Eigen3 is not linked this point is + // unreachable: LinearSolver throws an exception at construction + // in this case. + _eigen3SolveLocalSystem(A, x, b); + break; + } + case LSLibrary::petsc: { + // not covered since if PETSc is not linked this point is + // unreachable: LinearSolver throws an exception at construction + // in this case. + _petscSolveLocalSystem(A, x, b); + break; + } + default: { + throw UnexpectedError(::name(m_options.library()) + " cannot solve local systems"); + } + // LCOV_EXCL_STOP + } + } + + void + solveLocalSystem(const SmallMatrix<double>& A, SmallVector<double>& x, const SmallVector<const double>& b) + { + Assert((x.size() == b.size()) and (x.size() - A.numberOfColumns() == 0) and A.isSquare()); + switch (m_options.library()) { + case LSLibrary::builtin: { + _builtinSolveLocalSystem(A, x, b); + break; + } + // LCOV_EXCL_START + case LSLibrary::eigen3: { + // not covered since if Eigen3 is not linked this point is + // unreachable: LinearSolver throws an exception at construction + // in this case. + _eigen3SolveLocalSystem(A, x, b); + break; + } + case LSLibrary::petsc: { + // not covered since if PETSc is not linked this point is + // unreachable: LinearSolver throws an exception at construction + // in this case. + _petscSolveLocalSystem(A, x, b); + break; + } + default: { + throw UnexpectedError(::name(m_options.library()) + " cannot solve local systems"); + } + // LCOV_EXCL_STOP + } + } + template <size_t N> void solveLocalSystem(const TinyMatrix<N>& A, TinyVector<N>& x, const TinyVector<N>& b) { - SmallMatrix A_dense{A}; + Assert((x.size() == b.size()) and (x.size() - A.numberOfColumns() == 0) and A.isSquare()); + switch (m_options.library()) { + case LSLibrary::builtin: { + // Probably expensive but builtin is not the preferred way to + // solve linear systems + SmallMatrix A_dense{A}; + + SmallVector x_vector{x}; + SmallVector<const double> b_vector{b}; - SmallVector x_vector{x}; - SmallVector b_vector{b}; + this->solveLocalSystem(A_dense, x_vector, b_vector); - this->solveLocalSystem(A_dense, x_vector, b_vector); + for (size_t i = 0; i < N; ++i) { + x[i] = x_vector[i]; + } - for (size_t i = 0; i < N; ++i) { - x[i] = x_vector[i]; + _builtinSolveLocalSystem(A, x, b); + break; + } + // LCOV_EXCL_START + case LSLibrary::eigen3: { + // not covered since if Eigen3 is not linked this point is + // unreachable: LinearSolver throws an exception at construction + // in this case. + _eigen3SolveLocalSystem(A, x, b); + break; + } + case LSLibrary::petsc: { + // not covered since if PETSc is not linked this point is + // unreachable: LinearSolver throws an exception at construction + // in this case. + _petscSolveLocalSystem(A, x, b); + break; + } + default: { + throw UnexpectedError(::name(m_options.library()) + " cannot solve local systems"); + } + // LCOV_EXCL_STOP } } - void solveLocalSystem(const CRSMatrix<double, int>& A, Vector<double>& x, const Vector<const double>& b); - void solveLocalSystem(const SmallMatrix<double>& A, SmallVector<double>& x, const SmallVector<const double>& b); - LinearSolver(); LinearSolver(const LinearSolverOptions& options); }; diff --git a/src/algebra/LinearSolverOptions.hpp b/src/algebra/LinearSolverOptions.hpp index 940ce1ed63a2dbbf046022aee838c3f9438c1906..97a1748780f19d28e98f299fd7679d7ab15466f0 100644 --- a/src/algebra/LinearSolverOptions.hpp +++ b/src/algebra/LinearSolverOptions.hpp @@ -10,6 +10,7 @@ enum class LSLibrary : int8_t LS__begin = 0, // builtin = LS__begin, + eigen3, petsc, // LS__end @@ -49,6 +50,9 @@ name(const LSLibrary library) case LSLibrary::builtin: { return "builtin"; } + case LSLibrary::eigen3: { + return "Eigen3"; + } case LSLibrary::petsc: { return "PETSc"; } diff --git a/src/algebra/PETScUtils.hpp b/src/algebra/PETScUtils.hpp index c4db9ee5910ef8e58333dbfb6dc20c8a1987bb46..c6d2e8be88c6098e2f187cda9177c120facc6f23 100644 --- a/src/algebra/PETScUtils.hpp +++ b/src/algebra/PETScUtils.hpp @@ -6,6 +6,7 @@ #ifdef PUGS_HAS_PETSC #include <algebra/CRSMatrix.hpp> +#include <algebra/DenseMatrixWrapper.hpp> #include <algebra/SmallMatrix.hpp> #include <algebra/TinyMatrix.hpp> @@ -52,12 +53,8 @@ class PETScAijMatrixEmbedder return m_petscMat; } - template <size_t N> - PETScAijMatrixEmbedder(const TinyMatrix<N>& A) : PETScAijMatrixEmbedder{N, N, &A(0, 0)} - {} - - PETScAijMatrixEmbedder(const SmallMatrix<double>& A) - : PETScAijMatrixEmbedder{A.numberOfRows(), A.numberOfColumns(), &A(0, 0)} + PETScAijMatrixEmbedder(const DenseMatrixWrapper<double>& A) + : PETScAijMatrixEmbedder{A.numberOfRows(), A.numberOfColumns(), A.ptr()} {} PETScAijMatrixEmbedder(const CRSMatrix<double, int>& A); diff --git a/src/algebra/ShrinkVectorView.hpp b/src/algebra/ShrinkVectorView.hpp index cdf786c5a30970b747b0f540642d5637059198b8..83aed7cb2295ee4d20a044f7ba1039f34e7b5ffa 100644 --- a/src/algebra/ShrinkVectorView.hpp +++ b/src/algebra/ShrinkVectorView.hpp @@ -23,10 +23,10 @@ class ShrinkVectorView friend std::ostream& operator<<(std::ostream& os, const ShrinkVectorView& x) { - if (x.size() > 0) { + if (x.dimension() > 0) { os << 0 << ':' << NaNHelper(x[0]); } - for (size_t i = 1; i < x.size(); ++i) { + for (size_t i = 1; i < x.dimension(); ++i) { os << ' ' << i << ':' << NaNHelper(x[i]); } return os; diff --git a/src/algebra/VectorWrapper.hpp b/src/algebra/VectorWrapper.hpp new file mode 100644 index 0000000000000000000000000000000000000000..0bdeefbfd3f10407358613e7f1d2487bba22b26d --- /dev/null +++ b/src/algebra/VectorWrapper.hpp @@ -0,0 +1,47 @@ +#ifndef VECTOR_WRAPPER_HPP +#define VECTOR_WRAPPER_HPP + +#include <algebra/SmallVector.hpp> +#include <algebra/TinyVector.hpp> +#include <algebra/Vector.hpp> + +template <typename DataType> +class VectorWrapper +{ + private: + const size_t m_size; + DataType* const m_vector_ptr; + + public: + PUGS_INLINE + size_t + size() const + { + return m_size; + } + + PUGS_INLINE + DataType* const& + ptr() const + { + return m_vector_ptr; + } + + VectorWrapper(const Vector<DataType>& x) : m_size{x.size()}, m_vector_ptr{(m_size > 0) ? (&x[0]) : nullptr} {} + VectorWrapper(Vector<DataType>& x) : m_size{x.size()}, m_vector_ptr{(m_size > 0) ? (&x[0]) : nullptr} {} + + VectorWrapper(const SmallVector<DataType>& x) : m_size{x.size()}, m_vector_ptr{(m_size > 0) ? (&x[0]) : nullptr} {} + + template <size_t N> + VectorWrapper(const TinyVector<N, DataType>& x) : m_size{N}, m_vector_ptr{(N > 0) ? (&x[0]) : nullptr} + {} + + template <size_t N> + VectorWrapper(TinyVector<N, DataType>& x) : m_size{N}, m_vector_ptr{(N > 0) ? (&x[0]) : nullptr} + {} + + VectorWrapper(const VectorWrapper&) = delete; + VectorWrapper(VectorWrapper&&) = delete; +}; + +#endif // VECTOR_WRAPPER_HPP diff --git a/src/language/ast/ASTModulesImporter.cpp b/src/language/ast/ASTModulesImporter.cpp index 4cae0a921c139b3ae68a3decb0ce4f83876c4701..e1dc0043e689b506776540bb57e4851dc10883ae 100644 --- a/src/language/ast/ASTModulesImporter.cpp +++ b/src/language/ast/ASTModulesImporter.cpp @@ -26,10 +26,10 @@ ASTModulesImporter::_importModule(ASTNode& import_node) std::cout << " * importing '" << rang::fgB::green << module_name << rang::style::reset << "' module\n"; } - m_module_repository.populateSymbolTable(module_name_node, m_symbol_table); - m_module_repository.registerOperators(module_name); + ModuleRepository::getInstance().populateSymbolTable(module_name_node, m_symbol_table); + ModuleRepository::getInstance().registerOperators(module_name); if (CheckpointResumeRepository::isCreated()) { - m_module_repository.registerCheckpointResume(module_name); + ModuleRepository::getInstance().registerCheckpointResume(module_name); } } @@ -49,7 +49,7 @@ ASTModulesImporter::ASTModulesImporter(ASTNode& root_node) : m_symbol_table{*roo { Assert(root_node.is_root()); OperatorRepository::instance().reset(); - m_module_repository.populateMandatoryData(root_node, m_symbol_table); + ModuleRepository::getInstance().populateMandatoryData(root_node, m_symbol_table); this->_importAllModules(root_node); diff --git a/src/language/ast/ASTModulesImporter.hpp b/src/language/ast/ASTModulesImporter.hpp index 566155c4a19ecd00f0b049a29030c0036f212d8a..15ba8a7dc6cecbbb39bfb11c4d202d39200061a4 100644 --- a/src/language/ast/ASTModulesImporter.hpp +++ b/src/language/ast/ASTModulesImporter.hpp @@ -14,8 +14,6 @@ class ASTModulesImporter std::set<std::string> m_imported_modules; SymbolTable& m_symbol_table; - ModuleRepository m_module_repository; - void _importModule(ASTNode& import_node); void _importAllModules(ASTNode& node); @@ -23,7 +21,7 @@ class ASTModulesImporter const ModuleRepository& moduleRepository() const { - return m_module_repository; + return ModuleRepository::getInstance(); } ASTModulesImporter(ASTNode& root_node); diff --git a/src/language/modules/BinaryOperatorRegisterForVh.cpp b/src/language/modules/BinaryOperatorRegisterForVh.cpp index 3eb83537323d91bbf6266cbefebf4d004d74c2be..d1c20cb78718568a3535e30f0b5c9ce255be1db6 100644 --- a/src/language/modules/BinaryOperatorRegisterForVh.cpp +++ b/src/language/modules/BinaryOperatorRegisterForVh.cpp @@ -1,6 +1,6 @@ #include <language/modules/BinaryOperatorRegisterForVh.hpp> -#include <language/modules/SchemeModule.hpp> +#include <language/modules/SchemeModuleTypes.hpp> #include <language/utils/BinaryOperatorProcessorBuilder.hpp> #include <language/utils/DataHandler.hpp> #include <language/utils/DataVariant.hpp> diff --git a/src/language/modules/CoreModule.cpp b/src/language/modules/CoreModule.cpp index d7b331a1dc606a83cf77dfe405b184b9ff143308..5b5753eb8b3952d6d9c08c34ccc843f4bece2dc9 100644 --- a/src/language/modules/CoreModule.cpp +++ b/src/language/modules/CoreModule.cpp @@ -32,6 +32,7 @@ #include <language/utils/UnaryOperatorRegisterForRnxn.hpp> #include <language/utils/UnaryOperatorRegisterForZ.hpp> #include <utils/Messenger.hpp> +#include <utils/PartitionerOptions.hpp> #include <utils/PugsUtils.hpp> #include <utils/RandomEngine.hpp> #include <utils/Stop.hpp> @@ -42,7 +43,7 @@ #include <utils/checkpointing/ResumingManager.hpp> #include <utils/checkpointing/WriteOStream.hpp> -#include <random> +#include <language/modules/ModuleRepository.hpp> CoreModule::CoreModule() : BuiltinModule(true) { @@ -94,6 +95,27 @@ CoreModule::CoreModule() : BuiltinModule(true) )); + this->_addBuiltinFunction("setPartitionerLibrary", std::function( + + [](const std::string& library_name) -> void { + PartitionerOptions::default_options.library() = + getPartitionerEnumFromName<PartitionerLibrary>(library_name); + } + + )); + + this->_addBuiltinFunction("getPartitionerOptions", std::function( + + []() -> std::string { + std::ostringstream os; + os << rang::fgB::yellow << "Partitioner options" + << rang::style::reset << '\n'; + os << PartitionerOptions::default_options; + return os.str(); + } + + )); + this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const OStream>>); this->_addBuiltinFunction("ofstream", std::function( @@ -219,3 +241,5 @@ CoreModule::registerCheckpointResume() const #endif // PUGS_HAS_HDF5 } + +ModuleRepository::Subscribe<CoreModule> core_module; diff --git a/src/language/modules/DevUtilsModule.cpp b/src/language/modules/DevUtilsModule.cpp index 361b4357eda0ebbfe4350cc6c64bbe72cfaa0829..4595ea6c9d9a45431b303c00b4de6defdda38b7b 100644 --- a/src/language/modules/DevUtilsModule.cpp +++ b/src/language/modules/DevUtilsModule.cpp @@ -1,5 +1,7 @@ #include <language/modules/DevUtilsModule.hpp> +#include <language/modules/ModuleRepository.hpp> + #include <dev/ParallelChecker.hpp> #include <language/modules/MeshModuleTypes.hpp> #include <language/modules/SchemeModuleTypes.hpp> @@ -126,3 +128,5 @@ DevUtilsModule::registerOperators() const void DevUtilsModule::registerCheckpointResume() const {} + +ModuleRepository::Subscribe<DevUtilsModule> dev_utils_module; diff --git a/src/language/modules/LinearSolverModule.cpp b/src/language/modules/LinearSolverModule.cpp index aef6470d2455909f5ad2d307aa4e5cb859b8bcbb..1ff8be36d3d0649caaae56e250477fda2ee7690d 100644 --- a/src/language/modules/LinearSolverModule.cpp +++ b/src/language/modules/LinearSolverModule.cpp @@ -1,6 +1,9 @@ #include <language/modules/LinearSolverModule.hpp> -#include <algebra/LinearSolver.hpp> +#include <language/modules/ModuleRepository.hpp> + +#include <algebra/EigenvalueSolverOptions.hpp> +#include <algebra/LinearSolverOptions.hpp> #include <language/utils/BuiltinFunctionEmbedder.hpp> #include <language/utils/TypeDescriptor.hpp> @@ -88,6 +91,43 @@ LinearSolverModule::LinearSolverModule() return os.str(); } + )); + + this->_addBuiltinFunction("setESLibrary", std::function( + + [](const std::string& library_name) -> void { + EigenvalueSolverOptions::default_options.library() = + getESEnumFromName<ESLibrary>(library_name); + } + + )); + + this->_addBuiltinFunction("getESOptions", std::function( + + []() -> std::string { + std::ostringstream os; + os << rang::fgB::yellow << "Eigenvalue solver options" + << rang::style::reset << '\n'; + os << EigenvalueSolverOptions::default_options; + return os.str(); + } + + )); + + this->_addBuiltinFunction("getESAvailable", std::function( + + []() -> std::string { + std::ostringstream os; + + os << rang::fgB::yellow << "Available eigenvalue solver options" + << rang::style::reset << '\n'; + os << rang::fgB::blue << " libraries" << rang::style::reset << '\n'; + + printESEnumListNames<ESLibrary>(os); + + return os.str(); + } + )); } @@ -98,3 +138,5 @@ LinearSolverModule::registerOperators() const void LinearSolverModule::registerCheckpointResume() const {} + +ModuleRepository::Subscribe<LinearSolverModule> linear_solver_module; diff --git a/src/language/modules/MathFunctionRegisterForVh.cpp b/src/language/modules/MathFunctionRegisterForVh.cpp index b31fe47d0eba87ff2e7f0823a530bf170e410d6c..d9a30e86996a9cd6b99af63d64a8ab33264c7fe3 100644 --- a/src/language/modules/MathFunctionRegisterForVh.cpp +++ b/src/language/modules/MathFunctionRegisterForVh.cpp @@ -1,6 +1,7 @@ #include <language/modules/MathFunctionRegisterForVh.hpp> #include <language/modules/SchemeModule.hpp> +#include <language/modules/SchemeModuleTypes.hpp> #include <language/utils/BuiltinFunctionEmbedder.hpp> #include <language/utils/EmbeddedDiscreteFunctionMathFunctions.hpp> #include <scheme/DiscreteFunctionVariant.hpp> diff --git a/src/language/modules/MathModule.cpp b/src/language/modules/MathModule.cpp index 19a35a1529b1b65310c3c51d69fe4c5d02dc4d8a..1caea452428125e7a1c16aa88cb8278a5685fb35 100644 --- a/src/language/modules/MathModule.cpp +++ b/src/language/modules/MathModule.cpp @@ -1,5 +1,6 @@ #include <language/modules/MathModule.hpp> +#include <language/modules/ModuleRepository.hpp> #include <language/utils/BuiltinFunctionEmbedder.hpp> #include <cmath> @@ -136,3 +137,5 @@ MathModule::registerOperators() const void MathModule::registerCheckpointResume() const {} + +ModuleRepository::Subscribe<MathModule> math_module; diff --git a/src/language/modules/MeshModule.cpp b/src/language/modules/MeshModule.cpp index c76260ce58c75c130e4638054fc394458fedbf63..9b698b9e095ff6c227c58a2e6fe6bae5f7f72ad4 100644 --- a/src/language/modules/MeshModule.cpp +++ b/src/language/modules/MeshModule.cpp @@ -1,5 +1,7 @@ #include <language/modules/MeshModule.hpp> +#include <language/modules/ModuleRepository.hpp> + #include <algebra/TinyVector.hpp> #include <language/node_processor/ExecutionPolicy.hpp> #include <language/utils/BinaryOperatorProcessorBuilder.hpp> @@ -160,27 +162,28 @@ MeshModule::MeshModule() )); - this - ->_addBuiltinFunction("interpolate", - std::function( + this->_addBuiltinFunction("interpolate", + std::function( - [](std::shared_ptr<const MeshVariant> mesh_v, std::shared_ptr<const ItemType> item_type, - const FunctionSymbolId& function_id) -> std::shared_ptr<const ItemValueVariant> { - return ItemValueVariantFunctionInterpoler{mesh_v, *item_type, function_id}.interpolate(); - } + [](std::shared_ptr<const MeshVariant> mesh_v, std::shared_ptr<const ItemType> item_type, + const FunctionSymbolId& function_id) -> std::shared_ptr<const ItemValueVariant> { + return ItemValueVariantFunctionInterpoler{mesh_v, *item_type, function_id} + .interpolate(); + } - )); + )); - this->_addBuiltinFunction( - "interpolate_array", - std::function( + this->_addBuiltinFunction("interpolate_array", + std::function( - [](std::shared_ptr<const MeshVariant> mesh_v, std::shared_ptr<const ItemType> item_type, - const std::vector<FunctionSymbolId>& function_id_list) -> std::shared_ptr<const ItemArrayVariant> { - return ItemArrayVariantFunctionInterpoler{mesh_v, *item_type, function_id_list}.interpolate(); - } + [](std::shared_ptr<const MeshVariant> mesh_v, std::shared_ptr<const ItemType> item_type, + const std::vector<FunctionSymbolId>& function_id_list) + -> std::shared_ptr<const ItemArrayVariant> { + return ItemArrayVariantFunctionInterpoler{mesh_v, *item_type, function_id_list} + .interpolate(); + } - )); + )); this->_addBuiltinFunction("transform", std::function( @@ -305,6 +308,46 @@ MeshModule::MeshModule() return DualMeshManager::instance().getMedianDualMesh(mesh_v); } + )); + + this->_addBuiltinFunction("cell_owner", std::function( + + [](const std::shared_ptr<const MeshVariant>& mesh_v) + -> std::shared_ptr<const ItemValueVariant> { + return std::visit( + [&](auto&& mesh) { + const auto& connectivity = mesh->connectivity(); + auto cell_owner = connectivity.cellOwner(); + CellValue<long int> cell_owner_long{connectivity}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + cell_owner_long[cell_id] = cell_owner[cell_id]; + }); + return std::make_shared<const ItemValueVariant>(cell_owner_long); + }, + mesh_v->variant()); + } + + )); + + this->_addBuiltinFunction("node_owner", std::function( + + [](const std::shared_ptr<const MeshVariant>& mesh_v) + -> std::shared_ptr<const ItemValueVariant> { + return std::visit( + [&](auto&& mesh) { + const auto& connectivity = mesh->connectivity(); + auto node_owner = connectivity.nodeOwner(); + NodeValue<long int> node_owner_long{connectivity}; + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { + node_owner_long[node_id] = node_owner[node_id]; + }); + return std::make_shared<const ItemValueVariant>(node_owner_long); + }, + mesh_v->variant()); + } + )); } @@ -442,3 +485,5 @@ MeshModule::registerCheckpointResume() const #endif // PUGS_HAS_HDF5 } + +ModuleRepository::Subscribe<MeshModule> mesh_module; diff --git a/src/language/modules/ModuleRepository.cpp b/src/language/modules/ModuleRepository.cpp index 59689fb6fee512e45cd76c7c9d3cd5c11d543ee8..c9c487b67b5fdbf63d2b307baf1497519666a5d8 100644 --- a/src/language/modules/ModuleRepository.cpp +++ b/src/language/modules/ModuleRepository.cpp @@ -1,14 +1,6 @@ #include <language/modules/ModuleRepository.hpp> #include <language/ast/ASTNode.hpp> -#include <language/modules/CoreModule.hpp> -#include <language/modules/DevUtilsModule.hpp> -#include <language/modules/LinearSolverModule.hpp> -#include <language/modules/MathModule.hpp> -#include <language/modules/MeshModule.hpp> -#include <language/modules/SchemeModule.hpp> -#include <language/modules/SocketModule.hpp> -#include <language/modules/WriterModule.hpp> #include <language/utils/BasicAffectationRegistrerFor.hpp> #include <language/utils/BuiltinFunctionEmbedder.hpp> #include <language/utils/CheckpointResumeRepository.hpp> @@ -19,9 +11,29 @@ #include <utils/PugsAssert.hpp> -#include <algorithm> #include <set> +ModuleRepository* ModuleRepository::m_instance = nullptr; + +ModuleRepository& +ModuleRepository::getInstance() +{ + if (m_instance == nullptr) { + m_instance = new ModuleRepository; + } + + return *m_instance; +} + +void +ModuleRepository::destroy() +{ + if (m_instance != nullptr) { + delete m_instance; + m_instance = nullptr; + } +} + void ModuleRepository::_subscribe(std::unique_ptr<IModule> m) { @@ -52,18 +64,6 @@ ModuleRepository::_subscribe(std::unique_ptr<IModule> m) Assert(success, "module has already been subscribed"); } -ModuleRepository::ModuleRepository() -{ - this->_subscribe(std::make_unique<CoreModule>()); - this->_subscribe(std::make_unique<LinearSolverModule>()); - this->_subscribe(std::make_unique<MathModule>()); - this->_subscribe(std::make_unique<MeshModule>()); - this->_subscribe(std::make_unique<SchemeModule>()); - this->_subscribe(std::make_unique<SocketModule>()); - this->_subscribe(std::make_unique<DevUtilsModule>()); - this->_subscribe(std::make_unique<WriterModule>()); -} - template <typename NameEmbedderMapT, typename EmbedderTableT> void ModuleRepository::_populateEmbedderTableT(const ASTNode& module_node, diff --git a/src/language/modules/ModuleRepository.hpp b/src/language/modules/ModuleRepository.hpp index 407d7b55bd9b7c8454159a315b9c2f7a256eecb6..35bf6f7aa6a7704f2804125c488ac0bf115ddf39 100644 --- a/src/language/modules/ModuleRepository.hpp +++ b/src/language/modules/ModuleRepository.hpp @@ -31,7 +31,23 @@ class ModuleRepository const IModule::NameValueMap& name_value_map, SymbolTable& symbol_table); + static ModuleRepository* m_instance; + public: + static ModuleRepository& getInstance(); + + static void destroy(); + + template <typename ModuleT> + struct Subscribe + { + Subscribe() + { + static_assert(std::is_base_of_v<IModule, ModuleT>, "module must inherit IModule interface"); + ModuleRepository::getInstance()._subscribe(std::make_unique<ModuleT>()); + } + }; + void populateSymbolTable(const ASTNode& module_name_node, SymbolTable& symbol_table); void populateMandatoryData(const ASTNode& root_node, SymbolTable& symbol_table); void registerOperators(const std::string& module_name); @@ -46,7 +62,10 @@ class ModuleRepository ModuleRepository(const ModuleRepository&) = delete; ModuleRepository(ModuleRepository&&) = delete; - ModuleRepository(); + private: + ModuleRepository() = default; + + public: ~ModuleRepository() = default; }; diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp index b0dc16e29b899377a46b6add90aeab475bcc1504..6ffb816b48739d57412bb21db2bbd703bdc4bcf4 100644 --- a/src/language/modules/SchemeModule.cpp +++ b/src/language/modules/SchemeModule.cpp @@ -1,4 +1,7 @@ #include <language/modules/SchemeModule.hpp> +#include <language/modules/SchemeModuleTypes.hpp> + +#include <language/modules/ModuleRepository.hpp> #include <analysis/GaussLegendreQuadratureDescriptor.hpp> #include <analysis/GaussLobattoQuadratureDescriptor.hpp> @@ -1170,3 +1173,5 @@ SchemeModule::registerCheckpointResume() const #endif // PUGS_HAS_HDF5 } + +ModuleRepository::Subscribe<SchemeModule> scheme_module; diff --git a/src/language/modules/SchemeModule.hpp b/src/language/modules/SchemeModule.hpp index c61b3084406dbd8ae9889c79b4172195daaa6fe8..cf169175c4b46f7364799e16bdb4b84633d1f61c 100644 --- a/src/language/modules/SchemeModule.hpp +++ b/src/language/modules/SchemeModule.hpp @@ -2,7 +2,6 @@ #define SCHEME_MODULE_HPP #include <language/modules/BuiltinModule.hpp> -#include <language/modules/SchemeModuleTypes.hpp> class SchemeModule : public BuiltinModule { diff --git a/src/language/modules/SocketModule.cpp b/src/language/modules/SocketModule.cpp index 79321d088f960e0bb7450c82820cf6568e79d174..9c2ecdbe1b40730d0181d7f4ee6f3bf396f70a34 100644 --- a/src/language/modules/SocketModule.cpp +++ b/src/language/modules/SocketModule.cpp @@ -1,5 +1,7 @@ #include <language/modules/SocketModule.hpp> +#include <language/modules/ModuleRepository.hpp> + #include <language/utils/BinaryOperatorProcessorBuilder.hpp> #include <language/utils/BuiltinFunctionEmbedder.hpp> #include <language/utils/CheckpointResumeRepository.hpp> @@ -271,3 +273,5 @@ SocketModule::registerCheckpointResume() const throw NotImplementedError("checkpoint/resume with sockets"); })); } + +ModuleRepository::Subscribe<SocketModule> socket_module; diff --git a/src/language/modules/UnaryOperatorRegisterForVh.cpp b/src/language/modules/UnaryOperatorRegisterForVh.cpp index e2e9c2db9b72a9a687806d24dee836cfd37a22e8..3d52ab54687eabcd6162536d88325b3285d45e1f 100644 --- a/src/language/modules/UnaryOperatorRegisterForVh.cpp +++ b/src/language/modules/UnaryOperatorRegisterForVh.cpp @@ -1,6 +1,6 @@ #include <language/modules/UnaryOperatorRegisterForVh.hpp> -#include <language/modules/SchemeModule.hpp> +#include <language/modules/SchemeModuleTypes.hpp> #include <language/utils/DataHandler.hpp> #include <language/utils/DataVariant.hpp> #include <language/utils/EmbeddedDiscreteFunctionOperators.hpp> diff --git a/src/language/modules/WriterModule.cpp b/src/language/modules/WriterModule.cpp index 6d84506501de277f1970c13a7c24cd587ed79f20..e524a885639ac9a2a2da6330422565447bcb347d 100644 --- a/src/language/modules/WriterModule.cpp +++ b/src/language/modules/WriterModule.cpp @@ -1,7 +1,9 @@ #include <language/modules/WriterModule.hpp> -#include <language/modules/MeshModule.hpp> -#include <language/modules/SchemeModule.hpp> +#include <language/modules/ModuleRepository.hpp> + +#include <language/modules/MeshModuleTypes.hpp> +#include <language/modules/SchemeModuleTypes.hpp> #include <language/utils/BuiltinFunctionEmbedder.hpp> #include <language/utils/CheckpointResumeRepository.hpp> #include <language/utils/TypeDescriptor.hpp> @@ -242,3 +244,5 @@ WriterModule::registerCheckpointResume() const #endif // PUGS_HAS_HDF5 } + +ModuleRepository::Subscribe<WriterModule> writer_module{}; diff --git a/src/language/utils/EmbeddedDiscreteFunctionMathFunctions.cpp b/src/language/utils/EmbeddedDiscreteFunctionMathFunctions.cpp index 5d000813aeff1e670a2f547a69827a679cbfa699..892037219c389e6ff7ebd28e8b5afd586845c14f 100644 --- a/src/language/utils/EmbeddedDiscreteFunctionMathFunctions.cpp +++ b/src/language/utils/EmbeddedDiscreteFunctionMathFunctions.cpp @@ -234,10 +234,14 @@ dot(const std::shared_ptr<const DiscreteFunctionVariant>& f_v, throw NormalError(EmbeddedDiscreteFunctionUtils::invalidOperandType(f)); } } else if constexpr (is_discrete_function_P0_vector_v<TypeOfF>) { - if (f.size() == g.size()) { - return std::make_shared<DiscreteFunctionVariant>(dot(f, g)); + if constexpr (std::is_arithmetic_v<DataType>) { + if (f.size() == g.size()) { + return std::make_shared<DiscreteFunctionVariant>(dot(f, g)); + } else { + throw NormalError("operands have different dimension"); + } } else { - throw NormalError("operands have different dimension"); + throw NormalError(EmbeddedDiscreteFunctionUtils::invalidOperandType(f)); } } else { throw NormalError(EmbeddedDiscreteFunctionUtils::invalidOperandType(f)); @@ -691,8 +695,6 @@ sum_of_Vh_components(const std::shared_ptr<const DiscreteFunctionVariant>& f) [&](auto&& discrete_function) -> std::shared_ptr<const DiscreteFunctionVariant> { using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { - using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; - static_assert(std::is_same_v<DataType, double>); return std::make_shared<DiscreteFunctionVariant>(sumOfComponents(discrete_function)); } else { throw NormalError(EmbeddedDiscreteFunctionUtils::invalidOperandType(f)); diff --git a/src/main.cpp b/src/main.cpp index c1eeef6a633d5074fef20545a2348a77e5a6888a..cbaf6277e5ee0c1c7d689403e8d78cae54c3ed5f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,7 @@ #include <analysis/QuadratureManager.hpp> #include <dev/ParallelChecker.hpp> #include <language/PugsParser.hpp> +#include <language/modules/ModuleRepository.hpp> #include <mesh/DualConnectivityManager.hpp> #include <mesh/DualMeshManager.hpp> #include <mesh/MeshDataManager.hpp> @@ -12,6 +13,8 @@ #include <utils/RandomEngine.hpp> #include <utils/checkpointing/ResumingManager.hpp> +#include <utils/PluginsLoader.hpp> + int main(int argc, char* argv[]) { @@ -23,6 +26,8 @@ main(int argc, char* argv[]) std::string filename = initialize(argc, argv); + PluginsLoader plugins_loader; + SynchronizerManager::create(); RandomEngine::create(); QuadratureManager::create(); @@ -34,6 +39,8 @@ main(int argc, char* argv[]) parser(filename); ExecutionStatManager::printInfo(); + ModuleRepository::destroy(); + StencilManager::destroy(); DualMeshManager::destroy(); DualConnectivityManager::destroy(); diff --git a/src/mesh/CMakeLists.txt b/src/mesh/CMakeLists.txt index b4d70359a5a4a7f8a4d2e352ab702ecc26761dbc..781b7e583595eab942d2f924de705cf332841fad 100644 --- a/src/mesh/CMakeLists.txt +++ b/src/mesh/CMakeLists.txt @@ -50,5 +50,6 @@ add_library( target_link_libraries( PugsMesh + ${PARMETIS_TARGET} ${HIGHFIVE_TARGET} ) diff --git a/src/mesh/Connectivity.hpp b/src/mesh/Connectivity.hpp index c04bdbdf9d215ec9324d5545dcf2a079d4590c7f..111411baa8f527e544aa98105939593a99d0bd09 100644 --- a/src/mesh/Connectivity.hpp +++ b/src/mesh/Connectivity.hpp @@ -19,7 +19,6 @@ #include <utils/PugsTraits.hpp> #include <utils/PugsUtils.hpp> -#include <algorithm> #include <iostream> #include <set> #include <vector> diff --git a/src/mesh/StencilArray.hpp b/src/mesh/StencilArray.hpp index 9571c346bae6e4e345a16b09188dce15ab73b456..ffe8638bf0d8fdb472780150603c7fab21ca1eb9 100644 --- a/src/mesh/StencilArray.hpp +++ b/src/mesh/StencilArray.hpp @@ -12,6 +12,9 @@ class StencilArray public: using ItemToItemMatrixT = ItemToItemMatrix<source_item_type, target_item_type>; + using SourceItemId = ItemIdT<source_item_type>; + using TargetItemId = ItemIdT<target_item_type>; + class BoundaryDescriptorStencilArray { private: @@ -71,9 +74,9 @@ class StencilArray PUGS_INLINE auto - operator[](CellId cell_id) const + operator[](SourceItemId source_item_id) const { - return m_stencil_array[cell_id]; + return m_stencil_array[source_item_id]; } StencilArray(const ConnectivityMatrix& connectivity_matrix, diff --git a/src/mesh/StencilBuilder.cpp b/src/mesh/StencilBuilder.cpp index 31db762a1002e3c633afeaabd4f06d6a98dff634..20fc3bf61dd6a59ef82ff7bdb25dc5e32c7eef24 100644 --- a/src/mesh/StencilBuilder.cpp +++ b/src/mesh/StencilBuilder.cpp @@ -5,16 +5,23 @@ #include <utils/GlobalVariableManager.hpp> #include <utils/Messenger.hpp> -#include <set> - template <ItemType item_type> class StencilBuilder::Layer { using ItemId = ItemIdT<item_type>; + ItemValue<const int, item_type> m_number_of; + std::vector<ItemId> m_item_id_vector; std::vector<int> m_item_number_vector; public: + void + clear() + { + m_item_id_vector.clear(); + m_item_number_vector.clear(); + } + size_t size() const { @@ -22,9 +29,17 @@ class StencilBuilder::Layer return m_item_id_vector.size(); } + const std::vector<ItemId>& + itemIdList() const + { + return m_item_id_vector; + } + bool - hasItemNumber(const int item_number) const + hasItemNumber(const ItemId item_id) const { + const int item_number = m_number_of[item_id]; + ssize_t begin = 0; ssize_t end = m_item_number_vector.size(); @@ -37,26 +52,22 @@ class StencilBuilder::Layer if (begin == mid) { break; } - begin = mid - 1; + begin = mid; } else { if (end == mid) { break; } - end = mid + 1; + end = mid; } } return false; } - const std::vector<ItemId>& - itemIdList() const - { - return m_item_id_vector; - } - void - add(const ItemId item_id, const int item_number) + add(const ItemId item_id) { + const int item_number = m_number_of[item_id]; + ssize_t begin = 0; ssize_t end = m_item_number_vector.size(); @@ -97,7 +108,14 @@ class StencilBuilder::Layer } } - Layer() = default; + Layer& operator=(const Layer&) = default; + Layer& operator=(Layer&&) = default; + + template <size_t Dimension> + Layer(const Connectivity<Dimension>& connectivity) : m_number_of{connectivity.template number<item_type>()} + {} + + // Layer() = default; Layer(const Layer&) = default; Layer(Layer&&) = default; @@ -117,27 +135,25 @@ StencilBuilder::_buildSymmetryConnectingItemList(const ConnectivityType& connect symmetry_boundary_descriptor_list.size()}; symmetry_connecting_item_list.fill(false); - if constexpr (ConnectivityType::Dimension > 1) { - auto face_to_connecting_item_matrix = - connectivity.template getItemToItemMatrix<ItemType::face, connecting_item_type>(); + if constexpr (ItemTypeId<ConnectivityType::Dimension>::itemTId(connecting_item_type) == + ItemTypeId<ConnectivityType::Dimension>::itemTId(ItemType::face)) { size_t i_symmetry_boundary = 0; for (auto p_boundary_descriptor : symmetry_boundary_descriptor_list) { const IBoundaryDescriptor& boundary_descriptor = *p_boundary_descriptor; bool found = false; - for (size_t i_ref_face_list = 0; i_ref_face_list < connectivity.template numberOfRefItemList<ItemType::face>(); - ++i_ref_face_list) { - const auto& ref_face_list = connectivity.template refItemList<ItemType::face>(i_ref_face_list); - if (ref_face_list.refId() == boundary_descriptor) { - found = true; - for (size_t i_face = 0; i_face < ref_face_list.list().size(); ++i_face) { - const FaceId face_id = ref_face_list.list()[i_face]; - auto connecting_item_list = face_to_connecting_item_matrix[face_id]; - for (size_t i_connecting_item = 0; i_connecting_item < connecting_item_list.size(); ++i_connecting_item) { - const ConnectingItemId connecting_item_id = connecting_item_list[i_connecting_item]; + for (size_t i_ref_connecting_item_list = 0; + i_ref_connecting_item_list < connectivity.template numberOfRefItemList<connecting_item_type>(); + ++i_ref_connecting_item_list) { + const auto& ref_connecting_item_list = + connectivity.template refItemList<connecting_item_type>(i_ref_connecting_item_list); + if (ref_connecting_item_list.refId() == boundary_descriptor) { + found = true; + auto connecting_item_list = ref_connecting_item_list.list(); + for (size_t i_connecting_item = 0; i_connecting_item < connecting_item_list.size(); ++i_connecting_item) { + const ConnectingItemId connecting_item_id = connecting_item_list[i_connecting_item]; - symmetry_connecting_item_list[connecting_item_id][i_symmetry_boundary] = true; - } + symmetry_connecting_item_list[connecting_item_id][i_symmetry_boundary] = true; } break; } @@ -149,26 +165,27 @@ StencilBuilder::_buildSymmetryConnectingItemList(const ConnectivityType& connect throw NormalError(error_msg.str()); } } - - return symmetry_connecting_item_list; } else { + auto face_to_connecting_item_matrix = + connectivity.template getItemToItemMatrix<ItemType::face, connecting_item_type>(); size_t i_symmetry_boundary = 0; for (auto p_boundary_descriptor : symmetry_boundary_descriptor_list) { const IBoundaryDescriptor& boundary_descriptor = *p_boundary_descriptor; bool found = false; - for (size_t i_ref_connecting_item_list = 0; - i_ref_connecting_item_list < connectivity.template numberOfRefItemList<connecting_item_type>(); - ++i_ref_connecting_item_list) { - const auto& ref_connecting_item_list = - connectivity.template refItemList<connecting_item_type>(i_ref_connecting_item_list); - if (ref_connecting_item_list.refId() == boundary_descriptor) { - found = true; - auto connecting_item_list = ref_connecting_item_list.list(); - for (size_t i_connecting_item = 0; i_connecting_item < connecting_item_list.size(); ++i_connecting_item) { - const ConnectingItemId connecting_item_id = connecting_item_list[i_connecting_item]; + for (size_t i_ref_face_list = 0; i_ref_face_list < connectivity.template numberOfRefItemList<ItemType::face>(); + ++i_ref_face_list) { + const auto& ref_face_list = connectivity.template refItemList<ItemType::face>(i_ref_face_list); + if (ref_face_list.refId() == boundary_descriptor) { + found = true; + for (size_t i_face = 0; i_face < ref_face_list.list().size(); ++i_face) { + const FaceId face_id = ref_face_list.list()[i_face]; + auto connecting_item_list = face_to_connecting_item_matrix[face_id]; + for (size_t i_connecting_item = 0; i_connecting_item < connecting_item_list.size(); ++i_connecting_item) { + const ConnectingItemId connecting_item_id = connecting_item_list[i_connecting_item]; - symmetry_connecting_item_list[connecting_item_id][i_symmetry_boundary] = true; + symmetry_connecting_item_list[connecting_item_id][i_symmetry_boundary] = true; + } } break; } @@ -180,357 +197,572 @@ StencilBuilder::_buildSymmetryConnectingItemList(const ConnectivityType& connect throw NormalError(error_msg.str()); } } - - return symmetry_connecting_item_list; } + + return symmetry_connecting_item_list; } -template <typename ConnectivityType> -Array<const uint32_t> -StencilBuilder::_getRowMap(const ConnectivityType& connectivity) const +template <ItemType item_type, ItemType connecting_item_type, typename ConnectivityType> +StencilArray<item_type, item_type> +StencilBuilder::_build_for_same_source_and_target(const ConnectivityType& connectivity, + const size_t& number_of_layers, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const { - auto cell_to_node_matrix = connectivity.cellToNodeMatrix(); - auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); + if (number_of_layers == 0) { + throw NormalError("number of layers must be greater than 0 to build stencils"); + } + + auto item_to_connecting_item_matrix = connectivity.template getItemToItemMatrix<item_type, connecting_item_type>(); + auto connecting_item_to_item_matrix = connectivity.template getItemToItemMatrix<connecting_item_type, item_type>(); + + auto item_is_owned = connectivity.template isOwned<item_type>(); + auto item_number = connectivity.template number<item_type>(); + auto connecting_item_number = connectivity.template number<connecting_item_type>(); + + using ItemId = ItemIdT<item_type>; + using ConnectingItemId = ItemIdT<connecting_item_type>; - auto cell_is_owned = connectivity.cellIsOwned(); + ItemArray<bool, connecting_item_type> symmetry_item_list = + this->_buildSymmetryConnectingItemList<connecting_item_type>(connectivity, symmetry_boundary_descriptor_list); - Array<uint32_t> row_map{connectivity.numberOfCells() + 1}; + Array<uint32_t> row_map{connectivity.template numberOf<item_type>() + 1}; row_map[0] = 0; - std::vector<CellId> neighbors; - for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { - neighbors.resize(0); - // The stencil is not built for ghost cells - if (cell_is_owned[cell_id]) { - auto cell_nodes = cell_to_node_matrix[cell_id]; - for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { - const NodeId node_id = cell_nodes[i_node]; - auto node_cells = node_to_cell_matrix[node_id]; - for (size_t i_node_cell = 0; i_node_cell < node_cells.size(); ++i_node_cell) { - const CellId node_cell_id = node_cells[i_node_cell]; - if (node_cell_id != cell_id) { - neighbors.push_back(node_cells[i_node_cell]); + std::vector<Array<uint32_t>> symmetry_row_map_list(symmetry_boundary_descriptor_list.size()); + for (auto&& symmetry_row_map : symmetry_row_map_list) { + symmetry_row_map = Array<uint32_t>{connectivity.template numberOf<item_type>() + 1}; + symmetry_row_map[0] = 0; + } + + std::vector<ItemId> column_indices_vector; + std::vector<std::vector<uint32_t>> symmetry_column_indices_vector(symmetry_boundary_descriptor_list.size()); + + std::vector<Layer<item_type>> item_layer_list; + std::vector<Layer<connecting_item_type>> connecting_layer_list; + + std::vector<Layer<item_type>> symmetry_item_layer_list; + std::vector<Layer<connecting_item_type>> symmetry_connecting_layer_list; + + for (size_t i = 0; i < number_of_layers; ++i) { + item_layer_list.emplace_back(Layer<item_type>{connectivity}); + connecting_layer_list.emplace_back(Layer<connecting_item_type>{connectivity}); + + if (symmetry_boundary_descriptor_list.size() > 0) { + symmetry_item_layer_list.emplace_back(Layer<item_type>{connectivity}); + symmetry_connecting_layer_list.emplace_back(Layer<connecting_item_type>{connectivity}); + } + } + + for (ItemId item_id = 0; item_id < connectivity.template numberOf<item_type>(); ++item_id) { + if (item_is_owned[item_id]) { + for (auto&& item_layer : item_layer_list) { + item_layer.clear(); + } + for (auto&& connecting_layer : connecting_layer_list) { + connecting_layer.clear(); + } + + // First layer is a special case + { + auto& item_layer = item_layer_list[0]; + auto& connecting_layer = connecting_layer_list[0]; + + for (size_t i_connecting_item = 0; i_connecting_item < item_to_connecting_item_matrix[item_id].size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = item_to_connecting_item_matrix[item_id][i_connecting_item]; + + connecting_layer.add(connecting_item_id); + } + + for (auto connecting_item_id : connecting_layer.itemIdList()) { + for (size_t i_item = 0; i_item < connecting_item_to_item_matrix[connecting_item_id].size(); ++i_item) { + const ItemId layer_item_id = connecting_item_to_item_matrix[connecting_item_id][i_item]; + if (layer_item_id != item_id) { + item_layer.add(layer_item_id); + } } } } - std::sort(neighbors.begin(), neighbors.end()); - neighbors.erase(std::unique(neighbors.begin(), neighbors.end()), neighbors.end()); - } - // The cell itself is not counted - row_map[cell_id + 1] = row_map[cell_id] + neighbors.size(); - } - return row_map; -} + for (size_t i_layer = 1; i_layer < number_of_layers; ++i_layer) { + auto& connecting_layer = connecting_layer_list[i_layer]; -template <typename ConnectivityType> -Array<const uint32_t> -StencilBuilder::_getColumnIndices(const ConnectivityType& connectivity, const Array<const uint32_t>& row_map) const -{ - auto cell_number = connectivity.cellNumber(); + auto has_connecting_item = [&i_layer, &connecting_layer_list](ConnectingItemId connecting_item_id) -> bool { + for (ssize_t i = i_layer - 1; i >= 0; --i) { + if (connecting_layer_list[i].hasItemNumber(connecting_item_id)) { + return true; + } + } - Array<uint32_t> max_index(row_map.size() - 1); - parallel_for( - max_index.size(), PUGS_LAMBDA(size_t i) { max_index[i] = row_map[i]; }); - - auto cell_to_node_matrix = connectivity.cellToNodeMatrix(); - auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); - - auto cell_is_owned = connectivity.cellIsOwned(); - - Array<uint32_t> column_indices(row_map[row_map.size() - 1]); - column_indices.fill(std::numeric_limits<uint32_t>::max()); - - for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { - // The stencil is not built for ghost cells - if (cell_is_owned[cell_id]) { - auto cell_nodes = cell_to_node_matrix[cell_id]; - for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { - const NodeId node_id = cell_nodes[i_node]; - auto node_cells = node_to_cell_matrix[node_id]; - for (size_t i_node_cell = 0; i_node_cell < node_cells.size(); ++i_node_cell) { - const CellId node_cell_id = node_cells[i_node_cell]; - if (node_cell_id != cell_id) { - bool found = false; - for (size_t i_index = row_map[cell_id]; i_index < max_index[cell_id]; ++i_index) { - if (column_indices[i_index] == node_cell_id) { - found = true; - break; + return false; + }; + + for (auto&& previous_layer_item_id_list : item_layer_list[i_layer - 1].itemIdList()) { + const auto item_to_connecting_item_list = item_to_connecting_item_matrix[previous_layer_item_id_list]; + for (size_t i_connecting_item = 0; i_connecting_item < item_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = item_to_connecting_item_list[i_connecting_item]; + + if (not has_connecting_item(connecting_item_id)) { + connecting_layer.add(connecting_item_id); + } + } + } + + auto& item_layer = item_layer_list[i_layer]; + + auto has_layer_item = [&i_layer, &item_layer_list](ItemId layer_item_id) -> bool { + for (ssize_t i = i_layer - 1; i >= 0; --i) { + if (item_layer_list[i].hasItemNumber(layer_item_id)) { + return true; + } + } + + return false; + }; + + for (auto connecting_item_id : connecting_layer.itemIdList()) { + const auto& connecting_item_to_item_list = connecting_item_to_item_matrix[connecting_item_id]; + for (size_t i_item = 0; i_item < connecting_item_to_item_list.size(); ++i_item) { + const ItemId layer_item_id = connecting_item_to_item_list[i_item]; + if ((layer_item_id != item_id) and (not has_layer_item(layer_item_id))) { + item_layer.add(layer_item_id); + } + } + } + } + + for (size_t i_symmetry = 0; i_symmetry < symmetry_boundary_descriptor_list.size(); ++i_symmetry) { + for (auto&& symmetry_item_layer : symmetry_item_layer_list) { + symmetry_item_layer.clear(); + } + for (auto&& symmetry_connecting_layer : symmetry_connecting_layer_list) { + symmetry_connecting_layer.clear(); + } + + // First layer is a special case + for (auto&& connecting_item_id : connecting_layer_list[0].itemIdList()) { + if (symmetry_item_list[connecting_item_id][i_symmetry]) { + symmetry_connecting_layer_list[0].add(connecting_item_id); + } + } + + for (auto&& connecting_item_id : symmetry_connecting_layer_list[0].itemIdList()) { + auto item_of_connecting_item_list = connecting_item_to_item_matrix[connecting_item_id]; + for (size_t i_item_of_connecting_item = 0; i_item_of_connecting_item < item_of_connecting_item_list.size(); + ++i_item_of_connecting_item) { + const ItemId item_id_of_connecting_item = item_of_connecting_item_list[i_item_of_connecting_item]; + symmetry_item_layer_list[0].add(item_id_of_connecting_item); + } + } + + for (size_t i_layer = 1; i_layer < number_of_layers; ++i_layer) { + auto has_connecting_item = [&i_layer, + &symmetry_connecting_layer_list](ConnectingItemId connecting_item_id) -> bool { + for (ssize_t i = i_layer - 1; i >= 0; --i) { + if (symmetry_connecting_layer_list[i].hasItemNumber(connecting_item_id)) { + return true; } } - if (not found) { - const auto node_cell_number = cell_number[node_cell_id]; - size_t i_index = row_map[cell_id]; - // search for position for index - while ((i_index < max_index[cell_id])) { - if (node_cell_number > cell_number[CellId(column_indices[i_index])]) { - ++i_index; - } else { - break; - } + + return false; + }; + + for (auto&& symmetry_connecting_item_id : connecting_layer_list[i_layer].itemIdList()) { + if (symmetry_item_list[symmetry_connecting_item_id][i_symmetry]) { + if (not has_connecting_item(symmetry_connecting_item_id)) { + symmetry_connecting_layer_list[i_layer].add(symmetry_connecting_item_id); } + } + } - for (size_t i_destination = max_index[cell_id]; i_destination > i_index; --i_destination) { - const size_t i_source = i_destination - 1; + for (auto&& previous_layer_item_id_list : symmetry_item_layer_list[i_layer - 1].itemIdList()) { + const auto item_to_connecting_item_list = item_to_connecting_item_matrix[previous_layer_item_id_list]; + for (size_t i_connecting_item = 0; i_connecting_item < item_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = item_to_connecting_item_list[i_connecting_item]; - column_indices[i_destination] = column_indices[i_source]; + if (not has_connecting_item(connecting_item_id)) { + symmetry_connecting_layer_list[i_layer].add(connecting_item_id); + } + } + } + + auto has_symmetry_layer_item = [&i_layer, &symmetry_item_layer_list](ItemId layer_item_id) -> bool { + for (ssize_t i = i_layer - 1; i >= 0; --i) { + if (symmetry_item_layer_list[i].hasItemNumber(layer_item_id)) { + return true; + } + } + + return false; + }; + + for (auto&& connecting_item_id : symmetry_connecting_layer_list[i_layer].itemIdList()) { + auto item_of_connecting_item_list = connecting_item_to_item_matrix[connecting_item_id]; + for (size_t i_item_of_connecting_item = 0; i_item_of_connecting_item < item_of_connecting_item_list.size(); + ++i_item_of_connecting_item) { + const ItemId item_id_of_connecting_item = item_of_connecting_item_list[i_item_of_connecting_item]; + if (not has_symmetry_layer_item(item_id_of_connecting_item)) { + symmetry_item_layer_list[i_layer].add(item_id_of_connecting_item); } - ++max_index[cell_id]; - column_indices[i_index] = node_cell_id; } } } + + for (size_t i_layer = 0; i_layer < number_of_layers; ++i_layer) { + for (auto symmetry_layer_item_id : symmetry_item_layer_list[i_layer].itemIdList()) { + symmetry_column_indices_vector[i_symmetry].push_back(symmetry_layer_item_id); + } + } + + { + size_t stencil_size = 0; + for (size_t i_layer = 0; i_layer < number_of_layers; ++i_layer) { + auto& item_layer = symmetry_item_layer_list[i_layer]; + stencil_size += item_layer.size(); + } + + symmetry_row_map_list[i_symmetry][item_id + 1] = symmetry_row_map_list[i_symmetry][item_id] + stencil_size; + } + } + + { + size_t stencil_size = 0; + for (size_t i_layer = 0; i_layer < number_of_layers; ++i_layer) { + auto& item_layer = item_layer_list[i_layer]; + for (auto stencil_item_id : item_layer.itemIdList()) { + column_indices_vector.push_back(stencil_item_id); + } + stencil_size += item_layer.size(); + } + row_map[item_id + 1] = row_map[item_id] + stencil_size; + } + + } else { + row_map[item_id + 1] = row_map[item_id]; + for (size_t i = 0; i < symmetry_row_map_list.size(); ++i) { + symmetry_row_map_list[i][item_id + 1] = symmetry_row_map_list[i][item_id]; } } } - return column_indices; + Array<uint32_t> column_indices{column_indices_vector.size()}; + parallel_for( + column_indices_vector.size(), PUGS_LAMBDA(size_t i) { column_indices[i] = column_indices_vector[i]; }); + + ConnectivityMatrix primal_stencil{row_map, column_indices}; + + typename StencilArray<item_type, item_type>::BoundaryDescriptorStencilArrayList symmetry_boundary_stencil_list; + { + size_t i = 0; + for (auto&& p_boundary_descriptor : symmetry_boundary_descriptor_list) { + symmetry_boundary_stencil_list.emplace_back( + typename StencilArray<item_type, item_type>:: + BoundaryDescriptorStencilArray{p_boundary_descriptor, + ConnectivityMatrix{symmetry_row_map_list[i], + convert_to_array(symmetry_column_indices_vector[i])}}); + ++i; + } + } + + return {{primal_stencil}, {symmetry_boundary_stencil_list}}; } -template <ItemType item_type, ItemType connecting_item_type, typename ConnectivityType> -StencilArray<item_type, item_type> -StencilBuilder::_build_for_same_source_and_target(const ConnectivityType& connectivity, - const size_t& number_of_layers, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const +template <ItemType source_item_type, + ItemType connecting_item_type, + ItemType target_item_type, + typename ConnectivityType> +StencilArray<source_item_type, target_item_type> +StencilBuilder::_build_for_different_source_and_target( + const ConnectivityType& connectivity, + const size_t& number_of_layers, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const { + constexpr size_t Dimension = ConnectivityType::Dimension; + if (number_of_layers == 0) { throw NormalError("number of layers must be greater than 0 to build stencils"); } - if (number_of_layers > 2) { - throw NotImplementedError("number of layers too large"); - } - auto item_to_connecting_item_matrix = connectivity.template getItemToItemMatrix<item_type, connecting_item_type>(); - auto connecting_item_to_item_matrix = connectivity.template getItemToItemMatrix<connecting_item_type, item_type>(); - auto item_is_owned = connectivity.template isOwned<item_type>(); - auto item_number = connectivity.template number<item_type>(); + auto connecting_item_to_target_item_matrix = + connectivity.template getItemToItemMatrix<connecting_item_type, target_item_type>(); + auto target_item_to_connecting_item_matrix = + connectivity.template getItemToItemMatrix<target_item_type, connecting_item_type>(); + + auto source_item_to_connecting_item_matrix = + [](const auto& given_connectivity) -> ItemToItemMatrix<source_item_type, connecting_item_type> { + if constexpr (ItemTypeId<Dimension>::itemTId(source_item_type) == + ItemTypeId<Dimension>::itemTId(connecting_item_type)) { + return {ConnectivityMatrix{}}; + } else { + return given_connectivity.template getItemToItemMatrix<source_item_type, connecting_item_type>(); + } + }(connectivity); + + auto source_item_is_owned = connectivity.template isOwned<source_item_type>(); + auto target_item_number = connectivity.template number<target_item_type>(); auto connecting_item_number = connectivity.template number<connecting_item_type>(); - using ItemId = ItemIdT<item_type>; + using SourceItemId = ItemIdT<source_item_type>; + using TargetItemId = ItemIdT<target_item_type>; using ConnectingItemId = ItemIdT<connecting_item_type>; - if (symmetry_boundary_descriptor_list.size() == 0) { - if (number_of_layers == 1) { - Array<uint32_t> row_map{connectivity.template numberOf<item_type>() + 1}; - row_map[0] = 0; + ItemArray<bool, connecting_item_type> symmetry_item_list = + this->_buildSymmetryConnectingItemList<connecting_item_type>(connectivity, symmetry_boundary_descriptor_list); + + Array<uint32_t> row_map{connectivity.template numberOf<source_item_type>() + 1}; + row_map[0] = 0; + std::vector<Array<uint32_t>> symmetry_row_map_list(symmetry_boundary_descriptor_list.size()); + for (auto&& symmetry_row_map : symmetry_row_map_list) { + symmetry_row_map = Array<uint32_t>{connectivity.template numberOf<source_item_type>() + 1}; + symmetry_row_map[0] = 0; + } + + std::vector<TargetItemId> column_indices_vector; + std::vector<std::vector<uint32_t>> symmetry_column_indices_vector(symmetry_boundary_descriptor_list.size()); - std::vector<ItemId> column_indices_vector; + std::vector<Layer<target_item_type>> target_item_layer_list; + std::vector<Layer<connecting_item_type>> connecting_layer_list; - for (ItemId item_id = 0; item_id < connectivity.template numberOf<item_type>(); ++item_id) { - // First layer is a special case + std::vector<Layer<target_item_type>> symmetry_target_item_layer_list; + std::vector<Layer<connecting_item_type>> symmetry_connecting_layer_list; - Layer<item_type> item_layer; - Layer<connecting_item_type> connecting_layer; + for (size_t i = 0; i < number_of_layers; ++i) { + target_item_layer_list.emplace_back(Layer<target_item_type>{connectivity}); + connecting_layer_list.emplace_back(Layer<connecting_item_type>{connectivity}); - if (item_is_owned[item_id]) { - for (size_t i_connecting_item_1 = 0; i_connecting_item_1 < item_to_connecting_item_matrix[item_id].size(); - ++i_connecting_item_1) { - const ConnectingItemId layer_1_connecting_item_id = - item_to_connecting_item_matrix[item_id][i_connecting_item_1]; - connecting_layer.add(layer_1_connecting_item_id, connecting_item_number[layer_1_connecting_item_id]); - } + if (symmetry_boundary_descriptor_list.size() > 0) { + symmetry_target_item_layer_list.emplace_back(Layer<target_item_type>{connectivity}); + symmetry_connecting_layer_list.emplace_back(Layer<connecting_item_type>{connectivity}); + } + } - for (auto connecting_item_id : connecting_layer.itemIdList()) { - for (size_t i_item_1 = 0; i_item_1 < connecting_item_to_item_matrix[connecting_item_id].size(); - ++i_item_1) { - const ItemId layer_1_item_id = connecting_item_to_item_matrix[connecting_item_id][i_item_1]; - if (layer_1_item_id != item_id) { - item_layer.add(layer_1_item_id, item_number[layer_1_item_id]); - } - } + for (SourceItemId source_item_id = 0; source_item_id < connectivity.template numberOf<source_item_type>(); + ++source_item_id) { + if (source_item_is_owned[source_item_id]) { + for (auto&& target_item_layer : target_item_layer_list) { + target_item_layer.clear(); + } + for (auto&& connecting_layer : connecting_layer_list) { + connecting_layer.clear(); + } + + // First layer is a special case + { + auto& target_item_layer = target_item_layer_list[0]; + auto& connecting_layer = connecting_layer_list[0]; + + if constexpr (ItemTypeId<Dimension>::itemTId(source_item_type) == + ItemTypeId<Dimension>::itemTId(connecting_item_type)) { + connecting_layer.add(ConnectingItemId{static_cast<typename ConnectingItemId::base_type>(source_item_id)}); + } else { + for (size_t i_connecting_item = 0; + i_connecting_item < source_item_to_connecting_item_matrix[source_item_id].size(); ++i_connecting_item) { + const ConnectingItemId connecting_item_id = + source_item_to_connecting_item_matrix[source_item_id][i_connecting_item]; + + connecting_layer.add(connecting_item_id); } } - for (auto layer_item_id : item_layer.itemIdList()) { - column_indices_vector.push_back(layer_item_id); + for (auto connecting_item_id : connecting_layer.itemIdList()) { + for (size_t i_item = 0; i_item < connecting_item_to_target_item_matrix[connecting_item_id].size(); ++i_item) { + const TargetItemId layer_item_id = connecting_item_to_target_item_matrix[connecting_item_id][i_item]; + target_item_layer.add(layer_item_id); + } } - row_map[item_id + 1] = row_map[item_id] + item_layer.itemIdList().size(); } - if (row_map[row_map.size() - 1] != column_indices_vector.size()) { - throw UnexpectedError("incorrect stencil size"); - } - Array<uint32_t> column_indices(row_map[row_map.size() - 1]); - column_indices.fill(std::numeric_limits<uint32_t>::max()); - - for (size_t i = 0; i < column_indices.size(); ++i) { - column_indices[i] = column_indices_vector[i]; - } + for (size_t i_layer = 1; i_layer < number_of_layers; ++i_layer) { + auto& connecting_layer = connecting_layer_list[i_layer]; - return {ConnectivityMatrix{row_map, column_indices}, {}}; - } else { - Array<uint32_t> row_map{connectivity.template numberOf<item_type>() + 1}; - row_map[0] = 0; - - std::vector<ItemId> column_indices_vector; - - for (ItemId item_id = 0; item_id < connectivity.template numberOf<item_type>(); ++item_id) { - if (item_is_owned[item_id]) { - std::set<ItemId, std::function<bool(ItemId, ItemId)>> item_set( - [=](ItemId item_0, ItemId item_1) { return item_number[item_0] < item_number[item_1]; }); - - for (size_t i_connecting_item_1 = 0; i_connecting_item_1 < item_to_connecting_item_matrix[item_id].size(); - ++i_connecting_item_1) { - const ConnectingItemId layer_1_connecting_item_id = - item_to_connecting_item_matrix[item_id][i_connecting_item_1]; - - for (size_t i_item_1 = 0; i_item_1 < connecting_item_to_item_matrix[layer_1_connecting_item_id].size(); - ++i_item_1) { - ItemId item_1_id = connecting_item_to_item_matrix[layer_1_connecting_item_id][i_item_1]; - - for (size_t i_connecting_item_2 = 0; - i_connecting_item_2 < item_to_connecting_item_matrix[item_1_id].size(); ++i_connecting_item_2) { - const ConnectingItemId layer_2_connecting_item_id = - item_to_connecting_item_matrix[item_1_id][i_connecting_item_2]; - - for (size_t i_item_2 = 0; i_item_2 < connecting_item_to_item_matrix[layer_2_connecting_item_id].size(); - ++i_item_2) { - ItemId item_2_id = connecting_item_to_item_matrix[layer_2_connecting_item_id][i_item_2]; - - if (item_2_id != item_id) { - item_set.insert(item_2_id); - } - } - } + auto has_connecting_item = [&i_layer, &connecting_layer_list](ConnectingItemId connecting_item_id) -> bool { + for (ssize_t i = i_layer - 1; i >= 0; --i) { + if (connecting_layer_list[i].hasItemNumber(connecting_item_id)) { + return true; } } - for (auto stencil_item_id : item_set) { - column_indices_vector.push_back(stencil_item_id); + return false; + }; + + for (auto&& previous_layer_item_id_list : target_item_layer_list[i_layer - 1].itemIdList()) { + const auto target_item_to_connecting_item_list = + target_item_to_connecting_item_matrix[previous_layer_item_id_list]; + for (size_t i_connecting_item = 0; i_connecting_item < target_item_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = target_item_to_connecting_item_list[i_connecting_item]; + + if (not has_connecting_item(connecting_item_id)) { + connecting_layer.add(connecting_item_id); + } } - row_map[item_id + 1] = row_map[item_id] + item_set.size(); } - } - if (row_map[row_map.size() - 1] != column_indices_vector.size()) { - throw UnexpectedError("incorrect stencil size"); - } + auto& target_item_layer = target_item_layer_list[i_layer]; - Array<uint32_t> column_indices(row_map[row_map.size() - 1]); - column_indices.fill(std::numeric_limits<uint32_t>::max()); + auto has_layer_item = [&i_layer, &target_item_layer_list](TargetItemId layer_item_id) -> bool { + for (ssize_t i = i_layer - 1; i >= 0; --i) { + if (target_item_layer_list[i].hasItemNumber(layer_item_id)) { + return true; + } + } - for (size_t i = 0; i < column_indices.size(); ++i) { - column_indices[i] = column_indices_vector[i]; - } - ConnectivityMatrix primal_stencil{row_map, column_indices}; + return false; + }; - return {primal_stencil, {}}; - } - } else { - ItemArray<bool, connecting_item_type> symmetry_item_list = - this->_buildSymmetryConnectingItemList<connecting_item_type>(connectivity, symmetry_boundary_descriptor_list); - - Array<uint32_t> row_map{connectivity.template numberOf<item_type>() + 1}; - row_map[0] = 0; - std::vector<Array<uint32_t>> symmetry_row_map_list(symmetry_boundary_descriptor_list.size()); - for (auto&& symmetry_row_map : symmetry_row_map_list) { - symmetry_row_map = Array<uint32_t>{connectivity.template numberOf<item_type>() + 1}; - symmetry_row_map[0] = 0; - } + for (auto connecting_item_id : connecting_layer.itemIdList()) { + const auto& connecting_item_to_target_item_list = connecting_item_to_target_item_matrix[connecting_item_id]; + for (size_t i_item = 0; i_item < connecting_item_to_target_item_list.size(); ++i_item) { + const TargetItemId layer_item_id = connecting_item_to_target_item_list[i_item]; + if (not has_layer_item(layer_item_id)) { + target_item_layer.add(layer_item_id); + } + } + } + } - std::vector<uint32_t> column_indices_vector; - std::vector<std::vector<uint32_t>> symmetry_column_indices_vector(symmetry_boundary_descriptor_list.size()); + for (size_t i_symmetry = 0; i_symmetry < symmetry_boundary_descriptor_list.size(); ++i_symmetry) { + for (auto&& symmetry_target_item_layer : symmetry_target_item_layer_list) { + symmetry_target_item_layer.clear(); + } + for (auto&& symmetry_connecting_layer : symmetry_connecting_layer_list) { + symmetry_connecting_layer.clear(); + } - for (ItemId item_id = 0; item_id < connectivity.template numberOf<item_type>(); ++item_id) { - std::set<ItemId> item_set; - std::vector<std::set<ItemId>> by_boundary_symmetry_item(symmetry_boundary_descriptor_list.size()); + // First layer is a special case + for (auto&& connecting_item_id : connecting_layer_list[0].itemIdList()) { + if (symmetry_item_list[connecting_item_id][i_symmetry]) { + symmetry_connecting_layer_list[0].add(connecting_item_id); + } + } - if (item_is_owned[item_id]) { - auto item_to_connecting_item_list = item_to_connecting_item_matrix[item_id]; - for (size_t i_connecting_item_of_item = 0; i_connecting_item_of_item < item_to_connecting_item_list.size(); - ++i_connecting_item_of_item) { - const ConnectingItemId connecting_item_id_of_item = item_to_connecting_item_list[i_connecting_item_of_item]; - auto connecting_item_to_item_list = connecting_item_to_item_matrix[connecting_item_id_of_item]; - for (size_t i_item_of_connecting_item = 0; i_item_of_connecting_item < connecting_item_to_item_list.size(); + for (auto&& connecting_item_id : symmetry_connecting_layer_list[0].itemIdList()) { + auto item_of_connecting_item_list = connecting_item_to_target_item_matrix[connecting_item_id]; + for (size_t i_item_of_connecting_item = 0; i_item_of_connecting_item < item_of_connecting_item_list.size(); ++i_item_of_connecting_item) { - const ItemId item_id_of_connecting_item = connecting_item_to_item_list[i_item_of_connecting_item]; - if (item_id != item_id_of_connecting_item) { - item_set.insert(item_id_of_connecting_item); - } + const TargetItemId item_id_of_connecting_item = item_of_connecting_item_list[i_item_of_connecting_item]; + symmetry_target_item_layer_list[0].add(item_id_of_connecting_item); } } - { - std::vector<ItemId> item_vector; - for (auto&& set_item_id : item_set) { - item_vector.push_back(set_item_id); + for (size_t i_layer = 1; i_layer < number_of_layers; ++i_layer) { + auto has_connecting_item = [&i_layer, + &symmetry_connecting_layer_list](ConnectingItemId connecting_item_id) -> bool { + for (ssize_t i = i_layer - 1; i >= 0; --i) { + if (symmetry_connecting_layer_list[i].hasItemNumber(connecting_item_id)) { + return true; + } + } + + return false; + }; + + for (auto&& symmetry_connecting_item_id : connecting_layer_list[i_layer].itemIdList()) { + if (symmetry_item_list[symmetry_connecting_item_id][i_symmetry]) { + if (not has_connecting_item(symmetry_connecting_item_id)) { + symmetry_connecting_layer_list[i_layer].add(symmetry_connecting_item_id); + } + } } - std::sort(item_vector.begin(), item_vector.end(), - [&item_number](const ItemId& item0_id, const ItemId& item1_id) { - return item_number[item0_id] < item_number[item1_id]; - }); - for (auto&& vector_item_id : item_vector) { - column_indices_vector.push_back(vector_item_id); + for (auto&& previous_layer_target_item_id_list : symmetry_target_item_layer_list[i_layer - 1].itemIdList()) { + const auto item_to_connecting_item_list = + target_item_to_connecting_item_matrix[previous_layer_target_item_id_list]; + for (size_t i_connecting_item = 0; i_connecting_item < item_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = item_to_connecting_item_list[i_connecting_item]; + + if (not has_connecting_item(connecting_item_id)) { + symmetry_connecting_layer_list[i_layer].add(connecting_item_id); + } + } } - } - for (size_t i = 0; i < symmetry_boundary_descriptor_list.size(); ++i) { - std::set<ItemId> symmetry_item_set; - for (size_t i_connecting_item_of_item = 0; i_connecting_item_of_item < item_to_connecting_item_list.size(); - ++i_connecting_item_of_item) { - const ConnectingItemId connecting_item_id_of_item = item_to_connecting_item_list[i_connecting_item_of_item]; - if (symmetry_item_list[connecting_item_id_of_item][i]) { - auto item_of_connecting_item_list = connecting_item_to_item_matrix[connecting_item_id_of_item]; - for (size_t i_item_of_connecting_item = 0; - i_item_of_connecting_item < item_of_connecting_item_list.size(); ++i_item_of_connecting_item) { - const ItemId item_id_of_connecting_item = item_of_connecting_item_list[i_item_of_connecting_item]; - symmetry_item_set.insert(item_id_of_connecting_item); + auto has_symmetry_layer_item = [&i_layer, + &symmetry_target_item_layer_list](TargetItemId layer_target_item_id) -> bool { + for (ssize_t i = i_layer - 1; i >= 0; --i) { + if (symmetry_target_item_layer_list[i].hasItemNumber(layer_target_item_id)) { + return true; + } + } + + return false; + }; + + for (auto&& connecting_item_id : symmetry_connecting_layer_list[i_layer].itemIdList()) { + auto item_of_connecting_item_list = connecting_item_to_target_item_matrix[connecting_item_id]; + for (size_t i_target_item_of_connecting_item = 0; + i_target_item_of_connecting_item < item_of_connecting_item_list.size(); + ++i_target_item_of_connecting_item) { + const TargetItemId target_item_id_of_connecting_item = + item_of_connecting_item_list[i_target_item_of_connecting_item]; + if (not has_symmetry_layer_item(target_item_id_of_connecting_item)) { + symmetry_target_item_layer_list[i_layer].add(target_item_id_of_connecting_item); } } } - by_boundary_symmetry_item[i] = symmetry_item_set; + } - std::vector<ItemId> item_vector; - for (auto&& set_item_id : symmetry_item_set) { - item_vector.push_back(set_item_id); + for (size_t i_layer = 0; i_layer < number_of_layers; ++i_layer) { + for (auto symmetry_layer_target_item_id : symmetry_target_item_layer_list[i_layer].itemIdList()) { + symmetry_column_indices_vector[i_symmetry].push_back(symmetry_layer_target_item_id); } - std::sort(item_vector.begin(), item_vector.end(), - [&item_number](const ItemId& item0_id, const ItemId& item1_id) { - return item_number[item0_id] < item_number[item1_id]; - }); + } - for (auto&& vector_item_id : item_vector) { - symmetry_column_indices_vector[i].push_back(vector_item_id); + { + size_t stencil_size = 0; + for (size_t i_layer = 0; i_layer < number_of_layers; ++i_layer) { + auto& target_item_layer = symmetry_target_item_layer_list[i_layer]; + stencil_size += target_item_layer.size(); } + + symmetry_row_map_list[i_symmetry][source_item_id + 1] = + symmetry_row_map_list[i_symmetry][source_item_id] + stencil_size; } } - row_map[item_id + 1] = row_map[item_id] + item_set.size(); - for (size_t i = 0; i < symmetry_row_map_list.size(); ++i) { - symmetry_row_map_list[i][item_id + 1] = symmetry_row_map_list[i][item_id] + by_boundary_symmetry_item[i].size(); + { + size_t stencil_size = 0; + for (size_t i_layer = 0; i_layer < number_of_layers; ++i_layer) { + auto& item_layer = target_item_layer_list[i_layer]; + for (auto stencil_item_id : item_layer.itemIdList()) { + column_indices_vector.push_back(stencil_item_id); + } + stencil_size += item_layer.size(); + } + row_map[source_item_id + 1] = row_map[source_item_id] + stencil_size; } - } - ConnectivityMatrix primal_stencil{row_map, convert_to_array(column_indices_vector)}; - - typename StencilArray<item_type, item_type>::BoundaryDescriptorStencilArrayList symmetry_boundary_stencil_list; - { - size_t i = 0; - for (auto&& p_boundary_descriptor : symmetry_boundary_descriptor_list) { - symmetry_boundary_stencil_list.emplace_back( - typename StencilArray<item_type, item_type>:: - BoundaryDescriptorStencilArray{p_boundary_descriptor, - ConnectivityMatrix{symmetry_row_map_list[i], - convert_to_array(symmetry_column_indices_vector[i])}}); - ++i; + + } else { + row_map[source_item_id + 1] = row_map[source_item_id]; + for (size_t i = 0; i < symmetry_row_map_list.size(); ++i) { + symmetry_row_map_list[i][source_item_id + 1] = symmetry_row_map_list[i][source_item_id]; } } + } + + Array<uint32_t> column_indices{column_indices_vector.size()}; + parallel_for( + column_indices_vector.size(), PUGS_LAMBDA(size_t i) { column_indices[i] = column_indices_vector[i]; }); + + ConnectivityMatrix primal_stencil{row_map, column_indices}; - return {{primal_stencil}, {symmetry_boundary_stencil_list}}; + typename StencilArray<source_item_type, target_item_type>::BoundaryDescriptorStencilArrayList + symmetry_boundary_stencil_list; + { + size_t i = 0; + for (auto&& p_boundary_descriptor : symmetry_boundary_descriptor_list) { + symmetry_boundary_stencil_list.emplace_back( + typename StencilArray<source_item_type, target_item_type>:: + BoundaryDescriptorStencilArray{p_boundary_descriptor, + ConnectivityMatrix{symmetry_row_map_list[i], + convert_to_array(symmetry_column_indices_vector[i])}}); + ++i; + } } -} -template <ItemType source_item_type, - ItemType connecting_item_type, - ItemType target_item_type, - typename ConnectivityType> -StencilArray<source_item_type, target_item_type> -StencilBuilder::_build_for_different_source_and_target( - const ConnectivityType& connectivity, - const size_t& number_of_layers, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const -{ - static_assert(source_item_type != target_item_type); - throw NotImplementedError("different source target"); + return {{primal_stencil}, {symmetry_boundary_stencil_list}}; } template <ItemType source_item_type, @@ -631,7 +863,35 @@ StencilBuilder::buildC2C(const IConnectivity& connectivity, } NodeToCellStencilArray -StencilBuilder::buildN2C(const IConnectivity&, const StencilDescriptor&, const BoundaryDescriptorList&) const +StencilBuilder::buildN2C(const IConnectivity& connectivity, + const StencilDescriptor& stencil_descriptor, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const { - throw NotImplementedError("node to cell stencil"); + if ((parallel::size() > 1) and + (stencil_descriptor.numberOfLayers() > GlobalVariableManager::instance().getNumberOfGhostLayers())) { + std::ostringstream error_msg; + error_msg << "Stencil builder requires" << rang::fgB::yellow << stencil_descriptor.numberOfLayers() + << rang::fg::reset << " layers while parallel number of ghost layer is " + << GlobalVariableManager::instance().getNumberOfGhostLayers() << ".\n"; + error_msg << "Increase the number of ghost layers (using the '--number-of-ghost-layers' option)."; + throw NormalError(error_msg.str()); + } + + switch (connectivity.dimension()) { + case 1: { + return this->_build<ItemType::node, ItemType::cell>(dynamic_cast<const Connectivity<1>&>(connectivity), + stencil_descriptor, symmetry_boundary_descriptor_list); + } + case 2: { + return this->_build<ItemType::node, ItemType::cell>(dynamic_cast<const Connectivity<2>&>(connectivity), + stencil_descriptor, symmetry_boundary_descriptor_list); + } + case 3: { + return this->_build<ItemType::node, ItemType::cell>(dynamic_cast<const Connectivity<3>&>(connectivity), + stencil_descriptor, symmetry_boundary_descriptor_list); + } + default: { + throw UnexpectedError("invalid connectivity dimension"); + } + } } diff --git a/src/mesh/StencilBuilder.hpp b/src/mesh/StencilBuilder.hpp index 4f7ead00b618db999dd5386258ac058b664c65a7..2ed1d3a6c45fdac651ce62ba05690532c506cf26 100644 --- a/src/mesh/StencilBuilder.hpp +++ b/src/mesh/StencilBuilder.hpp @@ -19,13 +19,6 @@ class StencilBuilder template <ItemType item_type> class Layer; - template <typename ConnectivityType> - Array<const uint32_t> _getRowMap(const ConnectivityType& connectivity) const; - - template <typename ConnectivityType> - Array<const uint32_t> _getColumnIndices(const ConnectivityType& connectivity, - const Array<const uint32_t>& row_map) const; - template <ItemType connecting_item, typename ConnectivityType> auto _buildSymmetryConnectingItemList(const ConnectivityType& connectivity, const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; @@ -74,11 +67,12 @@ class StencilBuilder const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; friend class StencilManager; + template <ItemType source_item_type, ItemType target_item_type> StencilArray<source_item_type, target_item_type> - build(const IConnectivity& connectivity, - const StencilDescriptor& stencil_descriptor, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const + _build(const IConnectivity& connectivity, + const StencilDescriptor& stencil_descriptor, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const { if constexpr ((source_item_type == ItemType::cell) and (target_item_type == ItemType::cell)) { return buildC2C(connectivity, stencil_descriptor, symmetry_boundary_descriptor_list); diff --git a/src/mesh/StencilManager.cpp b/src/mesh/StencilManager.cpp index 842f8858f9d1a571e28d3f0397f70e97682da0af..04a8e32a1d50d872d95896e8e7a58e76e32e2189 100644 --- a/src/mesh/StencilManager.cpp +++ b/src/mesh/StencilManager.cpp @@ -49,8 +49,8 @@ StencilManager::_getStencilArray( Key{connectivity.id(), stencil_descriptor, symmetry_boundary_descriptor_list})) { stored_source_to_target_stencil_map[Key{connectivity.id(), stencil_descriptor, symmetry_boundary_descriptor_list}] = std::make_shared<StencilArray<source_item_type, target_item_type>>( - StencilBuilder{}.template build<source_item_type, target_item_type>(connectivity, stencil_descriptor, - symmetry_boundary_descriptor_list)); + StencilBuilder{}.template _build<source_item_type, target_item_type>(connectivity, stencil_descriptor, + symmetry_boundary_descriptor_list)); } return *stored_source_to_target_stencil_map.at( diff --git a/src/output/OutputNamedItemValueSet.hpp b/src/output/OutputNamedItemValueSet.hpp index aa2c5e2ed67140d47c7d4ddf1f57caecc2f28360..8c43330420c91417e0c339228b4bfb41f9dc908b 100644 --- a/src/output/OutputNamedItemValueSet.hpp +++ b/src/output/OutputNamedItemValueSet.hpp @@ -38,7 +38,7 @@ class NamedItemData } NamedItemData& operator=(const NamedItemData&) = default; - NamedItemData& operator=(NamedItemData&&) = default; + NamedItemData& operator=(NamedItemData&&) = default; NamedItemData(const std::string& name, const ItemDataT<DataType, item_type, ConnectivityPtr>& item_data) : m_name(name), m_item_data(item_data) @@ -113,24 +113,48 @@ class OutputNamedItemDataSet NodeArray<const long int>, NodeArray<const unsigned long int>, NodeArray<const double>, + NodeArray<const TinyVector<1, double>>, + NodeArray<const TinyVector<2, double>>, + NodeArray<const TinyVector<3, double>>, + NodeArray<const TinyMatrix<1, 1, double>>, + NodeArray<const TinyMatrix<2, 2, double>>, + NodeArray<const TinyMatrix<3, 3, double>>, EdgeArray<const bool>, EdgeArray<const int>, EdgeArray<const long int>, EdgeArray<const unsigned long int>, EdgeArray<const double>, + EdgeArray<const TinyVector<1, double>>, + EdgeArray<const TinyVector<2, double>>, + EdgeArray<const TinyVector<3, double>>, + EdgeArray<const TinyMatrix<1, 1, double>>, + EdgeArray<const TinyMatrix<2, 2, double>>, + EdgeArray<const TinyMatrix<3, 3, double>>, FaceArray<const bool>, FaceArray<const int>, FaceArray<const long int>, FaceArray<const unsigned long int>, FaceArray<const double>, + FaceArray<const TinyVector<1, double>>, + FaceArray<const TinyVector<2, double>>, + FaceArray<const TinyVector<3, double>>, + FaceArray<const TinyMatrix<1, 1, double>>, + FaceArray<const TinyMatrix<2, 2, double>>, + FaceArray<const TinyMatrix<3, 3, double>>, CellArray<const bool>, CellArray<const int>, CellArray<const long int>, CellArray<const unsigned long int>, - CellArray<const double>>; + CellArray<const double>, + CellArray<const TinyVector<1, double>>, + CellArray<const TinyVector<2, double>>, + CellArray<const TinyVector<3, double>>, + CellArray<const TinyMatrix<1, 1, double>>, + CellArray<const TinyMatrix<2, 2, double>>, + CellArray<const TinyMatrix<3, 3, double>>>; private: // We do not use a map, we want variables to be written in the diff --git a/src/output/VTKWriter.cpp b/src/output/VTKWriter.cpp index 64d35434462e5980a11b69ea2257ce779b3ec581..2aedce3b1fe264bf3a11a94592077f4631e666aa 100644 --- a/src/output/VTKWriter.cpp +++ b/src/output/VTKWriter.cpp @@ -396,10 +396,17 @@ VTKWriter::_write(const MeshType& mesh, << "\">\n"; fout << "<CellData>\n"; for (const auto& [name, item_value_variant] : output_named_item_data_set) { - std::visit([&, var_name = name]( - auto&& - item_value) { return this->_write_cell_data(fout, var_name, item_value, serialize_data_list); }, - item_value_variant); + std::visit( + [&, var_name = name](auto&& item_value) { + using IVType = std::decay_t<decltype(item_value)>; + using DataType = typename IVType::data_type; + if constexpr (is_item_array_v<IVType> and not std::is_arithmetic_v<DataType>) { + throw NotImplementedError("DiscreteFunctionP0Vector of non arithmetic type"); + } else { + return this->_write_cell_data(fout, var_name, item_value, serialize_data_list); + } + }, + item_value_variant); } if (parallel::size() > 1) { CellValue<uint8_t> vtk_ghost_type{mesh.connectivity()}; @@ -413,10 +420,17 @@ VTKWriter::_write(const MeshType& mesh, fout << "</CellData>\n"; fout << "<PointData>\n"; for (const auto& [name, item_value_variant] : output_named_item_data_set) { - std::visit([&, var_name = name]( - auto&& - item_value) { return this->_write_node_data(fout, var_name, item_value, serialize_data_list); }, - item_value_variant); + std::visit( + [&, var_name = name](auto&& item_value) { + using IVType = std::decay_t<decltype(item_value)>; + using DataType = typename IVType::data_type; + if constexpr (is_item_array_v<IVType> and not std::is_arithmetic_v<DataType>) { + throw NotImplementedError("DiscreteFunctionP0Vector of non arithmetic type"); + } else { + return this->_write_node_data(fout, var_name, item_value, serialize_data_list); + } + }, + item_value_variant); } fout << "</PointData>\n"; fout << "<Points>\n"; @@ -644,15 +658,33 @@ VTKWriter::_write(const MeshType& mesh, fout << "<PPointData>\n"; for (const auto& [name, item_value_variant] : output_named_item_data_set) { - std::visit([&, var_name = name](auto&& item_value) { return this->_write_node_pvtu(fout, var_name, item_value); }, - item_value_variant); + std::visit( + [&, var_name = name](auto&& item_value) { + using IVType = std::decay_t<decltype(item_value)>; + using DataType = typename IVType::data_type; + if constexpr (is_item_array_v<IVType> and not std::is_arithmetic_v<DataType>) { + throw NotImplementedError("DiscreteFunctionP0Vector of non arithmetic type"); + } else { + return this->_write_node_pvtu(fout, var_name, item_value); + } + }, + item_value_variant); } fout << "</PPointData>\n"; fout << "<PCellData>\n"; for (const auto& [name, item_value_variant] : output_named_item_data_set) { - std::visit([&, var_name = name](auto&& item_value) { return this->_write_cell_pvtu(fout, var_name, item_value); }, - item_value_variant); + std::visit( + [&, var_name = name](auto&& item_value) { + using IVType = std::decay_t<decltype(item_value)>; + using DataType = typename IVType::data_type; + if constexpr (is_item_array_v<IVType> and not std::is_arithmetic_v<DataType>) { + throw NotImplementedError("DiscreteFunctionP0Vector of non arithmetic type"); + } else { + return this->_write_cell_pvtu(fout, var_name, item_value); + } + }, + item_value_variant); } if (parallel::size() > 1) { fout << "<PDataArray type=\"UInt8\" Name=\"vtkGhostType\" NumberOfComponents=\"1\"/>\n"; diff --git a/src/scheme/DirichletVectorBoundaryConditionDescriptor.hpp b/src/scheme/DirichletVectorBoundaryConditionDescriptor.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c1f1dcc3fadeced1dbed1f560a6e72c86eebf0ed --- /dev/null +++ b/src/scheme/DirichletVectorBoundaryConditionDescriptor.hpp @@ -0,0 +1,58 @@ +#ifndef DIRICHLET_VECTOR_BOUNDARY_CONDITION_DESCRIPTOR_HPP +#define DIRICHLET_VECTOR_BOUNDARY_CONDITION_DESCRIPTOR_HPP + +#include <language/utils/FunctionSymbolId.hpp> +#include <mesh/IBoundaryDescriptor.hpp> +#include <scheme/BoundaryConditionDescriptorBase.hpp> + +#include <memory> +#include <vector> + +class DirichletVectorBoundaryConditionDescriptor : public BoundaryConditionDescriptorBase +{ + private: + std::ostream& + _write(std::ostream& os) const final + { + os << "dirichlet_vector(" << m_name << ',' << *m_boundary_descriptor << ")"; + return os; + } + + const std::string m_name; + + const std::vector<FunctionSymbolId> m_rhs_symbol_id_list; + + public: + const std::string& + name() const + { + return m_name; + } + + const std::vector<FunctionSymbolId>& + rhsSymbolIdList() const + { + return m_rhs_symbol_id_list; + } + + Type + type() const final + { + return Type::dirichlet_vector; + } + + DirichletVectorBoundaryConditionDescriptor(const std::string_view name, + std::shared_ptr<const IBoundaryDescriptor> boundary_descriptor, + const std::vector<FunctionSymbolId>& rhs_symbol_id_list) + : BoundaryConditionDescriptorBase{boundary_descriptor}, m_name{name}, m_rhs_symbol_id_list{rhs_symbol_id_list} + { + ; + } + + DirichletVectorBoundaryConditionDescriptor(const DirichletVectorBoundaryConditionDescriptor&) = delete; + DirichletVectorBoundaryConditionDescriptor(DirichletVectorBoundaryConditionDescriptor&&) = delete; + + ~DirichletVectorBoundaryConditionDescriptor() = default; +}; + +#endif // DIRICHLET_VECTOR_BOUNDARY_CONDITION_DESCRIPTOR_HPP diff --git a/src/scheme/DiscreteFunctionDPkVariant.hpp b/src/scheme/DiscreteFunctionDPkVariant.hpp index 0d683c1783c9aad299a81dcc4d7ef3817470d54a..2942f141f38452faed9e95a5f95ca675bc7d8c29 100644 --- a/src/scheme/DiscreteFunctionDPkVariant.hpp +++ b/src/scheme/DiscreteFunctionDPkVariant.hpp @@ -35,8 +35,28 @@ class DiscreteFunctionDPkVariant DiscreteFunctionDPk<3, const TinyMatrix<3>>, DiscreteFunctionDPkVector<1, const double>, + DiscreteFunctionDPkVector<1, const TinyVector<1>>, + DiscreteFunctionDPkVector<1, const TinyVector<2>>, + DiscreteFunctionDPkVector<1, const TinyVector<3>>, + DiscreteFunctionDPkVector<1, const TinyMatrix<1>>, + DiscreteFunctionDPkVector<1, const TinyMatrix<2>>, + DiscreteFunctionDPkVector<1, const TinyMatrix<3>>, + DiscreteFunctionDPkVector<2, const double>, - DiscreteFunctionDPkVector<3, const double>>; + DiscreteFunctionDPkVector<2, const TinyVector<1>>, + DiscreteFunctionDPkVector<2, const TinyVector<2>>, + DiscreteFunctionDPkVector<2, const TinyVector<3>>, + DiscreteFunctionDPkVector<2, const TinyMatrix<1>>, + DiscreteFunctionDPkVector<2, const TinyMatrix<2>>, + DiscreteFunctionDPkVector<2, const TinyMatrix<3>>, + + DiscreteFunctionDPkVector<3, const double>, + DiscreteFunctionDPkVector<3, const TinyVector<1>>, + DiscreteFunctionDPkVector<3, const TinyVector<2>>, + DiscreteFunctionDPkVector<3, const TinyVector<3>>, + DiscreteFunctionDPkVector<3, const TinyMatrix<1>>, + DiscreteFunctionDPkVector<3, const TinyMatrix<2>>, + DiscreteFunctionDPkVector<3, const TinyMatrix<3>>>; private: Variant m_discrete_function_dpk; @@ -91,7 +111,13 @@ class DiscreteFunctionDPkVariant DiscreteFunctionDPkVariant(const DiscreteFunctionDPkVector<Dimension, DataType>& discrete_function_dpk) : m_discrete_function_dpk{DiscreteFunctionDPkVector<Dimension, const DataType>{discrete_function_dpk}} { - static_assert(std::is_same_v<std::remove_const_t<DataType>, double>, + static_assert(std::is_same_v<std::remove_const_t<DataType>, double> or // + std::is_same_v<std::remove_const_t<DataType>, TinyVector<1, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyVector<2, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyVector<3, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<1, 1, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<2, 2, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<3, 3, double>>, "DiscreteFunctionDPkVector with this DataType is not allowed in variant"); } diff --git a/src/scheme/DiscreteFunctionDPkVector.hpp b/src/scheme/DiscreteFunctionDPkVector.hpp index 2fefc078b192304ab0a80b63782986867e13c3d8..e8ac729f4b3e002f8c1c3b2c1cdcbd5f4aef3f77 100644 --- a/src/scheme/DiscreteFunctionDPkVector.hpp +++ b/src/scheme/DiscreteFunctionDPkVector.hpp @@ -139,6 +139,16 @@ class DiscreteFunctionDPkVector return m_by_cell_coefficients[cell_id]; } + PUGS_INLINE auto + componentCoefficients(const CellId cell_id, size_t i_component) const noexcept(NO_ASSERT) + { + Assert(m_mesh_v.use_count() > 0, "DiscreteFunctionDPkVector is not built"); + Assert(i_component < m_number_of_components, "incorrect component number"); + + return ComponentCoefficientView{&m_by_cell_coefficients[cell_id][i_component * m_nb_coefficients_per_component], + m_nb_coefficients_per_component}; + } + PUGS_FORCEINLINE BasisView operator()(const CellId cell_id, size_t i_component) const noexcept(NO_ASSERT) { diff --git a/src/scheme/DiscreteFunctionP0Vector.hpp b/src/scheme/DiscreteFunctionP0Vector.hpp index da3904b2ad9853a05a521920c3a3a9434aac14a9..bc08cb2a7f27286b9bafdd8bb15d0c761b892d1b 100644 --- a/src/scheme/DiscreteFunctionP0Vector.hpp +++ b/src/scheme/DiscreteFunctionP0Vector.hpp @@ -19,8 +19,6 @@ class DiscreteFunctionP0Vector friend class DiscreteFunctionP0Vector<std::add_const_t<DataType>>; friend class DiscreteFunctionP0Vector<std::remove_const_t<DataType>>; - static_assert(std::is_arithmetic_v<DataType>, "DiscreteFunctionP0Vector are only defined for arithmetic data type"); - private: std::shared_ptr<const MeshVariant> m_mesh; CellArray<DataType> m_cell_arrays; @@ -188,16 +186,23 @@ class DiscreteFunctionP0Vector return product; } - PUGS_INLINE friend DiscreteFunctionP0<double> + PUGS_INLINE friend DiscreteFunctionP0<std::remove_const_t<DataType>> sumOfComponents(const DiscreteFunctionP0Vector& f) { - DiscreteFunctionP0<double> result{f.m_mesh}; + DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh}; parallel_for( f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { const auto& f_cell_id = f[cell_id]; - double sum = 0; + std::remove_const_t<DataType> sum = [] { + if constexpr (std::is_arithmetic_v<DataType>) { + return 0; + } else { + return zero; + } + }(); + for (size_t i = 0; i < f.size(); ++i) { sum += f_cell_id[i]; } @@ -213,6 +218,7 @@ class DiscreteFunctionP0Vector { Assert(f.meshVariant()->id() == g.meshVariant()->id(), "functions are nor defined on the same mesh"); Assert(f.size() == g.size()); + static_assert(std::is_arithmetic_v<std::decay_t<DataType>>); DiscreteFunctionP0<double> result{f.m_mesh}; parallel_for( f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { diff --git a/src/scheme/DiscreteFunctionVariant.hpp b/src/scheme/DiscreteFunctionVariant.hpp index 901b7989084249ca0e437f9eef24e0371dd723a8..616184298a212c10fe4865b451d0a7879a18b2ea 100644 --- a/src/scheme/DiscreteFunctionVariant.hpp +++ b/src/scheme/DiscreteFunctionVariant.hpp @@ -19,7 +19,13 @@ class DiscreteFunctionVariant DiscreteFunctionP0<const TinyMatrix<2>>, DiscreteFunctionP0<const TinyMatrix<3>>, - DiscreteFunctionP0Vector<const double>>; + DiscreteFunctionP0Vector<const double>, + DiscreteFunctionP0Vector<const TinyVector<1>>, + DiscreteFunctionP0Vector<const TinyVector<2>>, + DiscreteFunctionP0Vector<const TinyVector<3>>, + DiscreteFunctionP0Vector<const TinyMatrix<1>>, + DiscreteFunctionP0Vector<const TinyMatrix<2>>, + DiscreteFunctionP0Vector<const TinyMatrix<3>>>; Variant m_discrete_function; @@ -70,7 +76,13 @@ class DiscreteFunctionVariant DiscreteFunctionVariant(const DiscreteFunctionP0Vector<DataType>& discrete_function) : m_discrete_function{DiscreteFunctionP0Vector<const DataType>{discrete_function}} { - static_assert(std::is_same_v<std::remove_const_t<DataType>, double>, + static_assert(std::is_same_v<std::remove_const_t<DataType>, double> or // + std::is_same_v<std::remove_const_t<DataType>, TinyVector<1, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyVector<2, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyVector<3, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<1, 1, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<2, 2, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<3, 3, double>>, "DiscreteFunctionP0Vector with this DataType is not allowed in variant"); } diff --git a/src/scheme/FluxingAdvectionSolver.cpp b/src/scheme/FluxingAdvectionSolver.cpp index d57eae2c45d93968df66fac6ffb3503d3aa74815..6f85610e0f1f87d2bf77ba516ddc9211f0b93fe6 100644 --- a/src/scheme/FluxingAdvectionSolver.cpp +++ b/src/scheme/FluxingAdvectionSolver.cpp @@ -86,10 +86,15 @@ class FluxingAdvectionSolver m_remapped_list.emplace_back(copy(old_q.cellValues())); } + template <typename DataType> void - _storeValues(const DiscreteFunctionP0Vector<const double>& old_q) + _storeValues(const DiscreteFunctionP0Vector<const DataType>& old_q) { - m_remapped_list.emplace_back(copy(old_q.cellArrays())); + if constexpr (std::is_arithmetic_v<DataType>) { + m_remapped_list.emplace_back(copy(old_q.cellArrays())); + } else { + throw NormalError("remapping DiscreteFunctionP0Vector of non arithmetic data type is not supported"); + } } template <typename DataType> @@ -741,8 +746,12 @@ FluxingAdvectionSolver<MeshType>::remap( new_variables.push_back(std::make_shared<DiscreteFunctionVariant>( DiscreteFunctionT(m_new_mesh, std::get<CellValue<DataType>>(m_remapped_list[i])))); } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { - new_variables.push_back(std::make_shared<DiscreteFunctionVariant>( - DiscreteFunctionT(m_new_mesh, std::get<CellArray<DataType>>(m_remapped_list[i])))); + if constexpr (std::is_arithmetic_v<DataType>) { + new_variables.push_back(std::make_shared<DiscreteFunctionVariant>( + DiscreteFunctionT(m_new_mesh, std::get<CellArray<DataType>>(m_remapped_list[i])))); + } else { + throw NormalError("remapping DiscreteFunctionP0Vector of non arithmetic data type is not supported"); + } } else { throw UnexpectedError("invalid discrete function type"); } diff --git a/src/scheme/IBoundaryConditionDescriptor.hpp b/src/scheme/IBoundaryConditionDescriptor.hpp index fc9ff381b97758e79642647a36680364093c193b..63f698e16eb611f9cb03bc82e6d737372924d36d 100644 --- a/src/scheme/IBoundaryConditionDescriptor.hpp +++ b/src/scheme/IBoundaryConditionDescriptor.hpp @@ -13,6 +13,7 @@ class IBoundaryConditionDescriptor { axis, dirichlet, + dirichlet_vector, external, fourier, fixed, diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 2af32e157475b0fe99b9f2cc9cc6c445e05681b3..1a2b1e072734154516b01001cb9831bfaff8ea73 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -32,6 +32,14 @@ symmetrize_vector(const TinyVector<Dimension>& normal, const TinyVector<Dimensio return u - 2 * dot(u, normal) * normal; } +template <size_t Dimension> +PUGS_INLINE auto +symmetrize_matrix(const TinyVector<Dimension>& normal, const TinyMatrix<Dimension>& A) +{ + const TinyMatrix S = TinyMatrix<Dimension>{identity} - 2 * tensorProduct(normal, normal); + return S * A * S; +} + template <size_t Dimension> PUGS_INLINE auto symmetrize_coordinates(const TinyVector<Dimension>& origin, @@ -69,8 +77,28 @@ class PolynomialReconstruction::MutableDiscreteFunctionDPkVariant DiscreteFunctionDPk<3, TinyMatrix<3>>, DiscreteFunctionDPkVector<1, double>, + DiscreteFunctionDPkVector<1, TinyVector<1>>, + DiscreteFunctionDPkVector<1, TinyVector<2>>, + DiscreteFunctionDPkVector<1, TinyVector<3>>, + DiscreteFunctionDPkVector<1, TinyMatrix<1>>, + DiscreteFunctionDPkVector<1, TinyMatrix<2>>, + DiscreteFunctionDPkVector<1, TinyMatrix<3>>, + DiscreteFunctionDPkVector<2, double>, - DiscreteFunctionDPkVector<3, double>>; + DiscreteFunctionDPkVector<2, TinyVector<1>>, + DiscreteFunctionDPkVector<2, TinyVector<2>>, + DiscreteFunctionDPkVector<2, TinyVector<3>>, + DiscreteFunctionDPkVector<2, TinyMatrix<1>>, + DiscreteFunctionDPkVector<2, TinyMatrix<2>>, + DiscreteFunctionDPkVector<2, TinyMatrix<3>>, + + DiscreteFunctionDPkVector<3, double>, + DiscreteFunctionDPkVector<3, TinyVector<1>>, + DiscreteFunctionDPkVector<3, TinyVector<2>>, + DiscreteFunctionDPkVector<3, TinyVector<3>>, + DiscreteFunctionDPkVector<3, TinyMatrix<1>>, + DiscreteFunctionDPkVector<3, TinyMatrix<2>>, + DiscreteFunctionDPkVector<3, TinyMatrix<3>>>; private: Variant m_mutable_discrete_function_dpk; @@ -123,7 +151,13 @@ class PolynomialReconstruction::MutableDiscreteFunctionDPkVariant MutableDiscreteFunctionDPkVariant(const DiscreteFunctionDPkVector<Dimension, DataType>& discrete_function_dpk) : m_mutable_discrete_function_dpk{discrete_function_dpk} { - static_assert(std::is_same_v<DataType, double>, + static_assert(std::is_same_v<DataType, double> or // + std::is_same_v<DataType, TinyVector<1, double>> or // + std::is_same_v<DataType, TinyVector<2, double>> or // + std::is_same_v<DataType, TinyVector<3, double>> or // + std::is_same_v<DataType, TinyMatrix<1, 1, double>> or // + std::is_same_v<DataType, TinyMatrix<2, 2, double>> or // + std::is_same_v<DataType, TinyMatrix<3, 3, double>>, "DiscreteFunctionDPkVector with this DataType is not allowed in variant"); } @@ -698,7 +732,7 @@ PolynomialReconstruction::_getNumberOfColumns( using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { using data_type = std::decay_t<typename DiscreteFunctionT::data_type>; - if constexpr (std::is_same_v<data_type, double>) { + if constexpr (std::is_arithmetic_v<data_type>) { return 1; } else if constexpr (is_tiny_vector_v<data_type> or is_tiny_matrix_v<data_type>) { return data_type::Dimension; @@ -708,7 +742,16 @@ PolynomialReconstruction::_getNumberOfColumns( // LCOV_EXCL_STOP } } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { - return discrete_function.size(); + using data_type = std::decay_t<typename DiscreteFunctionT::data_type>; + if constexpr (std::is_arithmetic_v<data_type>) { + return discrete_function.size(); + } else if constexpr (is_tiny_vector_v<data_type> or is_tiny_matrix_v<data_type>) { + return discrete_function.size() * data_type::Dimension; + } else { + // LCOV_EXCL_START + throw UnexpectedError("unexpected data type " + demangle<data_type>()); + // LCOV_EXCL_STOP + } } else { // LCOV_EXCL_START throw UnexpectedError("unexpected discrete function type"); @@ -755,6 +798,44 @@ PolynomialReconstruction::_createMutableDiscreteFunctionDPKVariantList( return mutable_discrete_function_dpk_variant_list; } +template <MeshConcept MeshType> +void +PolynomialReconstruction::_checkDataAndSymmetriesCompatibility( + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const +{ + for (auto&& discrete_function_variant : discrete_function_variant_list) { + std::visit( + [&](auto&& discrete_function) { + using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; + if constexpr (is_discrete_function_P0_v<DiscreteFunctionT> or + is_discrete_function_P0_vector_v<DiscreteFunctionT>) { + using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; + if constexpr (is_tiny_vector_v<DataType>) { + if constexpr (DataType::Dimension != MeshType::Dimension) { + std::stringstream error_msg; + error_msg << "cannot symmetrize vectors of dimension " << DataType::Dimension + << " using a mesh of dimension " << MeshType::Dimension; + throw NormalError(error_msg.str()); + } + } else if constexpr (is_tiny_matrix_v<DataType>) { + if constexpr ((DataType::NumberOfRows != MeshType::Dimension) or + (DataType::NumberOfColumns != MeshType::Dimension)) { + std::stringstream error_msg; + error_msg << "cannot symmetrize matrices of dimensions " << DataType::NumberOfRows << 'x' + << DataType::NumberOfColumns << " using a mesh of dimension " << MeshType::Dimension; + throw NormalError(error_msg.str()); + } + } + } else { + // LCOV_EXCL_START + throw UnexpectedError("invalid discrete function type"); + // LCOV_EXCL_STOP + } + }, + discrete_function_variant->discreteFunction()); + } +} + template <MeshConcept MeshType> [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> PolynomialReconstruction::_build( @@ -765,6 +846,10 @@ PolynomialReconstruction::_build( using Rd = TinyVector<MeshType::Dimension>; + if (m_descriptor.symmetryBoundaryDescriptorList().size() > 0) { + this->_checkDataAndSymmetriesCompatibility<MeshType>(discrete_function_variant_list); + } + const size_t number_of_columns = this->_getNumberOfColumns(discrete_function_variant_list); const size_t basis_dimension = @@ -862,7 +947,7 @@ PolynomialReconstruction::_build( } parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_j_id) { + mesh.numberOfCells(), PUGS_CLASS_LAMBDA(const CellId cell_j_id) { if (cell_is_owned[cell_j_id]) { const int32_t t = tokens.acquire(); @@ -919,21 +1004,32 @@ PolynomialReconstruction::_build( B(index, kB) = qi_qj[k]; } } else { - const DataType& qi_qj = discrete_function[cell_i_id] - qj; - for (size_t kB = column_begin, k = 0; k < DataType::Dimension; ++k, ++kB) { - B(index, kB) = qi_qj[k]; - } + // LCOV_EXCL_START + std::stringstream error_msg; + error_msg << "cannot symmetrize vectors of dimension " << DataType::Dimension + << " using a mesh of dimension " << MeshType::Dimension; + throw UnexpectedError(error_msg.str()); + // LCOV_EXCL_STOP } } else if constexpr (is_tiny_matrix_v<DataType>) { if constexpr ((DataType::NumberOfColumns == DataType::NumberOfRows) and (DataType::NumberOfColumns == MeshType::Dimension)) { - throw NotImplementedError("TinyMatrix symmetries for reconstruction"); - } - const DataType& qi_qj = discrete_function[cell_i_id] - qj; - for (size_t k = 0; k < DataType::NumberOfRows; ++k) { - for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { - B(index, column_begin + k * DataType::NumberOfColumns + l) = qi_qj(k, l); + const Rd& normal = symmetry_normal_list[i_symmetry]; + + const DataType& qi = discrete_function[cell_i_id]; + const DataType& qi_qj = symmetrize_matrix(normal, qi) - qj; + for (size_t k = 0; k < DataType::NumberOfRows; ++k) { + for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { + B(index, column_begin + k * DataType::NumberOfColumns + l) = qi_qj(k, l); + } } + } else { + // LCOV_EXCL_START + std::stringstream error_msg; + error_msg << "cannot symmetrize matrices of dimensions " << DataType::NumberOfRows << 'x' + << DataType::NumberOfColumns << " using a mesh of dimension " << MeshType::Dimension; + throw UnexpectedError(error_msg.str()); + // LCOV_EXCL_STOP } } } @@ -945,32 +1041,95 @@ PolynomialReconstruction::_build( column_begin += DataType::Dimension; } } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { - using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; + using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; + const auto qj_vector = discrete_function[cell_j_id]; - size_t index = 0; - for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = stencil_cell_list[i]; - for (size_t l = 0; l < qj_vector.size(); ++l) { - const DataType& qj = qj_vector[l]; - const DataType& qi_qj = discrete_function[cell_i_id][l] - qj; - B(index, column_begin + l) = qi_qj; - } - } - for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); - ++i_symmetry) { - auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); - auto ghost_cell_list = ghost_stencil[cell_j_id]; - for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = ghost_cell_list[i]; + if constexpr (std::is_arithmetic_v<DataType>) { + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = stencil_cell_list[i]; for (size_t l = 0; l < qj_vector.size(); ++l) { const DataType& qj = qj_vector[l]; const DataType& qi_qj = discrete_function[cell_i_id][l] - qj; B(index, column_begin + l) = qi_qj; } } + + for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); + ++i_symmetry) { + auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + for (size_t l = 0; l < qj_vector.size(); ++l) { + const DataType& qj = qj_vector[l]; + const DataType& qi_qj = discrete_function[cell_i_id][l] - qj; + B(index, column_begin + l) = qi_qj; + } + } + } + } else if constexpr (is_tiny_vector_v<DataType>) { + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = stencil_cell_list[i]; + for (size_t l = 0; l < qj_vector.size(); ++l) { + const DataType& qj = qj_vector[l]; + const DataType& qi_qj = discrete_function[cell_i_id][l] - qj; + for (size_t kB = column_begin + l * DataType::Dimension, k = 0; k < DataType::Dimension; + ++k, ++kB) { + B(index, kB) = qi_qj[k]; + } + } + } + + for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); + ++i_symmetry) { + if constexpr (DataType::Dimension == MeshType::Dimension) { + auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + + const Rd& normal = symmetry_normal_list[i_symmetry]; + + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + + for (size_t l = 0; l < qj_vector.size(); ++l) { + const DataType& qj = qj_vector[l]; + const DataType& qi = discrete_function[cell_i_id][l]; + const DataType& qi_qj = symmetrize_vector(normal, qi) - qj; + for (size_t kB = column_begin + l * DataType::Dimension, k = 0; k < DataType::Dimension; + ++k, ++kB) { + B(index, kB) = qi_qj[k]; + } + } + } + } else { + // LCOV_EXCL_START + std::stringstream error_msg; + error_msg << "cannot symmetrize vectors of dimension " << DataType::Dimension + << " using a mesh of dimension " << MeshType::Dimension; + throw UnexpectedError(error_msg.str()); + // LCOV_EXCL_STOP + } + } + } else if constexpr (is_tiny_matrix_v<DataType>) { + for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); + ++i_symmetry) { + auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + if (ghost_cell_list.size() > 0) { + throw NotImplementedError("TinyMatrix symmetries for reconstruction of DiscreteFunctionP0Vector"); + } + } + } + + if constexpr (std::is_arithmetic_v<DataType>) { + column_begin += qj_vector.size(); + } else if constexpr (is_tiny_vector_v<DataType> or is_tiny_matrix_v<DataType>) { + column_begin += qj_vector.size() * DataType::Dimension; } - column_begin += qj_vector.size(); + } else { // LCOV_EXCL_START throw UnexpectedError("invalid discrete function type"); @@ -1289,6 +1448,24 @@ PolynomialReconstruction::_build( dpk_j_ip1 = X(i, column_begin); } ++column_begin; + } else if constexpr (is_tiny_vector_v<DataType>) { + if (m_descriptor.degree() > 1) { + auto& mean_j_of_ejk = mean_j_of_ejk_pool[t]; + for (size_t i = 0; i < basis_dimension - 1; ++i) { + auto& dpk_j_0 = dpk_j[0]; + for (size_t k = 0; k < DataType::Dimension; ++k) { + dpk_j_0[k] -= X(i, column_begin + k) * mean_j_of_ejk[i]; + } + } + } + + for (size_t i = 0; i < basis_dimension - 1; ++i) { + auto& dpk_j_ip1 = dpk_j[component_begin + i + 1]; + for (size_t k = 0; k < DataType::Dimension; ++k) { + dpk_j_ip1[k] = X(i, column_begin + k); + } + } + column_begin += DataType::Dimension; } else { // LCOV_EXCL_START throw UnexpectedError("unexpected data type"); diff --git a/src/scheme/PolynomialReconstruction.hpp b/src/scheme/PolynomialReconstruction.hpp index b2b855271d1ed4140ebb51d1b21314602caa0dc6..bf6013639f732fd52f9c145ba4982701c0e4bcfc 100644 --- a/src/scheme/PolynomialReconstruction.hpp +++ b/src/scheme/PolynomialReconstruction.hpp @@ -17,6 +17,10 @@ class PolynomialReconstruction size_t _getNumberOfColumns( const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; + template <MeshConcept MeshType> + void _checkDataAndSymmetriesCompatibility( + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; + template <MeshConcept MeshType> std::vector<MutableDiscreteFunctionDPkVariant> _createMutableDiscreteFunctionDPKVariantList( const std::shared_ptr<const MeshType>& p_mesh, diff --git a/src/utils/BuildInfo.cpp b/src/utils/BuildInfo.cpp index 81d7567750eaef8513b580daf11eac6d3f916642..2413bdbaeb5712e3b7eaf96fd9d09df5e8f0a6e0 100644 --- a/src/utils/BuildInfo.cpp +++ b/src/utils/BuildInfo.cpp @@ -17,6 +17,10 @@ #include <slepc.h> #endif // PUGS_HAS_PETSC +#ifdef PUGS_HAS_EIGEN3 +#include <eigen3/Eigen/Eigen> +#endif // PUGS_HAS_EIGEN3 + #ifdef PUGS_HAS_HDF5 #include <highfive/highfive.hpp> #endif // PUGS_HAS_HDF5 @@ -82,6 +86,16 @@ BuildInfo::slepcLibrary() #endif // PUGS_HAS_SLEPC } +std::string +BuildInfo::eigen3Library() +{ +#ifdef PUGS_HAS_EIGEN3 + return stringify(EIGEN_WORLD_VERSION) + "." + stringify(EIGEN_MAJOR_VERSION) + "." + stringify(EIGEN_MINOR_VERSION); +#else // PUGS_HAS_EIGEN3 + return "none"; +#endif // PUGS_HAS_EIGEN3 +} + std::string BuildInfo::hdf5Library() { diff --git a/src/utils/BuildInfo.hpp b/src/utils/BuildInfo.hpp index df4a32d93a284db7fc8d3abbea9ac2e43ea5d36e..86f7c04f035827a936a00c9b1f42e304e33e7b26 100644 --- a/src/utils/BuildInfo.hpp +++ b/src/utils/BuildInfo.hpp @@ -11,6 +11,7 @@ struct BuildInfo static std::string mpiLibrary(); static std::string petscLibrary(); static std::string slepcLibrary(); + static std::string eigen3Library(); static std::string hdf5Library(); static std::string slurmLibrary(); }; diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index 13b0db80862c2b28e80dc18ff22951b08157db28..bc66a97faf800dc0a10be3165f8d4f2c94059010 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -14,8 +14,12 @@ add_library( FPEManager.cpp GlobalVariableManager.cpp Messenger.cpp + ParMETISPartitioner.cpp Partitioner.cpp + PartitionerOptions.cpp PETScWrapper.cpp + PluginsLoader.cpp + PTScotchPartitioner.cpp PugsUtils.cpp RandomEngine.cpp ReproducibleSumManager.cpp @@ -32,11 +36,15 @@ endif() target_link_libraries( PugsUtils - ${PETSC_LIBRARIES} - ${SLEPC_LIBRARIES} + ${PETSC_TARGET} + ${SLEPC_TARGET} + ${SLURM_TARGET} ${HIGHFIVE_TARGET} ) +target_include_directories(PugsUtils PUBLIC ${PETSC_INCLUDE_DIRS}) +target_include_directories(PugsUtils PUBLIC ${SLURM_INCLUDE_DIRS}) + # --------------- get git revision info --------------- # Generates revision header file @@ -58,12 +66,11 @@ set_source_files_properties( add_custom_command(TARGET PugsGitRevison COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_CURRENT_BINARY_DIR}/pugs_git_revision - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/pugs_git_revision.hpp + POST_BUILD COMMENT "" ) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/pugs_git_revision.hpp - PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/pugs_git_revision ${CMAKE_CURRENT_BINARY_DIR}/pugs_git_revision.hpp @@ -73,7 +80,6 @@ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/pugs_git_revision.hpp ) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/pugs_git_revision - PRE_BUILD COMMAND ${CMAKE_COMMAND} -DPUGS_VERSION=${PUGS_VERSION} -DPUGS_SOURCE_DIR=${PUGS_SOURCE_DIR} -P ${PUGS_SOURCE_DIR}/cmake/GetPugsGitRevision.cmake COMMENT "Check pugs git status" VERBATIM diff --git a/src/utils/ConsoleManager.hpp b/src/utils/ConsoleManager.hpp index a2afe46fbc0ad16e263d1f5a9b3f8a84d5202d94..693219e30285d1b0acae26d4b43d310882069784 100644 --- a/src/utils/ConsoleManager.hpp +++ b/src/utils/ConsoleManager.hpp @@ -1,7 +1,7 @@ #ifndef CONSOLE_MANAGER_HPP #define CONSOLE_MANAGER_HPP -#include <string> +#include <ostream> class ConsoleManager { diff --git a/src/utils/PTScotchPartitioner.cpp b/src/utils/PTScotchPartitioner.cpp new file mode 100644 index 0000000000000000000000000000000000000000..112ddfecdf161d8b924b6f9b962d16e75c22c507 --- /dev/null +++ b/src/utils/PTScotchPartitioner.cpp @@ -0,0 +1,112 @@ +#include <utils/PTScotchPartitioner.hpp> + +#include <utils/pugs_config.hpp> + +#ifdef PUGS_HAS_PTSCOTCH + +#include <utils/Exceptions.hpp> +#include <utils/Messenger.hpp> + +#include <ptscotch.h> + +#include <iostream> +#include <vector> + +Array<int> +PTScotchPartitioner::partition(const CRSGraph& graph) +{ + std::cout << "Partitioning graph into " << rang::style::bold << parallel::size() << rang::style::reset + << " parts using " << rang::fgB::green << "PTScotch" << rang::fg::reset << '\n'; + + MPI_Group world_group; + MPI_Comm_group(parallel::Messenger::getInstance().comm(), &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> grp_ranks; + grp_ranks.reserve(graph_node_owners.size()); + for (size_t i = 0; i < graph_node_owners.size(); ++i) { + if (graph_node_owners[i] > 0) { + grp_ranks.push_back(i); + } + } + return grp_ranks; + }(); + + MPI_Group_incl(world_group, group_ranks.size(), &(group_ranks[0]), &mesh_group); + + MPI_Comm partitioning_comm; + MPI_Comm_create_group(parallel::Messenger::getInstance().comm(), mesh_group, 1, &partitioning_comm); + + Array<int> partition; + if (graph.numberOfNodes() > 0) { + SCOTCH_Strat scotch_strategy; + SCOTCH_Dgraph scotch_grah; + + SCOTCH_stratInit(&scotch_strategy); // use default strategy + SCOTCH_dgraphInit(&scotch_grah, partitioning_comm); + + const Array<const int>& entries = graph.entries(); + const Array<const int>& neighbors = graph.neighbors(); + + static_assert(std::is_same_v<int, SCOTCH_Num>); + + SCOTCH_Num* entries_ptr = const_cast<int*>(&(entries[0])); + SCOTCH_Num* neighbors_ptr = const_cast<int*>(&(neighbors[0])); + + if (SCOTCH_dgraphBuild(&scotch_grah, + 0, // 0 for C-like arrays + graph.numberOfNodes(), // + graph.numberOfNodes(), // + entries_ptr, // + nullptr, // + nullptr, // + nullptr, // optional local node label array + graph.neighbors().size(), // + graph.neighbors().size(), // + neighbors_ptr, // + nullptr, // + nullptr) != 0) { + // LCOV_EXCL_START + throw UnexpectedError("PTScotch invalid graph"); + // LCOV_EXCL_STOP + } + + partition = Array<int>(graph.numberOfNodes()); + + SCOTCH_Num* partition_ptr = static_cast<SCOTCH_Num*>(&(partition[0])); + + if (SCOTCH_dgraphPart(&scotch_grah, // + parallel::size(), // + &scotch_strategy, // + partition_ptr) != 0) { + // LCOV_EXCL_START + throw UnexpectedError("PTScotch Error"); + // LCOV_EXCL_STOP + } + + SCOTCH_dgraphExit(&scotch_grah); + SCOTCH_stratExit(&scotch_strategy); + } + + if (partitioning_comm != MPI_COMM_NULL){ + MPI_Comm_free(&partitioning_comm); + } + MPI_Group_free(&mesh_group); + MPI_Group_free(&world_group); + + return partition; +} + +#else // PUGS_HAS_PTSCOTCH + +Array<int> +PTScotchPartitioner::partition(const CRSGraph& graph) +{ + Array<int> partition{graph.entries().size() - 1}; + partition.fill(0); + return partition; +} + +#endif // PUGS_HAS_PTSCOTCH diff --git a/src/utils/PTScotchPartitioner.hpp b/src/utils/PTScotchPartitioner.hpp new file mode 100644 index 0000000000000000000000000000000000000000..cefb0a1df2e1c39966d6c8ed0eaffe655a836df0 --- /dev/null +++ b/src/utils/PTScotchPartitioner.hpp @@ -0,0 +1,19 @@ +#ifndef PT_SCOTCH_PARTITIONER_HPP +#define PT_SCOTCH_PARTITIONER_HPP + +#include <utils/Array.hpp> +#include <utils/CRSGraph.hpp> + +class PTScotchPartitioner +{ + private: + friend class Partitioner; + + // Forbids direct calls + static Array<int> partition(const CRSGraph& graph); + + public: + PTScotchPartitioner() = delete; +}; + +#endif // PT_SCOTCH_PARTITIONER_HPP diff --git a/src/utils/ParMETISPartitioner.cpp b/src/utils/ParMETISPartitioner.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f1b17c3d070a71e0290c8c6434b0e13d2e442bad --- /dev/null +++ b/src/utils/ParMETISPartitioner.cpp @@ -0,0 +1,94 @@ +#include <utils/ParMETISPartitioner.hpp> + +#include <utils/pugs_config.hpp> + +#ifdef PUGS_HAS_PARMETIS + +#include <utils/Exceptions.hpp> +#include <utils/Messenger.hpp> + +#include <parmetis.h> + +#include <iostream> +#include <vector> + +Array<int> +ParMETISPartitioner::partition(const CRSGraph& graph) +{ + std::cout << "Partitioning graph into " << rang::style::bold << parallel::size() << rang::style::reset + << " parts using " << rang::fgB::green << "ParMETIS" << rang::fg::reset << '\n'; + + MPI_Group world_group; + MPI_Comm_group(parallel::Messenger::getInstance().comm(), &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> grp_ranks; + grp_ranks.reserve(graph_node_owners.size()); + for (size_t i = 0; i < graph_node_owners.size(); ++i) { + if (graph_node_owners[i] > 0) { + grp_ranks.push_back(i); + } + } + return grp_ranks; + }(); + + MPI_Group_incl(world_group, group_ranks.size(), &(group_ranks[0]), &mesh_group); + + MPI_Comm partitioning_comm; + MPI_Comm_create_group(parallel::Messenger::getInstance().comm(), mesh_group, 1, &partitioning_comm); + + Array<int> partition; + if (graph.numberOfNodes() > 0) { + 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, 0, 0}; + int edgecut = 0; + + int local_number_of_nodes = graph.numberOfNodes(); + + partition = Array<int>(local_number_of_nodes); + std::vector<int> vtxdist{0, local_number_of_nodes}; + + const Array<const int>& entries = graph.entries(); + const Array<const int>& neighbors = graph.neighbors(); + + int* entries_ptr = const_cast<int*>(&(entries[0])); + int* neighbors_ptr = const_cast<int*>(&(neighbors[0])); + + int result = + ParMETIS_V3_PartKway(&(vtxdist[0]), entries_ptr, neighbors_ptr, NULL, NULL, &wgtflag, &numflag, &ncon, &npart, + &(tpwgts[0]), &(ubvec[0]), &(options[0]), &edgecut, &(partition[0]), &partitioning_comm); + // LCOV_EXCL_START + if (result == METIS_ERROR) { + throw UnexpectedError("Metis Error"); + } + // LCOV_EXCL_STOP + } + + if (partitioning_comm != MPI_COMM_NULL){ + MPI_Comm_free(&partitioning_comm); + } + MPI_Group_free(&mesh_group); + MPI_Group_free(&world_group); + + return partition; +} + +#else // PUGS_HAS_PARMETIS + +Array<int> +ParMETISPartitioner::partition(const CRSGraph& graph) +{ + Array<int> partition{graph.entries().size() - 1}; + partition.fill(0); + return partition; +} + +#endif // PUGS_HAS_PARMETIS diff --git a/src/utils/ParMETISPartitioner.hpp b/src/utils/ParMETISPartitioner.hpp new file mode 100644 index 0000000000000000000000000000000000000000..53c1d2dc50e96c85a2c57d0abfb59735b471cc6f --- /dev/null +++ b/src/utils/ParMETISPartitioner.hpp @@ -0,0 +1,19 @@ +#ifndef PARMETIS_PARTITIONER_HPP +#define PARMETIS_PARTITIONER_HPP + +#include <utils/Array.hpp> +#include <utils/CRSGraph.hpp> + +class ParMETISPartitioner +{ + private: + friend class Partitioner; + + // Forbids direct calls + static Array<int> partition(const CRSGraph& graph); + + public: + ParMETISPartitioner() = delete; +}; + +#endif // PARMETIS_PARTITIONER_HPP diff --git a/src/utils/Partitioner.cpp b/src/utils/Partitioner.cpp index 85e13c22d80f449c211f4c37ee20f3107556d6ba..4f53d322f5e52c130122ccc3966270e7220d34d7 100644 --- a/src/utils/Partitioner.cpp +++ b/src/utils/Partitioner.cpp @@ -1,94 +1,22 @@ #include <utils/Partitioner.hpp> -#include <utils/Messenger.hpp> -#include <utils/pugs_config.hpp> +#include <utils/PTScotchPartitioner.hpp> +#include <utils/ParMETISPartitioner.hpp> -#ifdef PUGS_HAS_MPI - -#define IDXTYPEWIDTH 64 -#define REALTYPEWIDTH 64 -#include <parmetis.h> - -#include <iostream> -#include <vector> - -#include <utils/Exceptions.hpp> +Partitioner::Partitioner(const PartitionerOptions& options) : m_partitioner_options{options} {} Array<int> Partitioner::partition(const CRSGraph& graph) { - std::cout << "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, 0, 0}; - int edgecut = 0; - Array<int> part(0); - - MPI_Group world_group; - - MPI_Comm_group(parallel::Messenger::getInstance().comm(), &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> grp_ranks; - grp_ranks.reserve(graph_node_owners.size()); - for (size_t i = 0; i < graph_node_owners.size(); ++i) { - if (graph_node_owners[i] > 0) { - grp_ranks.push_back(i); - } - } - return grp_ranks; - }(); - - MPI_Group_incl(world_group, group_ranks.size(), &(group_ranks[0]), &mesh_group); - - MPI_Comm parmetis_comm; - MPI_Comm_create_group(parallel::Messenger::getInstance().comm(), 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<const int>& entries = graph.entries(); - const Array<const int>& neighbors = graph.neighbors(); - - int* entries_ptr = const_cast<int*>(&(entries[0])); - int* neighbors_ptr = const_cast<int*>(&(neighbors[0])); - - int result = - ParMETIS_V3_PartKway(&(vtxdist[0]), entries_ptr, neighbors_ptr, NULL, NULL, &wgtflag, &numflag, &ncon, &npart, - &(tpwgts[0]), &(ubvec[0]), &(options[0]), &edgecut, &(part[0]), &parmetis_comm); - // LCOV_EXCL_START - if (result == METIS_ERROR) { - throw UnexpectedError("Metis Error"); - } - // LCOV_EXCL_STOP - - MPI_Comm_free(&parmetis_comm); + switch (m_partitioner_options.library()) { + case PartitionerLibrary::parmetis: { + return ParMETISPartitioner::partition(graph); + } + case PartitionerLibrary::ptscotch: { + return PTScotchPartitioner::partition(graph); + } + default: { + throw UnexpectedError("invalid partition library"); + } } - - MPI_Group_free(&mesh_group); - - return part; -} - -#else // PUGS_HAS_MPI - -Array<int> -Partitioner::partition(const CRSGraph& graph) -{ - Array<int> partition{graph.entries().size() - 1}; - partition.fill(0); - return partition; } - -#endif // PUGS_HAS_MPI diff --git a/src/utils/Partitioner.hpp b/src/utils/Partitioner.hpp index 2c720bfd87e7853e12cd0736a46c5eda246f085f..a923d2301635d43f346159aeff6de4331e7dceef 100644 --- a/src/utils/Partitioner.hpp +++ b/src/utils/Partitioner.hpp @@ -2,11 +2,15 @@ #define PARTITIONER_HPP #include <utils/CRSGraph.hpp> +#include <utils/PartitionerOptions.hpp> class Partitioner { + private: + PartitionerOptions m_partitioner_options; + public: - Partitioner() = default; + Partitioner(const PartitionerOptions& options = PartitionerOptions::default_options); Partitioner(const Partitioner&) = default; ~Partitioner() = default; diff --git a/src/utils/PartitionerOptions.cpp b/src/utils/PartitionerOptions.cpp new file mode 100644 index 0000000000000000000000000000000000000000..207d649803be16ac79795084517eb90feb97f283 --- /dev/null +++ b/src/utils/PartitionerOptions.cpp @@ -0,0 +1,10 @@ +#include <utils/PartitionerOptions.hpp> + +#include <rang.hpp> + +std::ostream& +operator<<(std::ostream& os, const PartitionerOptions& options) +{ + os << " library: " << rang::style::bold << name(options.library()) << rang::style::reset << '\n'; + return os; +} diff --git a/src/utils/PartitionerOptions.hpp b/src/utils/PartitionerOptions.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e4157f7659cac94ef8b4c7cd02a54d289156d4a8 --- /dev/null +++ b/src/utils/PartitionerOptions.hpp @@ -0,0 +1,86 @@ +#ifndef PARTITIONER_OPTIONS_HPP +#define PARTITIONER_OPTIONS_HPP + +#include <utils/Exceptions.hpp> +#include <utils/pugs_config.hpp> + +#include <iostream> + +enum class PartitionerLibrary : int8_t +{ + PT__begin = 0, + // + parmetis = PT__begin, + ptscotch, + // + PT__end +}; + +inline std::string +name(const PartitionerLibrary library) +{ + switch (library) { + case PartitionerLibrary::parmetis: { + return "ParMETIS"; + } + case PartitionerLibrary::ptscotch: { + return "PTScotch"; + } + case PartitionerLibrary::PT__end: { + } + } + throw UnexpectedError("Linear system library name is not defined!"); +} + +template <typename PartitionerEnumType> +inline PartitionerEnumType +getPartitionerEnumFromName(const std::string& enum_name) +{ + using BaseT = std::underlying_type_t<PartitionerEnumType>; + for (BaseT enum_value = static_cast<BaseT>(PartitionerEnumType::PT__begin); + enum_value < static_cast<BaseT>(PartitionerEnumType::PT__end); ++enum_value) { + if (name(PartitionerEnumType{enum_value}) == enum_name) { + return PartitionerEnumType{enum_value}; + } + } + throw NormalError(std::string{"could not find '"} + enum_name + "' associate type!"); +} + +class PartitionerOptions +{ + private: + PartitionerLibrary m_library = []() { +#if !defined(PUGS_HAS_PARMETIS) && defined(PUGS_HAS_PTSCOTCH) + return PartitionerLibrary::ptscotch; +#else // sets parmetis as default if no alternative is available + return PartitionerLibrary::parmetis; +#endif + }(); + + public: + static PartitionerOptions default_options; + + friend std::ostream& operator<<(std::ostream& os, const PartitionerOptions& options); + + PartitionerLibrary& + library() + { + return m_library; + } + + PartitionerLibrary + library() const + { + return m_library; + } + + PartitionerOptions(const PartitionerOptions&) = default; + PartitionerOptions(PartitionerOptions&&) = default; + + PartitionerOptions() = default; + ~PartitionerOptions() = default; +}; + +inline PartitionerOptions PartitionerOptions::default_options; + +#endif // PARTITIONER_OPTIONS_HPP diff --git a/src/utils/PluginsLoader.cpp b/src/utils/PluginsLoader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..35e24605e8a01b4d350aadb18bcacda313e9004c --- /dev/null +++ b/src/utils/PluginsLoader.cpp @@ -0,0 +1,104 @@ +#include <utils/PluginsLoader.hpp> + +#include <utils/ConsoleManager.hpp> + +#include <dlfcn.h> + +#include <rang.hpp> + +#include <filesystem> +#include <iostream> +#include <sstream> +#include <unistd.h> +#include <vector> + +std::vector<std::string> +split(const std::string& full_string) +{ + std::vector<std::string> split_string; + + std::stringstream is(full_string); + + std::string segment; + while (std::getline(is, segment, ';')) { + if (segment.size() > 0) { + split_string.push_back(segment); + } + } + return split_string; +} + +void +PluginsLoader::_open(const std::string& plugin) +{ + auto handle = dlopen(plugin.c_str(), RTLD_NOW); + if (handle != nullptr) { + m_dl_handler_stack.push(handle); + if (ConsoleManager::showPreamble()) { + std::cout << " * \"" << rang::fgB::green << plugin << rang::fg::reset << "\"\n"; + } + } else { + std::cerr << " " << rang::fgB::red << "cannot load " << rang::fg::reset << '\"' << rang::fgB::yellow << plugin + << rang::fg::reset << "\"\n"; + } +} + +PluginsLoader::PluginsLoader() +{ + std::vector<std::string> plugin_vector; + + { + char* env = getenv("PUGS_PLUGIN"); + if (env != nullptr) { + std::string plugins = env; + plugin_vector = split(plugins); + } + } + + { + char* env = getenv("PUGS_PLUGIN_DIR"); + if (env != nullptr) { + std::string paths = env; + std::vector<std::string> path_vector = split(paths); + for (auto&& path : path_vector) { + if (access(path.c_str(), R_OK) == -1) { + std::cerr << ' ' << rang::fgB::red << 'X' << rang::fg::reset << " cannot access plugin dir \"" + << rang::fgB::yellow << path << rang::fg::reset << "\"\n"; + } else { + for (auto&& entry : + std::filesystem::directory_iterator(path, + (std::filesystem::directory_options::follow_directory_symlink | + std::filesystem::directory_options::skip_permission_denied))) { + if (entry.path().extension() == ".so") { + plugin_vector.push_back(entry.path().string()); + } + } + } + } + } + } + + // keep unique entries + std::sort(plugin_vector.begin(), plugin_vector.end()); + plugin_vector.resize(std::distance(plugin_vector.begin(), std::unique(plugin_vector.begin(), plugin_vector.end()))); + + if (plugin_vector.size() > 0) { + if (ConsoleManager::showPreamble()) { + std::cout << rang::style::bold << "Loading plugins" << rang::style::reset << '\n'; + } + for (auto&& plugin : plugin_vector) { + this->_open(plugin); + } + if (ConsoleManager::showPreamble()) { + std::cout << "-------------------------------------------------------\n"; + } + } +} + +PluginsLoader::~PluginsLoader() +{ + while (not m_dl_handler_stack.empty()) { + dlclose(m_dl_handler_stack.top()); + m_dl_handler_stack.pop(); + } +} diff --git a/src/utils/PluginsLoader.hpp b/src/utils/PluginsLoader.hpp new file mode 100644 index 0000000000000000000000000000000000000000..76be25c6934c5f336360e938d9a40d75f075e6d3 --- /dev/null +++ b/src/utils/PluginsLoader.hpp @@ -0,0 +1,23 @@ +#ifndef PLUGINS_LOADER_HPP +#define PLUGINS_LOADER_HPP + +#include <stack> +#include <string> + +class PluginsLoader +{ + private: + std::stack<void*> m_dl_handler_stack; + + void _open(const std::string& filename); + + public: + PluginsLoader(); + + PluginsLoader(const PluginsLoader&) = delete; + PluginsLoader(PluginsLoader&&) = delete; + + ~PluginsLoader(); +}; + +#endif // PLUGINS_LOADER_HPP diff --git a/src/utils/PugsTraits.hpp b/src/utils/PugsTraits.hpp index cb8f154e3b344ae34bb2b769b5a0419e37d44e4a..e0008a7a4a0ecf30998e8a739bbf91511d73e3d4 100644 --- a/src/utils/PugsTraits.hpp +++ b/src/utils/PugsTraits.hpp @@ -15,6 +15,12 @@ class TinyVector; template <size_t M, size_t N, typename T> class TinyMatrix; +template <typename DataType> +class SmallMatrix; + +template <typename DataType, typename IndexType> +class CRSMatrix; + template <typename DataType, ItemType item_type, typename ConnectivityPtr> class ItemValue; template <typename DataType, ItemType item_type, typename ConnectivityPtr> @@ -132,6 +138,34 @@ inline constexpr bool is_tiny_matrix_v<const T> = is_tiny_matrix_v<std::remove_c template <typename T> inline constexpr bool is_tiny_matrix_v<T&> = is_tiny_matrix_v<std::remove_cvref_t<T>>; +// Traits is_small_matrix + +template <typename T> +inline constexpr bool is_small_matrix_v = false; + +template <typename DataType> +inline constexpr bool is_small_matrix_v<SmallMatrix<DataType>> = true; + +template <typename T> +inline constexpr bool is_small_matrix_v<const T> = is_small_matrix_v<std::remove_cvref_t<T>>; + +template <typename T> +inline constexpr bool is_small_matrix_v<T&> = is_small_matrix_v<std::remove_cvref_t<T>>; + +// Traits is_crs_matrix + +template <typename T> +inline constexpr bool is_crs_matrix_v = false; + +template <typename DataType, typename IndexType> +inline constexpr bool is_crs_matrix_v<CRSMatrix<DataType, IndexType>> = true; + +template <typename T> +inline constexpr bool is_crs_matrix_v<const T> = is_crs_matrix_v<std::remove_cvref_t<T>>; + +template <typename T> +inline constexpr bool is_crs_matrix_v<T&> = is_crs_matrix_v<std::remove_cvref_t<T>>; + // Trais is ItemValue template <typename T> diff --git a/src/utils/PugsUtils.cpp b/src/utils/PugsUtils.cpp index d33396b20af483afa968917f5fde80d1e9465ca4..9890894dcc3291d0f3136f27a88127ef22aecb9b 100644 --- a/src/utils/PugsUtils.cpp +++ b/src/utils/PugsUtils.cpp @@ -64,6 +64,7 @@ pugsBuildInfo() os << "MPI: " << rang::style::bold << BuildInfo::mpiLibrary() << rang::style::reset << '\n'; os << "PETSc: " << rang::style::bold << BuildInfo::petscLibrary() << rang::style::reset << '\n'; os << "SLEPc: " << rang::style::bold << BuildInfo::slepcLibrary() << rang::style::reset << '\n'; + os << "Eigen3: " << rang::style::bold << BuildInfo::eigen3Library() << rang::style::reset << '\n'; os << "HDF5: " << rang::style::bold << BuildInfo::hdf5Library() << rang::style::reset << '\n'; os << "SLURM: " << rang::style::bold << BuildInfo::slurmLibrary() << rang::style::reset << '\n'; os << "-------------------------------------------------------"; diff --git a/src/utils/Stop.cpp b/src/utils/Stop.cpp index 593094cc311a8d1d72ed687d9a90d3af65f1c881..fb2755c7d4a52fa04b0cdce0096793a990994e49 100644 --- a/src/utils/Stop.cpp +++ b/src/utils/Stop.cpp @@ -9,6 +9,41 @@ #ifdef PUGS_HAS_SLURM #include <slurm/slurm.h> + +// LCOV_EXCL_START +class SlurmWrapper +{ + private: + int m_slurm_job_id = -1; + + public: + bool + mustStop() const + { + if (m_slurm_job_id == -1) { + return false; + + } else { + return slurm_get_rem_time(m_slurm_job_id) < 150; + } + } + + SlurmWrapper() + { + char* env = getenv("SLURM_JOB_ID"); + if (env != nullptr) { + slurm_init(nullptr); + m_slurm_job_id = std::atoi(env); + } + } + + ~SlurmWrapper() + { + slurm_fini(); + } +}; +// LCOV_EXCL_STOP + #endif // PUGS_HAS_SLURM bool @@ -31,16 +66,9 @@ stop() #ifdef PUGS_HAS_SLURM // LCOV_EXCL_START - char* env = getenv("SLURM_JOB_ID"); - if (env != nullptr) { - slurm_init(nullptr); - int slurm_job_id = std::atoi(env); - - if (slurm_get_rem_time(slurm_job_id) < 150) { - stop = true; - } - - slurm_fini(); + static SlurmWrapper slurm_wrapper; + if (slurm_wrapper.mustStop()) { + stop = true; } // LCOV_EXCL_STOP #endif // PUGS_HAS_SLURM diff --git a/src/utils/checkpointing/CMakeLists.txt b/src/utils/checkpointing/CMakeLists.txt index b051975e8538df40a2b9b8a8d7f0486874cda938..06d34a349ab63a6e0de206908e50a82d418a857a 100644 --- a/src/utils/checkpointing/CMakeLists.txt +++ b/src/utils/checkpointing/CMakeLists.txt @@ -66,4 +66,8 @@ add_dependencies(PugsCheckpointing target_link_libraries( PugsCheckpointing ${HIGHFIVE_TARGET} +# ${HDF5_TARGET} ) + +#target_include_directories(PugsCheckpointing PUBLIC ${HDF5_INCLUDE_DIRS}) +#target_include_directories(PugsAlgebra PUBLIC ${SLEPC_INCLUDE_DIRS}) diff --git a/src/utils/checkpointing/Checkpoint.cpp b/src/utils/checkpointing/Checkpoint.cpp index 5976cb2fb5b3c5024bc56a1b77d384abd698e97c..ebef0dddd218887a7c48187909757eeb719a14a6 100644 --- a/src/utils/checkpointing/Checkpoint.cpp +++ b/src/utils/checkpointing/Checkpoint.cpp @@ -4,7 +4,6 @@ #ifdef PUGS_HAS_HDF5 -#include <algebra/LinearSolverOptions.hpp> #include <dev/ParallelChecker.hpp> #include <language/ast/ASTExecutionStack.hpp> #include <language/utils/ASTCheckpointsInfo.hpp> @@ -21,12 +20,13 @@ #include <utils/HighFivePugsUtils.hpp> #include <utils/RandomEngine.hpp> #include <utils/checkpointing/DualMeshTypeHFType.hpp> +#include <utils/checkpointing/EigenvalueSolverOptionsHFType.hpp> #include <utils/checkpointing/LinearSolverOptionsHFType.hpp> #include <utils/checkpointing/ParallelCheckerHFType.hpp> +#include <utils/checkpointing/PartitionerOptionsHFType.hpp> #include <utils/checkpointing/ResumingManager.hpp> #include <iostream> -#include <map> void checkpoint() @@ -122,6 +122,22 @@ checkpoint() linear_solver_options_default_group.createAttribute("method", default_options.method()); linear_solver_options_default_group.createAttribute("precond", default_options.precond()); } + { + HighFive::Group eigenvalue_solver_options_default_group = + checkpoint.createGroup("singleton/eigenvalue_solver_options_default"); + + const EigenvalueSolverOptions& default_options = EigenvalueSolverOptions::default_options; + + eigenvalue_solver_options_default_group.createAttribute("library", default_options.library()); + } + { + HighFive::Group partitioner_options_default_group = + checkpoint.createGroup("singleton/partitioner_options_default"); + + const PartitionerOptions& default_options = PartitionerOptions::default_options; + + partitioner_options_default_group.createAttribute("library", default_options.library()); + } { const auto& primal_to_dual_connectivity_info_map = DualConnectivityManager::instance().primalToDualConnectivityInfoMap(); diff --git a/src/utils/checkpointing/EigenvalueSolverOptionsHFType.hpp b/src/utils/checkpointing/EigenvalueSolverOptionsHFType.hpp new file mode 100644 index 0000000000000000000000000000000000000000..32995c1f5475b04534ac42f344d9e5e9ccef1fa2 --- /dev/null +++ b/src/utils/checkpointing/EigenvalueSolverOptionsHFType.hpp @@ -0,0 +1,15 @@ +#ifndef EIGENVALUE_SOLVER_OPTIONS_HF_TYPE_HPP +#define EIGENVALUE_SOLVER_OPTIONS_HF_TYPE_HPP + +#include <algebra/EigenvalueSolverOptions.hpp> +#include <utils/HighFivePugsUtils.hpp> +#include <utils/PugsMacros.hpp> + +HighFive::EnumType<ESLibrary> PUGS_INLINE +create_enum_ESOptions_library_type() +{ + return {{"eigen3", ESLibrary::eigen3}, {"slepsc", ESLibrary::slepsc}}; +} +HIGHFIVE_REGISTER_TYPE(ESLibrary, create_enum_ESOptions_library_type) + +#endif // EIGENVALUE_SOLVER_OPTIONS_HF_TYPE_HPP diff --git a/src/utils/checkpointing/IBoundaryConditionDescriptorHFType.hpp b/src/utils/checkpointing/IBoundaryConditionDescriptorHFType.hpp index 210b521164075f275829865397ea1771a460c207..4a6e5c92cded1a614d43a17ace96f211a3f48e2d 100644 --- a/src/utils/checkpointing/IBoundaryConditionDescriptorHFType.hpp +++ b/src/utils/checkpointing/IBoundaryConditionDescriptorHFType.hpp @@ -10,6 +10,7 @@ create_enum_i_boundary_condition_descriptor_type() { return {{"axis", IBoundaryConditionDescriptor::Type::axis}, {"dirichlet", IBoundaryConditionDescriptor::Type::dirichlet}, + {"dirichlet_vector", IBoundaryConditionDescriptor::Type::dirichlet_vector}, {"external", IBoundaryConditionDescriptor::Type::external}, {"fixed", IBoundaryConditionDescriptor::Type::fixed}, {"fourier", IBoundaryConditionDescriptor::Type::fourier}, diff --git a/src/utils/checkpointing/LinearSolverOptionsHFType.hpp b/src/utils/checkpointing/LinearSolverOptionsHFType.hpp index 94f88fc60088c297479af36975d533d1c94bdd71..1c94d74a948a27f9bc8fc54915ef819171379152 100644 --- a/src/utils/checkpointing/LinearSolverOptionsHFType.hpp +++ b/src/utils/checkpointing/LinearSolverOptionsHFType.hpp @@ -8,7 +8,7 @@ HighFive::EnumType<LSLibrary> PUGS_INLINE create_enum_LSOptions_library_type() { - return {{"builtin", LSLibrary::builtin}, {"petsc", LSLibrary::petsc}}; + return {{"builtin", LSLibrary::builtin}, {"eigen3", LSLibrary::eigen3}, {"petsc", LSLibrary::petsc}}; } HIGHFIVE_REGISTER_TYPE(LSLibrary, create_enum_LSOptions_library_type) diff --git a/src/utils/checkpointing/PartitionerOptionsHFType.hpp b/src/utils/checkpointing/PartitionerOptionsHFType.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e022bb66119943081d22573ac657b46736613037 --- /dev/null +++ b/src/utils/checkpointing/PartitionerOptionsHFType.hpp @@ -0,0 +1,15 @@ +#ifndef PARTITIONER_OPTIONS_HF_TYPE_HPP +#define PARTITIONER_OPTIONS_HF_TYPE_HPP + +#include <utils/HighFivePugsUtils.hpp> +#include <utils/PartitionerOptions.hpp> +#include <utils/PugsMacros.hpp> + +HighFive::EnumType<PartitionerLibrary> PUGS_INLINE +create_enum_PTOptions_library_type() +{ + return {{"ParMETIS", PartitionerLibrary::parmetis}, {"PTScotch", PartitionerLibrary::ptscotch}}; +} +HIGHFIVE_REGISTER_TYPE(PartitionerLibrary, create_enum_PTOptions_library_type) + +#endif // PARTITIONER_OPTIONS_HF_TYPE_HPP diff --git a/src/utils/checkpointing/ReadIBoundaryConditionDescriptor.cpp b/src/utils/checkpointing/ReadIBoundaryConditionDescriptor.cpp index 55b43c9f04bf1b3b585df208bd7a3ea86a3d2850..1f405b4d8a72a8cd15e7799732a16fc4a71ac12b 100644 --- a/src/utils/checkpointing/ReadIBoundaryConditionDescriptor.cpp +++ b/src/utils/checkpointing/ReadIBoundaryConditionDescriptor.cpp @@ -5,6 +5,7 @@ #include <language/utils/EmbeddedData.hpp> #include <scheme/AxisBoundaryConditionDescriptor.hpp> #include <scheme/DirichletBoundaryConditionDescriptor.hpp> +#include <scheme/DirichletVectorBoundaryConditionDescriptor.hpp> #include <scheme/ExternalBoundaryConditionDescriptor.hpp> #include <scheme/FixedBoundaryConditionDescriptor.hpp> #include <scheme/FourierBoundaryConditionDescriptor.hpp> @@ -48,6 +49,21 @@ readIBoundaryConditionDescriptor(const HighFive::Group& iboundaryconditiondecrip std::make_shared<const DirichletBoundaryConditionDescriptor>(name, i_boundary_descriptor, *ResumingData::instance().functionSymbolId(rhs_id)); break; + } + case IBoundaryConditionDescriptor::Type::dirichlet_vector: { + const std::string name = iboundaryconditiondecriptor_group.getAttribute("name").read<std::string>(); + + const std::vector function_id_list = + iboundaryconditiondecriptor_group.getAttribute("function_id_list").read<std::vector<size_t>>(); + + std::vector<FunctionSymbolId> function_symbol_id_list; + for (auto function_id : function_id_list) { + function_symbol_id_list.push_back(*ResumingData::instance().functionSymbolId(function_id)); + } + + bc_descriptor = std::make_shared<const DirichletVectorBoundaryConditionDescriptor>(name, i_boundary_descriptor, + function_symbol_id_list); + break; } // LCOV_EXCL_START case IBoundaryConditionDescriptor::Type::external: { diff --git a/src/utils/checkpointing/Resume.cpp b/src/utils/checkpointing/Resume.cpp index 9d58e1d9dec4050f218d3efad71e5194ccaa5111..f342a2466caf36567840a8fc21686a28c26b4d8f 100644 --- a/src/utils/checkpointing/Resume.cpp +++ b/src/utils/checkpointing/Resume.cpp @@ -4,6 +4,7 @@ #ifdef PUGS_HAS_HDF5 +#include <algebra/EigenvalueSolverOptions.hpp> #include <algebra/LinearSolverOptions.hpp> #include <language/ast/ASTExecutionStack.hpp> #include <language/utils/ASTCheckpointsInfo.hpp> @@ -14,8 +15,10 @@ #include <utils/ExecutionStatManager.hpp> #include <utils/HighFivePugsUtils.hpp> #include <utils/RandomEngine.hpp> +#include <utils/checkpointing/EigenvalueSolverOptionsHFType.hpp> #include <utils/checkpointing/LinearSolverOptionsHFType.hpp> #include <utils/checkpointing/ParallelCheckerHFType.hpp> +#include <utils/checkpointing/PartitionerOptionsHFType.hpp> #include <utils/checkpointing/ResumingData.hpp> #include <utils/checkpointing/ResumingManager.hpp> @@ -81,6 +84,21 @@ resume() default_options.method() = linear_solver_options_default_group.getAttribute("method").read<LSMethod>(); default_options.precond() = linear_solver_options_default_group.getAttribute("precond").read<LSPrecond>(); } + { + HighFive::Group eigenvalue_solver_options_default_group = + checkpoint.getGroup("singleton/eigenvalue_solver_options_default"); + + EigenvalueSolverOptions& default_options = EigenvalueSolverOptions::default_options; + + default_options.library() = eigenvalue_solver_options_default_group.getAttribute("library").read<ESLibrary>(); + } + { + HighFive::Group partitioner_options_default_group = checkpoint.getGroup("singleton/partitioner_options_default"); + + PartitionerOptions& default_options = PartitionerOptions::default_options; + + default_options.library() = partitioner_options_default_group.getAttribute("library").read<PartitionerLibrary>(); + } checkpointing::ResumingData::instance().readData(checkpoint, p_symbol_table); diff --git a/src/utils/checkpointing/WriteIBoundaryConditionDescriptor.cpp b/src/utils/checkpointing/WriteIBoundaryConditionDescriptor.cpp index 66a8670583ae63442313da42c847bbeee758a2d5..8a3c5dd062aca586bd800a19f9b0e27f4fc30e93 100644 --- a/src/utils/checkpointing/WriteIBoundaryConditionDescriptor.cpp +++ b/src/utils/checkpointing/WriteIBoundaryConditionDescriptor.cpp @@ -5,6 +5,7 @@ #include <language/utils/DataHandler.hpp> #include <scheme/AxisBoundaryConditionDescriptor.hpp> #include <scheme/DirichletBoundaryConditionDescriptor.hpp> +#include <scheme/DirichletVectorBoundaryConditionDescriptor.hpp> #include <scheme/FixedBoundaryConditionDescriptor.hpp> #include <scheme/FourierBoundaryConditionDescriptor.hpp> #include <scheme/FreeBoundaryConditionDescriptor.hpp> @@ -47,6 +48,18 @@ writeIBoundaryConditionDescriptor(HighFive::Group& variable_group, variable_group.createAttribute("name", dirichlet_bc_descriptor.name()); variable_group.createAttribute("rhs_function_id", dirichlet_bc_descriptor.rhsSymbolId().id()); break; + } + case IBoundaryConditionDescriptor::Type::dirichlet_vector: { + const DirichletVectorBoundaryConditionDescriptor& dirichlet_vector_bc_descriptor = + dynamic_cast<const DirichletVectorBoundaryConditionDescriptor&>(iboundary_condition_descriptor); + variable_group.createAttribute("name", dirichlet_vector_bc_descriptor.name()); + writeIBoundaryDescriptor(boundary_group, dirichlet_vector_bc_descriptor.boundaryDescriptor()); + std::vector<size_t> function_id_list; + for (auto&& function_symbol_id : dirichlet_vector_bc_descriptor.rhsSymbolIdList()) { + function_id_list.push_back(function_symbol_id.id()); + } + variable_group.createAttribute("function_id_list", function_id_list); + break; } // LCOV_EXCL_START case IBoundaryConditionDescriptor::Type::external: { diff --git a/src/utils/pugs_config.hpp.in b/src/utils/pugs_config.hpp.in index 7402a526d5f34505dbda8524226c971e4701d4b6..9560f7101d75033749dd89801cd560123cfbfb77 100644 --- a/src/utils/pugs_config.hpp.in +++ b/src/utils/pugs_config.hpp.in @@ -1,11 +1,14 @@ #ifndef PUGS_CONFIG_HPP #define PUGS_CONFIG_HPP +#cmakedefine PUGS_HAS_EIGEN3 #cmakedefine PUGS_HAS_FENV_H +#cmakedefine PUGS_HAS_HDF5 #cmakedefine PUGS_HAS_MPI +#cmakedefine PUGS_HAS_PARMETIS #cmakedefine PUGS_HAS_PETSC +#cmakedefine PUGS_HAS_PTSCOTCH #cmakedefine PUGS_HAS_SLEPC -#cmakedefine PUGS_HAS_HDF5 #cmakedefine PUGS_HAS_SLURM #cmakedefine SYSTEM_IS_LINUX diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a276da0bc0eb0e3ce20cbd5b99161ebeedae1bbc..e4796b255e2197b682851febda673a8d55861907 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,6 +4,20 @@ include_directories(${PUGS_SOURCE_DIR}/src) include_directories(${PUGS_BINARY_DIR}/src) include_directories(${PUGS_SOURCE_DIR}/tests) +install( + DIRECTORY "${PUGS_SOURCE_DIR}/packages/Catch2/src/catch2" + DESTINATION "include" + FILES_MATCHING + PATTERN "*.hpp" +) + +install( + DIRECTORY "${PUGS_BINARY_DIR}/generated-includes/catch2" + DESTINATION "include" + FILES_MATCHING + PATTERN "*.hpp" +) + set(checkpointing_sequential_TESTS # this one should enventually integrate parallel tests test_checkpointing_Checkpoint_sequential.cpp @@ -90,6 +104,7 @@ add_executable (unit_tests test_DualMeshManager.cpp test_DualMeshType.cpp test_EdgeIntegrator.cpp + test_Eigen3Utils.cpp test_EigenvalueSolver.cpp test_EmbeddedData.cpp test_EmbeddedDiscreteFunctionUtils.cpp @@ -262,10 +277,11 @@ add_executable (mpi_unit_tests test_OFStream.cpp test_ParallelChecker_read.cpp test_Partitioner.cpp - test_PolynomialReconstruction.cpp + test_PolynomialReconstruction_degree_1.cpp test_PolynomialReconstructionDescriptor.cpp test_RandomEngine.cpp - test_StencilBuilder.cpp + test_StencilBuilder_cell2cell.cpp + test_StencilBuilder_node2cell.cpp test_SubItemArrayPerItemVariant.cpp test_SubItemValuePerItem.cpp test_SubItemValuePerItemVariant.cpp diff --git a/tests/NbGhostLayersTester.hpp b/tests/NbGhostLayersTester.hpp new file mode 100644 index 0000000000000000000000000000000000000000..0468fe18305367834c9cbd0959ba5b3f443fe870 --- /dev/null +++ b/tests/NbGhostLayersTester.hpp @@ -0,0 +1,27 @@ +#ifndef NB_GHOST_LAYERS_TESTER_HPP +#define NB_GHOST_LAYERS_TESTER_HPP + +#include <cstddef> +#include <utils/GlobalVariableManager.hpp> + +class NbGhostLayersTester +{ + private: + const size_t m_original_number_of_ghost_layers; + + public: + PUGS_INLINE + NbGhostLayersTester(const size_t number_of_ghost_layers) + : m_original_number_of_ghost_layers{GlobalVariableManager::instance().getNumberOfGhostLayers()} + { + GlobalVariableManager::instance().m_number_of_ghost_layers = number_of_ghost_layers; + } + + PUGS_INLINE + ~NbGhostLayersTester() + { + GlobalVariableManager::instance().m_number_of_ghost_layers = m_original_number_of_ghost_layers; + } +}; + +#endif // NB_GHOST_LAYERS_TESTER_HPP diff --git a/tests/test_ConnectivityDispatcher.cpp b/tests/test_ConnectivityDispatcher.cpp index abe07cc21e21a1b34ae3f20dfe13ff7f52ac83b9..09ec9982e10e08d84c808f157fe4e11add3d756a 100644 --- a/tests/test_ConnectivityDispatcher.cpp +++ b/tests/test_ConnectivityDispatcher.cpp @@ -9,29 +9,12 @@ #include <utils/Messenger.hpp> #include <MeshDataBaseForTests.hpp> +#include <NbGhostLayersTester.hpp> #include <filesystem> // clazy:excludeall=non-pod-global-static -class NbGhostLayersTester -{ - private: - const size_t m_original_number_of_ghost_layers; - - public: - NbGhostLayersTester(const size_t number_of_ghost_layers) - : m_original_number_of_ghost_layers{GlobalVariableManager::instance().getNumberOfGhostLayers()} - { - GlobalVariableManager::instance().m_number_of_ghost_layers = number_of_ghost_layers; - } - - ~NbGhostLayersTester() - { - GlobalVariableManager::instance().m_number_of_ghost_layers = m_original_number_of_ghost_layers; - } -}; - TEST_CASE("ConnectivityDispatcher", "[mesh]") { auto check_number_of_ghost_layers = [](const auto& connectivity, const size_t number_of_layers) { diff --git a/tests/test_Eigen3Utils.cpp b/tests/test_Eigen3Utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0c51aad3c03ee90695f837c4a9cbff0566d566f3 --- /dev/null +++ b/tests/test_Eigen3Utils.cpp @@ -0,0 +1,107 @@ +#include <utils/pugs_config.hpp> + +#ifdef PUGS_HAS_EIGEN3 + +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <algebra/Eigen3Utils.hpp> + +#include <algebra/CRSMatrixDescriptor.hpp> + +#include <eigen3/Eigen/Core> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("Eigen3Utils", "[algebra]") +{ + SECTION("Eigen3DenseMatrixEmbedder") + { + SECTION("from TinyMatrix") + { + TinyMatrix<3> A{1, 2, 3, 4, 5, 6, 7, 8, 9}; + Eigen3DenseMatrixEmbedder eigen3_A{A}; + + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(eigen3_A.matrix().coeff(i, j) == 1 + i * 3 + j); + } + } + } + + SECTION("from SmallMatrix") + { + SmallMatrix<double> A(3, 3); + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + A(i, j) = 1 + i * 3 + j; + } + } + + Eigen3DenseMatrixEmbedder eigen3_A{A}; + + REQUIRE(eigen3_A.numberOfRows() == 3); + REQUIRE(eigen3_A.numberOfColumns() == 3); + + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(eigen3_A.matrix().coeff(i, j) == 1 + i * 3 + j); + } + } + } + + SECTION("from SmallMatrix [non-square]") + { + SmallMatrix<double> A(4, 3); + for (size_t i = 0; i < 4; ++i) { + for (size_t j = 0; j < 3; ++j) { + A(i, j) = 1 + i * 3 + j; + } + } + + Eigen3DenseMatrixEmbedder eigen3_A{A}; + + REQUIRE(eigen3_A.numberOfRows() == 4); + REQUIRE(eigen3_A.numberOfColumns() == 3); + + for (size_t i = 0; i < 4; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(eigen3_A.matrix().coeff(i, j) == 1 + i * 3 + j); + } + } + } + } + + SECTION("from CRSMatrix") + { + Array<int> non_zeros(4); + non_zeros[0] = 1; + non_zeros[1] = 2; + non_zeros[2] = 3; + non_zeros[3] = 2; + + CRSMatrixDescriptor<double, int> A(4, 3, non_zeros); + + A(0, 0) = 1; + A(1, 0) = 2; + A(1, 2) = 3; + A(2, 0) = 4; + A(2, 1) = 5; + A(2, 2) = 6; + A(3, 1) = 7; + A(3, 2) = 8; + + Eigen3SparseMatrixEmbedder eigen3_A{A.getCRSMatrix()}; + + REQUIRE(eigen3_A.matrix().coeff(0, 0) == 1); + REQUIRE(eigen3_A.matrix().coeff(1, 0) == 2); + REQUIRE(eigen3_A.matrix().coeff(1, 2) == 3); + REQUIRE(eigen3_A.matrix().coeff(2, 0) == 4); + REQUIRE(eigen3_A.matrix().coeff(2, 1) == 5); + REQUIRE(eigen3_A.matrix().coeff(2, 2) == 6); + REQUIRE(eigen3_A.matrix().coeff(3, 1) == 7); + REQUIRE(eigen3_A.matrix().coeff(3, 2) == 8); + } +} + +#endif // PUGS_HAS_EIGEN3 diff --git a/tests/test_EigenvalueSolver.cpp b/tests/test_EigenvalueSolver.cpp index fb7bf07907bf6c9e1db9333c9713365350918b06..325f3e39b0a6f34e028f99372956f67a226e39cc 100644 --- a/tests/test_EigenvalueSolver.cpp +++ b/tests/test_EigenvalueSolver.cpp @@ -12,109 +12,625 @@ TEST_CASE("EigenvalueSolver", "[algebra]") { - SECTION("Sparse Matrices") + SECTION("symmetric system") { - SECTION("symmetric system") + SECTION("SLEPc") { - Array<int> non_zeros(3); - non_zeros.fill(3); - CRSMatrixDescriptor<double> S{3, 3, non_zeros}; + EigenvalueSolverOptions options; + options.library() = ESLibrary::slepsc; - S(0, 0) = 3; - S(0, 1) = 2; - S(0, 2) = 4; + SECTION("Sparse Matrices") + { + Array<int> non_zeros(3); + non_zeros.fill(3); + CRSMatrixDescriptor<double> S{3, 3, non_zeros}; - S(1, 0) = 2; - S(1, 1) = 0; - S(1, 2) = 2; + S(0, 0) = 3; + S(0, 1) = 2; + S(0, 2) = 4; - S(2, 0) = 4; - S(2, 1) = 2; - S(2, 2) = 3; + S(1, 0) = 2; + S(1, 1) = 0; + S(1, 2) = 2; - CRSMatrix A{S.getCRSMatrix()}; + S(2, 0) = 4; + S(2, 1) = 2; + S(2, 2) = 3; - SECTION("eigenvalues") - { - SmallArray<double> eigenvalues; + CRSMatrix A{S.getCRSMatrix()}; + + SECTION("eigenvalues") + { + SmallArray<double> eigenvalues; + +#ifdef PUGS_HAS_SLEPC + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues); + REQUIRE(eigenvalues[0] == Catch::Approx(-1)); + REQUIRE(eigenvalues[1] == Catch::Approx(-1)); + REQUIRE(eigenvalues[2] == Catch::Approx(8)); +#else // PUGS_HAS_SLEPC + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues), + "error: SLEPSc is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_SLEPC + } + + SECTION("eigenvalues and eigenvectors") + { + SmallArray<double> eigenvalue_list; + std::vector<SmallVector<double>> eigenvector_list; #ifdef PUGS_HAS_SLEPC - EigenvalueSolver{}.computeForSymmetricMatrix(A, eigenvalues); - REQUIRE(eigenvalues[0] == Catch::Approx(-1)); - REQUIRE(eigenvalues[1] == Catch::Approx(-1)); - REQUIRE(eigenvalues[2] == Catch::Approx(8)); + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list); + + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + + SmallMatrix<double> P{3}; + SmallMatrix<double> PT{3}; + SmallMatrix<double> D{3}; + D = zero; + + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + P(i, j) = eigenvector_list[j][i]; + PT(i, j) = eigenvector_list[i][j]; + } + D(i, i) = eigenvalue_list[i]; + } + + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - S(i, j) == Catch::Approx(0).margin(1E-13)); + } + } #else // PUGS_HAS_SLEPC - REQUIRE_THROWS_WITH(EigenvalueSolver{}.computeForSymmetricMatrix(A, eigenvalues), - "not implemented yet: SLEPc is required to solve eigenvalue problems"); + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list), + "error: SLEPSc is not linked to pugs. Cannot use it!"); #endif // PUGS_HAS_SLEPC + } + + SECTION("eigenvalues and transition matrix") + { + SmallArray<double> eigenvalue_list; + SmallMatrix<double> P{}; + +#ifdef PUGS_HAS_SLEPC + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P); + + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + + SmallMatrix<double> D{3}; + D = zero; + for (size_t i = 0; i < 3; ++i) { + D(i, i) = eigenvalue_list[i]; + } + SmallMatrix PT = transpose(P); + + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - S(i, j) == Catch::Approx(0).margin(1E-13)); + } + } +#else // PUGS_HAS_SLEPC + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P), + "error: SLEPSc is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_SLEPC + } } - SECTION("eigenvalues and eigenvectors") + SECTION("SmallMatrix") { - SmallArray<double> eigenvalue_list; - std::vector<SmallVector<double>> eigenvector_list; + SmallMatrix<double> A{3, 3}; + A.fill(0); + A(0, 0) = 3; + A(0, 1) = 2; + A(0, 2) = 4; + + A(1, 0) = 2; + A(1, 1) = 0; + A(1, 2) = 2; + + A(2, 0) = 4; + A(2, 1) = 2; + A(2, 2) = 3; + + SECTION("eigenvalues") + { + SmallArray<double> eigenvalues; #ifdef PUGS_HAS_SLEPC - EigenvalueSolver{}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list); + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues); + REQUIRE(eigenvalues[0] == Catch::Approx(-1)); + REQUIRE(eigenvalues[1] == Catch::Approx(-1)); + REQUIRE(eigenvalues[2] == Catch::Approx(8)); +#else // PUGS_HAS_SLEPC + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues), + "error: SLEPSc is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_SLEPC + } - REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); - REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); - REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + SECTION("eigenvalues and eigenvectors") + { + SmallArray<double> eigenvalue_list; + std::vector<SmallVector<double>> eigenvector_list; + +#ifdef PUGS_HAS_SLEPC + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list); - SmallMatrix<double> P{3}; - SmallMatrix<double> PT{3}; - SmallMatrix<double> D{3}; - D = zero; + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); - for (size_t i = 0; i < 3; ++i) { - for (size_t j = 0; j < 3; ++j) { - P(i, j) = eigenvector_list[j][i]; - PT(i, j) = eigenvector_list[i][j]; + SmallMatrix<double> P{3}; + SmallMatrix<double> PT{3}; + SmallMatrix<double> D{3}; + D = zero; + + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + P(i, j) = eigenvector_list[j][i]; + PT(i, j) = eigenvector_list[i][j]; + } + D(i, i) = eigenvalue_list[i]; } - D(i, i) = eigenvalue_list[i]; - } - SmallMatrix PDPT = P * D * PT; - for (size_t i = 0; i < 3; ++i) { - for (size_t j = 0; j < 3; ++j) { - REQUIRE(PDPT(i, j) - S(i, j) == Catch::Approx(0).margin(1E-13)); + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - A(i, j) == Catch::Approx(0).margin(1E-13)); + } } +#else // PUGS_HAS_SLEPC + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list), + "error: SLEPSc is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_SLEPC } + + SECTION("eigenvalues and transition matrix") + { + SmallArray<double> eigenvalue_list; + SmallMatrix<double> P{}; + +#ifdef PUGS_HAS_SLEPC + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P); + + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + + SmallMatrix<double> D{3}; + D = zero; + for (size_t i = 0; i < 3; ++i) { + D(i, i) = eigenvalue_list[i]; + } + SmallMatrix PT = transpose(P); + + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - A(i, j) == Catch::Approx(0).margin(1E-13)); + } + } #else // PUGS_HAS_SLEPC - REQUIRE_THROWS_WITH(EigenvalueSolver{}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list), - "not implemented yet: SLEPc is required to solve eigenvalue problems"); + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P), + "error: SLEPSc is not linked to pugs. Cannot use it!"); #endif // PUGS_HAS_SLEPC + } } - SECTION("eigenvalues and passage matrix") + SECTION("TinyMatrix") { - SmallArray<double> eigenvalue_list; - SmallMatrix<double> P{}; + TinyMatrix<3> A = zero; -#ifdef PUGS_HAS_SLEPC - EigenvalueSolver{}.computeForSymmetricMatrix(A, eigenvalue_list, P); + A(0, 0) = 3; + A(0, 1) = 2; + A(0, 2) = 4; - REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); - REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); - REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + A(1, 0) = 2; + A(1, 1) = 0; + A(1, 2) = 2; - SmallMatrix<double> D{3}; - D = zero; - for (size_t i = 0; i < 3; ++i) { - D(i, i) = eigenvalue_list[i]; + A(2, 0) = 4; + A(2, 1) = 2; + A(2, 2) = 3; + + SECTION("eigenvalues") + { + SmallArray<double> eigenvalues; + +#ifdef PUGS_HAS_SLEPC + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues); + REQUIRE(eigenvalues[0] == Catch::Approx(-1)); + REQUIRE(eigenvalues[1] == Catch::Approx(-1)); + REQUIRE(eigenvalues[2] == Catch::Approx(8)); +#else // PUGS_HAS_SLEPC + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues), + "error: SLEPSc is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_SLEPC } - SmallMatrix PT = transpose(P); - SmallMatrix PDPT = P * D * PT; - for (size_t i = 0; i < 3; ++i) { - for (size_t j = 0; j < 3; ++j) { - REQUIRE(PDPT(i, j) - S(i, j) == Catch::Approx(0).margin(1E-13)); + SECTION("eigenvalues and eigenvectors") + { + SmallArray<double> eigenvalue_list; + std::vector<SmallVector<double>> eigenvector_list; + +#ifdef PUGS_HAS_SLEPC + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list); + + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + + SmallMatrix<double> P{3}; + SmallMatrix<double> PT{3}; + SmallMatrix<double> D{3}; + D = zero; + + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + P(i, j) = eigenvector_list[j][i]; + PT(i, j) = eigenvector_list[i][j]; + } + D(i, i) = eigenvalue_list[i]; + } + + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - A(i, j) == Catch::Approx(0).margin(1E-13)); + } } +#else // PUGS_HAS_SLEPC + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list), + "error: SLEPSc is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_SLEPC } + + SECTION("eigenvalues and transition matrix") + { + SmallArray<double> eigenvalue_list; + SmallMatrix<double> P{}; + +#ifdef PUGS_HAS_SLEPC + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P); + + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + + SmallMatrix<double> D{3}; + D = zero; + for (size_t i = 0; i < 3; ++i) { + D(i, i) = eigenvalue_list[i]; + } + SmallMatrix PT = transpose(P); + + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - A(i, j) == Catch::Approx(0).margin(1E-13)); + } + } #else // PUGS_HAS_SLEPC - REQUIRE_THROWS_WITH(EigenvalueSolver{}.computeForSymmetricMatrix(A, eigenvalue_list, P), - "not implemented yet: SLEPc is required to solve eigenvalue problems"); + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P), + "error: SLEPSc is not linked to pugs. Cannot use it!"); #endif // PUGS_HAS_SLEPC + } + } + } + + SECTION("Eigen3") + { + EigenvalueSolverOptions options; + options.library() = ESLibrary::eigen3; + + SECTION("Sparse Matrices") + { + Array<int> non_zeros(3); + non_zeros.fill(3); + CRSMatrixDescriptor<double> S{3, 3, non_zeros}; + + S(0, 0) = 3; + S(0, 1) = 2; + S(0, 2) = 4; + + S(1, 0) = 2; + S(1, 1) = 0; + S(1, 2) = 2; + + S(2, 0) = 4; + S(2, 1) = 2; + S(2, 2) = 3; + + CRSMatrix A{S.getCRSMatrix()}; + + SECTION("eigenvalues") + { + SmallArray<double> eigenvalues; + +#ifdef PUGS_HAS_EIGEN3 + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues); + REQUIRE(eigenvalues[0] == Catch::Approx(-1)); + REQUIRE(eigenvalues[1] == Catch::Approx(-1)); + REQUIRE(eigenvalues[2] == Catch::Approx(8)); +#else // PUGS_HAS_EIGEN3 + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues), + "error: Eigen3 is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_EIGEN3 + } + + SECTION("eigenvalues and eigenvectors") + { + SmallArray<double> eigenvalue_list; + std::vector<SmallVector<double>> eigenvector_list; + +#ifdef PUGS_HAS_EIGEN3 + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list); + + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + + SmallMatrix<double> P{3}; + SmallMatrix<double> PT{3}; + SmallMatrix<double> D{3}; + D = zero; + + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + P(i, j) = eigenvector_list[j][i]; + PT(i, j) = eigenvector_list[i][j]; + } + D(i, i) = eigenvalue_list[i]; + } + + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - S(i, j) == Catch::Approx(0).margin(1E-13)); + } + } +#else // PUGS_HAS_EIGEN3 + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list), + "error: Eigen3 is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_EIGEN3 + } + + SECTION("eigenvalues and transition matrix") + { + SmallArray<double> eigenvalue_list; + SmallMatrix<double> P{}; + +#ifdef PUGS_HAS_EIGEN3 + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P); + + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + + SmallMatrix<double> D{3}; + D = zero; + for (size_t i = 0; i < 3; ++i) { + D(i, i) = eigenvalue_list[i]; + } + SmallMatrix PT = transpose(P); + + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - S(i, j) == Catch::Approx(0).margin(1E-13)); + } + } +#else // PUGS_HAS_EIGEN3 + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P), + "error: Eigen3 is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_EIGEN3 + } + } + + SECTION("SmallMatrix") + { + SmallMatrix<double> A{3, 3}; + A.fill(0); + A(0, 0) = 3; + A(0, 1) = 2; + A(0, 2) = 4; + + A(1, 0) = 2; + A(1, 1) = 0; + A(1, 2) = 2; + + A(2, 0) = 4; + A(2, 1) = 2; + A(2, 2) = 3; + + SECTION("eigenvalues") + { + SmallArray<double> eigenvalues; + +#ifdef PUGS_HAS_EIGEN3 + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues); + REQUIRE(eigenvalues[0] == Catch::Approx(-1)); + REQUIRE(eigenvalues[1] == Catch::Approx(-1)); + REQUIRE(eigenvalues[2] == Catch::Approx(8)); +#else // PUGS_HAS_EIGEN3 + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues), + "error: Eigen3 is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_EIGEN3 + } + + SECTION("eigenvalues and eigenvectors") + { + SmallArray<double> eigenvalue_list; + std::vector<SmallVector<double>> eigenvector_list; + +#ifdef PUGS_HAS_EIGEN3 + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list); + + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + + SmallMatrix<double> P{3}; + SmallMatrix<double> PT{3}; + SmallMatrix<double> D{3}; + D = zero; + + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + P(i, j) = eigenvector_list[j][i]; + PT(i, j) = eigenvector_list[i][j]; + } + D(i, i) = eigenvalue_list[i]; + } + + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - A(i, j) == Catch::Approx(0).margin(1E-13)); + } + } +#else // PUGS_HAS_EIGEN3 + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list), + "error: Eigen3 is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_EIGEN3 + } + + SECTION("eigenvalues and transition matrix") + { + SmallArray<double> eigenvalue_list; + SmallMatrix<double> P{}; + +#ifdef PUGS_HAS_EIGEN3 + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P); + + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + + SmallMatrix<double> D{3}; + D = zero; + for (size_t i = 0; i < 3; ++i) { + D(i, i) = eigenvalue_list[i]; + } + SmallMatrix PT = transpose(P); + + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - A(i, j) == Catch::Approx(0).margin(1E-13)); + } + } +#else // PUGS_HAS_EIGEN3 + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P), + "error: Eigen3 is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_EIGEN3 + } + } + + SECTION("TinyMatrix") + { + TinyMatrix<3> A = zero; + + A(0, 0) = 3; + A(0, 1) = 2; + A(0, 2) = 4; + + A(1, 0) = 2; + A(1, 1) = 0; + A(1, 2) = 2; + + A(2, 0) = 4; + A(2, 1) = 2; + A(2, 2) = 3; + + SECTION("eigenvalues") + { + SmallArray<double> eigenvalues; + +#ifdef PUGS_HAS_EIGEN3 + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues); + REQUIRE(eigenvalues[0] == Catch::Approx(-1)); + REQUIRE(eigenvalues[1] == Catch::Approx(-1)); + REQUIRE(eigenvalues[2] == Catch::Approx(8)); +#else // PUGS_HAS_EIGEN3 + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues), + "error: Eigen3 is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_EIGEN3 + } + + SECTION("eigenvalues and eigenvectors") + { + SmallArray<double> eigenvalue_list; + std::vector<SmallVector<double>> eigenvector_list; + +#ifdef PUGS_HAS_EIGEN3 + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list); + + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + + SmallMatrix<double> P{3}; + SmallMatrix<double> PT{3}; + SmallMatrix<double> D{3}; + D = zero; + + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + P(i, j) = eigenvector_list[j][i]; + PT(i, j) = eigenvector_list[i][j]; + } + D(i, i) = eigenvalue_list[i]; + } + + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - A(i, j) == Catch::Approx(0).margin(1E-13)); + } + } +#else // PUGS_HAS_EIGEN3 + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list), + "error: Eigen3 is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_EIGEN3 + } + + SECTION("eigenvalues and transition matrix") + { + SmallArray<double> eigenvalue_list; + SmallMatrix<double> P{}; + +#ifdef PUGS_HAS_EIGEN3 + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P); + + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + + SmallMatrix<double> D{3}; + D = zero; + for (size_t i = 0; i < 3; ++i) { + D(i, i) = eigenvalue_list[i]; + } + SmallMatrix PT = transpose(P); + + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - A(i, j) == Catch::Approx(0).margin(1E-13)); + } + } +#else // PUGS_HAS_EIGEN3 + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P), + "error: Eigen3 is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_EIGEN3 + } } } } diff --git a/tests/test_LinearSolver.cpp b/tests/test_LinearSolver.cpp index 11195faab3dad2f94e82dba78b9afc368d0edcd0..76ef6aa2bb75c3ea94fcf392f59ddb74350dc50a 100644 --- a/tests/test_LinearSolver.cpp +++ b/tests/test_LinearSolver.cpp @@ -21,6 +21,12 @@ TEST_CASE("LinearSolver", "[algebra]") #else // PUGS_HAS_PETSC REQUIRE(linear_solver.hasLibrary(LSLibrary::petsc) == false); #endif // PUGS_HAS_PETSC + +#ifdef PUGS_HAS_EIGEN3 + REQUIRE(linear_solver.hasLibrary(LSLibrary::eigen3) == true); +#else // PUGS_HAS_PETSC + REQUIRE(linear_solver.hasLibrary(LSLibrary::eigen3) == false); +#endif // PUGS_HAS_PETSC } SECTION("check linear solver building") @@ -108,7 +114,7 @@ TEST_CASE("LinearSolver", "[algebra]") REQUIRE_NOTHROW(linear_solver.checkOptions(options)); } - SECTION("builtin precond") + SECTION("PETSc precond") { options.library() = LSLibrary::petsc; options.method() = LSMethod::cg; @@ -140,6 +146,66 @@ TEST_CASE("LinearSolver", "[algebra]") REQUIRE_THROWS_WITH(LinearSolver{options}, "error: PETSc is not linked to pugs. Cannot use it!"); } #endif // PUGS_HAS_PETSC + + SECTION("Eigen3") + { + LinearSolverOptions always_valid; + always_valid.library() = LSLibrary::builtin; + always_valid.method() = LSMethod::cg; + always_valid.precond() = LSPrecond::none; + + LinearSolver linear_solver{always_valid}; + + SECTION("Eigen3 methods") + { + options.library() = LSLibrary::eigen3; + options.precond() = LSPrecond::none; + + options.method() = LSMethod::cg; + REQUIRE_NOTHROW(linear_solver.checkOptions(options)); + + options.method() = LSMethod::bicgstab; + REQUIRE_NOTHROW(linear_solver.checkOptions(options)); + + options.method() = LSMethod::lu; + REQUIRE_NOTHROW(linear_solver.checkOptions(options)); + + options.method() = LSMethod::cholesky; + REQUIRE_NOTHROW(linear_solver.checkOptions(options)); + + options.method() = LSMethod::gmres; + REQUIRE_NOTHROW(linear_solver.checkOptions(options)); + } + + SECTION("Eigen3 precond") + { + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::cg; + + options.precond() = LSPrecond::none; + REQUIRE_NOTHROW(linear_solver.checkOptions(options)); + + options.precond() = LSPrecond::diagonal; + REQUIRE_NOTHROW(linear_solver.checkOptions(options)); + + options.precond() = LSPrecond::incomplete_LU; + REQUIRE_NOTHROW(linear_solver.checkOptions(options)); + + options.precond() = LSPrecond::incomplete_cholesky; + REQUIRE_NOTHROW(linear_solver.checkOptions(options)); + } + } + +#ifndef PUGS_HAS_EIGEN3 + SECTION("not linked Eigen3") + { + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::cg; + options.precond() = LSPrecond::none; + + REQUIRE_THROWS_WITH(LinearSolver{options}, "error: Eigen3 is not linked to pugs. Cannot use it!"); + } +#endif // PUGS_HAS_EIGEN3 } SECTION("Sparse Matrices") @@ -205,6 +271,101 @@ TEST_CASE("LinearSolver", "[algebra]") } } + SECTION("Eigen3") + { +#ifdef PUGS_HAS_EIGEN3 + + SECTION("CG") + { + LinearSolverOptions options; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::cg; + options.precond() = LSPrecond::none; + options.verbose() = true; + + SECTION("CG no preconditioner") + { + options.precond() = LSPrecond::none; + + Vector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + Vector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("CG Diagonal") + { + options.precond() = LSPrecond::diagonal; + + Vector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + Vector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("CG ICholesky") + { + options.precond() = LSPrecond::incomplete_cholesky; + + Vector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + Vector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("CG AMG") + { + options.precond() = LSPrecond::amg; + + Vector<double> x{5}; + x = zero; + + REQUIRE_THROWS_WITH(LinearSolver{options}, "error: AMG is not an Eigen3 preconditioner!"); + } + } + + SECTION("Cholesky") + { + LinearSolverOptions options; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::cholesky; + options.precond() = LSPrecond::none; + + Vector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + Vector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + +#else // PUGS_HAS_EIGEN3 + SECTION("Eigen3 not linked") + { + LinearSolverOptions options; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::cg; + options.precond() = LSPrecond::none; + + REQUIRE_THROWS_WITH(LinearSolver{options}, "error: Eigen3 is not linked to pugs. Cannot use it!"); + } +#endif // PUGS_HAS_EIGEN3 + } + SECTION("PETSc") { #ifdef PUGS_HAS_PETSC @@ -535,170 +696,150 @@ TEST_CASE("LinearSolver", "[algebra]") } #endif // PUGS_HAS_PETSC } - } - } - } - - SECTION("Dense Matrices") - { - SECTION("check linear solvers") - { - SECTION("symmetric system") - { - SmallMatrix<double> A{5}; - A = zero; - - A(0, 0) = 2; - A(0, 1) = -1; - - A(1, 0) = -1; - A(1, 1) = 2; - A(1, 2) = -1; - - A(2, 1) = -1; - A(2, 2) = 2; - A(2, 3) = -1; - - A(3, 2) = -1; - A(3, 3) = 2; - A(3, 4) = -1; - - A(4, 3) = -1; - A(4, 4) = 2; - - SmallVector<const double> x_exact = [] { - SmallVector<double> y{5}; - y[0] = 1; - y[1] = 3; - y[2] = 2; - y[3] = 4; - y[4] = 5; - return y; - }(); - - SmallVector<double> b = A * x_exact; - - SECTION("builtin") - { - SECTION("CG no preconditioner") - { - LinearSolverOptions options; - options.library() = LSLibrary::builtin; - options.method() = LSMethod::cg; - options.precond() = LSPrecond::none; - - SmallVector<double> x{5}; - x = zero; - - LinearSolver solver{options}; - - solver.solveLocalSystem(A, x, b); - SmallVector error = x - x_exact; - REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); - } - } - SECTION("PETSc") + SECTION("Eigen3") { -#ifdef PUGS_HAS_PETSC +#ifdef PUGS_HAS_EIGEN3 - SECTION("CG") + SECTION("BICGStab") { LinearSolverOptions options; - options.library() = LSLibrary::petsc; - options.method() = LSMethod::cg; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::bicgstab; options.precond() = LSPrecond::none; options.verbose() = true; - SECTION("CG no preconditioner") + SECTION("BICGStab no preconditioner") { options.precond() = LSPrecond::none; - SmallVector<double> x{5}; + Vector<double> x{5}; x = zero; LinearSolver solver{options}; solver.solveLocalSystem(A, x, b); - SmallVector error = x - x_exact; + Vector error = x - x_exact; REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } - SECTION("CG Diagonal") + SECTION("BICGStab Diagonal") { options.precond() = LSPrecond::diagonal; - SmallVector<double> x{5}; + Vector<double> x{5}; x = zero; LinearSolver solver{options}; solver.solveLocalSystem(A, x, b); - SmallVector error = x - x_exact; + Vector error = x - x_exact; REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } - SECTION("CG ICholesky") + SECTION("BICGStab ILU") { - options.precond() = LSPrecond::incomplete_cholesky; + options.precond() = LSPrecond::incomplete_LU; - SmallVector<double> x{5}; + Vector<double> x{5}; x = zero; LinearSolver solver{options}; solver.solveLocalSystem(A, x, b); - SmallVector error = x - x_exact; + Vector error = x - x_exact; REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } + } - SECTION("CG AMG") + SECTION("GMRES") + { + LinearSolverOptions options; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::gmres; + options.precond() = LSPrecond::none; + + SECTION("GMRES no preconditioner") { - options.precond() = LSPrecond::amg; + options.precond() = LSPrecond::none; - SmallVector<double> x{5}; + Vector<double> x{5}; x = zero; LinearSolver solver{options}; solver.solveLocalSystem(A, x, b); - SmallVector error = x - x_exact; + Vector error = x - x_exact; REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } - } - - SECTION("Cholesky") - { - LinearSolverOptions options; - options.library() = LSLibrary::petsc; - options.method() = LSMethod::cholesky; - options.precond() = LSPrecond::none; - SmallVector<double> x{5}; - x = zero; + SECTION("GMRES Diagonal") + { + options.precond() = LSPrecond::diagonal; + + Vector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + Vector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("GMRES ILU") + { + options.precond() = LSPrecond::incomplete_LU; + + Vector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + Vector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + } + + SECTION("LU") + { + LinearSolverOptions options; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::lu; + options.precond() = LSPrecond::none; + + Vector<double> x{5}; + x = zero; LinearSolver solver{options}; solver.solveLocalSystem(A, x, b); - SmallVector error = x - x_exact; + Vector error = x - x_exact; REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } -#else // PUGS_HAS_PETSC - SECTION("PETSc not linked") +#else // PUGS_HAS_EIGEN3 + SECTION("Eigen3 not linked") { LinearSolverOptions options; - options.library() = LSLibrary::petsc; + options.library() = LSLibrary::eigen3; options.method() = LSMethod::cg; options.precond() = LSPrecond::none; - REQUIRE_THROWS_WITH(LinearSolver{options}, "error: PETSc is not linked to pugs. Cannot use it!"); + REQUIRE_THROWS_WITH(LinearSolver{options}, "error: Eigen3 is not linked to pugs. Cannot use it!"); } -#endif // PUGS_HAS_PETSC +#endif // PUGS_HAS_EIGEN3 } } + } + } - SECTION("none symmetric system") + SECTION("Dense Matrices") + { + SECTION("check linear solvers") + { + SECTION("symmetric system") { SmallMatrix<double> A{5}; A = zero; @@ -706,20 +847,20 @@ TEST_CASE("LinearSolver", "[algebra]") A(0, 0) = 2; A(0, 1) = -1; - A(1, 0) = -0.2; + A(1, 0) = -1; A(1, 1) = 2; A(1, 2) = -1; A(2, 1) = -1; - A(2, 2) = 4; - A(2, 3) = -2; + A(2, 2) = 2; + A(2, 3) = -1; A(3, 2) = -1; A(3, 3) = 2; - A(3, 4) = -0.1; + A(3, 4) = -1; - A(4, 3) = 1; - A(4, 4) = 3; + A(4, 3) = -1; + A(4, 4) = 2; SmallVector<const double> x_exact = [] { SmallVector<double> y{5}; @@ -735,11 +876,11 @@ TEST_CASE("LinearSolver", "[algebra]") SECTION("builtin") { - SECTION("BICGStab no preconditioner") + SECTION("CG no preconditioner") { LinearSolverOptions options; options.library() = LSLibrary::builtin; - options.method() = LSMethod::bicgstab; + options.method() = LSMethod::cg; options.precond() = LSPrecond::none; SmallVector<double> x{5}; @@ -753,19 +894,19 @@ TEST_CASE("LinearSolver", "[algebra]") } } - SECTION("PETSc") + SECTION("Eigen3") { -#ifdef PUGS_HAS_PETSC +#ifdef PUGS_HAS_EIGEN3 - SECTION("BICGStab") + SECTION("CG") { LinearSolverOptions options; - options.library() = LSLibrary::petsc; - options.method() = LSMethod::bicgstab; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::cg; options.precond() = LSPrecond::none; options.verbose() = true; - SECTION("BICGStab no preconditioner") + SECTION("CG no preconditioner") { options.precond() = LSPrecond::none; @@ -779,7 +920,7 @@ TEST_CASE("LinearSolver", "[algebra]") REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } - SECTION("BICGStab Diagonal") + SECTION("CG Diagonal") { options.precond() = LSPrecond::diagonal; @@ -793,45 +934,75 @@ TEST_CASE("LinearSolver", "[algebra]") REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } - SECTION("BICGStab ILU") + SECTION("CG ICholesky") { - options.precond() = LSPrecond::incomplete_LU; + options.precond() = LSPrecond::incomplete_cholesky; SmallVector<double> x{5}; x = zero; LinearSolver solver{options}; - solver.solveLocalSystem(A, x, b); - SmallVector error = x - x_exact; - REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + REQUIRE_THROWS_WITH(solver.solveLocalSystem(A, x, b), + "error: incomplete cholesky is not available for dense matrices in Eigen3"); + } + + SECTION("CG AMG") + { + options.precond() = LSPrecond::amg; + + SmallVector<double> x{5}; + x = zero; + + REQUIRE_THROWS_WITH(LinearSolver{options}, "error: AMG is not an Eigen3 preconditioner!"); } } - SECTION("BICGStab2") + SECTION("Cholesky") { LinearSolverOptions options; - options.library() = LSLibrary::petsc; - options.method() = LSMethod::bicgstab2; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::cholesky; options.precond() = LSPrecond::none; - SECTION("BICGStab2 no preconditioner") - { - options.precond() = LSPrecond::none; + SmallVector<double> x{5}; + x = zero; - SmallVector<double> x{5}; - x = zero; + LinearSolver solver{options}; - LinearSolver solver{options}; + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } - solver.solveLocalSystem(A, x, b); - SmallVector error = x - x_exact; - REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); - } +#else // PUGS_HAS_EIGEN3 + SECTION("Eigen3 not linked") + { + LinearSolverOptions options; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::cg; + options.precond() = LSPrecond::none; - SECTION("BICGStab2 Diagonal") + REQUIRE_THROWS_WITH(LinearSolver{options}, "error: Eigen3 is not linked to pugs. Cannot use it!"); + } +#endif // PUGS_HAS_EIGEN3 + } + + SECTION("PETSc") + { +#ifdef PUGS_HAS_PETSC + + SECTION("CG") + { + LinearSolverOptions options; + options.library() = LSLibrary::petsc; + options.method() = LSMethod::cg; + options.precond() = LSPrecond::none; + options.verbose() = true; + + SECTION("CG no preconditioner") { - options.precond() = LSPrecond::diagonal; + options.precond() = LSPrecond::none; SmallVector<double> x{5}; x = zero; @@ -842,18 +1013,10 @@ TEST_CASE("LinearSolver", "[algebra]") SmallVector error = x - x_exact; REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } - } - SECTION("GMRES") - { - LinearSolverOptions options; - options.library() = LSLibrary::petsc; - options.method() = LSMethod::gmres; - options.precond() = LSPrecond::none; - - SECTION("GMRES no preconditioner") + SECTION("CG Diagonal") { - options.precond() = LSPrecond::none; + options.precond() = LSPrecond::diagonal; SmallVector<double> x{5}; x = zero; @@ -865,9 +1028,9 @@ TEST_CASE("LinearSolver", "[algebra]") REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } - SECTION("GMRES Diagonal") + SECTION("CG ICholesky") { - options.precond() = LSPrecond::diagonal; + options.precond() = LSPrecond::incomplete_cholesky; SmallVector<double> x{5}; x = zero; @@ -879,9 +1042,9 @@ TEST_CASE("LinearSolver", "[algebra]") REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } - SECTION("GMRES ILU") + SECTION("CG AMG") { - options.precond() = LSPrecond::incomplete_LU; + options.precond() = LSPrecond::amg; SmallVector<double> x{5}; x = zero; @@ -894,11 +1057,11 @@ TEST_CASE("LinearSolver", "[algebra]") } } - SECTION("LU") + SECTION("Cholesky") { LinearSolverOptions options; options.library() = LSLibrary::petsc; - options.method() = LSMethod::lu; + options.method() = LSMethod::cholesky; options.precond() = LSPrecond::none; SmallVector<double> x{5}; @@ -924,6 +1087,387 @@ TEST_CASE("LinearSolver", "[algebra]") #endif // PUGS_HAS_PETSC } } + + SECTION("none symmetric system") + { + SECTION("Dense matrix") + { + SmallMatrix<double> A{5}; + A = zero; + + A(0, 0) = 2; + A(0, 1) = -1; + + A(1, 0) = -0.2; + A(1, 1) = 2; + A(1, 2) = -1; + + A(2, 1) = -1; + A(2, 2) = 4; + A(2, 3) = -2; + + A(3, 2) = -1; + A(3, 3) = 2; + A(3, 4) = -0.1; + + A(4, 3) = 1; + A(4, 4) = 3; + + SmallVector<const double> x_exact = [] { + SmallVector<double> y{5}; + y[0] = 1; + y[1] = 3; + y[2] = 2; + y[3] = 4; + y[4] = 5; + return y; + }(); + + SmallVector<double> b = A * x_exact; + + SECTION("builtin") + { + SECTION("BICGStab no preconditioner") + { + LinearSolverOptions options; + options.library() = LSLibrary::builtin; + options.method() = LSMethod::bicgstab; + options.precond() = LSPrecond::none; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + } + + SECTION("Eigen3") + { +#ifdef PUGS_HAS_EIGEN3 + + SECTION("BICGStab") + { + LinearSolverOptions options; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::bicgstab; + options.precond() = LSPrecond::none; + options.verbose() = true; + + SECTION("BICGStab no preconditioner") + { + options.precond() = LSPrecond::none; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("BICGStab Diagonal") + { + options.precond() = LSPrecond::diagonal; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("BICGStab ILU") + { + options.precond() = LSPrecond::incomplete_LU; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + REQUIRE_THROWS_WITH(solver.solveLocalSystem(A, x, b), + "error: incomplete LU is not available for dense matrices in Eigen3"); + } + } + + SECTION("BICGStab2") + { + LinearSolverOptions options; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::bicgstab2; + options.precond() = LSPrecond::none; + + SECTION("BICGStab2 no preconditioner") + { + options.precond() = LSPrecond::none; + + SmallVector<double> x{5}; + x = zero; + + REQUIRE_THROWS_WITH(LinearSolver{options}, "error: BICGStab2 is not an Eigen3 linear solver!"); + } + } + + SECTION("GMRES") + { + LinearSolverOptions options; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::gmres; + options.precond() = LSPrecond::none; + + SECTION("GMRES no preconditioner") + { + options.precond() = LSPrecond::none; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("GMRES Diagonal") + { + options.precond() = LSPrecond::diagonal; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("GMRES ILU") + { + options.precond() = LSPrecond::incomplete_LU; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + REQUIRE_THROWS_WITH(solver.solveLocalSystem(A, x, b), + "error: incomplete LU is not available for dense matrices in Eigen3"); + } + } + + SECTION("LU") + { + LinearSolverOptions options; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::lu; + options.precond() = LSPrecond::none; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + +#else // PUGS_HAS_EIGEN3 + SECTION("Eigen3 not linked") + { + LinearSolverOptions options; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::cg; + options.precond() = LSPrecond::none; + + REQUIRE_THROWS_WITH(LinearSolver{options}, "error: Eigen3 is not linked to pugs. Cannot use it!"); + } +#endif // PUGS_HAS_EIGEN3 + } + + SECTION("PETSc") + { +#ifdef PUGS_HAS_PETSC + + SECTION("BICGStab") + { + LinearSolverOptions options; + options.library() = LSLibrary::petsc; + options.method() = LSMethod::bicgstab; + options.precond() = LSPrecond::none; + options.verbose() = true; + + SECTION("BICGStab no preconditioner") + { + options.precond() = LSPrecond::none; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("BICGStab Diagonal") + { + options.precond() = LSPrecond::diagonal; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("BICGStab ILU") + { + options.precond() = LSPrecond::incomplete_LU; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + } + + SECTION("BICGStab2") + { + LinearSolverOptions options; + options.library() = LSLibrary::petsc; + options.method() = LSMethod::bicgstab2; + options.precond() = LSPrecond::none; + + SECTION("BICGStab2 no preconditioner") + { + options.precond() = LSPrecond::none; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("BICGStab2 Diagonal") + { + options.precond() = LSPrecond::diagonal; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + } + + SECTION("GMRES") + { + LinearSolverOptions options; + options.library() = LSLibrary::petsc; + options.method() = LSMethod::gmres; + options.precond() = LSPrecond::none; + + SECTION("GMRES no preconditioner") + { + options.precond() = LSPrecond::none; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("GMRES Diagonal") + { + options.precond() = LSPrecond::diagonal; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("GMRES ILU") + { + options.precond() = LSPrecond::incomplete_LU; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + } + + SECTION("LU") + { + LinearSolverOptions options; + options.library() = LSLibrary::petsc; + options.method() = LSMethod::lu; + options.precond() = LSPrecond::none; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + +#else // PUGS_HAS_PETSC + SECTION("PETSc not linked") + { + LinearSolverOptions options; + options.library() = LSLibrary::petsc; + options.method() = LSMethod::cg; + options.precond() = LSPrecond::none; + + REQUIRE_THROWS_WITH(LinearSolver{options}, "error: PETSc is not linked to pugs. Cannot use it!"); + } +#endif // PUGS_HAS_PETSC + } + } + } } } } diff --git a/tests/test_LinearSolverOptions.cpp b/tests/test_LinearSolverOptions.cpp index 90519ddf9797f0d6fa745cbc34e2f80443eed33d..7d078a7fb73e094ca09f888ab43ed12980081f2f 100644 --- a/tests/test_LinearSolverOptions.cpp +++ b/tests/test_LinearSolverOptions.cpp @@ -68,6 +68,7 @@ TEST_CASE("LinearSolverOptions", "[algebra]") SECTION("library name") { REQUIRE(name(LSLibrary::builtin) == "builtin"); + REQUIRE(name(LSLibrary::eigen3) == "Eigen3"); REQUIRE(name(LSLibrary::petsc) == "PETSc"); REQUIRE_THROWS_WITH(name(LSLibrary::LS__end), "unexpected error: Linear system library name is not defined!"); } @@ -135,6 +136,7 @@ TEST_CASE("LinearSolverOptions", "[algebra]") const std::string library_list = R"( - builtin + - Eigen3 - PETSc )"; diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction.cpp deleted file mode 100644 index cdfb532f2cd6a30327f09c5f0b1b8e54e6917a29..0000000000000000000000000000000000000000 --- a/tests/test_PolynomialReconstruction.cpp +++ /dev/null @@ -1,983 +0,0 @@ -#include <catch2/catch_approx.hpp> -#include <catch2/catch_test_macros.hpp> -#include <catch2/matchers/catch_matchers_all.hpp> - -#include <Kokkos_Core.hpp> - -#include <utils/PugsAssert.hpp> -#include <utils/Types.hpp> - -#include <algebra/SmallMatrix.hpp> -#include <algebra/SmallVector.hpp> -#include <mesh/Mesh.hpp> -#include <mesh/MeshDataManager.hpp> -#include <scheme/DiscreteFunctionDPkVariant.hpp> -#include <scheme/DiscreteFunctionP0.hpp> -#include <scheme/DiscreteFunctionVariant.hpp> -#include <scheme/PolynomialReconstruction.hpp> - -#include <MeshDataBaseForTests.hpp> - -// clazy:excludeall=non-pod-global-static - -TEST_CASE("PolynomialReconstruction", "[scheme]") -{ - SECTION("degree 1") - { - std::vector<PolynomialReconstructionDescriptor> descriptor_list = { - PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, 1}, - PolynomialReconstructionDescriptor{IntegrationMethodType::element, 1}, - }; - - for (auto descriptor : descriptor_list) { - SECTION(name(descriptor.integrationMethodType())) - { - SECTION("1D") - { - using R1 = TinyVector<1>; - - SECTION("R data") - { - for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { - SECTION(named_mesh.name()) - { - auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); - auto& mesh = *p_mesh; - - auto R_affine = [](const R1& x) { return 2.3 + 1.7 * x[0]; }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - - DiscreteFunctionP0<double> fh{p_mesh}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); - - auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); - - auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); - - { - double max_mean_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - max_mean_error = - std::max(max_mean_error, std::abs(dpk_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - double max_slope_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const double reconstructed_slope = - (dpk_fh[cell_id](R1{0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R1{0.1})) / 0.2; - - max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - 1.7)); - } - REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); - } - } - } - } - - SECTION("R^3 data") - { - using R3 = TinyVector<3>; - - for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { - SECTION(named_mesh.name()) - { - auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); - auto& mesh = *p_mesh; - - auto R3_affine = [](const R1& x) -> R3 { - return R3{+2.3 + 1.7 * x[0], // - +1.4 - 0.6 * x[0], // - -0.2 + 3.1 * x[0]}; - }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - - DiscreteFunctionP0<R3> uh{p_mesh}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); - - auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); - - auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3>>(); - - { - double max_mean_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - max_mean_error = - std::max(max_mean_error, l2Norm(dpk_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - double max_slope_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R3 reconstructed_slope = - (1 / 0.2) * (dpk_uh[cell_id](R1{0.1} + xj[cell_id]) - dpk_uh[cell_id](xj[cell_id] - R1{0.1})); - - max_slope_error = std::max(max_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1})); - } - REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); - } - } - } - } - - SECTION("R^3x3 data") - { - using R3x3 = TinyMatrix<3, 3>; - - for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { - SECTION(named_mesh.name()) - { - auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); - auto& mesh = *p_mesh; - - auto R3x3_affine = [](const R1& x) -> R3x3 { - return R3x3{ - +2.3 + 1.7 * x[0], -1.7 + 2.1 * x[0], +1.4 - 0.6 * x[0], // - +2.4 - 2.3 * x[0], -0.2 + 3.1 * x[0], -3.2 - 3.6 * x[0], - -4.1 + 3.1 * x[0], +0.8 + 2.9 * x[0], -1.6 + 2.3 * x[0], - }; - }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - - DiscreteFunctionP0<R3x3> Ah{p_mesh}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R3x3_affine(xj[cell_id]); }); - - auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); - - auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3x3>>(); - - { - double max_mean_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - max_mean_error = - std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - R3x3_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - double max_slope_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R3x3 reconstructed_slope = - (1 / 0.2) * (dpk_Ah[cell_id](R1{0.1} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R1{0.1})); - - R3x3 slops = R3x3{+1.7, +2.1, -0.6, // - -2.3, +3.1, -3.6, // - +3.1, +2.9, +2.3}; - - max_slope_error = std::max(max_slope_error, // - frobeniusNorm(reconstructed_slope - slops)); - } - REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-13)); - } - } - } - } - - SECTION("R vector data") - { - using R3 = TinyVector<3>; - - for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { - SECTION(named_mesh.name()) - { - auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); - auto& mesh = *p_mesh; - - auto vector_affine = [](const R1& x) -> R3 { - return R3{+2.3 + 1.7 * x[0], -1.7 + 2.1 * x[0], +1.4 - 0.6 * x[0]}; - }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - - DiscreteFunctionP0Vector<double> Vh{p_mesh, 3}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { - auto vector = vector_affine(xj[cell_id]); - for (size_t i = 0; i < vector.dimension(); ++i) { - Vh[cell_id][i] = vector[i]; - } - }); - - auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); - - auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const double>>(); - - { - double max_mean_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - auto vector = vector_affine(xj[cell_id]); - for (size_t i = 0; i < vector.dimension(); ++i) { - max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, i)(xj[cell_id]) - vector[i])); - } - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - double max_slope_error = 0; - const TinyVector<3> slope{+1.7, +2.1, -0.6}; - - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - for (size_t i = 0; i < slope.dimension(); ++i) { - const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R1{0.1} + xj[cell_id]) - - dpk_Vh(cell_id, i)(xj[cell_id] - R1{0.1})); - - max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope[i])); - } - } - REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); - } - } - } - } - - SECTION("list of various types") - { - using R3x3 = TinyMatrix<3>; - using R3 = TinyVector<3>; - - for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { - SECTION(named_mesh.name()) - { - auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); - auto& mesh = *p_mesh; - - auto R_affine = [](const R1& x) { return 2.3 + 1.7 * x[0]; }; - - auto R3_affine = [](const R1& x) -> R3 { - return R3{+2.3 + 1.7 * x[0], // - +1.4 - 0.6 * x[0], // - -0.2 + 3.1 * x[0]}; - }; - - auto R3x3_affine = [](const R1& x) -> R3x3 { - return R3x3{ - +2.3 + 1.7 * x[0], -1.7 + 2.1 * x[0], +1.4 - 0.6 * x[0], // - +2.4 - 2.3 * x[0], -0.2 + 3.1 * x[0], -3.2 - 3.6 * x[0], - -4.1 + 3.1 * x[0], +0.8 + 2.9 * x[0], -1.6 + 2.3 * x[0], - }; - }; - - auto vector_affine = [](const R1& x) -> R3 { - return R3{+2.3 + 1.7 * x[0], -1.7 + 2.1 * x[0], +1.4 - 0.6 * x[0]}; - }; - - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - - DiscreteFunctionP0<double> fh{p_mesh}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); - - DiscreteFunctionP0<R3> uh{p_mesh}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); - - DiscreteFunctionP0<R3x3> Ah{p_mesh}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R3x3_affine(xj[cell_id]); }); - - DiscreteFunctionP0Vector<double> Vh{p_mesh, 3}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { - auto vector = vector_affine(xj[cell_id]); - for (size_t i = 0; i < vector.dimension(); ++i) { - Vh[cell_id][i] = vector[i]; - } - }); - - auto reconstructions = - PolynomialReconstruction{descriptor}.build(std::make_shared<DiscreteFunctionVariant>(fh), uh, - std::make_shared<DiscreteFunctionP0<R3x3>>(Ah), - DiscreteFunctionVariant(Vh)); - - auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); - - { - double max_mean_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - max_mean_error = - std::max(max_mean_error, std::abs(dpk_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - double max_slope_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const double reconstructed_slope = - (dpk_fh[cell_id](R1{0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R1{0.1})) / 0.2; - - max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - 1.7)); - } - REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); - } - - auto dpk_uh = reconstructions[1]->get<DiscreteFunctionDPk<1, const R3>>(); - - { - double max_mean_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - max_mean_error = - std::max(max_mean_error, l2Norm(dpk_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - double max_slope_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R3 reconstructed_slope = - (1 / 0.2) * (dpk_uh[cell_id](R1{0.1} + xj[cell_id]) - dpk_uh[cell_id](xj[cell_id] - R1{0.1})); - - max_slope_error = std::max(max_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1})); - } - REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); - } - - auto dpk_Ah = reconstructions[2]->get<DiscreteFunctionDPk<1, const R3x3>>(); - - { - double max_mean_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - max_mean_error = - std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - R3x3_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - double max_slope_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R3x3 reconstructed_slope = - (1 / 0.2) * (dpk_Ah[cell_id](R1{0.1} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R1{0.1})); - - R3x3 slops = R3x3{+1.7, +2.1, -0.6, // - -2.3, +3.1, -3.6, // - +3.1, +2.9, +2.3}; - - max_slope_error = std::max(max_slope_error, // - frobeniusNorm(reconstructed_slope - slops)); - } - REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-13)); - } - - auto dpk_Vh = reconstructions[3]->get<DiscreteFunctionDPkVector<1, const double>>(); - - { - double max_mean_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - auto vector = vector_affine(xj[cell_id]); - for (size_t i = 0; i < vector.dimension(); ++i) { - max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, i)(xj[cell_id]) - vector[i])); - } - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - double max_slope_error = 0; - const TinyVector<3> slope{+1.7, +2.1, -0.6}; - - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - for (size_t i = 0; i < slope.dimension(); ++i) { - const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R1{0.1} + xj[cell_id]) - - dpk_Vh(cell_id, i)(xj[cell_id] - R1{0.1})); - - max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope[i])); - } - } - REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); - } - } - } - } - } - - SECTION("2D") - { - using R2 = TinyVector<2>; - - SECTION("R data") - { - for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { - SECTION(named_mesh.name()) - { - auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); - auto& mesh = *p_mesh; - - auto R_affine = [](const R2& x) { return 2.3 + 1.7 * x[0] - 1.3 * x[1]; }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - - DiscreteFunctionP0<double> fh{p_mesh}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); - - auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); - - auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>(); - - { - double max_mean_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - max_mean_error = - std::max(max_mean_error, std::abs(dpk_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - double max_x_slope_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const double reconstructed_slope = - (dpk_fh[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R2{0.1, 0})) / 0.2; - - max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - 1.7)); - } - REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); - } - - { - double max_y_slope_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const double reconstructed_slope = - (dpk_fh[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R2{0, 0.1})) / 0.2; - - max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - (-1.3))); - } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-14)); - } - } - } - } - - SECTION("R^3 data") - { - using R3 = TinyVector<3>; - - for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { - SECTION(named_mesh.name()) - { - auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); - auto& mesh = *p_mesh; - - auto R3_affine = [](const R2& x) -> R3 { - return R3{+2.3 + 1.7 * x[0] - 2.2 * x[1], // - +1.4 - 0.6 * x[0] + 1.3 * x[1], // - -0.2 + 3.1 * x[0] - 1.1 * x[1]}; - }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - - DiscreteFunctionP0<R3> uh{p_mesh}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); - - auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); - - auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R3>>(); - - { - double max_mean_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - max_mean_error = - std::max(max_mean_error, l2Norm(dpk_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - double max_x_slope_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R2{0.1, 0} + xj[cell_id]) - - dpk_uh[cell_id](xj[cell_id] - R2{0.1, 0})); - - max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1})); - } - REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); - } - - { - double max_y_slope_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R2{0, 0.1} + xj[cell_id]) - - dpk_uh[cell_id](xj[cell_id] - R2{0, 0.1})); - - max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - R3{-2.2, 1.3, -1.1})); - } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); - } - } - } - } - - SECTION("R^2x2 data") - { - using R2x2 = TinyMatrix<2, 2>; - - for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { - SECTION(named_mesh.name()) - { - auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); - auto& mesh = *p_mesh; - - auto R2x2_affine = [](const R2& x) -> R2x2 { - return R2x2{+2.3 + 1.7 * x[0] + 1.2 * x[1], -1.7 + 2.1 * x[0] - 2.2 * x[1], // - +1.4 - 0.6 * x[0] - 2.1 * x[1], +2.4 - 2.3 * x[0] + 1.3 * x[1]}; - }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - - DiscreteFunctionP0<R2x2> Ah{p_mesh}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); }); - - auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); - - auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); - - { - double max_mean_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - max_mean_error = - std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - double max_x_slope_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R2{0.1, 0} + xj[cell_id]) - - dpk_Ah[cell_id](xj[cell_id] - R2{0.1, 0})); - - max_x_slope_error = - std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, +2.1, // - -0.6, -2.3})); - } - REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); - } - - { - double max_y_slope_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R2{0, 0.1} + xj[cell_id]) - - dpk_Ah[cell_id](xj[cell_id] - R2{0, 0.1})); - - max_y_slope_error = - std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // - -2.1, +1.3})); - } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); - } - } - } - } - - SECTION("vector data") - { - using R4 = TinyVector<4>; - - for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { - SECTION(named_mesh.name()) - { - auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); - auto& mesh = *p_mesh; - - auto vector_affine = [](const R2& x) -> R4 { - return R4{+2.3 + 1.7 * x[0] + 1.2 * x[1], -1.7 + 2.1 * x[0] - 2.2 * x[1], // - +1.4 - 0.6 * x[0] - 2.1 * x[1], +2.4 - 2.3 * x[0] + 1.3 * x[1]}; - }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - - DiscreteFunctionP0Vector<double> Vh{p_mesh, 4}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { - auto vector = vector_affine(xj[cell_id]); - for (size_t i = 0; i < vector.dimension(); ++i) { - Vh[cell_id][i] = vector[i]; - } - }); - - auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); - - auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const double>>(); - - { - double max_mean_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - auto vector = vector_affine(xj[cell_id]); - for (size_t i = 0; i < vector.dimension(); ++i) { - max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, i)(xj[cell_id]) - vector[i])); - } - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - double max_x_slope_error = 0; - const R4 slope{+1.7, +2.1, -0.6, -2.3}; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - for (size_t i = 0; i < slope.dimension(); ++i) { - const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R2{0.1, 0} + xj[cell_id]) - - dpk_Vh(cell_id, i)(xj[cell_id] - R2{0.1, 0})); - - max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - slope[i])); - } - } - REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); - } - - { - double max_y_slope_error = 0; - const R4 slope{+1.2, -2.2, -2.1, +1.3}; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - for (size_t i = 0; i < slope.dimension(); ++i) { - const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R2{0, 0.1} + xj[cell_id]) - - dpk_Vh(cell_id, i)(xj[cell_id] - R2{0, 0.1})); - - max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - slope[i])); - } - } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); - } - } - } - } - } - - SECTION("3D") - { - using R3 = TinyVector<3>; - - SECTION("R data") - { - for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { - SECTION(named_mesh.name()) - { - auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); - auto& mesh = *p_mesh; - - auto R_affine = [](const R3& x) { return 2.3 + 1.7 * x[0] - 1.3 * x[1] + 2.1 * x[2]; }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - - DiscreteFunctionP0<double> fh{p_mesh}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); - - auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); - - auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); - - { - double max_mean_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - max_mean_error = - std::max(max_mean_error, std::abs(dpk_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - double max_x_slope_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const double reconstructed_slope = - (dpk_fh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0.1, 0, 0})) / - 0.2; - - max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - 1.7)); - } - REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); - } - - { - double max_y_slope_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const double reconstructed_slope = - (dpk_fh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0, 0.1, 0})) / - 0.2; - - max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - (-1.3))); - } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); - } - - { - double max_z_slope_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const double reconstructed_slope = - (dpk_fh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0, 0, 0.1})) / - 0.2; - - max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope - 2.1)); - } - REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); - } - } - } - } - - SECTION("R^3 data") - { - for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { - SECTION(named_mesh.name()) - { - auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); - auto& mesh = *p_mesh; - - auto R3_affine = [](const R3& x) -> R3 { - return R3{+2.3 + 1.7 * x[0] - 2.2 * x[1] + 1.8 * x[2], // - +1.4 - 0.6 * x[0] + 1.3 * x[1] - 3.7 * x[2], // - -0.2 + 3.1 * x[0] - 1.1 * x[1] + 1.9 * x[2]}; - }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - - DiscreteFunctionP0<R3> uh{p_mesh}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); - - auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); - - auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); - - { - double max_mean_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - max_mean_error = - std::max(max_mean_error, l2Norm(dpk_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - double max_x_slope_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - - dpk_uh[cell_id](xj[cell_id] - R3{0.1, 0, 0})); - - max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1})); - } - REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-12)); - } - - { - double max_y_slope_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - - dpk_uh[cell_id](xj[cell_id] - R3{0, 0.1, 0})); - - max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - R3{-2.2, 1.3, -1.1})); - } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); - } - - { - double max_z_slope_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - - dpk_uh[cell_id](xj[cell_id] - R3{0, 0, 0.1})); - - max_z_slope_error = std::max(max_z_slope_error, l2Norm(reconstructed_slope - R3{1.8, -3.7, 1.9})); - } - REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); - } - } - } - } - - SECTION("R^2x2 data") - { - using R2x2 = TinyMatrix<2, 2>; - - for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { - SECTION(named_mesh.name()) - { - auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); - auto& mesh = *p_mesh; - - auto R2x2_affine = [](const R3& x) -> R2x2 { - return R2x2{+2.3 + 1.7 * x[0] + 1.2 * x[1] - 1.3 * x[2], -1.7 + 2.1 * x[0] - 2.2 * x[1] - 2.4 * x[2], - // - +2.4 - 2.3 * x[0] + 1.3 * x[1] + 1.4 * x[2], -0.2 + 3.1 * x[0] + 0.8 * x[1] - 1.8 * x[2]}; - }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - - DiscreteFunctionP0<R2x2> Ah{p_mesh}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); }); - - descriptor.setRowWeighting(false); - auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); - - auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); - - { - double max_mean_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - max_mean_error = - std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - double max_x_slope_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - - dpk_Ah[cell_id](xj[cell_id] - R3{0.1, 0, 0})); - - max_x_slope_error = - std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, 2.1, // - -2.3, +3.1})); - } - REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); - } - - { - double max_y_slope_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - - dpk_Ah[cell_id](xj[cell_id] - R3{0, 0.1, 0})); - - max_y_slope_error = - std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // - 1.3, +0.8})); - } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); - } - - { - double max_z_slope_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - - dpk_Ah[cell_id](xj[cell_id] - R3{0, 0, 0.1})); - - max_z_slope_error = - std::max(max_z_slope_error, frobeniusNorm(reconstructed_slope - R2x2{-1.3, -2.4, // - +1.4, -1.8})); - } - REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); - } - } - } - } - - SECTION("vector data") - { - using R4 = TinyVector<4>; - - for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { - SECTION(named_mesh.name()) - { - auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); - auto& mesh = *p_mesh; - - auto vector_affine = [](const R3& x) -> R4 { - return R4{+2.3 + 1.7 * x[0] + 1.2 * x[1] - 1.3 * x[2], -1.7 + 2.1 * x[0] - 2.2 * x[1] - 2.4 * x[2], - // - +2.4 - 2.3 * x[0] + 1.3 * x[1] + 1.4 * x[2], -0.2 + 3.1 * x[0] + 0.8 * x[1] - 1.8 * x[2]}; - }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - - DiscreteFunctionP0Vector<double> Vh{p_mesh, 4}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { - auto vector = vector_affine(xj[cell_id]); - for (size_t i = 0; i < vector.dimension(); ++i) { - Vh[cell_id][i] = vector[i]; - } - }); - - descriptor.setPreconditioning(false); - auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); - - auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const double>>(); - - { - double max_mean_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - auto vector = vector_affine(xj[cell_id]); - for (size_t i = 0; i < vector.dimension(); ++i) { - max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, i)(xj[cell_id]) - vector[i])); - } - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - double max_x_slope_error = 0; - const R4 slope{+1.7, 2.1, -2.3, +3.1}; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - for (size_t i = 0; i < slope.dimension(); ++i) { - const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R3{0.1, 0, 0} + xj[cell_id]) - - dpk_Vh(cell_id, i)(xj[cell_id] - R3{0.1, 0, 0})); - - max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - slope[i])); - } - } - REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); - } - - { - double max_y_slope_error = 0; - const R4 slope{+1.2, -2.2, 1.3, +0.8}; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - for (size_t i = 0; i < slope.dimension(); ++i) { - const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R3{0, 0.1, 0} + xj[cell_id]) - - dpk_Vh(cell_id, i)(xj[cell_id] - R3{0, 0.1, 0})); - - max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - slope[i])); - } - } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); - } - - { - double max_z_slope_error = 0; - const R4 slope{-1.3, -2.4, +1.4, -1.8}; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - for (size_t i = 0; i < slope.dimension(); ++i) { - const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R3{0, 0, 0.1} + xj[cell_id]) - - dpk_Vh(cell_id, i)(xj[cell_id] - R3{0, 0, 0.1})); - - max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope - slope[i])); - } - } - REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-13)); - } - } - } - } - } - - SECTION("errors") - { - auto p_mesh1 = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); - DiscreteFunctionP0<double> f1{p_mesh1}; - - auto p_mesh2 = MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>(); - DiscreteFunctionP0<double> f2{p_mesh2}; - - REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(f1, f2), - "error: cannot reconstruct functions living of different meshes simultaneously"); - } - } - } - } -} diff --git a/tests/test_PolynomialReconstruction_degree_1.cpp b/tests/test_PolynomialReconstruction_degree_1.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7c4f07266514eaf8b277af5f26751c2ec17bc8ae --- /dev/null +++ b/tests/test_PolynomialReconstruction_degree_1.cpp @@ -0,0 +1,1912 @@ +#include <catch2/catch_approx.hpp> +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <Kokkos_Core.hpp> + +#include <utils/PugsAssert.hpp> +#include <utils/Types.hpp> + +#include <algebra/SmallMatrix.hpp> +#include <algebra/SmallVector.hpp> +#include <analysis/GaussQuadratureDescriptor.hpp> +#include <analysis/QuadratureFormula.hpp> +#include <analysis/QuadratureManager.hpp> +#include <geometry/LineTransformation.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshDataManager.hpp> +#include <mesh/NamedBoundaryDescriptor.hpp> +#include <scheme/DiscreteFunctionDPkVariant.hpp> +#include <scheme/DiscreteFunctionP0.hpp> +#include <scheme/DiscreteFunctionVariant.hpp> +#include <scheme/PolynomialReconstruction.hpp> + +#include <MeshDataBaseForTests.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") +{ + SECTION("without symmetries") + { + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, 1}, + PolynomialReconstructionDescriptor{IntegrationMethodType::element, 1}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, 1}}; + + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) + { + SECTION("1D") + { + using R1 = TinyVector<1>; + + SECTION("R data") + { + for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { + SECTION(named_mesh.name()) + { + auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); + auto& mesh = *p_mesh; + + auto R_affine = [](const R1& x) { return 2.3 + 1.7 * x[0]; }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + auto xr = mesh.xr(); + + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + + DiscreteFunctionP0<double> fh{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); + + auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor{2}); + { + double max_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + auto cell_nodes = cell_to_node_matrix[cell_id]; + LineTransformation<1> T{xr[cell_nodes[0]], xr[cell_nodes[1]]}; + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + max_error = std::max(max_error, std::abs(dpk_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); + } + } + + REQUIRE(parallel::allReduceMax(max_error) == 0 // Catch::Approx(0).margin(0 * 1E-14) + ); + } + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + max_mean_error = + std::max(max_mean_error, std::abs(dpk_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } + + { + double max_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const double reconstructed_slope = + (dpk_fh[cell_id](R1{0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R1{0.1})) / 0.2; + + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - 1.7)); + } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); + } + } + } + } + + SECTION("R^3 data") + { + using R3 = TinyVector<3>; + + for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { + SECTION(named_mesh.name()) + { + auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); + auto& mesh = *p_mesh; + + auto R3_affine = [](const R1& x) -> R3 { + return R3{+2.3 + 1.7 * x[0], // + +1.4 - 0.6 * x[0], // + -0.2 + 3.1 * x[0]}; + }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0<R3> uh{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + max_mean_error = + std::max(max_mean_error, l2Norm(dpk_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } + + { + double max_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R3 reconstructed_slope = + (1 / 0.2) * (dpk_uh[cell_id](R1{0.1} + xj[cell_id]) - dpk_uh[cell_id](xj[cell_id] - R1{0.1})); + + max_slope_error = std::max(max_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1})); + } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); + } + } + } + } + + SECTION("R^3x3 data") + { + using R3x3 = TinyMatrix<3, 3>; + + for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { + SECTION(named_mesh.name()) + { + auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); + auto& mesh = *p_mesh; + + auto R3x3_affine = [](const R1& x) -> R3x3 { + return R3x3{ + +2.3 + 1.7 * x[0], -1.7 + 2.1 * x[0], +1.4 - 0.6 * x[0], // + +2.4 - 2.3 * x[0], -0.2 + 3.1 * x[0], -3.2 - 3.6 * x[0], + -4.1 + 3.1 * x[0], +0.8 + 2.9 * x[0], -1.6 + 2.3 * x[0], + }; + }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0<R3x3> Ah{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R3x3_affine(xj[cell_id]); }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3x3>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + max_mean_error = + std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - R3x3_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } + + { + double max_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R3x3 reconstructed_slope = + (1 / 0.2) * (dpk_Ah[cell_id](R1{0.1} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R1{0.1})); + + R3x3 slops = R3x3{+1.7, +2.1, -0.6, // + -2.3, +3.1, -3.6, // + +3.1, +2.9, +2.3}; + + max_slope_error = std::max(max_slope_error, // + frobeniusNorm(reconstructed_slope - slops)); + } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + + SECTION("R vector data") + { + using R3 = TinyVector<3>; + + for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { + SECTION(named_mesh.name()) + { + auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); + auto& mesh = *p_mesh; + + auto vector_affine = [](const R1& x) -> R3 { + return R3{+2.3 + 1.7 * x[0], -1.7 + 2.1 * x[0], +1.4 - 0.6 * x[0]}; + }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0Vector<double> Vh{p_mesh, 3}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto vector = vector_affine(xj[cell_id]); + for (size_t i = 0; i < vector.dimension(); ++i) { + Vh[cell_id][i] = vector[i]; + } + }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const double>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + auto vector = vector_affine(xj[cell_id]); + for (size_t i = 0; i < vector.dimension(); ++i) { + max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, i)(xj[cell_id]) - vector[i])); + } + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } + + { + double max_slope_error = 0; + const TinyVector<3> slope{+1.7, +2.1, -0.6}; + + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + for (size_t i = 0; i < slope.dimension(); ++i) { + const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R1{0.1} + xj[cell_id]) - + dpk_Vh(cell_id, i)(xj[cell_id] - R1{0.1})); + + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope[i])); + } + } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); + } + } + } + } + + SECTION("R3 vector data") + { + using R3 = TinyVector<3>; + + for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { + SECTION(named_mesh.name()) + { + auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); + auto& mesh = *p_mesh; + + auto vector_affine0 = [](const R1& x) -> R3 { + return R3{+2.3 + 1.7 * x[0], -1.7 + 2.1 * x[0], +1.4 - 0.6 * x[0]}; + }; + + auto vector_affine1 = [](const R1& x) -> R3 { + return R3{+1.6 + 0.7 * x[0], -2.1 + 1.2 * x[0], +1.1 - 0.3 * x[0]}; + }; + + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0Vector<R3> Vh{p_mesh, 2}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + Vh[cell_id][0] = vector_affine0(xj[cell_id]); + Vh[cell_id][1] = vector_affine1(xj[cell_id]); + }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const R3>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + max_mean_error = + std::max(max_mean_error, l2Norm(dpk_Vh(cell_id, 0)(xj[cell_id]) - vector_affine0(xj[cell_id]))); + max_mean_error = + std::max(max_mean_error, l2Norm(dpk_Vh(cell_id, 1)(xj[cell_id]) - vector_affine1(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } + + { + double max_slope_error = 0; + { + const TinyVector<3> slope0{+1.7, +2.1, -0.6}; + + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + for (size_t i = 0; i < R3::Dimension; ++i) { + const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, 0)(R1{0.1} + xj[cell_id])[i] - + dpk_Vh(cell_id, 0)(xj[cell_id] - R1{0.1})[i]); + + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope0[i])); + } + } + } + + { + const TinyVector<3> slope1{+0.7, +1.2, -0.3}; + + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + for (size_t i = 0; i < R3::Dimension; ++i) { + const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, 1)(R1{0.1} + xj[cell_id])[i] - + dpk_Vh(cell_id, 1)(xj[cell_id] - R1{0.1})[i]); + + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope1[i])); + } + } + } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); + } + } + } + } + + SECTION("list of various types") + { + using R3x3 = TinyMatrix<3>; + using R3 = TinyVector<3>; + + for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { + SECTION(named_mesh.name()) + { + auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); + auto& mesh = *p_mesh; + + auto R_affine = [](const R1& x) { return 2.3 + 1.7 * x[0]; }; + + auto R3_affine = [](const R1& x) -> R3 { + return R3{+2.3 + 1.7 * x[0], // + +1.4 - 0.6 * x[0], // + -0.2 + 3.1 * x[0]}; + }; + + auto R3x3_affine = [](const R1& x) -> R3x3 { + return R3x3{ + +2.3 + 1.7 * x[0], -1.7 + 2.1 * x[0], +1.4 - 0.6 * x[0], // + +2.4 - 2.3 * x[0], -0.2 + 3.1 * x[0], -3.2 - 3.6 * x[0], + -4.1 + 3.1 * x[0], +0.8 + 2.9 * x[0], -1.6 + 2.3 * x[0], + }; + }; + + auto vector_affine = [](const R1& x) -> R3 { + return R3{+2.3 + 1.7 * x[0], -1.7 + 2.1 * x[0], +1.4 - 0.6 * x[0]}; + }; + + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0<double> fh{p_mesh}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); + + DiscreteFunctionP0<R3> uh{p_mesh}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + + DiscreteFunctionP0<R3x3> Ah{p_mesh}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R3x3_affine(xj[cell_id]); }); + + DiscreteFunctionP0Vector<double> Vh{p_mesh, 3}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto vector = vector_affine(xj[cell_id]); + for (size_t i = 0; i < vector.dimension(); ++i) { + Vh[cell_id][i] = vector[i]; + } + }); + + auto reconstructions = + PolynomialReconstruction{descriptor}.build(std::make_shared<DiscreteFunctionVariant>(fh), uh, + std::make_shared<DiscreteFunctionP0<R3x3>>(Ah), + DiscreteFunctionVariant(Vh)); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + max_mean_error = + std::max(max_mean_error, std::abs(dpk_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } + + { + double max_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const double reconstructed_slope = + (dpk_fh[cell_id](R1{0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R1{0.1})) / 0.2; + + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - 1.7)); + } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); + } + + auto dpk_uh = reconstructions[1]->get<DiscreteFunctionDPk<1, const R3>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + max_mean_error = + std::max(max_mean_error, l2Norm(dpk_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } + + { + double max_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R3 reconstructed_slope = + (1 / 0.2) * (dpk_uh[cell_id](R1{0.1} + xj[cell_id]) - dpk_uh[cell_id](xj[cell_id] - R1{0.1})); + + max_slope_error = std::max(max_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1})); + } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); + } + + auto dpk_Ah = reconstructions[2]->get<DiscreteFunctionDPk<1, const R3x3>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + max_mean_error = + std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - R3x3_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } + + { + double max_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R3x3 reconstructed_slope = + (1 / 0.2) * (dpk_Ah[cell_id](R1{0.1} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R1{0.1})); + + R3x3 slops = R3x3{+1.7, +2.1, -0.6, // + -2.3, +3.1, -3.6, // + +3.1, +2.9, +2.3}; + + max_slope_error = std::max(max_slope_error, // + frobeniusNorm(reconstructed_slope - slops)); + } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-13)); + } + + auto dpk_Vh = reconstructions[3]->get<DiscreteFunctionDPkVector<1, const double>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + auto vector = vector_affine(xj[cell_id]); + for (size_t i = 0; i < vector.dimension(); ++i) { + max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, i)(xj[cell_id]) - vector[i])); + } + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } + + { + double max_slope_error = 0; + const TinyVector<3> slope{+1.7, +2.1, -0.6}; + + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + for (size_t i = 0; i < slope.dimension(); ++i) { + const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R1{0.1} + xj[cell_id]) - + dpk_Vh(cell_id, i)(xj[cell_id] - R1{0.1})); + + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope[i])); + } + } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); + } + } + } + } + } + + SECTION("2D") + { + using R2 = TinyVector<2>; + + SECTION("R data") + { + for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { + SECTION(named_mesh.name()) + { + auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); + auto& mesh = *p_mesh; + + auto R_affine = [](const R2& x) { return 2.3 + 1.7 * x[0] - 1.3 * x[1]; }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0<double> fh{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + max_mean_error = + std::max(max_mean_error, std::abs(dpk_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } + + { + double max_x_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const double reconstructed_slope = + (dpk_fh[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R2{0.1, 0})) / 0.2; + + max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - 1.7)); + } + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); + } + + { + double max_y_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const double reconstructed_slope = + (dpk_fh[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R2{0, 0.1})) / 0.2; + + max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - (-1.3))); + } + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-14)); + } + } + } + } + + SECTION("R^3 data") + { + using R3 = TinyVector<3>; + + for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { + SECTION(named_mesh.name()) + { + auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); + auto& mesh = *p_mesh; + + auto R3_affine = [](const R2& x) -> R3 { + return R3{+2.3 + 1.7 * x[0] - 2.2 * x[1], // + +1.4 - 0.6 * x[0] + 1.3 * x[1], // + -0.2 + 3.1 * x[0] - 1.1 * x[1]}; + }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0<R3> uh{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R3>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + max_mean_error = + std::max(max_mean_error, l2Norm(dpk_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } + + { + double max_x_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R2{0.1, 0} + xj[cell_id]) - + dpk_uh[cell_id](xj[cell_id] - R2{0.1, 0})); + + max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1})); + } + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); + } + + { + double max_y_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R2{0, 0.1} + xj[cell_id]) - + dpk_uh[cell_id](xj[cell_id] - R2{0, 0.1})); + + max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - R3{-2.2, 1.3, -1.1})); + } + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + + SECTION("R^2x2 data") + { + using R2x2 = TinyMatrix<2, 2>; + + for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { + SECTION(named_mesh.name()) + { + auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); + auto& mesh = *p_mesh; + + auto R2x2_affine = [](const R2& x) -> R2x2 { + return R2x2{+2.3 + 1.7 * x[0] + 1.2 * x[1], -1.7 + 2.1 * x[0] - 2.2 * x[1], // + +1.4 - 0.6 * x[0] - 2.1 * x[1], +2.4 - 2.3 * x[0] + 1.3 * x[1]}; + }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0<R2x2> Ah{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + max_mean_error = + std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } + + { + double max_x_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R2{0.1, 0} + xj[cell_id]) - + dpk_Ah[cell_id](xj[cell_id] - R2{0.1, 0})); + + max_x_slope_error = + std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, +2.1, // + -0.6, -2.3})); + } + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); + } + + { + double max_y_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R2{0, 0.1} + xj[cell_id]) - + dpk_Ah[cell_id](xj[cell_id] - R2{0, 0.1})); + + max_y_slope_error = + std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // + -2.1, +1.3})); + } + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + + SECTION("vector data") + { + using R4 = TinyVector<4>; + + for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { + SECTION(named_mesh.name()) + { + auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); + auto& mesh = *p_mesh; + + auto vector_affine = [](const R2& x) -> R4 { + return R4{+2.3 + 1.7 * x[0] + 1.2 * x[1], -1.7 + 2.1 * x[0] - 2.2 * x[1], // + +1.4 - 0.6 * x[0] - 2.1 * x[1], +2.4 - 2.3 * x[0] + 1.3 * x[1]}; + }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0Vector<double> Vh{p_mesh, 4}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto vector = vector_affine(xj[cell_id]); + for (size_t i = 0; i < vector.dimension(); ++i) { + Vh[cell_id][i] = vector[i]; + } + }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const double>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + auto vector = vector_affine(xj[cell_id]); + for (size_t i = 0; i < vector.dimension(); ++i) { + max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, i)(xj[cell_id]) - vector[i])); + } + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } + + { + double max_x_slope_error = 0; + const R4 slope{+1.7, +2.1, -0.6, -2.3}; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + for (size_t i = 0; i < slope.dimension(); ++i) { + const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R2{0.1, 0} + xj[cell_id]) - + dpk_Vh(cell_id, i)(xj[cell_id] - R2{0.1, 0})); + + max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - slope[i])); + } + } + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); + } + + { + double max_y_slope_error = 0; + const R4 slope{+1.2, -2.2, -2.1, +1.3}; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + for (size_t i = 0; i < slope.dimension(); ++i) { + const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R2{0, 0.1} + xj[cell_id]) - + dpk_Vh(cell_id, i)(xj[cell_id] - R2{0, 0.1})); + + max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - slope[i])); + } + } + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + } + + SECTION("3D") + { + using R3 = TinyVector<3>; + + SECTION("R data") + { + for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { + SECTION(named_mesh.name()) + { + auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); + auto& mesh = *p_mesh; + + auto R_affine = [](const R3& x) { return 2.3 + 1.7 * x[0] - 1.3 * x[1] + 2.1 * x[2]; }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0<double> fh{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + max_mean_error = + std::max(max_mean_error, std::abs(dpk_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } + + { + double max_x_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const double reconstructed_slope = + (dpk_fh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0.1, 0, 0})) / + 0.2; + + max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - 1.7)); + } + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); + } + + { + double max_y_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const double reconstructed_slope = + (dpk_fh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0, 0.1, 0})) / + 0.2; + + max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - (-1.3))); + } + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); + } + + { + double max_z_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const double reconstructed_slope = + (dpk_fh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0, 0, 0.1})) / + 0.2; + + max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope - 2.1)); + } + REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + } + + SECTION("R^3 data") + { + for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { + SECTION(named_mesh.name()) + { + auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); + auto& mesh = *p_mesh; + + auto R3_affine = [](const R3& x) -> R3 { + return R3{+2.3 + 1.7 * x[0] - 2.2 * x[1] + 1.8 * x[2], // + +1.4 - 0.6 * x[0] + 1.3 * x[1] - 3.7 * x[2], // + -0.2 + 3.1 * x[0] - 1.1 * x[1] + 1.9 * x[2]}; + }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0<R3> uh{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + max_mean_error = + std::max(max_mean_error, l2Norm(dpk_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } + + { + double max_x_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - + dpk_uh[cell_id](xj[cell_id] - R3{0.1, 0, 0})); + + max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1})); + } + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-12)); + } + + { + double max_y_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - + dpk_uh[cell_id](xj[cell_id] - R3{0, 0.1, 0})); + + max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - R3{-2.2, 1.3, -1.1})); + } + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); + } + + { + double max_z_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - + dpk_uh[cell_id](xj[cell_id] - R3{0, 0, 0.1})); + + max_z_slope_error = std::max(max_z_slope_error, l2Norm(reconstructed_slope - R3{1.8, -3.7, 1.9})); + } + REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + } + + SECTION("R^2x2 data") + { + using R2x2 = TinyMatrix<2, 2>; + + for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { + SECTION(named_mesh.name()) + { + auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); + auto& mesh = *p_mesh; + + auto R2x2_affine = [](const R3& x) -> R2x2 { + return R2x2{+2.3 + 1.7 * x[0] + 1.2 * x[1] - 1.3 * x[2], -1.7 + 2.1 * x[0] - 2.2 * x[1] - 2.4 * x[2], + // + +2.4 - 2.3 * x[0] + 1.3 * x[1] + 1.4 * x[2], -0.2 + 3.1 * x[0] + 0.8 * x[1] - 1.8 * x[2]}; + }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0<R2x2> Ah{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); }); + + descriptor.setRowWeighting(false); + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + max_mean_error = + std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } + + { + double max_x_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - + dpk_Ah[cell_id](xj[cell_id] - R3{0.1, 0, 0})); + + max_x_slope_error = + std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, 2.1, // + -2.3, +3.1})); + } + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); + } + + { + double max_y_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - + dpk_Ah[cell_id](xj[cell_id] - R3{0, 0.1, 0})); + + max_y_slope_error = + std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // + 1.3, +0.8})); + } + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); + } + + { + double max_z_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - + dpk_Ah[cell_id](xj[cell_id] - R3{0, 0, 0.1})); + + max_z_slope_error = + std::max(max_z_slope_error, frobeniusNorm(reconstructed_slope - R2x2{-1.3, -2.4, // + +1.4, -1.8})); + } + REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + } + + SECTION("vector data") + { + using R4 = TinyVector<4>; + + for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { + SECTION(named_mesh.name()) + { + auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); + auto& mesh = *p_mesh; + + auto vector_affine = [](const R3& x) -> R4 { + return R4{+2.3 + 1.7 * x[0] + 1.2 * x[1] - 1.3 * x[2], -1.7 + 2.1 * x[0] - 2.2 * x[1] - 2.4 * x[2], + // + +2.4 - 2.3 * x[0] + 1.3 * x[1] + 1.4 * x[2], -0.2 + 3.1 * x[0] + 0.8 * x[1] - 1.8 * x[2]}; + }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0Vector<double> Vh{p_mesh, 4}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto vector = vector_affine(xj[cell_id]); + for (size_t i = 0; i < vector.dimension(); ++i) { + Vh[cell_id][i] = vector[i]; + } + }); + + descriptor.setPreconditioning(false); + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const double>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + auto vector = vector_affine(xj[cell_id]); + for (size_t i = 0; i < vector.dimension(); ++i) { + max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, i)(xj[cell_id]) - vector[i])); + } + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } + + { + double max_x_slope_error = 0; + const R4 slope{+1.7, 2.1, -2.3, +3.1}; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + for (size_t i = 0; i < slope.dimension(); ++i) { + const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R3{0.1, 0, 0} + xj[cell_id]) - + dpk_Vh(cell_id, i)(xj[cell_id] - R3{0.1, 0, 0})); + + max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - slope[i])); + } + } + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); + } + + { + double max_y_slope_error = 0; + const R4 slope{+1.2, -2.2, 1.3, +0.8}; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + for (size_t i = 0; i < slope.dimension(); ++i) { + const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R3{0, 0.1, 0} + xj[cell_id]) - + dpk_Vh(cell_id, i)(xj[cell_id] - R3{0, 0.1, 0})); + + max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - slope[i])); + } + } + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); + } + + { + double max_z_slope_error = 0; + const R4 slope{-1.3, -2.4, +1.4, -1.8}; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + for (size_t i = 0; i < slope.dimension(); ++i) { + const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R3{0, 0, 0.1} + xj[cell_id]) - + dpk_Vh(cell_id, i)(xj[cell_id] - R3{0, 0, 0.1})); + + max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope - slope[i])); + } + } + REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + } + + SECTION("errors") + { + auto p_mesh1 = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + DiscreteFunctionP0<double> f1{p_mesh1}; + + auto p_mesh2 = MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>(); + DiscreteFunctionP0<double> f2{p_mesh2}; + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(f1, f2), + "error: cannot reconstruct functions living of different meshes simultaneously"); + } + } + } + } + + SECTION("with symmetries") + { + SECTION("errors") + { + SECTION("1D") + { + auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::element, 1, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}; + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyVector<2>>{p_mesh}), + "error: cannot symmetrize vectors of dimension 2 using a mesh of dimension 1"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyVector<3>>{p_mesh}), + "error: cannot symmetrize vectors of dimension 3 using a mesh of dimension 1"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyVector<2>>{p_mesh, 1}), + "error: cannot symmetrize vectors of dimension 2 using a mesh of dimension 1"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyVector<3>>{p_mesh, 1}), + "error: cannot symmetrize vectors of dimension 3 using a mesh of dimension 1"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyMatrix<2>>{p_mesh}), + "error: cannot symmetrize matrices of dimensions 2x2 using a mesh of dimension 1"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyMatrix<3>>{p_mesh}), + "error: cannot symmetrize matrices of dimensions 3x3 using a mesh of dimension 1"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyMatrix<2>>{p_mesh, 1}), + "error: cannot symmetrize matrices of dimensions 2x2 using a mesh of dimension 1"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyMatrix<3>>{p_mesh, 1}), + "error: cannot symmetrize matrices of dimensions 3x3 using a mesh of dimension 1"); + } + + SECTION("2D") + { + auto p_mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::element, 1, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}; + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyVector<1>>{p_mesh}), + "error: cannot symmetrize vectors of dimension 1 using a mesh of dimension 2"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyVector<3>>{p_mesh}), + "error: cannot symmetrize vectors of dimension 3 using a mesh of dimension 2"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyVector<1>>{p_mesh, 1}), + "error: cannot symmetrize vectors of dimension 1 using a mesh of dimension 2"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyVector<3>>{p_mesh, 1}), + "error: cannot symmetrize vectors of dimension 3 using a mesh of dimension 2"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyMatrix<1>>{p_mesh}), + "error: cannot symmetrize matrices of dimensions 1x1 using a mesh of dimension 2"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyMatrix<3>>{p_mesh}), + "error: cannot symmetrize matrices of dimensions 3x3 using a mesh of dimension 2"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyMatrix<1>>{p_mesh, 1}), + "error: cannot symmetrize matrices of dimensions 1x1 using a mesh of dimension 2"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyMatrix<3>>{p_mesh, 1}), + "error: cannot symmetrize matrices of dimensions 3x3 using a mesh of dimension 2"); + } + + SECTION("3D") + { + auto p_mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::element, 1, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}; + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyVector<1>>{p_mesh}), + "error: cannot symmetrize vectors of dimension 1 using a mesh of dimension 3"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyVector<2>>{p_mesh}), + "error: cannot symmetrize vectors of dimension 2 using a mesh of dimension 3"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyVector<1>>{p_mesh, 1}), + "error: cannot symmetrize vectors of dimension 1 using a mesh of dimension 3"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyVector<2>>{p_mesh, 1}), + "error: cannot symmetrize vectors of dimension 2 using a mesh of dimension 3"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyMatrix<1>>{p_mesh}), + "error: cannot symmetrize matrices of dimensions 1x1 using a mesh of dimension 3"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyMatrix<2>>{p_mesh}), + "error: cannot symmetrize matrices of dimensions 2x2 using a mesh of dimension 3"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyMatrix<1>>{p_mesh, 1}), + "error: cannot symmetrize matrices of dimensions 1x1 using a mesh of dimension 3"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyMatrix<2>>{p_mesh, 1}), + "error: cannot symmetrize matrices of dimensions 2x2 using a mesh of dimension 3"); + } + } + + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, 1, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::element, 1, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, 1, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}}; + + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) + { + SECTION("1D") + { + using R1 = TinyVector<1>; + + SECTION("R^1 data") + { + auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + auto& mesh = *p_mesh; + + auto R1_affine = [](const R1& x) { return R1{1.7 * (x[0] + 1)}; }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0<R1> fh{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R1_affine(xj[cell_id]); }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R1>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + max_mean_error = + std::max(max_mean_error, std::abs((dpk_fh[cell_id](xj[cell_id]) - R1_affine(xj[cell_id]))[0])); + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } + + { + double max_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const double reconstructed_slope = + (dpk_fh[cell_id](R1{0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R1{0.1}))[0] / 0.2; + + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - 1.7)); + } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); + } + } + + SECTION("R1 vector data") + { + for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { + SECTION(named_mesh.name()) + { + auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); + auto& mesh = *p_mesh; + + auto vector_affine0 = [](const R1& x) -> R1 { return R1{+1.7 * (x[0] + 1)}; }; + + auto vector_affine1 = [](const R1& x) -> R1 { return R1{-0.3 * (x[0] + 1)}; }; + + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0Vector<R1> Vh{p_mesh, 2}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + Vh[cell_id][0] = vector_affine0(xj[cell_id]); + Vh[cell_id][1] = vector_affine1(xj[cell_id]); + }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const R1>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + max_mean_error = + std::max(max_mean_error, l2Norm(dpk_Vh(cell_id, 0)(xj[cell_id]) - vector_affine0(xj[cell_id]))); + max_mean_error = + std::max(max_mean_error, l2Norm(dpk_Vh(cell_id, 1)(xj[cell_id]) - vector_affine1(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } + + { + double max_slope_error = 0; + { + const TinyVector<1> slope0{+1.7}; + + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + for (size_t i = 0; i < R1::Dimension; ++i) { + const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, 0)(R1{0.1} + xj[cell_id])[i] - + dpk_Vh(cell_id, 0)(xj[cell_id] - R1{0.1})[i]); + + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope0[i])); + } + } + } + + { + const TinyVector<1> slope1{-0.3}; + + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + for (size_t i = 0; i < R1::Dimension; ++i) { + const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, 1)(R1{0.1} + xj[cell_id])[i] - + dpk_Vh(cell_id, 1)(xj[cell_id] - R1{0.1})[i]); + + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope1[i])); + } + } + } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); + } + } + } + } + } + + SECTION("2D") + { +#warning not done + using R2 = TinyVector<2>; + + // SECTION("R data") + // { + // for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { + // SECTION(named_mesh.name()) + // { + // auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); + // auto& mesh = *p_mesh; + + // auto R_affine = [](const R2& x) { return 2.3 + 1.7 * x[0] - 1.3 * x[1]; }; + // auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + // DiscreteFunctionP0<double> fh{p_mesh}; + + // parallel_for( + // mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + // auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>(); + + // { + // double max_mean_error = 0; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // max_mean_error = + // std::max(max_mean_error, std::abs(dpk_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); + // } + // REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + // } + + // { + // double max_x_slope_error = 0; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // const double reconstructed_slope = + // (dpk_fh[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R2{0.1, 0})) / 0.2; + + // max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - 1.7)); + // } + // REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); + // } + + // { + // double max_y_slope_error = 0; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // const double reconstructed_slope = + // (dpk_fh[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R2{0, 0.1})) / 0.2; + + // max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - (-1.3))); + // } + // REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-14)); + // } + // } + // } + // } + + // SECTION("R^3 data") + // { + // using R3 = TinyVector<3>; + + // for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { + // SECTION(named_mesh.name()) + // { + // auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); + // auto& mesh = *p_mesh; + + // auto R3_affine = [](const R2& x) -> R3 { + // return R3{+2.3 + 1.7 * x[0] - 2.2 * x[1], // + // +1.4 - 0.6 * x[0] + 1.3 * x[1], // + // -0.2 + 3.1 * x[0] - 1.1 * x[1]}; + // }; + // auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + // DiscreteFunctionP0<R3> uh{p_mesh}; + + // parallel_for( + // mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R3>>(); + + // { + // double max_mean_error = 0; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // max_mean_error = + // std::max(max_mean_error, l2Norm(dpk_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); + // } + // REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + // } + + // { + // double max_x_slope_error = 0; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R2{0.1, 0} + xj[cell_id]) - + // dpk_uh[cell_id](xj[cell_id] - R2{0.1, 0})); + + // max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{1.7, + // -0.6, 3.1})); + // } + // REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); + // } + + // { + // double max_y_slope_error = 0; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R2{0, 0.1} + xj[cell_id]) - + // dpk_uh[cell_id](xj[cell_id] - R2{0, 0.1})); + + // max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - R3{-2.2, 1.3, + // -1.1})); + // } + // REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); + // } + // } + // } + // } + + // SECTION("R^2x2 data") + // { + // using R2x2 = TinyMatrix<2, 2>; + + // for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { + // SECTION(named_mesh.name()) + // { + // auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); + // auto& mesh = *p_mesh; + + // auto R2x2_affine = [](const R2& x) -> R2x2 { + // return R2x2{+2.3 + 1.7 * x[0] + 1.2 * x[1], -1.7 + 2.1 * x[0] - 2.2 * x[1], // + // +1.4 - 0.6 * x[0] - 2.1 * x[1], +2.4 - 2.3 * x[0] + 1.3 * x[1]}; + // }; + // auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + // DiscreteFunctionP0<R2x2> Ah{p_mesh}; + + // parallel_for( + // mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); + // }); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + // auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); + + // { + // double max_mean_error = 0; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // max_mean_error = + // std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - + // R2x2_affine(xj[cell_id]))); + // } + // REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + // } + + // { + // double max_x_slope_error = 0; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R2{0.1, 0} + xj[cell_id]) - + // dpk_Ah[cell_id](xj[cell_id] - R2{0.1, 0})); + + // max_x_slope_error = + // std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, +2.1, // + // -0.6, -2.3})); + // } + // REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); + // } + + // { + // double max_y_slope_error = 0; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R2{0, 0.1} + xj[cell_id]) - + // dpk_Ah[cell_id](xj[cell_id] - R2{0, 0.1})); + + // max_y_slope_error = + // std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // + // -2.1, +1.3})); + // } + // REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); + // } + // } + // } + // } + + // SECTION("vector data") + // { + // using R4 = TinyVector<4>; + + // for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { + // SECTION(named_mesh.name()) + // { + // auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); + // auto& mesh = *p_mesh; + + // auto vector_affine = [](const R2& x) -> R4 { + // return R4{+2.3 + 1.7 * x[0] + 1.2 * x[1], -1.7 + 2.1 * x[0] - 2.2 * x[1], // + // +1.4 - 0.6 * x[0] - 2.1 * x[1], +2.4 - 2.3 * x[0] + 1.3 * x[1]}; + // }; + // auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + // DiscreteFunctionP0Vector<double> Vh{p_mesh, 4}; + + // parallel_for( + // mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + // auto vector = vector_affine(xj[cell_id]); + // for (size_t i = 0; i < vector.dimension(); ++i) { + // Vh[cell_id][i] = vector[i]; + // } + // }); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + // auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const double>>(); + + // { + // double max_mean_error = 0; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // auto vector = vector_affine(xj[cell_id]); + // for (size_t i = 0; i < vector.dimension(); ++i) { + // max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, i)(xj[cell_id]) - + // vector[i])); + // } + // } + // REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + // } + + // { + // double max_x_slope_error = 0; + // const R4 slope{+1.7, +2.1, -0.6, -2.3}; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // for (size_t i = 0; i < slope.dimension(); ++i) { + // const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R2{0.1, 0} + xj[cell_id]) - + // dpk_Vh(cell_id, i)(xj[cell_id] - R2{0.1, 0})); + + // max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - slope[i])); + // } + // } + // REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); + // } + + // { + // double max_y_slope_error = 0; + // const R4 slope{+1.2, -2.2, -2.1, +1.3}; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // for (size_t i = 0; i < slope.dimension(); ++i) { + // const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R2{0, 0.1} + xj[cell_id]) - + // dpk_Vh(cell_id, i)(xj[cell_id] - R2{0, 0.1})); + + // max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - slope[i])); + // } + // } + // REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); + // } + // } + // } + // } + } + + SECTION("3D") + { + using R3 = TinyVector<3>; +#warning not done + + // SECTION("R data") + // { + // for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { + // SECTION(named_mesh.name()) + // { + // auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); + // auto& mesh = *p_mesh; + + // auto R_affine = [](const R3& x) { return 2.3 + 1.7 * x[0] - 1.3 * x[1] + 2.1 * x[2]; }; + // auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + // DiscreteFunctionP0<double> fh{p_mesh}; + + // parallel_for( + // mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + // auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); + + // { + // double max_mean_error = 0; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // max_mean_error = + // std::max(max_mean_error, std::abs(dpk_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); + // } + // REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + // } + + // { + // double max_x_slope_error = 0; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // const double reconstructed_slope = + // (dpk_fh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0.1, 0, 0})) / + // 0.2; + + // max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - 1.7)); + // } + // REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); + // } + + // { + // double max_y_slope_error = 0; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // const double reconstructed_slope = + // (dpk_fh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0, 0.1, 0})) / + // 0.2; + + // max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - (-1.3))); + // } + // REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); + // } + + // { + // double max_z_slope_error = 0; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // const double reconstructed_slope = + // (dpk_fh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0, 0, 0.1})) / + // 0.2; + + // max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope - 2.1)); + // }// REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); + // } + // } + // } + // } + + // SECTION("R^3 data") + // { + // for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { + // SECTION(named_mesh.name()) + // { + // auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); + // auto& mesh = *p_mesh; + + // auto R3_affine = [](const R3& x) -> R3 { + // return R3{+2.3 + 1.7 * x[0] - 2.2 * x[1] + 1.8 * x[2], // + // +1.4 - 0.6 * x[0] + 1.3 * x[1] - 3.7 * x[2], // + // -0.2 + 3.1 * x[0] - 1.1 * x[1] + 1.9 * x[2]}; + // }; + // auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + // DiscreteFunctionP0<R3> uh{p_mesh}; + + // parallel_for( + // mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); + + // { + // double max_mean_error = 0; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // max_mean_error = + // std::max(max_mean_error, l2Norm(dpk_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); + // } + // REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + // } + + // { + // double max_x_slope_error = 0; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - + // dpk_uh[cell_id](xj[cell_id] - R3{0.1, 0, 0})); + + // max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{1.7, + // -0.6, 3.1})); + // } + // REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-12)); + // } + + // { + // double max_y_slope_error = 0; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - + // dpk_uh[cell_id](xj[cell_id] - R3{0, 0.1, 0})); + + // max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - R3{-2.2, 1.3, + // -1.1})); + // } + // REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); + // } + + // { + // double max_z_slope_error = 0; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - + // dpk_uh[cell_id](xj[cell_id] - R3{0, 0, 0.1})); + + // max_z_slope_error = std::max(max_z_slope_error, l2Norm(reconstructed_slope - R3{1.8, + // -3.7, 1.9})); + // } + // REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); + // } + // } + // } + // } + + // SECTION("R^2x2 data") + // { + // using R2x2 = TinyMatrix<2, 2>; + + // for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { + // SECTION(named_mesh.name()) + // { + // auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); + // auto& mesh = *p_mesh; + + // auto R2x2_affine = [](const R3& x) -> R2x2 { + // return R2x2{+2.3 + 1.7 * x[0] + 1.2 * x[1] - 1.3 * x[2], -1.7 + 2.1 * x[0] - 2.2 * x[1] - 2.4 * + // x[2], + // // + // +2.4 - 2.3 * x[0] + 1.3 * x[1] + 1.4 * x[2], -0.2 + 3.1 * x[0] + 0.8 * x[1] - 1.8 * + // x[2]}; + // }; + // auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + // DiscreteFunctionP0<R2x2> Ah{p_mesh}; + + // parallel_for( + // mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); + // }); + + // descriptor.setRowWeighting(false); + // auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + // auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); + + // { + // double max_mean_error = 0; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // max_mean_error = + // std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - + // R2x2_affine(xj[cell_id]))); + // } + // REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + // } + + // { + // double max_x_slope_error = 0; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - + // dpk_Ah[cell_id](xj[cell_id] - R3{0.1, 0, 0})); + + // max_x_slope_error = + // std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, 2.1, // + // -2.3, +3.1})); + // } + // REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); + // } + + // { + // double max_y_slope_error = 0; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - + // dpk_Ah[cell_id](xj[cell_id] - R3{0, 0.1, 0})); + + // max_y_slope_error = + // std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // + // 1.3, +0.8})); + // } + // REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); + // } + + // { + // double max_z_slope_error = 0; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - + // dpk_Ah[cell_id](xj[cell_id] - R3{0, 0, 0.1})); + + // max_z_slope_error = + // std::max(max_z_slope_error, frobeniusNorm(reconstructed_slope - R2x2{-1.3, -2.4, // + // +1.4, -1.8})); + // } + // REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); + // } + // } + // } + // } + + // SECTION("vector data") + // { + // using R4 = TinyVector<4>; + + // for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { + // SECTION(named_mesh.name()) + // { + // auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); + // auto& mesh = *p_mesh; + + // auto vector_affine = [](const R3& x) -> R4 { + // return R4{+2.3 + 1.7 * x[0] + 1.2 * x[1] - 1.3 * x[2], -1.7 + 2.1 * x[0] - 2.2 * x[1] - 2.4 * x[2], + // // + // +2.4 - 2.3 * x[0] + 1.3 * x[1] + 1.4 * x[2], -0.2 + 3.1 * x[0] + 0.8 * x[1] - 1.8 * + // x[2]}; + // }; + // auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + // DiscreteFunctionP0Vector<double> Vh{p_mesh, 4}; + + // parallel_for( + // mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + // auto vector = vector_affine(xj[cell_id]); + // for (size_t i = 0; i < vector.dimension(); ++i) { + // Vh[cell_id][i] = vector[i]; + // } + // }); + + // descriptor.setPreconditioning(false); + // auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + // auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const double>>(); + + // { + // double max_mean_error = 0; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // auto vector = vector_affine(xj[cell_id]); + // for (size_t i = 0; i < vector.dimension(); ++i) { + // max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, i)(xj[cell_id]) - + // vector[i])); + // } + // } + // REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + // } + + // { + // double max_x_slope_error = 0; + // const R4 slope{+1.7, 2.1, -2.3, +3.1}; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // for (size_t i = 0; i < slope.dimension(); ++i) { + // const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R3{0.1, 0, 0} + xj[cell_id]) + // - + // dpk_Vh(cell_id, i)(xj[cell_id] - R3{0.1, 0, + // 0})); + + // max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - slope[i])); + // } + // } + // REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); + // } + + // { + // double max_y_slope_error = 0; + // const R4 slope{+1.2, -2.2, 1.3, +0.8}; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // for (size_t i = 0; i < slope.dimension(); ++i) { + // const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R3{0, 0.1, 0} + xj[cell_id]) + // - + // dpk_Vh(cell_id, i)(xj[cell_id] - R3{0, 0.1, + // 0})); + + // max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - slope[i])); + // } + // } + // REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); + // } + + // { + // double max_z_slope_error = 0; + // const R4 slope{-1.3, -2.4, +1.4, -1.8}; + // for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + // for (size_t i = 0; i < slope.dimension(); ++i) { + // const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R3{0, 0, 0.1} + xj[cell_id]) + // - + // dpk_Vh(cell_id, i)(xj[cell_id] - R3{0, 0, + // 0.1})); + + // max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope - slope[i])); + // } + // } + // REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-13)); + // } + // } + // } + // } + } + } + } + } +} diff --git a/tests/test_PugsUtils.cpp b/tests/test_PugsUtils.cpp index fb86486527e3f1d91ce5c2f1e7d2653a1dea3bde..5ba8817fcaa34ddf31f0955ed9fd118f841bd87d 100644 --- a/tests/test_PugsUtils.cpp +++ b/tests/test_PugsUtils.cpp @@ -51,6 +51,7 @@ TEST_CASE("PugsUtils", "[utils]") os << "MPI: " << rang::style::bold << BuildInfo::mpiLibrary() << rang::style::reset << '\n'; os << "PETSc: " << rang::style::bold << BuildInfo::petscLibrary() << rang::style::reset << '\n'; os << "SLEPc: " << rang::style::bold << BuildInfo::slepcLibrary() << rang::style::reset << '\n'; + os << "Eigen3: " << rang::style::bold << BuildInfo::eigen3Library() << rang::style::reset << '\n'; os << "HDF5: " << rang::style::bold << BuildInfo::hdf5Library() << rang::style::reset << '\n'; os << "SLURM: " << rang::style::bold << BuildInfo::slurmLibrary() << rang::style::reset << '\n'; os << "-------------------------------------------------------"; diff --git a/tests/test_StencilBuilder.cpp b/tests/test_StencilBuilder.cpp deleted file mode 100644 index b99ba218aa1e3eb5d8ec884e974cfad2f4a157db..0000000000000000000000000000000000000000 --- a/tests/test_StencilBuilder.cpp +++ /dev/null @@ -1,349 +0,0 @@ -#include <catch2/catch_test_macros.hpp> -#include <catch2/matchers/catch_matchers_all.hpp> - -#include <MeshDataBaseForTests.hpp> -#include <mesh/Connectivity.hpp> -#include <mesh/ConnectivityUtils.hpp> -#include <mesh/ItemValue.hpp> -#include <mesh/ItemValueUtils.hpp> -#include <mesh/Mesh.hpp> -#include <mesh/MeshFlatNodeBoundary.hpp> -#include <mesh/MeshVariant.hpp> -#include <mesh/NamedBoundaryDescriptor.hpp> -#include <mesh/StencilManager.hpp> -#include <utils/Messenger.hpp> - -// clazy:excludeall=non-pod-global-static - -TEST_CASE("StencilBuilder", "[mesh]") -{ - SECTION("inner stencil") - { - auto is_valid = [](const auto& connectivity, const auto& stencil_array) { - auto cell_to_node_matrix = connectivity.cellToNodeMatrix(); - auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); - - auto cell_is_owned = connectivity.cellIsOwned(); - auto cell_number = connectivity.cellNumber(); - - for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { - if (cell_is_owned[cell_id]) { - std::set<CellId, std::function<bool(CellId, CellId)>> cell_set( - [=](CellId cell_0, CellId cell_1) { return cell_number[cell_0] < cell_number[cell_1]; }); - auto cell_nodes = cell_to_node_matrix[cell_id]; - for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { - const NodeId node_id = cell_nodes[i_node]; - auto node_cells = node_to_cell_matrix[node_id]; - for (size_t i_node_cell = 0; i_node_cell < node_cells.size(); ++i_node_cell) { - const CellId node_cell_id = node_cells[i_node_cell]; - if (node_cell_id != cell_id) { - cell_set.insert(node_cell_id); - } - } - } - - auto cell_stencil = stencil_array[cell_id]; - - auto i_set_cell = cell_set.begin(); - for (size_t index = 0; index < cell_stencil.size(); ++index, ++i_set_cell) { - if (*i_set_cell != cell_stencil[index]) { - return false; - } - } - } - } - return true; - }; - - SECTION("1D") - { - SECTION("cartesian") - { - const auto& mesh = *MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>(); - - const Connectivity<1>& connectivity = mesh.connectivity(); - - auto stencil_array = - StencilManager::instance() - .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}); - - REQUIRE(is_valid(connectivity, stencil_array)); - } - - SECTION("unordered") - { - const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); - - const Connectivity<1>& connectivity = mesh.connectivity(); - auto stencil_array = - StencilManager::instance() - .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}); - - REQUIRE(is_valid(connectivity, stencil_array)); - } - } - - SECTION("2D") - { - SECTION("cartesian") - { - const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>(); - - const Connectivity<2>& connectivity = mesh.connectivity(); - REQUIRE( - is_valid(connectivity, - StencilManager::instance() - .getCellToCellStencilArray(connectivity, - StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}))); - } - - SECTION("hybrid") - { - const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); - - const Connectivity<2>& connectivity = mesh.connectivity(); - REQUIRE( - is_valid(connectivity, - StencilManager::instance() - .getCellToCellStencilArray(connectivity, - StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}))); - } - } - - SECTION("3D") - { - SECTION("carteian") - { - const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); - - const Connectivity<3>& connectivity = mesh.connectivity(); - REQUIRE( - is_valid(connectivity, - StencilManager::instance() - .getCellToCellStencilArray(connectivity, - StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}))); - } - - SECTION("hybrid") - { - const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); - - const Connectivity<3>& connectivity = mesh.connectivity(); - REQUIRE( - is_valid(connectivity, - StencilManager::instance() - .getCellToCellStencilArray(connectivity, - StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}))); - } - } - } - - SECTION("Stencil using symmetries") - { - auto check_ghost_cells_have_empty_stencils = [](const auto& stencil_array, const auto& connecticity) { - bool is_empty = true; - - auto cell_is_owned = connecticity.cellIsOwned(); - - for (CellId cell_id = 0; cell_id < connecticity.numberOfCells(); ++cell_id) { - if (not cell_is_owned[cell_id]) { - is_empty &= (stencil_array[cell_id].size() == 0); - for (size_t i_symmetry_stencil = 0; - i_symmetry_stencil < stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry_stencil) { - is_empty &= - (stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry_stencil].stencilArray()[cell_id].size() == - 0); - } - } - } - - return is_empty; - }; - - auto symmetry_stencils_are_valid = [](const auto& stencil_array, const auto& mesh) { - bool is_valid = true; - - auto node_to_cell_matrix = mesh.connectivity().nodeToCellMatrix(); - auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); - auto cell_is_owned = mesh.connectivity().cellIsOwned(); - auto cell_number = mesh.connectivity().cellNumber(); - - for (size_t i_symmetry_stencil = 0; i_symmetry_stencil < stencil_array.symmetryBoundaryStencilArrayList().size(); - ++i_symmetry_stencil) { - const IBoundaryDescriptor& boundary_descriptor = - stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry_stencil].boundaryDescriptor(); - - auto boundary_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry_stencil].stencilArray(); - auto boundary_node_list = getMeshFlatNodeBoundary(mesh, boundary_descriptor); - - CellValue<bool> boundary_cell{mesh.connectivity()}; - boundary_cell.fill(false); - auto node_list = boundary_node_list.nodeList(); - for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { - const NodeId node_id = node_list[i_node]; - auto node_cell_list = node_to_cell_matrix[node_id]; - for (size_t i_cell = 0; i_cell < node_cell_list.size(); ++i_cell) { - const CellId cell_id = node_cell_list[i_cell]; - boundary_cell[cell_id] = true; - } - } - - std::set<NodeId> symmetry_node; - for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { - const NodeId node_id = node_list[i_node]; - symmetry_node.insert(node_id); - } - - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - if ((not boundary_cell[cell_id]) or (not cell_is_owned[cell_id])) { - is_valid &= (boundary_stencil[cell_id].size() == 0); - } else { - auto cell_node_list = cell_to_node_matrix[cell_id]; - std::set<CellId, std::function<bool(CellId, CellId)>> cell_set( - [&](CellId cell0_id, CellId cell1_id) { return cell_number[cell0_id] < cell_number[cell1_id]; }); - for (size_t i_node = 0; i_node < cell_node_list.size(); ++i_node) { - const NodeId node_id = cell_node_list[i_node]; - if (symmetry_node.contains(node_id)) { - auto node_cell_list = node_to_cell_matrix[node_id]; - for (size_t i_node_cell = 0; i_node_cell < node_cell_list.size(); ++i_node_cell) { - const CellId node_cell_id = node_cell_list[i_node_cell]; - cell_set.insert(node_cell_id); - } - } - } - - if (cell_set.size() == boundary_stencil[cell_id].size()) { - size_t i = 0; - for (auto&& id : cell_set) { - is_valid &= (id == boundary_stencil[cell_id][i++]); - } - } else { - is_valid = false; - } - } - } - } - - return is_valid; - }; - - SECTION("1D") - { - StencilManager::BoundaryDescriptorList list; - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); - - SECTION("cartesian") - { - const auto& mesh = *MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>(); - - const Connectivity<1>& connectivity = mesh.connectivity(); - - auto stencil_array = - StencilManager::instance() - .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, - list); - - REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); - REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(symmetry_stencils_are_valid(stencil_array, mesh)); - } - - SECTION("hybrid") - { - const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); - - const Connectivity<1>& connectivity = mesh.connectivity(); - auto stencil = - StencilManager::instance() - .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, - list); - - REQUIRE(stencil.symmetryBoundaryStencilArrayList().size() == 2); - REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); - REQUIRE(symmetry_stencils_are_valid(stencil, mesh)); - } - } - - SECTION("2D") - { - StencilManager::BoundaryDescriptorList list; - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMAX")); - - SECTION("cartesian") - { - const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>(); - - const Connectivity<2>& connectivity = mesh.connectivity(); - - auto stencil_array = - StencilManager::instance() - .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, - list); - - REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); - REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(symmetry_stencils_are_valid(stencil_array, mesh)); - } - - SECTION("hybrid") - { - const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); - - const Connectivity<2>& connectivity = mesh.connectivity(); - auto stencil = - StencilManager::instance() - .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, - list); - - REQUIRE(stencil.symmetryBoundaryStencilArrayList().size() == 4); - REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); - REQUIRE(symmetry_stencils_are_valid(stencil, mesh)); - } - } - - SECTION("3D") - { - StencilManager::BoundaryDescriptorList list; - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMAX")); - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("ZMIN")); - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("ZMAX")); - - SECTION("cartesian") - { - const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); - - const Connectivity<3>& connectivity = mesh.connectivity(); - auto stencil = - StencilManager::instance() - .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, - list); - - REQUIRE(stencil.symmetryBoundaryStencilArrayList().size() == 6); - REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); - REQUIRE(symmetry_stencils_are_valid(stencil, mesh)); - } - - SECTION("hybrid") - { - const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); - - const Connectivity<3>& connectivity = mesh.connectivity(); - auto stencil = - StencilManager::instance() - .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, - list); - - REQUIRE(stencil.symmetryBoundaryStencilArrayList().size() == 6); - REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); - REQUIRE(symmetry_stencils_are_valid(stencil, mesh)); - } - } - } -} diff --git a/tests/test_StencilBuilder_cell2cell.cpp b/tests/test_StencilBuilder_cell2cell.cpp new file mode 100644 index 0000000000000000000000000000000000000000..42ceb6a5da583a9869035db09f5239d008c6f780 --- /dev/null +++ b/tests/test_StencilBuilder_cell2cell.cpp @@ -0,0 +1,1081 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <MeshDataBaseForTests.hpp> +#include <mesh/CartesianMeshBuilder.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/ConnectivityUtils.hpp> +#include <mesh/ItemValue.hpp> +#include <mesh/ItemValueUtils.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshFaceBoundary.hpp> +#include <mesh/MeshVariant.hpp> +#include <mesh/NamedBoundaryDescriptor.hpp> +#include <mesh/StencilManager.hpp> +#include <utils/Messenger.hpp> + +#include <NbGhostLayersTester.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("StencilBuilder cell2cell", "[mesh]") +{ + auto is_valid = []<ItemType connecting_item_type>(const auto& connectivity, const auto& stencil_array, + const size_t number_of_layers) { + auto cell_to_connecting_item_matrix = + connectivity.template getItemToItemMatrix<ItemType::cell, connecting_item_type>(); + auto connecting_to_cell_matrix = connectivity.template getItemToItemMatrix<connecting_item_type, ItemType::cell>(); + auto cell_is_owned = connectivity.cellIsOwned(); + auto cell_number = connectivity.cellNumber(); + + using ConnectingItemId = ItemIdT<connecting_item_type>; + + for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { + if (cell_is_owned[cell_id]) { + std::vector<CellId> expected_stencil; + + std::set<CellId> marked_cell_set; + marked_cell_set.insert(cell_id); + + std::set<ConnectingItemId> marked_connecting_item_set; + std::set<ConnectingItemId> layer_connecting_item_set; + { + auto cell_to_connecting_item_list = cell_to_connecting_item_matrix[cell_id]; + for (size_t i_connecting_item = 0; i_connecting_item < cell_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = cell_to_connecting_item_list[i_connecting_item]; + layer_connecting_item_set.insert(connecting_item_id); + marked_connecting_item_set.insert(connecting_item_id); + } + } + + for (size_t i_layer = 0; i_layer < number_of_layers; ++i_layer) { + std::set<CellId, std::function<bool(CellId, CellId)>> cell_set( + [=](CellId cell_0, CellId cell_1) { return cell_number[cell_0] < cell_number[cell_1]; }); + + for (auto&& connecting_item_id : layer_connecting_item_set) { + auto connecting_item_to_cell_list = connecting_to_cell_matrix[connecting_item_id]; + for (size_t i_connecting_item_of_cell = 0; i_connecting_item_of_cell < connecting_item_to_cell_list.size(); + ++i_connecting_item_of_cell) { + const CellId connecting_item_id_of_cell = connecting_item_to_cell_list[i_connecting_item_of_cell]; + if (not marked_cell_set.contains(connecting_item_id_of_cell)) { + cell_set.insert(connecting_item_id_of_cell); + marked_cell_set.insert(connecting_item_id_of_cell); + } + } + } + + layer_connecting_item_set.clear(); + for (auto layer_cell_id : cell_set) { + auto cell_to_connecting_item_list = cell_to_connecting_item_matrix[layer_cell_id]; + for (size_t i_connecting_item = 0; i_connecting_item < cell_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = cell_to_connecting_item_list[i_connecting_item]; + if (not marked_connecting_item_set.contains(connecting_item_id)) { + layer_connecting_item_set.insert(connecting_item_id); + marked_connecting_item_set.insert(connecting_item_id); + } + } + } + + for (auto&& set_cell_id : cell_set) { + expected_stencil.push_back(set_cell_id); + } + } + + auto cell_stencil = stencil_array[cell_id]; + + auto i_set_cell = expected_stencil.begin(); + if (cell_stencil.size() != expected_stencil.size()) { + return false; + } + + for (size_t index = 0; index < cell_stencil.size(); ++index, ++i_set_cell) { + if (*i_set_cell != cell_stencil[index]) { + return false; + } + } + } + } + return true; + }; + + SECTION("inner stencil") + { + SECTION("1 layer") + { + SECTION("1D") + { + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + const Connectivity<1>& connectivity = mesh.connectivity(); + + REQUIRE( + is_valid.template + operator()<ItemType::node>(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::edge>(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::face>(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } + + SECTION("unordered") + { + const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + const Connectivity<1>& connectivity = mesh.connectivity(); + + REQUIRE( + is_valid.template + operator()<ItemType::node>(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::edge>(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::face>(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } + } + + SECTION("2D") + { + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + REQUIRE( + is_valid.template + operator()<ItemType::node>(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::edge>(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::face>(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } + + SECTION("hybrid") + { + const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + REQUIRE( + is_valid.template + operator()<ItemType::node>(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::edge>(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::face>(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } + } + + SECTION("3D") + { + SECTION("carteian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); + + const Connectivity<3>& connectivity = mesh.connectivity(); + REQUIRE( + is_valid.template + operator()<ItemType::node>(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::edge>(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::face>(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } + + SECTION("hybrid") + { + const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + + const Connectivity<3>& connectivity = mesh.connectivity(); + REQUIRE( + is_valid.template + operator()<ItemType::node>(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::edge>(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::face>(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } + } + } + + SECTION("2 layers") + { + NbGhostLayersTester nb_ghost_layers_tester(2); + + SECTION("1D") + { + SECTION("cartesian") + { + auto mesh_v = CartesianMeshBuilder(TinyVector<1>{0}, TinyVector<1>{1}, TinyVector<1, uint64_t>(20)).mesh(); + + const auto& mesh = *mesh_v->get<Mesh<1>>(); + + REQUIRE( + is_valid.template + operator()<ItemType::node>(mesh.connectivity(), + StencilManager::instance() + .getCellToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_nodes}), + 2)); + + REQUIRE( + is_valid.template + operator()<ItemType::edge>(mesh.connectivity(), + StencilManager::instance() + .getCellToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_edges}), + 2)); + + REQUIRE( + is_valid.template + operator()<ItemType::face>(mesh.connectivity(), + StencilManager::instance() + .getCellToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_faces}), + 2)); + } + } + + SECTION("2D") + { + SECTION("cartesian") + { + auto mesh_v = + CartesianMeshBuilder(TinyVector<2>{0, 0}, TinyVector<2>{1, 2}, TinyVector<2, uint64_t>(5, 7)).mesh(); + const auto& mesh = *mesh_v->get<Mesh<2>>(); + + REQUIRE( + is_valid.template + operator()<ItemType::node>(mesh.connectivity(), + StencilManager::instance() + .getCellToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_nodes}), + 2)); + + REQUIRE( + is_valid.template + operator()<ItemType::edge>(mesh.connectivity(), + StencilManager::instance() + .getCellToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_edges}), + 2)); + + REQUIRE( + is_valid.template + operator()<ItemType::face>(mesh.connectivity(), + StencilManager::instance() + .getCellToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_faces}), + 2)); + } + } + + SECTION("3D") + { + SECTION("carteian") + { + auto mesh_v = + CartesianMeshBuilder(TinyVector<3>{0, 0, 0}, TinyVector<3>{1, 1, 2}, TinyVector<3, uint64_t>(3, 4, 5)) + .mesh(); + const auto& mesh = *mesh_v->get<Mesh<3>>(); + + REQUIRE( + is_valid.template + operator()<ItemType::node>(mesh.connectivity(), + StencilManager::instance() + .getCellToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_nodes}), + 2)); + + REQUIRE( + is_valid.template + operator()<ItemType::edge>(mesh.connectivity(), + StencilManager::instance() + .getCellToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_edges}), + 2)); + + REQUIRE( + is_valid.template + operator()<ItemType::face>(mesh.connectivity(), + StencilManager::instance() + .getCellToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_faces}), + 2)); + } + } + } + } + + SECTION("Stencil using symmetries") + { + auto check_ghost_cells_have_empty_stencils = [](const auto& stencil_array, const auto& connecticity) { + bool is_empty = true; + + auto cell_is_owned = connecticity.cellIsOwned(); + + for (CellId cell_id = 0; cell_id < connecticity.numberOfCells(); ++cell_id) { + if (not cell_is_owned[cell_id]) { + is_empty &= (stencil_array[cell_id].size() == 0); + for (size_t i_symmetry_stencil = 0; + i_symmetry_stencil < stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry_stencil) { + is_empty &= + (stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry_stencil].stencilArray()[cell_id].size() == + 0); + } + } + } + + return is_empty; + }; + + auto are_symmetry_stencils_valid = []<ItemType connecting_item_type>(const auto& stencil_array, const auto& mesh, + const size_t number_of_layers) { + bool are_valid_symmetries = true; + + auto connecting_to_cell_matrix = + mesh.connectivity().template getItemToItemMatrix<connecting_item_type, ItemType::cell>(); + auto cell_to_connecting_item_matrix = + mesh.connectivity().template getItemToItemMatrix<ItemType::cell, connecting_item_type>(); + auto cell_is_owned = mesh.connectivity().cellIsOwned(); + auto cell_number = mesh.connectivity().cellNumber(); + + auto connecting_number = mesh.connectivity().template number<connecting_item_type>(); + + using ConnectingItemId = ItemIdT<connecting_item_type>; + using MeshType = std::decay_t<decltype(mesh)>; + + for (size_t i_symmetry_stencil = 0; i_symmetry_stencil < stencil_array.symmetryBoundaryStencilArrayList().size(); + ++i_symmetry_stencil) { + const IBoundaryDescriptor& boundary_descriptor = + stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry_stencil].boundaryDescriptor(); + + auto boundary_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry_stencil].stencilArray(); + auto boundary_face_list = getMeshFaceBoundary(mesh, boundary_descriptor); + + std::set<ConnectingItemId> sym_connecting_item_set; + + if constexpr (ItemTypeId<MeshType::Dimension>::itemTId(connecting_item_type) == + ItemTypeId<MeshType::Dimension>::itemTId(ItemType::face)) { + for (size_t i_face = 0; i_face < boundary_face_list.faceList().size(); ++i_face) { + const FaceId face_id = boundary_face_list.faceList()[i_face]; + sym_connecting_item_set.insert(ConnectingItemId{FaceId::base_type{face_id}}); + } + + } else { + auto face_to_connecting_item_matrix = + mesh.connectivity().template getItemToItemMatrix<ItemType::face, connecting_item_type>(); + + for (size_t i_face = 0; i_face < boundary_face_list.faceList().size(); ++i_face) { + const FaceId face_id = boundary_face_list.faceList()[i_face]; + auto connecting_item_list = face_to_connecting_item_matrix[face_id]; + for (size_t i_connecting_item = 0; i_connecting_item < connecting_item_list.size(); ++i_connecting_item) { + sym_connecting_item_set.insert(connecting_item_list[i_connecting_item]); + } + } + } + + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + if (not cell_is_owned[cell_id]) { + are_valid_symmetries &= (boundary_stencil[cell_id].size() == 0); + } else { + std::vector<CellId> expected_stencil; + + std::set<CellId> marked_cell_set; + marked_cell_set.insert(cell_id); + + std::set<ConnectingItemId> marked_connecting_item_set; + std::set<ConnectingItemId> layer_connecting_item_set; + { + auto cell_to_connecting_item_list = cell_to_connecting_item_matrix[cell_id]; + for (size_t i_connecting_item = 0; i_connecting_item < cell_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = cell_to_connecting_item_list[i_connecting_item]; + layer_connecting_item_set.insert(connecting_item_id); + marked_connecting_item_set.insert(connecting_item_id); + } + } + + std::set<ConnectingItemId> marked_sym_connecting_item_set; + std::set<ConnectingItemId> layer_sym_connecting_item_set; + + for (auto&& connecting_item_id : marked_connecting_item_set) { + if (sym_connecting_item_set.contains(connecting_item_id)) { + marked_sym_connecting_item_set.insert(connecting_item_id); + layer_sym_connecting_item_set.insert(connecting_item_id); + } + } + + std::set<CellId> marked_sym_cell_set; + + for (size_t i_layer = 0; i_layer < number_of_layers; ++i_layer) { + std::set<CellId, std::function<bool(CellId, CellId)>> cell_set( + [=](CellId cell_0, CellId cell_1) { return cell_number[cell_0] < cell_number[cell_1]; }); + + for (auto&& connecting_item_id : layer_connecting_item_set) { + auto connecting_item_to_cell_list = connecting_to_cell_matrix[connecting_item_id]; + for (size_t i_connecting_item_of_cell = 0; + i_connecting_item_of_cell < connecting_item_to_cell_list.size(); ++i_connecting_item_of_cell) { + const CellId connecting_item_id_of_cell = connecting_item_to_cell_list[i_connecting_item_of_cell]; + if (not marked_cell_set.contains(connecting_item_id_of_cell)) { + cell_set.insert(connecting_item_id_of_cell); + marked_cell_set.insert(connecting_item_id_of_cell); + } + } + } + + std::set<CellId, std::function<bool(CellId, CellId)>> sym_cell_set( + [=](CellId cell_0, CellId cell_1) { return cell_number[cell_0] < cell_number[cell_1]; }); + + for (auto&& connecting_item_id : layer_sym_connecting_item_set) { + auto connecting_item_to_cell_list = connecting_to_cell_matrix[connecting_item_id]; + for (size_t i_connecting_item_of_cell = 0; + i_connecting_item_of_cell < connecting_item_to_cell_list.size(); ++i_connecting_item_of_cell) { + const CellId connecting_item_id_of_cell = connecting_item_to_cell_list[i_connecting_item_of_cell]; + if (not marked_sym_cell_set.contains(connecting_item_id_of_cell)) { + sym_cell_set.insert(connecting_item_id_of_cell); + marked_sym_cell_set.insert(connecting_item_id_of_cell); + } + } + } + + for (auto&& set_sym_cell_id : sym_cell_set) { + expected_stencil.push_back(set_sym_cell_id); + } + + layer_connecting_item_set.clear(); + for (auto&& layer_cell_id : cell_set) { + auto cell_to_connecting_item_list = cell_to_connecting_item_matrix[layer_cell_id]; + for (size_t i_connecting_item = 0; i_connecting_item < cell_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = cell_to_connecting_item_list[i_connecting_item]; + if (not marked_connecting_item_set.contains(connecting_item_id)) { + layer_connecting_item_set.insert(connecting_item_id); + marked_connecting_item_set.insert(connecting_item_id); + } + } + } + + layer_sym_connecting_item_set.clear(); + + for (auto&& connecting_item_id : layer_connecting_item_set) { + if (sym_connecting_item_set.contains(connecting_item_id)) { + if (not marked_sym_connecting_item_set.contains(connecting_item_id)) { + marked_sym_connecting_item_set.insert(connecting_item_id); + layer_sym_connecting_item_set.insert(connecting_item_id); + } + } + } + + for (auto layer_sym_cell_id : sym_cell_set) { + auto cell_to_connecting_item_list = cell_to_connecting_item_matrix[layer_sym_cell_id]; + for (size_t i_connecting_item = 0; i_connecting_item < cell_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = cell_to_connecting_item_list[i_connecting_item]; + if (not marked_sym_connecting_item_set.contains(connecting_item_id)) { + marked_sym_connecting_item_set.insert(connecting_item_id); + layer_sym_connecting_item_set.insert(connecting_item_id); + } + } + } + } + + auto cell_stencil = boundary_stencil[cell_id]; + + if (cell_stencil.size() != expected_stencil.size()) { + are_valid_symmetries = false; + } + + auto i_set_cell = expected_stencil.begin(); + for (size_t index = 0; index < cell_stencil.size(); ++index, ++i_set_cell) { + if (*i_set_cell != cell_stencil[index]) { + are_valid_symmetries = false; + } + } + } + } + } + + return are_valid_symmetries; + }; + + SECTION("1 layer") + { + SECTION("1D") + { + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>(); + + const Connectivity<1>& connectivity = mesh.connectivity(); + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1)); + } + } + + SECTION("unordered") + { + const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + const Connectivity<1>& connectivity = mesh.connectivity(); + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1)); + } + } + } + + SECTION("2D") + { + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMAX")); + + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::edge>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(not(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1))); + REQUIRE(is_valid.template operator()<ItemType::face>(connectivity, stencil_array, 1)); + } + } + + SECTION("hybrid") + { + const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::edge>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(not(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1))); + REQUIRE(is_valid.template operator()<ItemType::face>(connectivity, stencil_array, 1)); + } + } + } + + SECTION("3D") + { + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("ZMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("ZMAX")); + + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); + + const Connectivity<3>& connectivity = mesh.connectivity(); + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::edge>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::face>(connectivity, stencil_array, 1)); + } + } + + SECTION("hybrid") + { + const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + + const Connectivity<3>& connectivity = mesh.connectivity(); + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::edge>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::face>(connectivity, stencil_array, 1)); + } + } + } + } + + SECTION("2 layers") + { + NbGhostLayersTester nb_ghost_layers_tester(2); + + SECTION("1D") + { + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + + SECTION("cartesian") + { + auto mesh_v = CartesianMeshBuilder(TinyVector<1>{0}, TinyVector<1>{1}, TinyVector<1, uint64_t>(20)).mesh(); + const auto& mesh = *mesh_v->get<Mesh<1>>(); + + const Connectivity<1>& connectivity = mesh.connectivity(); + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::node>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 2)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::edge>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 2)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::face>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 2)); + } + } + } + + SECTION("2D") + { + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMAX")); + + SECTION("cartesian") + { + auto mesh_v = + CartesianMeshBuilder(TinyVector<2>{0, 0}, TinyVector<2>{1, 2}, TinyVector<2, uint64_t>(5, 7)).mesh(); + const auto& mesh = *mesh_v->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::node>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 2)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::edge>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::edge>(connectivity, stencil_array, 2)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::face>(stencil_array, mesh, 2)); + REQUIRE(not(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 2))); + REQUIRE(is_valid.template operator()<ItemType::face>(connectivity, stencil_array, 2)); + } + } + } + + SECTION("3D") + { + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("ZMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("ZMAX")); + + SECTION("cartesian") + { + auto mesh_v = + CartesianMeshBuilder(TinyVector<3>{0, 0, 0}, TinyVector<3>{1, 1, 2}, TinyVector<3, uint64_t>(3, 4, 5)) + .mesh(); + const auto& mesh = *mesh_v->get<Mesh<3>>(); + + const Connectivity<3>& connectivity = mesh.connectivity(); + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::node>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 2)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::edge>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::edge>(connectivity, stencil_array, 2)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::face>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::face>(connectivity, stencil_array, 2)); + } + } + } + } + } +} diff --git a/tests/test_StencilBuilder_node2cell.cpp b/tests/test_StencilBuilder_node2cell.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6d9c429c6d4ba903185c90a1e6c37d63bf9c9feb --- /dev/null +++ b/tests/test_StencilBuilder_node2cell.cpp @@ -0,0 +1,1184 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <MeshDataBaseForTests.hpp> +#include <mesh/CartesianMeshBuilder.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/ConnectivityUtils.hpp> +#include <mesh/ItemValue.hpp> +#include <mesh/ItemValueUtils.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshFaceBoundary.hpp> +#include <mesh/MeshVariant.hpp> +#include <mesh/NamedBoundaryDescriptor.hpp> +#include <mesh/StencilManager.hpp> +#include <utils/Messenger.hpp> + +#include <NbGhostLayersTester.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("StencilBuilder node2cell", "[mesh]") +{ + auto is_valid = []<ItemType source_item_type, ItemType connecting_item_type>(const auto& connectivity, + const auto& stencil_array, + const size_t number_of_layers) { + constexpr size_t Dimension = std::decay_t<decltype(connectivity)>::Dimension; + + auto cell_to_connecting_item_matrix = + connectivity.template getItemToItemMatrix<ItemType::cell, connecting_item_type>(); + + auto source_item_to_connecting_item_matrix = + [](const auto& given_connectivity) -> ItemToItemMatrix<source_item_type, connecting_item_type> { + if constexpr (ItemTypeId<Dimension>::itemTId(source_item_type) == + ItemTypeId<Dimension>::itemTId(connecting_item_type)) { + return ConnectivityMatrix{}; + } else { + return given_connectivity.template getItemToItemMatrix<source_item_type, connecting_item_type>(); + } + }(connectivity); + + auto connecting_to_cell_matrix = connectivity.template getItemToItemMatrix<connecting_item_type, ItemType::cell>(); + auto cell_is_owned = connectivity.cellIsOwned(); + auto cell_number = connectivity.cellNumber(); + auto source_item_is_owned = connectivity.template isOwned<source_item_type>(); + + using SourceItemId = ItemIdT<source_item_type>; + using ConnectingItemId = ItemIdT<connecting_item_type>; + + for (SourceItemId source_item_id = 0; source_item_id < connectivity.template numberOf<source_item_type>(); + ++source_item_id) { + if (source_item_is_owned[source_item_id]) { + std::vector<CellId> expected_stencil; + + std::set<CellId> marked_cell_set; + if constexpr (source_item_type == ItemType::cell) { + marked_cell_set.insert(source_item_id); + } + + std::set<ConnectingItemId> marked_connecting_item_set; + std::set<ConnectingItemId> layer_connecting_item_set; + { + if constexpr (ItemTypeId<Dimension>::itemTId(source_item_type) == + ItemTypeId<Dimension>::itemTId(connecting_item_type)) { + ConnectingItemId connecting_id = + ConnectingItemId{static_cast<typename SourceItemId::base_type>(source_item_id)}; + layer_connecting_item_set.insert(connecting_id); + marked_connecting_item_set.insert(connecting_id); + } else { + auto source_item_to_connecting_item_list = source_item_to_connecting_item_matrix[source_item_id]; + for (size_t i_connecting_item = 0; i_connecting_item < source_item_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = source_item_to_connecting_item_list[i_connecting_item]; + layer_connecting_item_set.insert(connecting_item_id); + marked_connecting_item_set.insert(connecting_item_id); + } + } + } + + for (size_t i_layer = 0; i_layer < number_of_layers; ++i_layer) { + std::set<CellId, std::function<bool(CellId, CellId)>> cell_set( + [=](CellId cell_0, CellId cell_1) { return cell_number[cell_0] < cell_number[cell_1]; }); + + for (auto&& connecting_item_id : layer_connecting_item_set) { + auto connecting_item_to_cell_list = connecting_to_cell_matrix[connecting_item_id]; + for (size_t i_connecting_item_of_cell = 0; i_connecting_item_of_cell < connecting_item_to_cell_list.size(); + ++i_connecting_item_of_cell) { + const CellId connecting_item_id_of_cell = connecting_item_to_cell_list[i_connecting_item_of_cell]; + if (not marked_cell_set.contains(connecting_item_id_of_cell)) { + cell_set.insert(connecting_item_id_of_cell); + marked_cell_set.insert(connecting_item_id_of_cell); + } + } + } + + layer_connecting_item_set.clear(); + for (auto layer_cell_id : cell_set) { + auto cell_to_connecting_item_list = cell_to_connecting_item_matrix[layer_cell_id]; + for (size_t i_connecting_item = 0; i_connecting_item < cell_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = cell_to_connecting_item_list[i_connecting_item]; + if (not marked_connecting_item_set.contains(connecting_item_id)) { + layer_connecting_item_set.insert(connecting_item_id); + marked_connecting_item_set.insert(connecting_item_id); + } + } + } + + for (auto&& set_cell_id : cell_set) { + expected_stencil.push_back(set_cell_id); + } + } + + auto cell_stencil = stencil_array[source_item_id]; + + auto i_set_cell = expected_stencil.begin(); + if (cell_stencil.size() != expected_stencil.size()) { + return false; + } + + for (size_t index = 0; index < cell_stencil.size(); ++index, ++i_set_cell) { + if (*i_set_cell != cell_stencil[index]) { + return false; + } + } + } + } + return true; + }; + + SECTION("inner stencil") + { + SECTION("1 layer") + { + SECTION("1D") + { + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + const Connectivity<1>& connectivity = mesh.connectivity(); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::node>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::edge>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::face>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } + + SECTION("unordered") + { + const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + const Connectivity<1>& connectivity = mesh.connectivity(); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::node>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::edge>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::face>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } + } + + SECTION("2D") + { + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::node>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::edge>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::face>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } + + SECTION("hybrid") + { + const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::node>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::edge>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::face>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } + } + + SECTION("3D") + { + SECTION("carteian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); + + const Connectivity<3>& connectivity = mesh.connectivity(); + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::node>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::edge>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::face>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } + + SECTION("hybrid") + { + const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + + const Connectivity<3>& connectivity = mesh.connectivity(); + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::node>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::edge>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::face>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } + } + } + + SECTION("2 layers") + { + NbGhostLayersTester nb_ghost_layers_tester(2); + + SECTION("1D") + { + SECTION("cartesian") + { + auto mesh_v = CartesianMeshBuilder(TinyVector<1>{0}, TinyVector<1>{1}, TinyVector<1, uint64_t>(20)).mesh(); + + const auto& mesh = *mesh_v->get<Mesh<1>>(); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::node>(mesh.connectivity(), + StencilManager::instance() + .getNodeToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_nodes}), + 2)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::edge>(mesh.connectivity(), + StencilManager::instance() + .getNodeToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_edges}), + 2)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::face>(mesh.connectivity(), + StencilManager::instance() + .getNodeToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_faces}), + 2)); + } + } + + SECTION("2D") + { + SECTION("cartesian") + { + auto mesh_v = + CartesianMeshBuilder(TinyVector<2>{0, 0}, TinyVector<2>{1, 2}, TinyVector<2, uint64_t>(5, 7)).mesh(); + const auto& mesh = *mesh_v->get<Mesh<2>>(); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::node>(mesh.connectivity(), + StencilManager::instance() + .getNodeToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_nodes}), + 2)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::edge>(mesh.connectivity(), + StencilManager::instance() + .getNodeToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_edges}), + 2)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::face>(mesh.connectivity(), + StencilManager::instance() + .getNodeToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_faces}), + 2)); + } + } + + SECTION("3D") + { + SECTION("carteian") + { + auto mesh_v = + CartesianMeshBuilder(TinyVector<3>{0, 0, 0}, TinyVector<3>{1, 1, 2}, TinyVector<3, uint64_t>(3, 4, 5)) + .mesh(); + const auto& mesh = *mesh_v->get<Mesh<3>>(); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::node>(mesh.connectivity(), + StencilManager::instance() + .getNodeToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_nodes}), + 2)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::edge>(mesh.connectivity(), + StencilManager::instance() + .getNodeToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_edges}), + 2)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::face>(mesh.connectivity(), + StencilManager::instance() + .getNodeToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_faces}), + 2)); + } + } + } + } + + SECTION("Stencil using symmetries") + { + auto check_ghost_nodes_have_empty_stencils = [](const auto& stencil_array, const auto& connecticity) { + bool is_empty = true; + + auto node_is_owned = connecticity.nodeIsOwned(); + + for (NodeId node_id = 0; node_id < connecticity.numberOfNodes(); ++node_id) { + if (not node_is_owned[node_id]) { + is_empty &= (stencil_array[node_id].size() == 0); + for (size_t i_symmetry_stencil = 0; + i_symmetry_stencil < stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry_stencil) { + is_empty &= + (stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry_stencil].stencilArray()[node_id].size() == + 0); + } + } + } + + return is_empty; + }; + + auto are_symmetry_stencils_valid = + []<ItemType source_item_type, ItemType connecting_item_type>(const auto& stencil_array, const auto& mesh, + const size_t number_of_layers) { + bool are_valid_symmetries = true; + + constexpr const size_t Dimension = std::decay_t<decltype(mesh)>::Dimension; + + auto connecting_to_cell_matrix = + mesh.connectivity().template getItemToItemMatrix<connecting_item_type, ItemType::cell>(); + auto cell_to_connecting_item_matrix = + mesh.connectivity().template getItemToItemMatrix<ItemType::cell, connecting_item_type>(); + // auto source_item_is_owned = mesh.connectivity().cellIsOwned(); + auto cell_number = mesh.connectivity().cellNumber(); + auto source_item_is_owned = mesh.connectivity().template isOwned<source_item_type>(); + + auto connecting_number = mesh.connectivity().template number<connecting_item_type>(); + + auto source_item_to_connecting_item_matrix = + [](const auto& given_connectivity) -> ItemToItemMatrix<source_item_type, connecting_item_type> { + if constexpr (ItemTypeId<Dimension>::itemTId(source_item_type) == + ItemTypeId<Dimension>::itemTId(connecting_item_type)) { + return ConnectivityMatrix{}; + } else { + return given_connectivity.template getItemToItemMatrix<source_item_type, connecting_item_type>(); + } + }(mesh.connectivity()); + + using SourceItemId = ItemIdT<source_item_type>; + using ConnectingItemId = ItemIdT<connecting_item_type>; + using MeshType = std::decay_t<decltype(mesh)>; + + for (size_t i_symmetry_stencil = 0; + i_symmetry_stencil < stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry_stencil) { + const IBoundaryDescriptor& boundary_descriptor = + stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry_stencil].boundaryDescriptor(); + + auto boundary_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry_stencil].stencilArray(); + auto boundary_face_list = getMeshFaceBoundary(mesh, boundary_descriptor); + + std::set<ConnectingItemId> sym_connecting_item_set; + + if constexpr (ItemTypeId<MeshType::Dimension>::itemTId(connecting_item_type) == + ItemTypeId<MeshType::Dimension>::itemTId(ItemType::face)) { + for (size_t i_face = 0; i_face < boundary_face_list.faceList().size(); ++i_face) { + const FaceId face_id = boundary_face_list.faceList()[i_face]; + sym_connecting_item_set.insert(ConnectingItemId{FaceId::base_type{face_id}}); + } + + } else { + auto face_to_connecting_item_matrix = + mesh.connectivity().template getItemToItemMatrix<ItemType::face, connecting_item_type>(); + + for (size_t i_face = 0; i_face < boundary_face_list.faceList().size(); ++i_face) { + const FaceId face_id = boundary_face_list.faceList()[i_face]; + auto connecting_item_list = face_to_connecting_item_matrix[face_id]; + for (size_t i_connecting_item = 0; i_connecting_item < connecting_item_list.size(); ++i_connecting_item) { + sym_connecting_item_set.insert(connecting_item_list[i_connecting_item]); + } + } + } + + for (SourceItemId source_item_id = 0; source_item_id < mesh.template numberOf<source_item_type>(); + ++source_item_id) { + if (not source_item_is_owned[source_item_id]) { + are_valid_symmetries &= (boundary_stencil[source_item_id].size() == 0); + } else { + std::vector<CellId> expected_stencil; + + std::set<CellId> marked_cell_set; + if constexpr (source_item_type == ItemType::cell) { + marked_cell_set.insert(source_item_id); + } + + std::set<ConnectingItemId> marked_connecting_item_set; + std::set<ConnectingItemId> layer_connecting_item_set; + if constexpr (ItemTypeId<Dimension>::itemTId(source_item_type) == + ItemTypeId<Dimension>::itemTId(connecting_item_type)) { + const ConnectingItemId connecting_item_id = + static_cast<typename SourceItemId::base_type>(source_item_id); + layer_connecting_item_set.insert(connecting_item_id); + marked_connecting_item_set.insert(connecting_item_id); + } else { + auto source_item_to_connecting_item_list = source_item_to_connecting_item_matrix[source_item_id]; + for (size_t i_connecting_item = 0; i_connecting_item < source_item_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = source_item_to_connecting_item_list[i_connecting_item]; + layer_connecting_item_set.insert(connecting_item_id); + marked_connecting_item_set.insert(connecting_item_id); + } + } + + std::set<ConnectingItemId> marked_sym_connecting_item_set; + std::set<ConnectingItemId> layer_sym_connecting_item_set; + + for (auto&& connecting_item_id : marked_connecting_item_set) { + if (sym_connecting_item_set.contains(connecting_item_id)) { + marked_sym_connecting_item_set.insert(connecting_item_id); + layer_sym_connecting_item_set.insert(connecting_item_id); + } + } + + std::set<CellId> marked_sym_cell_set; + + for (size_t i_layer = 0; i_layer < number_of_layers; ++i_layer) { + std::set<CellId, std::function<bool(CellId, CellId)>> cell_set( + [=](CellId cell_0, CellId cell_1) { return cell_number[cell_0] < cell_number[cell_1]; }); + + for (auto&& connecting_item_id : layer_connecting_item_set) { + auto connecting_item_to_cell_list = connecting_to_cell_matrix[connecting_item_id]; + for (size_t i_connecting_item_of_cell = 0; + i_connecting_item_of_cell < connecting_item_to_cell_list.size(); ++i_connecting_item_of_cell) { + const CellId connecting_item_id_of_cell = connecting_item_to_cell_list[i_connecting_item_of_cell]; + if (not marked_cell_set.contains(connecting_item_id_of_cell)) { + cell_set.insert(connecting_item_id_of_cell); + marked_cell_set.insert(connecting_item_id_of_cell); + } + } + } + + std::set<CellId, std::function<bool(CellId, CellId)>> sym_cell_set( + [=](CellId cell_0, CellId cell_1) { return cell_number[cell_0] < cell_number[cell_1]; }); + + for (auto&& connecting_item_id : layer_sym_connecting_item_set) { + auto connecting_item_to_cell_list = connecting_to_cell_matrix[connecting_item_id]; + for (size_t i_connecting_item_of_cell = 0; + i_connecting_item_of_cell < connecting_item_to_cell_list.size(); ++i_connecting_item_of_cell) { + const CellId connecting_item_id_of_cell = connecting_item_to_cell_list[i_connecting_item_of_cell]; + if (not marked_sym_cell_set.contains(connecting_item_id_of_cell)) { + sym_cell_set.insert(connecting_item_id_of_cell); + marked_sym_cell_set.insert(connecting_item_id_of_cell); + } + } + } + + for (auto&& set_sym_cell_id : sym_cell_set) { + expected_stencil.push_back(set_sym_cell_id); + } + + layer_connecting_item_set.clear(); + for (auto&& layer_cell_id : cell_set) { + auto cell_to_connecting_item_list = cell_to_connecting_item_matrix[layer_cell_id]; + for (size_t i_connecting_item = 0; i_connecting_item < cell_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = cell_to_connecting_item_list[i_connecting_item]; + if (not marked_connecting_item_set.contains(connecting_item_id)) { + layer_connecting_item_set.insert(connecting_item_id); + marked_connecting_item_set.insert(connecting_item_id); + } + } + } + + layer_sym_connecting_item_set.clear(); + + for (auto&& connecting_item_id : layer_connecting_item_set) { + if (sym_connecting_item_set.contains(connecting_item_id)) { + if (not marked_sym_connecting_item_set.contains(connecting_item_id)) { + marked_sym_connecting_item_set.insert(connecting_item_id); + layer_sym_connecting_item_set.insert(connecting_item_id); + } + } + } + + for (auto layer_sym_cell_id : sym_cell_set) { + auto cell_to_connecting_item_list = cell_to_connecting_item_matrix[layer_sym_cell_id]; + for (size_t i_connecting_item = 0; i_connecting_item < cell_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = cell_to_connecting_item_list[i_connecting_item]; + if (not marked_sym_connecting_item_set.contains(connecting_item_id)) { + marked_sym_connecting_item_set.insert(connecting_item_id); + layer_sym_connecting_item_set.insert(connecting_item_id); + } + } + } + } + + auto cell_stencil = boundary_stencil[source_item_id]; + + if (cell_stencil.size() != expected_stencil.size()) { + are_valid_symmetries = false; + } + + auto i_set_cell = expected_stencil.begin(); + for (size_t index = 0; index < cell_stencil.size(); ++index, ++i_set_cell) { + if (*i_set_cell != cell_stencil[index]) { + are_valid_symmetries = false; + } + } + } + } + } + + return are_valid_symmetries; + }; + + SECTION("1 layer") + { + SECTION("1D") + { + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>(); + + const Connectivity<1>& connectivity = mesh.connectivity(); + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 1)); + } + } + + SECTION("unordered") + { + const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + const Connectivity<1>& connectivity = mesh.connectivity(); + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 1)); + } + } + } + + SECTION("2D") + { + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMAX")); + + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::edge>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::face>(connectivity, stencil_array, 1)); + } + } + + SECTION("hybrid") + { + const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::edge>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::face>(connectivity, stencil_array, 1)); + } + } + } + + SECTION("3D") + { + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("ZMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("ZMAX")); + + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); + + const Connectivity<3>& connectivity = mesh.connectivity(); + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::edge>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::face>(connectivity, stencil_array, 1)); + } + } + + SECTION("hybrid") + { + const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + + const Connectivity<3>& connectivity = mesh.connectivity(); + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::edge>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::face>(connectivity, stencil_array, 1)); + } + } + } + } + + SECTION("2 layers") + { + NbGhostLayersTester nb_ghost_layers_tester(2); + + SECTION("1D") + { + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + + SECTION("cartesian") + { + auto mesh_v = CartesianMeshBuilder(TinyVector<1>{0}, TinyVector<1>{1}, TinyVector<1, uint64_t>(20)).mesh(); + const auto& mesh = *mesh_v->get<Mesh<1>>(); + + const Connectivity<1>& connectivity = mesh.connectivity(); + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::node>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 2)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::edge>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 2)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::face>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 2)); + } + } + } + + SECTION("2D") + { + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMAX")); + + SECTION("cartesian") + { + auto mesh_v = + CartesianMeshBuilder(TinyVector<2>{0, 0}, TinyVector<2>{1, 2}, TinyVector<2, uint64_t>(5, 7)).mesh(); + const auto& mesh = *mesh_v->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::node>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 2)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::edge>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::edge>(connectivity, stencil_array, 2)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::face>(stencil_array, mesh, 2)); + REQUIRE(not(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 2))); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::face>(connectivity, stencil_array, 2)); + } + } + } + + SECTION("3D") + { + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("ZMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("ZMAX")); + + SECTION("cartesian") + { + auto mesh_v = + CartesianMeshBuilder(TinyVector<3>{0, 0, 0}, TinyVector<3>{1, 1, 2}, TinyVector<3, uint64_t>(3, 4, 5)) + .mesh(); + const auto& mesh = *mesh_v->get<Mesh<3>>(); + + const Connectivity<3>& connectivity = mesh.connectivity(); + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::node>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 2)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::edge>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::edge>(connectivity, stencil_array, 2)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::face>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::face>(connectivity, stencil_array, 2)); + } + } + } + } + } +} diff --git a/tests/test_checkpointing_HFTypes.cpp b/tests/test_checkpointing_HFTypes.cpp index 41fc6948c382e3d8f4a3f0bf100cd94405c50875..1a5f02b4a4328910fe9caca1ff3c780954f77197 100644 --- a/tests/test_checkpointing_HFTypes.cpp +++ b/tests/test_checkpointing_HFTypes.cpp @@ -3,6 +3,7 @@ #include <utils/checkpointing/DiscreteFunctionTypeHFType.hpp> #include <utils/checkpointing/DualMeshTypeHFType.hpp> +#include <utils/checkpointing/EigenvalueSolverOptionsHFType.hpp> #include <utils/checkpointing/IBoundaryConditionDescriptorHFType.hpp> #include <utils/checkpointing/IBoundaryDescriptorHFType.hpp> #include <utils/checkpointing/IInterfaceDescriptorHFType.hpp> @@ -160,6 +161,7 @@ TEST_CASE("HFTypes", "[utils/checkpointing]") SECTION("LinearSolverOptionsHFType") { file.createAttribute("builtin", LSLibrary::builtin); + file.createAttribute("eigen3", LSLibrary::eigen3); file.createAttribute("petsc", LSLibrary::petsc); file.createAttribute("cg", LSMethod::cg); @@ -177,6 +179,7 @@ TEST_CASE("HFTypes", "[utils/checkpointing]") REQUIRE(file.getAttribute("builtin").read<LSLibrary>() == LSLibrary::builtin); REQUIRE(file.getAttribute("petsc").read<LSLibrary>() == LSLibrary::petsc); + REQUIRE(file.getAttribute("eigen3").read<LSLibrary>() == LSLibrary::eigen3); REQUIRE(file.getAttribute("cg").read<LSMethod>() == LSMethod::cg); REQUIRE(file.getAttribute("bicgstab").read<LSMethod>() == LSMethod::bicgstab); diff --git a/tests/test_checkpointing_IBoundaryConditionDescriptor.cpp b/tests/test_checkpointing_IBoundaryConditionDescriptor.cpp index 34505db62eff529b480c912ce5ca36714c7bc17b..ab192f387231dba8c5a1802f8d503303852c7b13 100644 --- a/tests/test_checkpointing_IBoundaryConditionDescriptor.cpp +++ b/tests/test_checkpointing_IBoundaryConditionDescriptor.cpp @@ -9,6 +9,7 @@ #include <mesh/NumberedBoundaryDescriptor.hpp> #include <scheme/AxisBoundaryConditionDescriptor.hpp> #include <scheme/DirichletBoundaryConditionDescriptor.hpp> +#include <scheme/DirichletVectorBoundaryConditionDescriptor.hpp> #include <scheme/ExternalBoundaryConditionDescriptor.hpp> #include <scheme/FixedBoundaryConditionDescriptor.hpp> #include <scheme/FourierBoundaryConditionDescriptor.hpp> @@ -179,6 +180,17 @@ let i: R -> R, x -> x+3; p_dirichlet_bc_descriptor)}, file, useless_group, symbol_table_group); + const std::vector<FunctionSymbolId> dirichlet_vector_function_id_list{FunctionSymbolId{2, symbol_table}, + FunctionSymbolId{3, symbol_table}}; + auto p_dirichlet_vector_bc_descriptor = + std::make_shared<const DirichletVectorBoundaryConditionDescriptor>("dirichlet_vector_name", p_boundary_1, + dirichlet_vector_function_id_list); + checkpointing::writeIBoundaryConditionDescriptor("dirichlet_vector_bc_descriptor", + EmbeddedData{std::make_shared< + DataHandler<const IBoundaryConditionDescriptor>>( + p_dirichlet_vector_bc_descriptor)}, + file, useless_group, symbol_table_group); + const FunctionSymbolId neumann_function_id{1, symbol_table}; auto p_neumann_bc_descriptor = std::make_shared<const NeumannBoundaryConditionDescriptor>("neumann_name", p_boundary_1, neumann_function_id); @@ -245,6 +257,9 @@ let i: R -> R, x -> x+3; EmbeddedData read_dirichlet_bc_descriptor = checkpointing::readIBoundaryConditionDescriptor("dirichlet_bc_descriptor", symbol_table_group); + EmbeddedData read_dirichlet_vector_bc_descriptor = + checkpointing::readIBoundaryConditionDescriptor("dirichlet_vector_bc_descriptor", symbol_table_group); + EmbeddedData read_neumann_bc_descriptor = checkpointing::readIBoundaryConditionDescriptor("neumann_bc_descriptor", symbol_table_group); @@ -268,6 +283,7 @@ let i: R -> R, x -> x+3; REQUIRE_NOTHROW(get_value(read_free_bc_descriptor)); REQUIRE_NOTHROW(get_value(read_fixed_bc_descriptor)); REQUIRE_NOTHROW(get_value(read_dirichlet_bc_descriptor)); + REQUIRE_NOTHROW(get_value(read_dirichlet_vector_bc_descriptor)); REQUIRE_NOTHROW(get_value(read_neumann_bc_descriptor)); REQUIRE_NOTHROW(get_value(read_fourier_bc_descriptor)); REQUIRE_NOTHROW(get_value(read_inflow_bc_descriptor)); @@ -280,6 +296,8 @@ let i: R -> R, x -> x+3; REQUIRE(get_value(read_free_bc_descriptor).type() == IBoundaryConditionDescriptor::Type::free); REQUIRE(get_value(read_fixed_bc_descriptor).type() == IBoundaryConditionDescriptor::Type::fixed); REQUIRE(get_value(read_dirichlet_bc_descriptor).type() == IBoundaryConditionDescriptor::Type::dirichlet); + REQUIRE(get_value(read_dirichlet_vector_bc_descriptor).type() == + IBoundaryConditionDescriptor::Type::dirichlet_vector); REQUIRE(get_value(read_neumann_bc_descriptor).type() == IBoundaryConditionDescriptor::Type::neumann); REQUIRE(get_value(read_fourier_bc_descriptor).type() == IBoundaryConditionDescriptor::Type::fourier); REQUIRE(get_value(read_inflow_bc_descriptor).type() == IBoundaryConditionDescriptor::Type::inflow); @@ -293,6 +311,8 @@ let i: R -> R, x -> x+3; REQUIRE_NOTHROW(dynamic_cast<const FixedBoundaryConditionDescriptor&>(get_value(read_fixed_bc_descriptor))); REQUIRE_NOTHROW( dynamic_cast<const DirichletBoundaryConditionDescriptor&>(get_value(read_dirichlet_bc_descriptor))); + REQUIRE_NOTHROW(dynamic_cast<const DirichletVectorBoundaryConditionDescriptor&>( + get_value(read_dirichlet_vector_bc_descriptor))); REQUIRE_NOTHROW(dynamic_cast<const NeumannBoundaryConditionDescriptor&>(get_value(read_neumann_bc_descriptor))); REQUIRE_NOTHROW(dynamic_cast<const FourierBoundaryConditionDescriptor&>(get_value(read_fourier_bc_descriptor))); REQUIRE_NOTHROW(dynamic_cast<const InflowBoundaryConditionDescriptor&>(get_value(read_inflow_bc_descriptor))); @@ -308,6 +328,8 @@ let i: R -> R, x -> x+3; auto& read_fixed_bc = dynamic_cast<const FixedBoundaryConditionDescriptor&>(get_value(read_fixed_bc_descriptor)); auto& read_dirichlet_bc = dynamic_cast<const DirichletBoundaryConditionDescriptor&>(get_value(read_dirichlet_bc_descriptor)); + auto& read_dirichlet_vector_bc = + dynamic_cast<const DirichletVectorBoundaryConditionDescriptor&>(get_value(read_dirichlet_vector_bc_descriptor)); auto& read_neumann_bc = dynamic_cast<const NeumannBoundaryConditionDescriptor&>(get_value(read_neumann_bc_descriptor)); auto& read_fourier_bc = @@ -326,6 +348,15 @@ let i: R -> R, x -> x+3; REQUIRE(read_dirichlet_bc.boundaryDescriptor().type() == p_dirichlet_bc_descriptor->boundaryDescriptor().type()); REQUIRE(read_dirichlet_bc.name() == p_dirichlet_bc_descriptor->name()); REQUIRE(read_dirichlet_bc.rhsSymbolId().id() == p_dirichlet_bc_descriptor->rhsSymbolId().id()); + REQUIRE(read_dirichlet_vector_bc.boundaryDescriptor().type() == + p_dirichlet_vector_bc_descriptor->boundaryDescriptor().type()); + REQUIRE(read_dirichlet_vector_bc.name() == p_dirichlet_vector_bc_descriptor->name()); + REQUIRE(read_dirichlet_vector_bc.rhsSymbolIdList().size() == + p_dirichlet_vector_bc_descriptor->rhsSymbolIdList().size()); + for (size_t i = 0; i < read_dirichlet_vector_bc.rhsSymbolIdList().size(); ++i) { + REQUIRE(read_dirichlet_vector_bc.rhsSymbolIdList()[i].id() == + p_dirichlet_vector_bc_descriptor->rhsSymbolIdList()[i].id()); + } REQUIRE(read_neumann_bc.boundaryDescriptor().type() == p_neumann_bc_descriptor->boundaryDescriptor().type()); REQUIRE(read_neumann_bc.name() == p_neumann_bc_descriptor->name()); REQUIRE(read_neumann_bc.rhsSymbolId().id() == p_neumann_bc_descriptor->rhsSymbolId().id()); @@ -337,6 +368,8 @@ let i: R -> R, x -> x+3; REQUIRE(read_inflow_bc.functionSymbolId().id() == p_inflow_bc_descriptor->functionSymbolId().id()); REQUIRE(read_inflow_list_bc.boundaryDescriptor().type() == p_inflow_list_bc_descriptor->boundaryDescriptor().type()); + REQUIRE(read_inflow_list_bc.functionSymbolIdList().size() == + p_inflow_list_bc_descriptor->functionSymbolIdList().size()); for (size_t i = 0; i < read_inflow_list_bc.functionSymbolIdList().size(); ++i) { REQUIRE(read_inflow_list_bc.functionSymbolIdList()[i].id() == p_inflow_list_bc_descriptor->functionSymbolIdList()[i].id()); diff --git a/tools/generate-plugin.sh b/tools/generate-plugin.sh new file mode 100755 index 0000000000000000000000000000000000000000..275d1795f79753fbd259a2079119d977c89b7d82 --- /dev/null +++ b/tools/generate-plugin.sh @@ -0,0 +1,95 @@ +#!/bin/bash + +BOLD='\e[1m' +RESET='\e[0m' + +GREEN='\e[92m' +RED='\e[91m' +YELLOW='\e[93m' + +echo -ne ${BOLD} +echo -e "---------------------" +echo -e "pugs plugin generator" +echo -e "---------------------" +echo -e ${RESET} + +CURRENT_DIR="$(pwd -P)" +SCRIPT_DIR="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" +PUGS_DIR="$(dirname ${SCRIPT_DIR})" + +if [[ "${CURRENT_DIR}" =~ "${PUGS_DIR}" ]] +then + echo -e ${RED}"Aborting..."${RESET} + echo -e "run this script outside of pugs sources" + exit 1 +fi + +NAME_RE='^[A-Z][a-zA-Z0-9]*$' + +echo " Plugin name must fulfill the following constrains:" +echo " - be a single word that starts by an upper case," +echo " - contains only letters or numbers," +echo " and preferably separate words with caps." +echo +echo " ex.: MyFirstPlugin" +echo + +while [[ ! "${PLUGIN_NAME}" =~ $NAME_RE ]] +do + echo -n "Give plugin name: " + read -r PLUGIN_NAME + + if [[ ! "${PLUGIN_NAME}" =~ $NAME_RE ]] + then + echo -e ${RED}" invalid name!"${RESET} + echo + unset PLUGIN_NAME + fi + +done + +PLUGIN_UP="${PLUGIN_NAME^^}" +PLUGIN_LOW="${PLUGIN_NAME,,}" +echo +echo -e "creating plugin ${YELLOW}${PLUGIN_NAME}${RESET} in directory ${YELLOW}${PLUGIN_LOW}${RESET}" +echo + +if [[ -e ${PLUGIN_LOW} ]] +then + echo -e ${RED}"Aborting..."${RESET} + echo -e "directory \"${PLUGIN_LOW}\" ${YELLOW}already exists${RESET}!" + exit 1 +fi + +function substitute() +{ + sed s/_PLUGIN_NAME_/${PLUGIN_NAME}/g | sed s/_PLUGIN_LOW_/${PLUGIN_LOW}/g | sed s/_PLUGIN_UP_/${PLUGIN_UP}/g +} + +mkdir -p "${PLUGIN_LOW}/cmake" +mkdir -p "${PLUGIN_LOW}/tests" + +cp "${PUGS_DIR}"/tests/MeshDataBaseForTests.hpp "${PLUGIN_LOW}"/tests/ +cp "${PUGS_DIR}"/tests/MeshDataBaseForTests.cpp "${PLUGIN_LOW}"/tests/ +cp "${PUGS_DIR}"/tests/ParallelCheckerTester.hpp "${PLUGIN_LOW}"/tests/ +cp "${PUGS_DIR}"/tests/ParallelCheckerTester.cpp "${PLUGIN_LOW}"/tests/ +cp "${PUGS_DIR}"/tests/test_main.cpp "${PLUGIN_LOW}"/tests/ +cp "${PUGS_DIR}"/tests/mpi_test_main.cpp "${PLUGIN_LOW}"/tests/ + +cp "${PUGS_DIR}"/cmake/CheckNotInSources.cmake "${PLUGIN_LOW}"/cmake/ +cp "${PUGS_DIR}"/tools/plugin-template/FindPugs.cmake "${PLUGIN_LOW}"/cmake/ +cp "${PUGS_DIR}"/.gitignore "${PLUGIN_LOW}" +cp "${PUGS_DIR}"/.clang-format "${PLUGIN_LOW}" + +cat "${PUGS_DIR}"/tools/plugin-template/CMakeLists.txt-template | substitute > "${PLUGIN_LOW}"/CMakeLists.txt +cat "${PUGS_DIR}"/tools/plugin-template/Module.hpp-template | substitute > "${PLUGIN_LOW}"/${PLUGIN_NAME}Module.hpp +cat "${PUGS_DIR}"/tools/plugin-template/Module.cpp-template | substitute > "${PLUGIN_LOW}"/${PLUGIN_NAME}Module.cpp +cat "${PUGS_DIR}"/tools/plugin-template/README.md-template | substitute > "${PLUGIN_LOW}"/README.md + +cat "${PUGS_DIR}"/tools/plugin-template/tests-CMakeLists.txt-template | substitute > "${PLUGIN_LOW}"/tests/CMakeLists.txt + +(cd "${PLUGIN_LOW}"; git init -q) +(cd "${PLUGIN_LOW}"; git add .) +(cd "${PLUGIN_LOW}"; git commit -m "init" -q) + +echo -e ${GREEN}"Creation finished successfully!"${RESET} diff --git a/tools/plugin-template/CMakeLists.txt-template b/tools/plugin-template/CMakeLists.txt-template new file mode 100644 index 0000000000000000000000000000000000000000..9c6478b0711cbbd5e38a41f3eaa6c4736a300e67 --- /dev/null +++ b/tools/plugin-template/CMakeLists.txt-template @@ -0,0 +1,152 @@ +cmake_minimum_required (VERSION 3.19) + +project("_PLUGIN_LOW_") + +set(PUGS_PREFIX_PATH "" CACHE STRING "pugs intall dir") + +string(COMPARE EQUAL "${PUGS_PREFIX_PATH}" "" pugs_prefix_undefined) + +if (pugs_prefix_undefined) + message(FATAL_ERROR "PUGS_PREFIX_PATH must be defined") +endif() + +# CMake utils +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +# Forbids in-source builds +include(CheckNotInSources) + +# use PkgConfig to find packages +find_package(PkgConfig REQUIRED) +find_package(Pugs REQUIRED) + +list(APPEND CMAKE_MODULE_PATH "${PUGS_PREFIX_PATH}/lib/cmake/Kokkos") +include(KokkosConfig) + +set(HDF5_PREFER_PARALLEL TRUE) +list(APPEND CMAKE_MODULE_PATH "${PUGS_PREFIX_PATH}/lib/cmake/HighFive") +include(HighFiveConfig) + +list(APPEND CMAKE_MODULE_PATH "${PUGS_PREFIX_PATH}/lib/cmake/pugs") +include(PugsTargets) +include(PugsCompileFlags) + +#------------------------------------------------------ + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${PUGS_CMAKE_CXX_FLAGS}") +set(CMAKE_CXX_STANDARD "${PUGS_CMAKE_CXX_STANDARD}") + +# ----------------------------------------------------- +# dynamic libraries + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +link_libraries("-rdynamic") +set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") + +#------------------------------------------------------ + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +#------------------------------------------------------ + +set(_PLUGIN_UP__SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") +set(_PLUGIN_UP__BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}") + +#------------------------------------------------------ +# use pugs compilation settings + +set(CMAKE_BUILD_TYPE "${PUGS_CMAKE_BUILD_TYPE}" CACHE STRING "" FORCE) +set(CMAKE_CXX_COMPILER "${PUGS_CMAKE_CXX_COMPILER}" CACHE STRING "" FORCE) +set(CMAKE_C_COMPILER "${PUGS_CMAKE_C_COMPILER}" CACHE STRING "" FORCE) + +mark_as_advanced(CMAKE_BUILD_TYPE CMAKE_CXX_COMPILER CMAKE_C_COMPILER) + +#------------------------------------------------------ +# default build shared libraries +set(BUILD_SHARED_LIBS ON CACHE STRING "" FORCE) + +#------------------------------------------------------ + +# Checks if compiler version is compatible with Pugs sources +set(GNU_CXX_MIN_VERSION "10.0.0") +set(CLANG_CXX_MIN_VERSION "11.0.0") + +#------------------------------------------------------ + +include_directories("${CMAKE_CURRENT_SOURCE_DIR}") +include_directories(SYSTEM "${PUGS_PREFIX_PATH}/include") +include_directories(SYSTEM "${PUGS_PREFIX_PATH}/include/kokkos") +include_directories(SYSTEM "${PUGS_PREFIX_PATH}/include/tao/") +include_directories(SYSTEM "${MPI_CXX_INCLUDE_DIRS}") + +get_target_property(_prop Pugs::PugsAlgebra INTERFACE_INCLUDE_DIRECTORIES) +set(PUGS_INC_DIR "${PUGS_INC_DIR};${_prop}") +get_target_property(_prop Pugs::PugsUtils INTERFACE_INCLUDE_DIRECTORIES) +set(PUGS_INC_DIR "${PUGS_INC_DIR};${_prop}") +get_target_property(_prop Pugs::pugs INTERFACE_INCLUDE_DIRECTORIES) +set(PUGS_INC_DIR "${PUGS_INC_DIR};${_prop}") + +include_directories(SYSTEM ${PUGS_INC_DIR}) +link_directories(${PUGS_PREFIX_PATH}/lib) + +#------------------------------------------------------ + +if(${PUGS_HAS_MPI}) + set(MPIEXEC_OPTION_FLAGS --oversubscribe) + if (NOT "$ENV{GITLAB_CI}" STREQUAL "") + set(MPIEXEC_OPTION_FLAGS ${MPIEXEC_OPTION_FLAGS} --allow-run-as-root) + endif() + set(MPIEXEC_NUMPROC 3) + set(MPIEXEC_PATH_FLAG --path) + set(MPI_LAUNCHER ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MPIEXEC_NUMPROC} ${MPIEXEC_OPTION_FLAGS}) +endif() + +add_custom_target(all_unit_tests + DEPENDS unit_tests mpi_unit_tests +) + +add_custom_target(check + DEPENDS test + ) + +add_custom_target(test + DEPENDS run_all_unit_tests + ) + +add_custom_target(run_all_unit_tests + DEPENDS run_mpi_unit_tests + ) + +if(PUGS_HAS_MPI) + set(RUN_MPI_UNIT_TESTS_COMMENT "Running mpi_unit_tests [using ${MPIEXEC_NUMPROC} procs]") +else() + set(RUN_MPI_UNIT_TESTS_COMMENT "Running mpi_unit_tests [sequentially]") +endif() + +add_custom_target(run_mpi_unit_tests + COMMAND ${MPI_LAUNCHER} "${_PLUGIN_UP__BINARY_DIR}/mpi_unit_tests" --allow-running-no-tests + WORKING_DIRECTORY ${_PLUGIN_UP__BINARY_DIR} + DEPENDS run_unit_tests + COMMENT ${RUN_MPI_UNIT_TESTS_COMMENT} + ) + +add_custom_target(run_unit_tests + COMMAND "${_PLUGIN_UP__BINARY_DIR}/unit_tests" --allow-running-no-tests + DEPENDS all_unit_tests + COMMENT "Running unit_tests" + ) + +#------------------------------------------------------ + +add_library(_PLUGIN_NAME_ + _PLUGIN_NAME_Module.cpp + # add cpp sources files here +) + +#------------------------------------------------------ + +add_subdirectory(tests) + +#------------------------------------------------------ + +install(TARGETS _PLUGIN_NAME_) diff --git a/tools/plugin-template/FindPugs.cmake b/tools/plugin-template/FindPugs.cmake new file mode 100644 index 0000000000000000000000000000000000000000..6269bed8a9055339894cdd9fa9e63b2b479c9b70 --- /dev/null +++ b/tools/plugin-template/FindPugs.cmake @@ -0,0 +1,10 @@ +# Finds for the pugs installation directory + +find_path(PUGS_PREFIX_PATH include/utils/pugs_version.hpp + HINTS + $ENV{PUGS_INSTALL_DIR} + /usr/local/pugs + NO_DEFAULT_PATH +) + +find_package_handle_standard_args(Pugs REQUIRED_VARS PUGS_PREFIX_PATH ) diff --git a/tools/plugin-template/Module.cpp-template b/tools/plugin-template/Module.cpp-template new file mode 100644 index 0000000000000000000000000000000000000000..dd264e03631590e3cf0db152779e2118df86d474 --- /dev/null +++ b/tools/plugin-template/Module.cpp-template @@ -0,0 +1,28 @@ +#include <_PLUGIN_NAME_Module.hpp> + +#include <language/modules/ModuleRepository.hpp> +#include <language/utils/BuiltinFunctionEmbedder.hpp> + +_PLUGIN_NAME_Module::_PLUGIN_NAME_Module() : BuiltinModule(false) +{ + // Simple hello world example + this->_addBuiltinFunction("_PLUGIN_LOW__hello", std::function( + + []() -> void { std::cout << "_PLUGIN_NAME_: Hello world\n"; } + + )); +} + +void +_PLUGIN_NAME_Module::registerOperators() const +{ + // Kept empty for basic use +} + +void +_PLUGIN_NAME_Module::registerCheckpointResume() const +{ + // kept empty for basic use +} + +ModuleRepository::Subscribe<_PLUGIN_NAME_Module> _PLUGIN_LOW__module; diff --git a/tools/plugin-template/Module.hpp-template b/tools/plugin-template/Module.hpp-template new file mode 100644 index 0000000000000000000000000000000000000000..757e3b5e650390251d15515cfdcf26a9c970a102 --- /dev/null +++ b/tools/plugin-template/Module.hpp-template @@ -0,0 +1,22 @@ +#ifndef _PLUGIN_UP__MODULE_HPP +#define _PLUGIN_UP__MODULE_HPP + +#include <language/modules/BuiltinModule.hpp> + +class _PLUGIN_NAME_Module : public BuiltinModule +{ + public: + std::string_view + name() const final + { + return "_PLUGIN_LOW_"; + } + + void registerOperators() const final; + void registerCheckpointResume() const final; + + _PLUGIN_NAME_Module(); + ~_PLUGIN_NAME_Module() = default; +}; + +#endif // _PLUGIN_UP__MODULE_HPP diff --git a/tools/plugin-template/README.md-template b/tools/plugin-template/README.md-template new file mode 100644 index 0000000000000000000000000000000000000000..48485137f1b8c9f8063fec872aa38e15916d9350 --- /dev/null +++ b/tools/plugin-template/README.md-template @@ -0,0 +1,70 @@ +`pugs`'s plugin `_PLUGIN_NAME_` +=============================== + +# Building `_PLUGIN_NAME_` + +## `pugs` installation + +Building this plugin requires an **installed** version of `pugs`. +`pugs` follows standard `cmake` installation recipes. + +Before building `pugs` one should define its installation directory. +In the `pugs` compilation directory one should execute +```shell +cmake -DCMAKE_INSTALL_PREFIX=[pugs_install_dir] [pugs_src_path] [...] +``` +where `[pugs_install_dir]` is the chosen installation directory. + +Then one simply runs +```shell +make install +``` + +## building the plugin `_PLUGIN_NAME_` + +> **Warning**:<br> +> Building `_PLUGIN_NAME_` in its source directory is +> **forbidden**. Trying to do so will result in a failure. However it +> generally leaves some garbage files in the source directory, namely +> the `CMakeCache.txt` and the `CMakeFiles` directory. `CMake` itself +> is not able to remove them, to avoid the risk of compilation issues, +> one has to dot it manually... + +In the plugin build directory one runs +```shell +cmake -DPUGS_PREFIX_PATH=[pugs_install_dir] [_PLUGIN_LOW__src_dir] +``` +where `[pugs_install_dir]` has the same value as above and `[_PLUGIN_LOW__src_dir]` +is the plugin source directory that contains this `README.md` file. + +Then to build the plugin, one runs +```shell +make +``` + +If anything runs fine, the dynamic library `lib_PLUGIN_NAME_.so` is +built. + +# Using `_PLUGIN_NAME_` + +In order to use the created plugin, one simply has to give the +location of `lib_PLUGIN_NAME_.so` to `pugs`. This is done by means of +environment variables. There are two possibilities: +- `PUGS_PLUGIN` contains a semicolumn separated list of plugin + libraries, +- `PUGS_PLUGIN_DIR` contains a semicolumn separated list of path to + plugin libraries. + +Example +```shell +export PUGS_PLUGIN="/pathtoplugin/lib_PLUGIN_NAME_.so" +``` +or +```shell +export PUGS_PLUGIN_DIR="/pathtoplugin1;/pathtoplugin2" +``` + +Then one launches `pugs` classically. +```shell +[pugs_install_dir]/bin/pugs [script.pgs] +``` diff --git a/tools/plugin-template/tests-CMakeLists.txt-template b/tools/plugin-template/tests-CMakeLists.txt-template new file mode 100644 index 0000000000000000000000000000000000000000..026ff2c2962ed988d35e2680b8b97919801c7a4b --- /dev/null +++ b/tools/plugin-template/tests-CMakeLists.txt-template @@ -0,0 +1,95 @@ +set(EXECUTABLE_OUTPUT_PATH ${_PLUGIN_UP__BINARY_DIR}) + +include_directories(${_PLUGIN_UP__SOURCE_DIR}/tests) + +add_executable (unit_tests + test_main.cpp + + # add unit tests here + ) + + set(_PLUGIN_UP__checkpointing_TESTS + ) + +if(PUGS_HAS_HDF5) + list(APPEND _PLUGIN_UP__checkpointing_TESTS + ) +endif(PUGS_HAS_HDF5) + +add_executable (mpi_unit_tests + mpi_test_main.cpp + ${_PLUGIN_UP__checkpointing_TESTS} + + # add mpi unit tests here +) + +add_library(test_MeshDataBase + MeshDataBaseForTests.cpp) + +add_library(test_ParallelCheckerTester + ParallelCheckerTester.cpp) + +target_link_libraries (test_ParallelCheckerTester + ${HIGHFIVE_TARGET}) + +target_link_libraries (unit_tests + test_MeshDataBase + test_ParallelCheckerTester + PugsAlgebra + PugsAnalysis + PugsUtils + PugsLanguage + PugsLanguageAST + PugsLanguageModules + PugsMesh + PugsAlgebra + PugsUtils + PugsLanguageUtils + PugsScheme + PugsOutput + PugsUtils + PugsCheckpointing + PugsDev + PugsAlgebra + PugsMesh + Kokkos::kokkos + ${PARMETIS_LIBRARIES} + ${MPI_CXX_LINK_FLAGS} ${MPI_CXX_LIBRARIES} + ${PETSC_LIBRARIES} + Catch2 + ${PUGS_STD_LINK_FLAGS} + ${HIGHFIVE_TARGET} + ${SLURM_LIBRARY} + stdc++fs + ) + +target_link_libraries (mpi_unit_tests + test_MeshDataBase + test_ParallelCheckerTester + PugsAlgebra + PugsAnalysis + PugsUtils + PugsLanguage + PugsLanguageAST + PugsLanguageModules + PugsMesh + PugsAlgebra + PugsUtils + PugsLanguageUtils + PugsScheme + PugsOutput + PugsUtils + PugsCheckpointing + PugsDev + PugsAlgebra + PugsMesh + Kokkos::kokkos + ${PARMETIS_LIBRARIES} + ${MPI_CXX_LINK_FLAGS} ${MPI_CXX_LIBRARIES} + ${PETSC_LIBRARIES} + Catch2 + ${PUGS_STD_LINK_FLAGS} + ${HIGHFIVE_TARGET} + ${SLURM_LIBRARY} + stdc++fs + )