diff --git a/CMakeLists.txt b/CMakeLists.txt index d3306db95476b2e91773271f548b3937a417b421..bb8832a50565bde6f37ab589737a15e08f116030 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -737,6 +737,7 @@ target_link_libraries( PugsMesh PugsAlgebra PugsScheme + PugsSchemeReconstructionUtils PugsUtils PugsOutput PugsLanguageUtils @@ -774,6 +775,7 @@ target_link_libraries( PugsLanguageModules PugsLanguageUtils PugsScheme + PugsSchemeReconstructionUtils PugsDev PugsAnalysis PugsAlgebra @@ -816,6 +818,7 @@ install(TARGETS PugsMesh PugsOutput PugsScheme + PugsSchemeReconstructionUtils kokkos Catch2 diff --git a/src/geometry/LineTransformation.hpp b/src/geometry/LineTransformation.hpp index b9e9ada96ca028f07497c910f06476f8b67eb61f..e3cb676e1aacae5965d649cf1e0ce0449e8f971c 100644 --- a/src/geometry/LineTransformation.hpp +++ b/src/geometry/LineTransformation.hpp @@ -24,12 +24,20 @@ class LineTransformation<1> return m_jacobian * x + m_shift; } + PUGS_INLINE double jacobianDeterminant() const { return m_jacobian; } + PUGS_INLINE + double + jacobianDeterminant(const TinyVector<1>&) const + { + return m_jacobian; + } + PUGS_INLINE LineTransformation(const TinyVector<Dimension>& a, const TinyVector<Dimension>& b) { diff --git a/src/geometry/SymmetryUtils.hpp b/src/geometry/SymmetryUtils.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6524a0791aef79b5440110d7180a472865c12d89 --- /dev/null +++ b/src/geometry/SymmetryUtils.hpp @@ -0,0 +1,32 @@ +#ifndef SYMMETRY_UTILS_HPP +#define SYMMETRY_UTILS_HPP + +#include <algebra/TinyMatrix.hpp> +#include <algebra/TinyVector.hpp> +#include <utils/PugsMacros.hpp> + +template <size_t Dimension> +PUGS_INLINE auto +symmetrize_vector(const TinyVector<Dimension>& normal, const TinyVector<Dimension>& u) +{ + 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, + const TinyVector<Dimension>& normal, + const TinyVector<Dimension>& u) +{ + return u - 2 * dot(u - origin, normal) * normal; +} + +#endif // SYMMETRY_UTILS_HPP diff --git a/src/geometry/TetrahedronTransformation.hpp b/src/geometry/TetrahedronTransformation.hpp index 64244266957a350e8be8f78c2eab3d5fead629e1..a81a344bfbabd0999e45b1bcab0b52519f9db4ed 100644 --- a/src/geometry/TetrahedronTransformation.hpp +++ b/src/geometry/TetrahedronTransformation.hpp @@ -24,12 +24,20 @@ class TetrahedronTransformation return m_jacobian * x + m_shift; } + PUGS_INLINE double jacobianDeterminant() const { return m_jacobian_determinant; } + PUGS_INLINE + double + jacobianDeterminant(const TinyVector<3>&) const + { + return m_jacobian_determinant; + } + PUGS_INLINE TetrahedronTransformation(const TinyVector<Dimension>& a, const TinyVector<Dimension>& b, diff --git a/src/geometry/TriangleTransformation.hpp b/src/geometry/TriangleTransformation.hpp index 9210e0bda55bb19dfdfd877e509801fa87f0a4f0..df8444d894a7ed56c0fe3825ffbf1cc14a742531 100644 --- a/src/geometry/TriangleTransformation.hpp +++ b/src/geometry/TriangleTransformation.hpp @@ -26,12 +26,20 @@ class TriangleTransformation<2> return m_jacobian * x + m_shift; } + PUGS_INLINE double jacobianDeterminant() const { return m_jacobian_determinant; } + PUGS_INLINE + double + jacobianDeterminant(const TinyVector<2>&) const + { + return m_jacobian_determinant; + } + PUGS_INLINE TriangleTransformation(const TinyVector<Dimension>& a, const TinyVector<Dimension>& b, const TinyVector<Dimension>& c) { diff --git a/src/mesh/StencilBuilder.cpp b/src/mesh/StencilBuilder.cpp index 20fc3bf61dd6a59ef82ff7bdb25dc5e32c7eef24..de7501d44b5b0aef6e50f830460e9be110456331 100644 --- a/src/mesh/StencilBuilder.cpp +++ b/src/mesh/StencilBuilder.cpp @@ -447,8 +447,7 @@ StencilBuilder::_build_for_same_source_and_target(const ConnectivityType& connec } 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]; }); + 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}; @@ -743,8 +742,7 @@ StencilBuilder::_build_for_different_source_and_target( } 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]; }); + 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}; @@ -836,7 +834,7 @@ StencilBuilder::buildC2C(const IConnectivity& connectivity, 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() + 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)."; @@ -870,7 +868,7 @@ StencilBuilder::buildN2C(const IConnectivity& connectivity, 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() + 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)."; diff --git a/src/scheme/CMakeLists.txt b/src/scheme/CMakeLists.txt index b533ab51516484131ea8909411c8b3c5b5f77098..8cf2d535895a555e172d33f6c11c20483995d9ce 100644 --- a/src/scheme/CMakeLists.txt +++ b/src/scheme/CMakeLists.txt @@ -1,5 +1,7 @@ # ------------------- Source files -------------------- +add_subdirectory(reconstruction_utils) + add_library( PugsScheme AcousticSolver.cpp @@ -24,3 +26,9 @@ target_link_libraries( PugsScheme ${HIGHFIVE_TARGET} ) + +# Additional dependencies +add_dependencies( + PugsScheme + PugsSchemeReconstructionUtils +) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index eea42b65b93e20d03a4db2857ba1f2e32447d43f..c646dc4fbec060bf5983a24ec33ca8bdb2718725 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -2,21 +2,8 @@ #include <algebra/Givens.hpp> #include <algebra/ShrinkMatrixView.hpp> -#include <algebra/ShrinkVectorView.hpp> #include <algebra/SmallMatrix.hpp> -#include <analysis/GaussLegendreQuadratureDescriptor.hpp> -#include <analysis/GaussQuadratureDescriptor.hpp> -#include <analysis/QuadratureFormula.hpp> -#include <analysis/QuadratureManager.hpp> -#include <geometry/CubeTransformation.hpp> -#include <geometry/LineCubicTransformation.hpp> -#include <geometry/LineParabolicTransformation.hpp> -#include <geometry/LineTransformation.hpp> -#include <geometry/PrismTransformation.hpp> -#include <geometry/PyramidTransformation.hpp> -#include <geometry/SquareTransformation.hpp> -#include <geometry/TetrahedronTransformation.hpp> -#include <geometry/TriangleTransformation.hpp> +#include <geometry/SymmetryUtils.hpp> #include <mesh/MeshData.hpp> #include <mesh/MeshDataManager.hpp> #include <mesh/MeshFlatFaceBoundary.hpp> @@ -26,867 +13,532 @@ #include <scheme/DiscreteFunctionDPkVariant.hpp> #include <scheme/DiscreteFunctionUtils.hpp> #include <scheme/DiscreteFunctionVariant.hpp> +#include <scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp> +#include <scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.hpp> +#include <scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp> +#include <scheme/reconstruction_utils/MutableDiscreteFunctionDPkVariant.hpp> -template <size_t Dimension> -PUGS_INLINE auto -symmetrize_vector(const TinyVector<Dimension>& normal, const TinyVector<Dimension>& u) -{ - 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, - const TinyVector<Dimension>& normal, - const TinyVector<Dimension>& u) -{ - return u - 2 * dot(u - origin, normal) * normal; -} - -class PolynomialReconstruction::MutableDiscreteFunctionDPkVariant +template <MeshConcept MeshType> +class PolynomialReconstruction::Internal { - public: - using Variant = std::variant<DiscreteFunctionDPk<1, double>, - DiscreteFunctionDPk<1, TinyVector<1>>, - DiscreteFunctionDPk<1, TinyVector<2>>, - DiscreteFunctionDPk<1, TinyVector<3>>, - DiscreteFunctionDPk<1, TinyMatrix<1>>, - DiscreteFunctionDPk<1, TinyMatrix<2>>, - DiscreteFunctionDPk<1, TinyMatrix<3>>, - - DiscreteFunctionDPk<2, double>, - DiscreteFunctionDPk<2, TinyVector<1>>, - DiscreteFunctionDPk<2, TinyVector<2>>, - DiscreteFunctionDPk<2, TinyVector<3>>, - DiscreteFunctionDPk<2, TinyMatrix<1>>, - DiscreteFunctionDPk<2, TinyMatrix<2>>, - DiscreteFunctionDPk<2, TinyMatrix<3>>, - - DiscreteFunctionDPk<3, double>, - DiscreteFunctionDPk<3, TinyVector<1>>, - DiscreteFunctionDPk<3, TinyVector<2>>, - DiscreteFunctionDPk<3, TinyVector<3>>, - DiscreteFunctionDPk<3, TinyMatrix<1>>, - DiscreteFunctionDPk<3, TinyMatrix<2>>, - 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<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; + using Rd = TinyVector<MeshType::Dimension>; - public: - PUGS_INLINE - const Variant& - mutableDiscreteFunctionDPk() const - { - return m_mutable_discrete_function_dpk; - } + friend PolynomialReconstruction; - template <typename DiscreteFunctionDPkT> - PUGS_INLINE auto&& - get() const + template <typename MatrixType> + static void + buildB(const CellId& cell_j_id, + const CellToCellStencilArray& stencil_array, + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list, + SmallArray<const Rd> symmetry_normal_list, + ShrinkMatrixView<MatrixType>& B) { - static_assert(is_discrete_function_dpk_v<DiscreteFunctionDPkT>, "invalid template argument"); - -#ifndef NDEBUG - if (not std::holds_alternative<DiscreteFunctionDPkT>(this->m_mutable_discrete_function_dpk)) { - std::ostringstream error_msg; - error_msg << "invalid discrete function type\n"; - error_msg << "- required " << rang::fgB::red << demangle<DiscreteFunctionDPkT>() << rang::fg::reset << '\n'; - error_msg << "- contains " << rang::fgB::yellow - << std::visit([](auto&& f) -> std::string { return demangle<decltype(f)>(); }, - this->m_mutable_discrete_function_dpk) - << rang::fg::reset; - throw NormalError(error_msg.str()); - } -#endif // NDEBUG - - return std::get<DiscreteFunctionDPkT>(this->mutableDiscreteFunctionDPk()); - } + auto stencil_cell_list = stencil_array[cell_j_id]; + + size_t column_begin = 0; + for (size_t i_discrete_function_variant = 0; i_discrete_function_variant < discrete_function_variant_list.size(); + ++i_discrete_function_variant) { + const auto& discrete_function_variant = discrete_function_variant_list[i_discrete_function_variant]; + + std::visit( + [&](auto&& discrete_function) { + using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; + if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { + using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; + const DataType& qj = 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]; + const DataType& qi_qj = discrete_function[cell_i_id] - qj; + if constexpr (std::is_arithmetic_v<DataType>) { + B(index, column_begin) = qi_qj; + } else if constexpr (is_tiny_vector_v<DataType>) { + for (size_t kB = column_begin, k = 0; k < DataType::Dimension; ++k, ++kB) { + B(index, kB) = qi_qj[k]; + } + } else if constexpr (is_tiny_matrix_v<DataType>) { + for (size_t p = 0; p < DataType::NumberOfRows; ++p) { + const size_t kB = column_begin + p * DataType::NumberOfColumns; + for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { + B(index, kB + q) = qi_qj(p, q); + } + } + } + } - template <size_t Dimension, typename DataType> - MutableDiscreteFunctionDPkVariant(const DiscreteFunctionDPk<Dimension, DataType>& discrete_function_dpk) - : m_mutable_discrete_function_dpk{discrete_function_dpk} - { - 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>>, - "DiscreteFunctionDPk with this DataType is not allowed in variant"); - } + 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>) { + const DataType& qi_qj = discrete_function[cell_i_id] - qj; + B(index, column_begin) = qi_qj; + } else if constexpr (is_tiny_vector_v<DataType>) { + if constexpr (DataType::Dimension == MeshType::Dimension) { + const Rd& normal = symmetry_normal_list[i_symmetry]; - template <size_t Dimension, typename DataType> - MutableDiscreteFunctionDPkVariant(const DiscreteFunctionDPkVector<Dimension, DataType>& discrete_function_dpk) - : m_mutable_discrete_function_dpk{discrete_function_dpk} - { - 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"); - } + const DataType& qi = discrete_function[cell_i_id]; + const DataType& qi_qj = symmetrize_vector(normal, qi) - qj; + for (size_t kB = column_begin, 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>) { + if constexpr ((DataType::NumberOfColumns == DataType::NumberOfRows) and + (DataType::NumberOfColumns == MeshType::Dimension)) { + 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 p = 0; p < DataType::NumberOfRows; ++p) { + for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { + B(index, column_begin + p * DataType::NumberOfColumns + q) = qi_qj(p, q); + } + } + } 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 + } + } + } + } - MutableDiscreteFunctionDPkVariant& operator=(MutableDiscreteFunctionDPkVariant&&) = default; - MutableDiscreteFunctionDPkVariant& operator=(const MutableDiscreteFunctionDPkVariant&) = default; + if constexpr (std::is_arithmetic_v<DataType>) { + ++column_begin; + } else if constexpr (is_tiny_vector_v<DataType> or is_tiny_matrix_v<DataType>) { + column_begin += DataType::Dimension; + } + } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { + using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; - MutableDiscreteFunctionDPkVariant(const MutableDiscreteFunctionDPkVariant&) = default; - MutableDiscreteFunctionDPkVariant(MutableDiscreteFunctionDPkVariant&&) = default; + const auto qj_vector = discrete_function[cell_j_id]; - MutableDiscreteFunctionDPkVariant() = delete; - ~MutableDiscreteFunctionDPkVariant() = default; -}; + 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; + } + } -template <typename ConformTransformationT> -void -_computeEjkMean(const QuadratureFormula<1>& quadrature, - const ConformTransformationT& T, - const TinyVector<1>& Xj, - const double Vi, - const size_t degree, - const size_t dimension, - SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk) -{ - using Rd = TinyVector<1>; - mean_of_ejk.fill(0); + 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_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { - const double wq = quadrature.weight(i_q); - const Rd& xi_q = quadrature.point(i_q); - const double detT = T.jacobianDeterminant(); + 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 X_Xj = T(xi_q) - Xj; + const Rd& normal = symmetry_normal_list[i_symmetry]; - const double x_xj = X_Xj[0]; + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; - { - size_t k = 0; - inv_Vj_wq_detJ_ek[k++] = wq * detT / Vi; - for (; k <= degree; ++k) { - inv_Vj_wq_detJ_ek[k] = x_xj * inv_Vj_wq_detJ_ek[k - 1]; - } - } + 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>) { + 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 = discrete_function[cell_i_id][l]; + const DataType& qi_qj = qi - qj; + + for (size_t p = 0; p < DataType::NumberOfRows; ++p) { + const size_t kB = column_begin + l * DataType::Dimension + p * DataType::NumberOfColumns; + for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { + B(index, kB + q) = qi_qj(p, q); + } + } + } + } - for (size_t k = 1; k < dimension; ++k) { - mean_of_ejk[k - 1] += inv_Vj_wq_detJ_ek[k]; - } - } -} + for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); + ++i_symmetry) { + if constexpr ((DataType::NumberOfRows == MeshType::Dimension) and + (DataType::NumberOfColumns == MeshType::Dimension)) { + auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; -template <typename ConformTransformationT> -void -_computeEjkMean(const QuadratureFormula<2>& quadrature, - const ConformTransformationT& T, - const TinyVector<2>& Xj, - const double Vi, - const size_t degree, - const size_t dimension, - SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk) -{ - using Rd = TinyVector<2>; - - mean_of_ejk.fill(0); - - for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { - const double wq = quadrature.weight(i_q); - const Rd& xi_q = quadrature.point(i_q); - const double detT = [&] { - if constexpr (std::is_same_v<TriangleTransformation<2>, std::decay_t<decltype(T)>>) { - return T.jacobianDeterminant(); - } else { - return T.jacobianDeterminant(xi_q); - } - }(); + const Rd& normal = symmetry_normal_list[i_symmetry]; - const Rd X_Xj = T(xi_q) - Xj; + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; - const double x_xj = X_Xj[0]; - const double y_yj = X_Xj[1]; + 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_matrix(normal, qi) - qj; - { - size_t k = 0; - inv_Vj_wq_detJ_ek[k++] = wq * detT / Vi; - for (; k <= degree; ++k) { - inv_Vj_wq_detJ_ek[k] = x_xj * inv_Vj_wq_detJ_ek[k - 1]; - } + for (size_t p = 0; p < DataType::NumberOfRows; ++p) { + const size_t kB = column_begin + l * DataType::Dimension + p * DataType::NumberOfColumns; + for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { + B(index, kB + q) = qi_qj(p, q); + } + } + } + } + } 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 + } + } + } - for (size_t i_y = 1; i_y <= degree; ++i_y) { - const size_t begin_i_y_1 = ((i_y - 1) * (2 * degree - i_y + 4)) / 2; - for (size_t l = 0; l <= degree - i_y; ++l) { - inv_Vj_wq_detJ_ek[k++] = y_yj * inv_Vj_wq_detJ_ek[begin_i_y_1 + l]; - } - } - } + 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; + } - for (size_t k = 1; k < dimension; ++k) { - mean_of_ejk[k - 1] += inv_Vj_wq_detJ_ek[k]; + } else { + // LCOV_EXCL_START + throw UnexpectedError("invalid discrete function type"); + // LCOV_EXCL_STOP + } + }, + discrete_function_variant->discreteFunction()); } } -} - -template <typename ConformTransformationT> -void -_computeEjkMean(const QuadratureFormula<3>& quadrature, - const ConformTransformationT& T, - const TinyVector<3>& Xj, - const double Vi, - const size_t degree, - const size_t dimension, - SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk) -{ - using Rd = TinyVector<3>; - mean_of_ejk.fill(0); - - for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { - const double wq = quadrature.weight(i_q); - const Rd& xi_q = quadrature.point(i_q); - const double detT = [&] { - if constexpr (std::is_same_v<TetrahedronTransformation, std::decay_t<decltype(T)>>) { - return T.jacobianDeterminant(); - } else { - return T.jacobianDeterminant(xi_q); - } - }(); - - const Rd X_Xj = T(xi_q) - Xj; - - const double x_xj = X_Xj[0]; - const double y_yj = X_Xj[1]; - const double z_zj = X_Xj[2]; - - { - size_t k = 0; - inv_Vj_wq_detJ_ek[k++] = wq * detT / Vi; - for (; k <= degree; ++k) { - inv_Vj_wq_detJ_ek[k] = x_xj * inv_Vj_wq_detJ_ek[k - 1]; - } - for (size_t i_y = 1; i_y <= degree; ++i_y) { - const size_t begin_i_y_1 = ((i_y - 1) * (2 * degree - i_y + 4)) / 2; - for (size_t l = 0; l <= degree - i_y; ++l) { - inv_Vj_wq_detJ_ek[k++] = y_yj * inv_Vj_wq_detJ_ek[begin_i_y_1 + l]; - } + template <typename MatrixType> + static void + rowWeighting(const CellId& cell_j_id, + const CellToCellStencilArray& stencil_array, + const CellValue<const Rd>& xj, + const SmallArray<const Rd>& symmetry_origin_list, + const SmallArray<const Rd>& symmetry_normal_list, + ShrinkMatrixView<MatrixType>& A, + ShrinkMatrixView<MatrixType>& B) + { + // Add row weighting (give more importance to cells that are + // closer to j) + auto stencil_cell_list = stencil_array[cell_j_id]; + + const Rd& Xj = xj[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]; + const double weight = 1. / l2Norm(xj[cell_i_id] - Xj); + for (size_t l = 0; l < A.numberOfColumns(); ++l) { + A(index, l) *= weight; } - - for (size_t i_z = 1; i_z <= degree; ++i_z) { - const size_t begin_i_z_1 = ((i_z - 1) * (3 * (degree + 2) * (degree + 3) + (i_z - (3 * degree + 8)) * i_z)) / 6; - for (size_t i_y = 0; i_y <= degree - i_z; ++i_y) { - const size_t begin_i_y_1 = (i_y * (2 * degree - i_y + 3)) / 2 + begin_i_z_1; - for (size_t l = 0; l <= degree - i_y - i_z; ++l) { - inv_Vj_wq_detJ_ek[k++] = z_zj * inv_Vj_wq_detJ_ek[begin_i_y_1 + l]; - } - } + for (size_t l = 0; l < B.numberOfColumns(); ++l) { + B(index, l) *= weight; } } - - for (size_t k = 1; k < dimension; ++k) { - mean_of_ejk[k - 1] += inv_Vj_wq_detJ_ek[k]; - } - } -} - -template <typename NodeListT, size_t Dimension> -void _computeEjkMean(const TinyVector<Dimension>& Xj, - const NodeValue<const TinyVector<Dimension>>& xr, - const NodeListT& node_list, - const CellType& cell_type, - const double Vi, - const size_t degree, - const size_t basis_dimension, - SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk); - -template <typename NodeListT> -void -_computeEjkMean(const TinyVector<1>& Xj, - const NodeValue<const TinyVector<1>>& xr, - const NodeListT& node_list, - const CellType& cell_type, - const double Vi, - const size_t degree, - const size_t basis_dimension, - SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk) -{ - if (cell_type == CellType::Line) { - const LineTransformation<1> T{xr[node_list[0]], xr[node_list[1]]}; - - const auto& quadrature = QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor{degree}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - - } else { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); - } -} - -template <typename NodeListT> -void -_computeEjkMeanInSymmetricCell(const TinyVector<1>& origin, - const TinyVector<1>& normal, - const TinyVector<1>& Xj, - const NodeValue<const TinyVector<1>>& xr, - const NodeListT& node_list, - const CellType& cell_type, - const double Vi, - const size_t degree, - const size_t basis_dimension, - SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk) -{ - if (cell_type == CellType::Line) { - const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); - const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); - - const LineTransformation<1> T{x0, x1}; - - const auto& quadrature = QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor{degree}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - - } else { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); - } -} - -template <typename ConformTransformationT> -void _computeEjkBoundaryMean(const QuadratureFormula<1>& quadrature, - const ConformTransformationT& T, - const TinyVector<2>& Xj, - const double Vi, - const size_t degree, - const size_t dimension, - SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, - SmallArray<double>& mean_of_ejk); - -template <> -void -_computeEjkBoundaryMean(const QuadratureFormula<1>& quadrature, - const LineTransformation<2>& T, - const TinyVector<2>& Xj, - const double Vi, - const size_t degree, - const size_t dimension, - SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, - SmallArray<double>& mean_of_ejk) -{ - using Rd = TinyVector<2>; - - const double velocity_perp_e1 = T.velocity()[1]; - - for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { - const double wq = quadrature.weight(i_q); - const TinyVector<1> xi_q = quadrature.point(i_q); - - const Rd X_Xj = T(xi_q) - Xj; - - const double x_xj = X_Xj[0]; - const double y_yj = X_Xj[1]; - - { - size_t k = 0; - inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = x_xj * wq * velocity_perp_e1 / Vi; - for (; k <= degree; ++k) { - inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k] = - x_xj * inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k - 1] * ((1. * k) / (k + 1)); - } - - for (size_t i_y = 1; i_y <= degree; ++i_y) { - const size_t begin_i_y_1 = ((i_y - 1) * (2 * degree - i_y + 4)) / 2; - for (size_t l = 0; l <= degree - i_y; ++l) { - inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = y_yj * inv_Vj_alpha_p_1_wq_X_prime_orth_ek[begin_i_y_1 + l]; + 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]; + + const Rd& origin = symmetry_origin_list[i_symmetry]; + 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]; + const double weight = 1. / l2Norm(symmetrize_coordinates(origin, normal, xj[cell_i_id]) - Xj); + for (size_t l = 0; l < A.numberOfColumns(); ++l) { + A(index, l) *= weight; + } + for (size_t l = 0; l < B.numberOfColumns(); ++l) { + B(index, l) *= weight; } } } - - for (size_t k = 1; k < dimension; ++k) { - mean_of_ejk[k - 1] += inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k]; - } } -} - -template <typename ConformTransformationT> -void -_computeEjkBoundaryMean(const QuadratureFormula<1>& quadrature, - const ConformTransformationT& T, - const TinyVector<2>& Xj, - const double Vi, - const size_t degree, - const size_t dimension, - SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, - SmallArray<double>& mean_of_ejk) -{ - using Rd = TinyVector<2>; - - for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { - const double wq = quadrature.weight(i_q); - const TinyVector<1> xi_q = quadrature.point(i_q); - - const double velocity_perp_e1 = T.velocity(xi_q)[1]; - const Rd X_Xj = T(xi_q) - Xj; - - const double x_xj = X_Xj[0]; - const double y_yj = X_Xj[1]; - { - size_t k = 0; - inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = x_xj * wq * velocity_perp_e1 / Vi; - for (; k <= degree; ++k) { - inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k] = - x_xj * inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k - 1] * ((1. * k) / (k + 1)); - } + template <typename MatrixType> + static void + solveCollectionInPlaceWithPreconditionner(const ShrinkMatrixView<MatrixType>& A, + const SmallMatrix<double>& X, + const ShrinkMatrixView<MatrixType>& B, + const SmallVector<double>& G) + { + for (size_t l = 0; l < A.numberOfColumns(); ++l) { + double g = 0; + for (size_t i = 0; i < A.numberOfRows(); ++i) { + const double Ail = A(i, l); - for (size_t i_y = 1; i_y <= degree; ++i_y) { - const size_t begin_i_y_1 = ((i_y - 1) * (2 * degree - i_y + 4)) / 2; - for (size_t l = 0; l <= degree - i_y; ++l) { - inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = y_yj * inv_Vj_alpha_p_1_wq_X_prime_orth_ek[begin_i_y_1 + l]; - } + g += Ail * Ail; } + G[l] = std::sqrt(g); } - for (size_t k = 1; k < dimension; ++k) { - mean_of_ejk[k - 1] += inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k]; - } - } -} - -template <MeshConcept MeshType> -void -_computeEjkMeanByBoundary(const MeshType& mesh, - const TinyVector<2>& Xj, - const CellId& cell_id, - const auto& cell_to_face_matrix, - const auto& face_to_node_matrix, - const auto& cell_face_is_reversed, - const CellValue<const double>& Vi, - const size_t degree, - const size_t basis_dimension, - SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, - SmallArray<double>& mean_of_ejk) -{ - if constexpr (is_polygonal_mesh_v<MeshType>) { - const auto& quadrature = - QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(degree + 1)); - - auto xr = mesh.xr(); - mean_of_ejk.fill(0); - auto cell_face_list = cell_to_face_matrix[cell_id]; - for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { - bool is_reversed = cell_face_is_reversed[cell_id][i_face]; - - const FaceId face_id = cell_face_list[i_face]; - auto face_node_list = face_to_node_matrix[face_id]; - if (is_reversed) { - const LineTransformation<2> T{xr[face_node_list[1]], xr[face_node_list[0]]}; - _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension, - inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk); - } else { - const LineTransformation<2> T{xr[face_node_list[0]], xr[face_node_list[1]]}; - _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension, - inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk); + for (size_t l = 0; l < A.numberOfColumns(); ++l) { + const double Gl = G[l]; + for (size_t i = 0; i < A.numberOfRows(); ++i) { + A(i, l) *= Gl; } } - } else { - static_assert(is_polynomial_mesh_v<MeshType>); - // (degree + 1): divergence theorem - // mesh.degree(): transformation polynomial degree - // (mesh.degree() - 1): degree of the line abscissa velocity - const auto& quadrature = QuadratureManager::instance().getLineFormula( - GaussLegendreQuadratureDescriptor((degree + 1) * mesh.degree() + (mesh.degree() - 1))); - - auto xr = mesh.xr(); - auto xl = mesh.xl(); - mean_of_ejk.fill(0); - auto cell_face_list = cell_to_face_matrix[cell_id]; - for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { - bool is_reversed = cell_face_is_reversed[cell_id][i_face]; - - const FaceId face_id = cell_face_list[i_face]; - auto face_node_list = face_to_node_matrix[face_id]; -#warning rework - switch (mesh.degree()) { - case 2: { - if (is_reversed) { - const LineParabolicTransformation<2> T{xr[face_node_list[1]], xl[face_id][0], xr[face_node_list[0]]}; - _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension, - inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk); - } else { - const LineParabolicTransformation<2> T{xr[face_node_list[0]], xl[face_id][0], xr[face_node_list[1]]}; - _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension, - inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk); - } - break; - } - case 3: { - if (is_reversed) { - const LineCubicTransformation<2> T{xr[face_node_list[1]], xl[face_id][1], xl[face_id][0], - xr[face_node_list[0]]}; - _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension, - inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk); - } else { - const LineCubicTransformation<2> T{xr[face_node_list[0]], xl[face_id][0], xl[face_id][1], - xr[face_node_list[1]]}; - _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension, - inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk); - } - break; - } - default: { - std::ostringstream error_msg; - error_msg << "reconstruction on meshes of degree " << mesh.degree(); - throw NotImplementedError(error_msg.str()); - } - } - } - } -} -template <MeshConcept MeshType> -void -_computeEjkMeanByBoundaryInSymmetricCell(const MeshType& mesh, - const TinyVector<2>& origin, - const TinyVector<2>& normal, - const TinyVector<2>& Xj, - const CellId& cell_id, - const NodeValue<const TinyVector<2>>& xr, - const auto& cell_to_face_matrix, - const auto& face_to_node_matrix, - const auto& cell_face_is_reversed, - const CellValue<const double>& Vi, - const size_t degree, - const size_t basis_dimension, - SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, - SmallArray<double>& mean_of_ejk) -{ - if constexpr (is_polygonal_mesh_v<MeshType>) { - const auto& quadrature = - QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(degree + 1)); - - mean_of_ejk.fill(0); - auto cell_face_list = cell_to_face_matrix[cell_id]; - for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { - bool is_reversed = cell_face_is_reversed[cell_id][i_face]; - - const FaceId face_id = cell_face_list[i_face]; - auto face_node_list = face_to_node_matrix[face_id]; - - const auto x0 = symmetrize_coordinates(origin, normal, xr[face_node_list[1]]); - const auto x1 = symmetrize_coordinates(origin, normal, xr[face_node_list[0]]); - - if (is_reversed) { - const LineTransformation<2> T{x1, x0}; - _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension, - inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk); - } else { - const LineTransformation<2> T{x0, x1}; - _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension, - inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk); - } - } + Givens::solveCollectionInPlace(A, X, B); - } else { - static_assert(is_polynomial_mesh_v<MeshType>); - // (degree + 1): divergence theorem - // mesh.degree(): transformation polynomial degree - // (mesh.degree() - 1): degree of the line abscissa velocity - const auto& quadrature = QuadratureManager::instance().getLineFormula( - GaussLegendreQuadratureDescriptor((degree + 1) * mesh.degree() + (mesh.degree() - 1))); - - auto xl = mesh.xl(); - - mean_of_ejk.fill(0); - auto cell_face_list = cell_to_face_matrix[cell_id]; - for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { - bool is_reversed = cell_face_is_reversed[cell_id][i_face]; - - const FaceId face_id = cell_face_list[i_face]; - auto face_node_list = face_to_node_matrix[face_id]; - - switch (mesh.degree()) { - case 2: { - const auto x0 = symmetrize_coordinates(origin, normal, xr[face_node_list[1]]); - const auto x1 = symmetrize_coordinates(origin, normal, xl[face_id][0]); - const auto x2 = symmetrize_coordinates(origin, normal, xr[face_node_list[0]]); - - if (is_reversed) { - const LineParabolicTransformation<2> T{x2, x1, x0}; - _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension, - inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk); - } else { - const LineParabolicTransformation<2> T{x0, x1, x2}; - _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension, - inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk); - } - break; - } - case 3: { - const auto x0 = symmetrize_coordinates(origin, normal, xr[face_node_list[1]]); - const auto x1 = symmetrize_coordinates(origin, normal, xl[face_id][1]); - const auto x2 = symmetrize_coordinates(origin, normal, xl[face_id][0]); - const auto x3 = symmetrize_coordinates(origin, normal, xr[face_node_list[0]]); - - if (is_reversed) { - const LineCubicTransformation<2> T{x3, x2, x1, x0}; - _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension, - inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk); - } else { - const LineCubicTransformation<2> T{x0, x1, x2, x3}; - _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension, - inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk); - } - break; - } - default: { - std::ostringstream error_msg; - error_msg << "reconstruction on meshes of degree " << mesh.degree(); - throw NotImplementedError(error_msg.str()); - } + for (size_t l = 0; l < X.numberOfRows(); ++l) { + const double Gl = G[l]; + for (size_t i = 0; i < X.numberOfColumns(); ++i) { + X(l, i) *= Gl; } } } -} - -template <typename NodeListT> -void -_computeEjkMean(const TinyVector<2>& Xj, - const NodeValue<const TinyVector<2>>& xr, - const NodeListT& node_list, - const CellType& cell_type, - const double Vi, - const size_t degree, - const size_t basis_dimension, - SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk) -{ - switch (cell_type) { - case CellType::Triangle: { - const TriangleTransformation<2> T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]]}; - const auto& quadrature = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{degree}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - break; - } - case CellType::Quadrangle: { - const SquareTransformation<2> T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], xr[node_list[3]]}; - const auto& quadrature = - QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor{degree + 1}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - break; - } - default: { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); - } - } -} - -template <typename NodeListT> -void -_computeEjkMeanInSymmetricCell(const TinyVector<2>& origin, - const TinyVector<2>& normal, - const TinyVector<2>& Xj, - const NodeValue<const TinyVector<2>>& xr, - const NodeListT& node_list, - const CellType& cell_type, - const double Vi, - const size_t degree, - const size_t basis_dimension, - SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk) -{ - switch (cell_type) { - case CellType::Triangle: { - const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); - const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); - const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); - - const TriangleTransformation<2> T{x0, x1, x2}; - const auto& quadrature = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{degree}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - break; - } - case CellType::Quadrangle: { - const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); - const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); - const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); - const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); - - const SquareTransformation<2> T{x0, x1, x2, x3}; - const auto& quadrature = - QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor{degree + 1}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - break; - } - default: { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); - } - } -} - -template <typename NodeListT> -void -_computeEjkMean(const TinyVector<3>& Xj, - const NodeValue<const TinyVector<3>>& xr, - const NodeListT& node_list, - const CellType& cell_type, - const double Vi, - const size_t degree, - const size_t basis_dimension, - SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk) -{ - if (cell_type == CellType::Tetrahedron) { - const TetrahedronTransformation T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], xr[node_list[3]]}; - - const auto& quadrature = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{degree}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - } else if (cell_type == CellType::Prism) { - const PrismTransformation T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], // - xr[node_list[3]], xr[node_list[4]], xr[node_list[5]]}; - - const auto& quadrature = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{degree + 1}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - - } else if (cell_type == CellType::Pyramid) { - const PyramidTransformation T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], xr[node_list[3]], - xr[node_list[4]]}; - - const auto& quadrature = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{degree + 1}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - - } else if (cell_type == CellType::Hexahedron) { - const CubeTransformation T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], xr[node_list[3]], - xr[node_list[4]], xr[node_list[5]], xr[node_list[6]], xr[node_list[7]]}; - - const auto& quadrature = - QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{degree + 1}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - - } else { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); - } -} - -template <typename NodeListT> -void -_computeEjkMeanInSymmetricCell(const TinyVector<3>& origin, - const TinyVector<3>& normal, - const TinyVector<3>& Xj, - const NodeValue<const TinyVector<3>>& xr, - const NodeListT& node_list, - const CellType& cell_type, - const double Vi, - const size_t degree, - const size_t basis_dimension, - SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk) -{ - if (cell_type == CellType::Tetrahedron) { - const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); - const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); - const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); - const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); - - const TetrahedronTransformation T{x0, x1, x2, x3}; - - const auto& quadrature = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{degree}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - - } else if (cell_type == CellType::Prism) { - const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); - const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); - const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); - const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[4]]); - const auto x4 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); - const auto x5 = symmetrize_coordinates(origin, normal, xr[node_list[5]]); - - const PrismTransformation T{x0, x1, x2, // - x3, x4, x5}; + template <typename ReconstructionMatrixBuilderType> + static void + populateDiscreteFunctionDPkByCell( + const CellId& cell_j_id, + const size_t& degree, + const SmallMatrix<double>& X, + const ReconstructionMatrixBuilderType& reconstruction_matrix_builder, + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list, + const std::vector<PolynomialReconstruction::MutableDiscreteFunctionDPkVariant>& + mutable_discrete_function_dpk_variant_list) + { + size_t column_begin = 0; + for (size_t i_dpk_variant = 0; i_dpk_variant < mutable_discrete_function_dpk_variant_list.size(); ++i_dpk_variant) { + const auto& dpk_variant = mutable_discrete_function_dpk_variant_list[i_dpk_variant]; - const auto& quadrature = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{degree + 1}); + const auto& discrete_function_variant = discrete_function_variant_list[i_dpk_variant]; - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + std::visit( + [&](auto&& dpk_function, auto&& p0_function) { + using DPkFunctionT = std::decay_t<decltype(dpk_function)>; + using P0FunctionT = std::decay_t<decltype(p0_function)>; + using DataType = std::remove_const_t<std::decay_t<typename DPkFunctionT::data_type>>; + using P0DataType = std::remove_const_t<std::decay_t<typename P0FunctionT::data_type>>; - } else if (cell_type == CellType::Pyramid) { - const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); - const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); - const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); - const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); - const auto x4 = symmetrize_coordinates(origin, normal, xr[node_list[4]]); - const PyramidTransformation T{x0, x1, x2, x3, x4}; + if constexpr (std::is_same_v<DataType, P0DataType>) { + if constexpr (is_discrete_function_P0_v<P0FunctionT>) { + if constexpr (is_discrete_function_dpk_scalar_v<DPkFunctionT>) { + auto dpk_j = dpk_function.coefficients(cell_j_id); + dpk_j[0] = p0_function[cell_j_id]; - const auto& quadrature = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{degree + 1}); + if constexpr (std::is_arithmetic_v<DataType>) { + if constexpr (ReconstructionMatrixBuilderType::handles_high_degrees) { + if (degree > 1) { + const auto& mean_j_of_ejk = reconstruction_matrix_builder.meanjOfEjk(); + for (size_t i = 0; i < X.numberOfRows(); ++i) { + dpk_j[0] -= X(i, column_begin) * mean_j_of_ejk[i]; + } + } + } - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + for (size_t i = 0; i < X.numberOfRows(); ++i) { + auto& dpk_j_ip1 = dpk_j[i + 1]; + dpk_j_ip1 = X(i, column_begin); + } + ++column_begin; + } else if constexpr (is_tiny_vector_v<DataType>) { + if constexpr (ReconstructionMatrixBuilderType::handles_high_degrees) { + if (degree > 1) { + const auto& mean_j_of_ejk = reconstruction_matrix_builder.meanjOfEjk(); + for (size_t i = 0; i < X.numberOfRows(); ++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]; + } + } + } + } - } else if (cell_type == CellType::Hexahedron) { - const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); - const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); - const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); - const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); - const auto x4 = symmetrize_coordinates(origin, normal, xr[node_list[7]]); - const auto x5 = symmetrize_coordinates(origin, normal, xr[node_list[6]]); - const auto x6 = symmetrize_coordinates(origin, normal, xr[node_list[5]]); - const auto x7 = symmetrize_coordinates(origin, normal, xr[node_list[4]]); + for (size_t i = 0; i < X.numberOfRows(); ++i) { + auto& dpk_j_ip1 = dpk_j[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 if constexpr (is_tiny_matrix_v<DataType>) { + if constexpr (ReconstructionMatrixBuilderType::handles_high_degrees) { + if (degree > 1) { + const auto& mean_j_of_ejk = reconstruction_matrix_builder.meanjOfEjk(); + for (size_t i = 0; i < X.numberOfRows(); ++i) { + auto& dpk_j_0 = dpk_j[0]; + for (size_t k = 0; k < DataType::NumberOfRows; ++k) { + for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { + dpk_j_0(k, l) -= X(i, column_begin + k * DataType::NumberOfColumns + l) * mean_j_of_ejk[i]; + } + } + } + } + } - const CubeTransformation T{x0, x1, x2, x3, x4, x5, x6, x7}; + for (size_t i = 0; i < X.numberOfRows(); ++i) { + auto& dpk_j_ip1 = dpk_j[i + 1]; + for (size_t k = 0; k < DataType::NumberOfRows; ++k) { + for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { + dpk_j_ip1(k, l) = X(i, column_begin + k * DataType::NumberOfColumns + l); + } + } + } + column_begin += DataType::Dimension; + } else { + // LCOV_EXCL_START + throw UnexpectedError("unexpected data type"); + // LCOV_EXCL_STOP + } + } else { + // LCOV_EXCL_START + throw UnexpectedError("unexpected discrete dpk function type"); + // LCOV_EXCL_STOP + } + } else if constexpr (is_discrete_function_P0_vector_v<P0FunctionT>) { + if constexpr (is_discrete_function_dpk_vector_v<DPkFunctionT>) { + auto dpk_j = dpk_function.coefficients(cell_j_id); + auto cell_vector = p0_function[cell_j_id]; + const size_t size = X.numberOfRows() + 1; + + for (size_t l = 0; l < cell_vector.size(); ++l) { + const size_t component_begin = l * size; + dpk_j[component_begin] = cell_vector[l]; + if constexpr (std::is_arithmetic_v<DataType>) { + if constexpr (ReconstructionMatrixBuilderType::handles_high_degrees) { + const auto& mean_j_of_ejk = reconstruction_matrix_builder.meanjOfEjk(); + if (degree > 1) { + for (size_t i = 0; i < X.numberOfRows(); ++i) { + dpk_j[component_begin] -= X(i, column_begin) * mean_j_of_ejk[i]; + } + } + } - const auto& quadrature = - QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{degree + 1}); + for (size_t i = 0; i < X.numberOfRows(); ++i) { + auto& dpk_j_ip1 = dpk_j[component_begin + i + 1]; + dpk_j_ip1 = X(i, column_begin); + } + ++column_begin; + } else if constexpr (is_tiny_vector_v<DataType>) { + if constexpr (ReconstructionMatrixBuilderType::handles_high_degrees) { + if (degree > 1) { + const auto& mean_j_of_ejk = reconstruction_matrix_builder.meanjOfEjk(); + for (size_t i = 0; i < X.numberOfRows(); ++i) { + auto& dpk_j_0 = dpk_j[component_begin]; + for (size_t k = 0; k < DataType::Dimension; ++k) { + dpk_j_0[k] -= X(i, column_begin + k) * mean_j_of_ejk[i]; + } + } + } + } - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + for (size_t i = 0; i < X.numberOfRows(); ++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 if constexpr (is_tiny_matrix_v<DataType>) { + if constexpr (ReconstructionMatrixBuilderType::handles_high_degrees) { + if (degree > 1) { + const auto& mean_j_of_ejk = reconstruction_matrix_builder.meanjOfEjk(); + for (size_t i = 0; i < X.numberOfRows(); ++i) { + auto& dpk_j_0 = dpk_j[component_begin]; + for (size_t p = 0; p < DataType::NumberOfRows; ++p) { + for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { + dpk_j_0(p, q) -= + X(i, column_begin + p * DataType::NumberOfColumns + q) * mean_j_of_ejk[i]; + } + } + } + } + } - } else { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + for (size_t i = 0; i < X.numberOfRows(); ++i) { + auto& dpk_j_ip1 = dpk_j[component_begin + i + 1]; + for (size_t p = 0; p < DataType::NumberOfRows; ++p) { + for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { + dpk_j_ip1(p, q) = X(i, column_begin + p * DataType::NumberOfColumns + q); + } + } + } + column_begin += DataType::Dimension; + } else { + // LCOV_EXCL_START + throw UnexpectedError("unexpected data type"); + // LCOV_EXCL_STOP + } + } + } else { + // LCOV_EXCL_START + throw UnexpectedError("unexpected discrete dpk function type"); + // LCOV_EXCL_STOP + } + } else { + // LCOV_EXCL_START + throw UnexpectedError("unexpected discrete function type"); + // LCOV_EXCL_STOP + } + } else { + // LCOV_EXCL_START + throw UnexpectedError("incompatible data types"); + // LCOV_EXCL_STOP + } + }, + dpk_variant.mutableDiscreteFunctionDPk(), discrete_function_variant->discreteFunction()); + } } -} +}; size_t PolynomialReconstruction::_getNumberOfColumns( @@ -1003,12 +655,14 @@ PolynomialReconstruction::_checkDataAndSymmetriesCompatibility( } } -template <MeshConcept MeshType> +template <typename ReconstructionMatrixBuilderType, MeshConcept MeshType> [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> PolynomialReconstruction::_build( const std::shared_ptr<const MeshType>& p_mesh, const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const { + static_assert(std::is_same_v<MeshType, typename ReconstructionMatrixBuilderType::MeshType>); + const MeshType& mesh = *p_mesh; using Rd = TinyVector<MeshType::Dimension>; @@ -1030,10 +684,8 @@ PolynomialReconstruction::_build( auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); auto Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); - auto cell_is_owned = mesh.connectivity().cellIsOwned(); - auto cell_type = mesh.connectivity().cellType(); - auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); - auto cell_to_face_matrix = mesh.connectivity().cellToFaceMatrix(); + auto cell_is_owned = mesh.connectivity().cellIsOwned(); + auto cell_type = mesh.connectivity().cellType(); auto full_stencil_size = [&](const CellId cell_id) { auto stencil_cell_list = stencil_array[cell_id]; @@ -1093,24 +745,19 @@ PolynomialReconstruction::_build( SmallArray<SmallVector<double>> G_pool(Kokkos::DefaultExecutionSpace::concurrency()); SmallArray<SmallMatrix<double>> X_pool(Kokkos::DefaultExecutionSpace::concurrency()); - SmallArray<SmallArray<double>> inv_Vj_wq_detJ_ek_pool(Kokkos::DefaultExecutionSpace::concurrency()); - SmallArray<SmallArray<double>> mean_j_of_ejk_pool(Kokkos::DefaultExecutionSpace::concurrency()); - SmallArray<SmallArray<double>> mean_i_of_ejk_pool(Kokkos::DefaultExecutionSpace::concurrency()); - - SmallArray<SmallArray<double>> inv_Vj_alpha_p_1_wq_X_prime_orth_ek_pool(Kokkos::DefaultExecutionSpace::concurrency()); - SmallArray<SmallArray<double>> mean_l_of_ejk_pool(Kokkos::DefaultExecutionSpace::concurrency()); - for (size_t i = 0; i < A_pool.size(); ++i) { A_pool[i] = SmallMatrix<double>(max_stencil_size, basis_dimension - 1); B_pool[i] = SmallMatrix<double>(max_stencil_size, number_of_columns); G_pool[i] = SmallVector<double>(basis_dimension - 1); X_pool[i] = SmallMatrix<double>(basis_dimension - 1, number_of_columns); + } - inv_Vj_wq_detJ_ek_pool[i] = SmallArray<double>(basis_dimension); - mean_j_of_ejk_pool[i] = SmallArray<double>(basis_dimension - 1); - mean_i_of_ejk_pool[i] = SmallArray<double>(basis_dimension - 1); + SmallArray<std::shared_ptr<ReconstructionMatrixBuilderType>> reconstruction_matrix_builder_pool(A_pool.size()); - inv_Vj_alpha_p_1_wq_X_prime_orth_ek_pool[i] = SmallArray<double>(basis_dimension); + for (size_t t = 0; t < reconstruction_matrix_builder_pool.size(); ++t) { + reconstruction_matrix_builder_pool[t] = + std::make_shared<ReconstructionMatrixBuilderType>(*p_mesh, m_descriptor.degree(), symmetry_origin_list, + symmetry_normal_list, stencil_array); } parallel_for( @@ -1118,545 +765,34 @@ PolynomialReconstruction::_build( if (cell_is_owned[cell_j_id]) { const int32_t t = tokens.acquire(); - auto stencil_cell_list = stencil_array[cell_j_id]; - - ShrinkMatrixView B(B_pool[t], full_stencil_size(cell_j_id)); - - size_t column_begin = 0; - for (size_t i_discrete_function_variant = 0; - i_discrete_function_variant < discrete_function_variant_list.size(); ++i_discrete_function_variant) { - const auto& discrete_function_variant = discrete_function_variant_list[i_discrete_function_variant]; - - std::visit( - [&](auto&& discrete_function) { - using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; - if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { - using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; - const DataType& qj = 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]; - const DataType& qi_qj = discrete_function[cell_i_id] - qj; - if constexpr (std::is_arithmetic_v<DataType>) { - B(index, column_begin) = qi_qj; - } else if constexpr (is_tiny_vector_v<DataType>) { - for (size_t kB = column_begin, k = 0; k < DataType::Dimension; ++k, ++kB) { - B(index, kB) = qi_qj[k]; - } - } else if constexpr (is_tiny_matrix_v<DataType>) { - 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); - } - } - } - } - - 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>) { - const DataType& qi_qj = discrete_function[cell_i_id] - qj; - B(index, column_begin) = qi_qj; - } else if constexpr (is_tiny_vector_v<DataType>) { - if constexpr (DataType::Dimension == MeshType::Dimension) { - const Rd& normal = symmetry_normal_list[i_symmetry]; - - const DataType& qi = discrete_function[cell_i_id]; - const DataType& qi_qj = symmetrize_vector(normal, qi) - qj; - for (size_t kB = column_begin, 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>) { - if constexpr ((DataType::NumberOfColumns == DataType::NumberOfRows) and - (DataType::NumberOfColumns == MeshType::Dimension)) { - 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 - } - } - } - } - - if constexpr (std::is_arithmetic_v<DataType>) { - ++column_begin; - } else if constexpr (is_tiny_vector_v<DataType> or is_tiny_matrix_v<DataType>) { - column_begin += DataType::Dimension; - } - } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { - using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; - - const auto qj_vector = discrete_function[cell_j_id]; - - 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; - } - - } else { - // LCOV_EXCL_START - throw UnexpectedError("invalid discrete function type"); - // LCOV_EXCL_STOP - } - }, - discrete_function_variant->discreteFunction()); - } - ShrinkMatrixView A(A_pool[t], full_stencil_size(cell_j_id)); + ShrinkMatrixView B(B_pool[t], full_stencil_size(cell_j_id)); - if ((m_descriptor.degree() == 1) and - (m_descriptor.integrationMethodType() == IntegrationMethodType::cell_center)) { - const Rd& Xj = xj[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]; - const Rd Xi_Xj = xj[cell_i_id] - Xj; - for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(index, l) = Xi_Xj[l]; - } - } - 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]; - - const Rd& origin = symmetry_origin_list[i_symmetry]; - 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]; - const Rd Xi_Xj = symmetrize_coordinates(origin, normal, xj[cell_i_id]) - Xj; - for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(index, l) = Xi_Xj[l]; - } - } - } - - } else if ((m_descriptor.integrationMethodType() == IntegrationMethodType::element) or - (m_descriptor.integrationMethodType() == IntegrationMethodType::boundary)) { - if ((m_descriptor.integrationMethodType() == IntegrationMethodType::boundary) and - (MeshType::Dimension == 2)) { - if constexpr (MeshType::Dimension == 2) { - SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek = inv_Vj_alpha_p_1_wq_X_prime_orth_ek_pool[t]; - SmallArray<double>& mean_j_of_ejk = mean_j_of_ejk_pool[t]; - SmallArray<double>& mean_i_of_ejk = mean_i_of_ejk_pool[t]; - - auto face_to_node_matrix = p_mesh->connectivity().faceToNodeMatrix(); - auto cell_face_is_reversed = p_mesh->connectivity().cellFaceIsReversed(); - const Rd& Xj = xj[cell_j_id]; - - _computeEjkMeanByBoundary(*p_mesh, Xj, cell_j_id, cell_to_face_matrix, face_to_node_matrix, - cell_face_is_reversed, Vj, m_descriptor.degree(), basis_dimension, - inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_j_of_ejk); - - 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]; - - _computeEjkMeanByBoundary(*p_mesh, Xj, cell_i_id, cell_to_face_matrix, face_to_node_matrix, - cell_face_is_reversed, Vj, m_descriptor.degree(), basis_dimension, - inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_i_of_ejk); - - for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(index, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; - } - } - 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]; - - const Rd& origin = symmetry_origin_list[i_symmetry]; - 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]; - - _computeEjkMeanByBoundaryInSymmetricCell(*p_mesh, origin, normal, // - Xj, cell_i_id, xr, cell_to_face_matrix, face_to_node_matrix, - cell_face_is_reversed, Vj, m_descriptor.degree(), - basis_dimension, inv_Vj_alpha_p_1_wq_X_prime_orth_ek, - mean_i_of_ejk); - - for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(index, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; - } - } - } - } else { - throw UnexpectedError("invalid mesh dimension"); - } - } else { - SmallArray<double>& inv_Vj_wq_detJ_ek = inv_Vj_wq_detJ_ek_pool[t]; - SmallArray<double>& mean_j_of_ejk = mean_j_of_ejk_pool[t]; - SmallArray<double>& mean_i_of_ejk = mean_i_of_ejk_pool[t]; - - const Rd& Xj = xj[cell_j_id]; - - _computeEjkMean(Xj, xr, cell_to_node_matrix[cell_j_id], cell_type[cell_j_id], Vj[cell_j_id], - m_descriptor.degree(), basis_dimension, inv_Vj_wq_detJ_ek, mean_j_of_ejk); - - 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]; - - _computeEjkMean(Xj, xr, cell_to_node_matrix[cell_i_id], cell_type[cell_i_id], Vj[cell_i_id], - m_descriptor.degree(), basis_dimension, inv_Vj_wq_detJ_ek, mean_i_of_ejk); - - for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(index, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; - } - } - 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]; - - const Rd& origin = symmetry_origin_list[i_symmetry]; - 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]; - - _computeEjkMeanInSymmetricCell(origin, normal, // - Xj, xr, cell_to_node_matrix[cell_i_id], cell_type[cell_i_id], - Vj[cell_i_id], m_descriptor.degree(), basis_dimension, inv_Vj_wq_detJ_ek, - mean_i_of_ejk); + Internal<MeshType>::buildB(cell_j_id, stencil_array, discrete_function_variant_list, symmetry_normal_list, B); - for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(index, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; - } - } - } - } - } else { - throw UnexpectedError("invalid integration strategy"); - } + ReconstructionMatrixBuilderType& reconstruction_matrix_builder = *reconstruction_matrix_builder_pool[t]; + reconstruction_matrix_builder.build(cell_j_id, A); if (m_descriptor.rowWeighting()) { - // Add row weighting (give more importance to cells that are - // closer to j) - const Rd& Xj = xj[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]; - const double weight = 1. / l2Norm(xj[cell_i_id] - Xj); - for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(index, l) *= weight; - } - for (size_t l = 0; l < number_of_columns; ++l) { - B(index, l) *= weight; - } - } - 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]; - - const Rd& origin = symmetry_origin_list[i_symmetry]; - 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]; - const double weight = 1. / l2Norm(symmetrize_coordinates(origin, normal, xj[cell_i_id]) - Xj); - for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(index, l) *= weight; - } - for (size_t l = 0; l < number_of_columns; ++l) { - B(index, l) *= weight; - } - } - } + Internal<MeshType>::rowWeighting(cell_j_id, stencil_array, xj, symmetry_origin_list, symmetry_normal_list, A, + B); } const SmallMatrix<double>& X = X_pool[t]; if (m_descriptor.preconditioning()) { - // Add column weighting preconditioning (increase the presition) + // Add column weighting preconditioning (increase the precision) SmallVector<double>& G = G_pool[t]; - for (size_t l = 0; l < A.numberOfColumns(); ++l) { - double g = 0; - for (size_t i = 0; i < A.numberOfRows(); ++i) { - const double Ail = A(i, l); - - g += Ail * Ail; - } - G[l] = std::sqrt(g); - } - - for (size_t l = 0; l < A.numberOfColumns(); ++l) { - const double Gl = G[l]; - for (size_t i = 0; i < A.numberOfRows(); ++i) { - A(i, l) *= Gl; - } - } - - Givens::solveCollectionInPlace(A, X, B); - - for (size_t l = 0; l < X.numberOfRows(); ++l) { - const double Gl = G[l]; - for (size_t i = 0; i < X.numberOfColumns(); ++i) { - X(l, i) *= Gl; - } - } + Internal<MeshType>::solveCollectionInPlaceWithPreconditionner(A, X, B, G); } else { Givens::solveCollectionInPlace(A, X, B); } - column_begin = 0; - for (size_t i_dpk_variant = 0; i_dpk_variant < mutable_discrete_function_dpk_variant_list.size(); - ++i_dpk_variant) { - const auto& dpk_variant = mutable_discrete_function_dpk_variant_list[i_dpk_variant]; - - const auto& discrete_function_variant = discrete_function_variant_list[i_dpk_variant]; - - std::visit( - [&](auto&& dpk_function, auto&& p0_function) { - using DPkFunctionT = std::decay_t<decltype(dpk_function)>; - using P0FunctionT = std::decay_t<decltype(p0_function)>; - using DataType = std::remove_const_t<std::decay_t<typename DPkFunctionT::data_type>>; - using P0DataType = std::remove_const_t<std::decay_t<typename P0FunctionT::data_type>>; - - if constexpr (std::is_same_v<DataType, P0DataType>) { - if constexpr (is_discrete_function_P0_v<P0FunctionT>) { - if constexpr (is_discrete_function_dpk_scalar_v<DPkFunctionT>) { - auto dpk_j = dpk_function.coefficients(cell_j_id); - dpk_j[0] = p0_function[cell_j_id]; - - if constexpr (std::is_arithmetic_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) { - dpk_j[0] -= X(i, column_begin) * mean_j_of_ejk[i]; - } - } - - for (size_t i = 0; i < basis_dimension - 1; ++i) { - auto& dpk_j_ip1 = dpk_j[i + 1]; - 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[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 if constexpr (is_tiny_matrix_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::NumberOfRows; ++k) { - for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { - dpk_j_0(k, l) -= - X(i, column_begin + k * DataType::NumberOfColumns + l) * mean_j_of_ejk[i]; - } - } - } - } - - for (size_t i = 0; i < basis_dimension - 1; ++i) { - auto& dpk_j_ip1 = dpk_j[i + 1]; - for (size_t k = 0; k < DataType::NumberOfRows; ++k) { - for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { - dpk_j_ip1(k, l) = X(i, column_begin + k * DataType::NumberOfColumns + l); - } - } - } - column_begin += DataType::Dimension; - } else { - // LCOV_EXCL_START - throw UnexpectedError("unexpected data type"); - // LCOV_EXCL_STOP - } - } else { - // LCOV_EXCL_START - throw UnexpectedError("unexpected discrete dpk function type"); - // LCOV_EXCL_STOP - } - } else if constexpr (is_discrete_function_P0_vector_v<P0FunctionT>) { - if constexpr (is_discrete_function_dpk_vector_v<DPkFunctionT>) { - auto dpk_j = dpk_function.coefficients(cell_j_id); - auto cell_vector = p0_function[cell_j_id]; - const size_t size = basis_dimension; - - for (size_t l = 0; l < cell_vector.size(); ++l) { - const size_t component_begin = l * size; - dpk_j[component_begin] = cell_vector[l]; - if constexpr (std::is_arithmetic_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) { - dpk_j[component_begin] -= X(i, column_begin) * 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]; - 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"); - // LCOV_EXCL_STOP - } - } - } else { - // LCOV_EXCL_START - throw UnexpectedError("unexpected discrete dpk function type"); - // LCOV_EXCL_STOP - } - } else { - // LCOV_EXCL_START - throw UnexpectedError("unexpected discrete function type"); - // LCOV_EXCL_STOP - } - } else { - // LCOV_EXCL_START - throw UnexpectedError("incompatible data types"); - // LCOV_EXCL_STOP - } - }, - dpk_variant.mutableDiscreteFunctionDPk(), discrete_function_variant->discreteFunction()); - } + Internal<MeshType>::template populateDiscreteFunctionDPkByCell(cell_j_id, m_descriptor.degree(), X, + reconstruction_matrix_builder, + discrete_function_variant_list, + mutable_discrete_function_dpk_variant_list); tokens.release(t); } @@ -1677,6 +813,34 @@ PolynomialReconstruction::_build( return discrete_function_dpk_variant_list; } +template <MeshConcept MeshType> +[[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> +PolynomialReconstruction::_build( + const std::shared_ptr<const MeshType>& p_mesh, + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const +{ + switch (m_descriptor.integrationMethodType()) { + case IntegrationMethodType::cell_center: { + return this->_build<CellCenterReconstructionMatrixBuilder<MeshType>>(p_mesh, discrete_function_variant_list); + } + case IntegrationMethodType::boundary: { + if constexpr (MeshType::Dimension == 2) { + return this->_build<BoundaryIntegralReconstructionMatrixBuilder<MeshType>>(p_mesh, + discrete_function_variant_list); + } + [[fallthrough]]; + } + case IntegrationMethodType::element: { + return this->_build<ElementIntegralReconstructionMatrixBuilder<MeshType>>(p_mesh, discrete_function_variant_list); + } + // LCOV_EXCL_START + default: { + throw UnexpectedError("invalid reconstruction matrix builder type"); + } + // LCOV_EXCL_STOP + } +} + std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> PolynomialReconstruction::build( const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const diff --git a/src/scheme/PolynomialReconstruction.hpp b/src/scheme/PolynomialReconstruction.hpp index bf6013639f732fd52f9c145ba4982701c0e4bcfc..4f76844711ada4641b69234c5ac787c472247a37 100644 --- a/src/scheme/PolynomialReconstruction.hpp +++ b/src/scheme/PolynomialReconstruction.hpp @@ -10,8 +10,20 @@ class DiscreteFunctionVariant; class PolynomialReconstruction { private: + template <MeshConcept MeshType> + class Internal; + class MutableDiscreteFunctionDPkVariant; + template <MeshConcept MeshType> + class CellCenterReconstructionMatrixBuilder; + + template <MeshConcept MeshType> + class ElementIntegralReconstructionMatrixBuilder; + + template <MeshConcept MeshType> + class BoundaryIntegralReconstructionMatrixBuilder; + const PolynomialReconstructionDescriptor m_descriptor; size_t _getNumberOfColumns( @@ -26,6 +38,11 @@ class PolynomialReconstruction const std::shared_ptr<const MeshType>& p_mesh, const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; + template <typename ReconstructionMatrixBuilderType, MeshConcept MeshType> + [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> _build( + const std::shared_ptr<const MeshType>& p_mesh, + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; + template <MeshConcept MeshType> [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> _build( const std::shared_ptr<const MeshType>& p_mesh, diff --git a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.cpp b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d030ac6d51d22d70eb209fa437a8684064d57529 --- /dev/null +++ b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.cpp @@ -0,0 +1,365 @@ +#include <scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp> + +#include <analysis/GaussLegendreQuadratureDescriptor.hpp> +#include <analysis/QuadratureManager.hpp> +#include <geometry/LineCubicTransformation.hpp> +#include <geometry/LineParabolicTransformation.hpp> +#include <geometry/SymmetryUtils.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshDataManager.hpp> +#include <mesh/PolynomialMesh.hpp> +#include <scheme/DiscreteFunctionDPk.hpp> + +template <MeshConcept MeshTypeT> +template <typename ConformTransformationT> +void +PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<MeshTypeT>::_computeEjkBoundaryMean( + const QuadratureFormula<MeshType::Dimension - 1>& quadrature, + const ConformTransformationT& T, + const Rd& Xj, + const double inv_Vi, + SmallArray<double>& mean_of_ejk) +{ + if constexpr (std::is_same_v<LineTransformation<MeshType::Dimension>, ConformTransformationT>) { + const double velocity_perp_e1 = T.velocity()[1] * inv_Vi; + + for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { + const double wq = quadrature.weight(i_q); + const TinyVector<1> xi_q = quadrature.point(i_q); + + const Rd X_Xj = T(xi_q) - Xj; + + const double x_xj = X_Xj[0]; + const double y_yj = X_Xj[1]; + + { + size_t k = 0; + m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = x_xj * wq * velocity_perp_e1; + for (; k <= m_polynomial_degree; ++k) { + m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k] = + x_xj * m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k - 1] * m_antiderivative_coef[k]; + } + + for (size_t i_y = 1; i_y <= m_polynomial_degree; ++i_y) { + const size_t begin_i_y_1 = ((i_y - 1) * (2 * m_polynomial_degree - i_y + 4)) / 2; + for (size_t l = 0; l <= m_polynomial_degree - i_y; ++l) { + m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = y_yj * m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[begin_i_y_1 + l]; + } + } + } + + for (size_t k = 1; k < m_basis_dimension; ++k) { + mean_of_ejk[k - 1] += m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k]; + } + } + } else { + for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { + const double wq = quadrature.weight(i_q); + const TinyVector<1> xi_q = quadrature.point(i_q); + + const double velocity_perp_e1 = T.velocity(xi_q)[1] * inv_Vi; + + const Rd X_Xj = T(xi_q) - Xj; + + const double x_xj = X_Xj[0]; + const double y_yj = X_Xj[1]; + + { + size_t k = 0; + m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = x_xj * wq * velocity_perp_e1; + for (; k <= m_polynomial_degree; ++k) { + m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k] = + x_xj * m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k - 1] * m_antiderivative_coef[k]; + } + + for (size_t i_y = 1; i_y <= m_polynomial_degree; ++i_y) { + const size_t begin_i_y_1 = ((i_y - 1) * (2 * m_polynomial_degree - i_y + 4)) / 2; + for (size_t l = 0; l <= m_polynomial_degree - i_y; ++l) { + m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = y_yj * m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[begin_i_y_1 + l]; + } + } + } + + for (size_t k = 1; k < m_basis_dimension; ++k) { + mean_of_ejk[k - 1] += m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k]; + } + } + } +} + +template <MeshConcept MeshTypeT> +void +PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<MeshTypeT>::_computeEjkMeanByBoundary( + const Rd& Xj, + const CellId& cell_id, + SmallArray<double>& mean_of_ejk) +{ + if constexpr (is_polygonal_mesh_v<MeshType>) { + const auto& quadrature = + QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(m_polynomial_degree + 1)); + + const double inv_Vi = 1. / m_Vj[cell_id]; + + mean_of_ejk.fill(0); + auto cell_face_list = m_cell_to_face_matrix[cell_id]; + for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { + bool is_reversed = m_cell_face_is_reversed[cell_id][i_face]; + + const FaceId face_id = cell_face_list[i_face]; + auto face_node_list = m_face_to_node_matrix[face_id]; + if (is_reversed) { + const LineTransformation<2> T{m_xr[face_node_list[1]], m_xr[face_node_list[0]]}; + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); + } else { + const LineTransformation<2> T{m_xr[face_node_list[0]], m_xr[face_node_list[1]]}; + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); + } + } + } else { + static_assert(is_polynomial_mesh_v<MeshType>); + // (degree + 1): divergence theorem + // mesh.degree(): transformation polynomial degree + // (mesh.degree() - 1): degree of the line abscissa velocity + const auto& quadrature = QuadratureManager::instance().getLineFormula( + GaussLegendreQuadratureDescriptor((m_polynomial_degree + 1) * m_mesh.degree() + (m_mesh.degree() - 1))); + + const double inv_Vi = 1. / m_Vj[cell_id]; + + auto xr = m_mesh.xr(); + auto xl = m_mesh.xl(); + mean_of_ejk.fill(0); + auto cell_face_list = m_cell_to_face_matrix[cell_id]; + for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { + bool is_reversed = m_cell_face_is_reversed[cell_id][i_face]; + + const FaceId face_id = cell_face_list[i_face]; + auto face_node_list = m_face_to_node_matrix[face_id]; +#warning rework + switch (m_mesh.degree()) { + case 2: { + if (is_reversed) { + const LineParabolicTransformation<2> T{xr[face_node_list[1]], xl[face_id][0], xr[face_node_list[0]]}; + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); + } else { + const LineParabolicTransformation<2> T{xr[face_node_list[0]], xl[face_id][0], xr[face_node_list[1]]}; + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); + } + break; + } + case 3: { + if (is_reversed) { + const LineCubicTransformation<2> T{xr[face_node_list[1]], xl[face_id][1], xl[face_id][0], + xr[face_node_list[0]]}; + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); + } else { + const LineCubicTransformation<2> T{xr[face_node_list[0]], xl[face_id][0], xl[face_id][1], + xr[face_node_list[1]]}; + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); + } + break; + } + default: { + std::ostringstream error_msg; + error_msg << "reconstruction on meshes of degree " << m_mesh.degree(); + throw NotImplementedError(error_msg.str()); + } + } + } + } +} + +template <MeshConcept MeshTypeT> +void +PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< + MeshTypeT>::_computeEjkMeanByBoundaryInSymmetricCell(const Rd& origin, + const Rd& normal, + const Rd& Xj, + const CellId& cell_id, + SmallArray<double>& mean_of_ejk) +{ + if constexpr (is_polygonal_mesh_v<MeshType>) { + const auto& quadrature = + QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(m_polynomial_degree + 1)); + + const double inv_Vi = 1. / m_Vj[cell_id]; + + mean_of_ejk.fill(0); + auto cell_face_list = m_cell_to_face_matrix[cell_id]; + for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { + bool is_reversed = m_cell_face_is_reversed[cell_id][i_face]; + + const FaceId face_id = cell_face_list[i_face]; + auto face_node_list = m_face_to_node_matrix[face_id]; + + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[1]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[0]]); + + if (is_reversed) { + const LineTransformation<2> T{x1, x0}; + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); + } else { + const LineTransformation<2> T{x0, x1}; + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); + } + } + } else { + static_assert(is_polynomial_mesh_v<MeshType>); + // (degree + 1): divergence theorem + // mesh.degree(): transformation polynomial degree + // (mesh.degree() - 1): degree of the line abscissa velocity + const auto& quadrature = QuadratureManager::instance().getLineFormula( + GaussLegendreQuadratureDescriptor((m_polynomial_degree + 1) * m_mesh.degree() + (m_mesh.degree() - 1))); + + const double inv_Vi = 1. / m_Vj[cell_id]; + + auto xl = m_mesh.xl(); + + mean_of_ejk.fill(0); + auto cell_face_list = m_cell_to_face_matrix[cell_id]; + for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { + bool is_reversed = m_cell_face_is_reversed[cell_id][i_face]; + + const FaceId face_id = cell_face_list[i_face]; + auto face_node_list = m_face_to_node_matrix[face_id]; + + switch (m_mesh.degree()) { + case 2: { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[1]]); + const auto x1 = symmetrize_coordinates(origin, normal, xl[face_id][0]); + const auto x2 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[0]]); + + if (is_reversed) { + const LineParabolicTransformation<2> T{x2, x1, x0}; + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); + } else { + const LineParabolicTransformation<2> T{x0, x1, x2}; + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); + } + break; + } + case 3: { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[1]]); + const auto x1 = symmetrize_coordinates(origin, normal, xl[face_id][1]); + const auto x2 = symmetrize_coordinates(origin, normal, xl[face_id][0]); + const auto x3 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[0]]); + + if (is_reversed) { + const LineCubicTransformation<2> T{x3, x2, x1, x0}; + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); + } else { + const LineCubicTransformation<2> T{x0, x1, x2, x3}; + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); + } + break; + } + default: { + std::ostringstream error_msg; + error_msg << "reconstruction on meshes of degree " << m_mesh.degree(); + throw NotImplementedError(error_msg.str()); + } + } + } + } +} + +template <MeshConcept MeshTypeT> +void +PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<MeshTypeT>::build( + const CellId cell_j_id, + ShrinkMatrixView<SmallMatrix<double>>& A) +{ + if constexpr (MeshType::Dimension == 2) { + const auto& stencil_cell_list = m_stencil_array[cell_j_id]; + + const Rd& Xj = m_xj[cell_j_id]; + + _computeEjkMeanByBoundary(Xj, cell_j_id, m_mean_j_of_ejk); + + 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]; + + _computeEjkMeanByBoundary(Xj, cell_i_id, m_mean_i_of_ejk); + + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; + } + } + for (size_t i_symmetry = 0; i_symmetry < m_stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry) { + auto& ghost_stencil = m_stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + + const Rd& origin = m_symmetry_origin_list[i_symmetry]; + const Rd& normal = m_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]; + + _computeEjkMeanByBoundaryInSymmetricCell(origin, normal, Xj, cell_i_id, m_mean_i_of_ejk); + + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; + } + } + } + } else { + throw NotImplementedError("invalid mesh dimension"); + } +} + +template <MeshConcept MeshTypeT> +PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< + MeshTypeT>::BoundaryIntegralReconstructionMatrixBuilder(const MeshType& mesh, + const size_t polynomial_degree, + const SmallArray<const Rd>& symmetry_origin_list, + const SmallArray<const Rd>& symmetry_normal_list, + const CellToCellStencilArray& stencil_array) + : m_mesh{mesh}, + m_basis_dimension{ + DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(polynomial_degree)}, + m_polynomial_degree{polynomial_degree}, + m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek{m_basis_dimension}, + m_mean_j_of_ejk{m_basis_dimension - 1}, + m_mean_i_of_ejk{m_basis_dimension - 1}, + m_cell_to_face_matrix{mesh.connectivity().cellToFaceMatrix()}, + m_face_to_node_matrix{mesh.connectivity().faceToNodeMatrix()}, + m_cell_face_is_reversed{mesh.connectivity().cellFaceIsReversed()}, + m_stencil_array{stencil_array}, + m_symmetry_origin_list{symmetry_origin_list}, + m_symmetry_normal_list{symmetry_normal_list}, + m_Vj{MeshDataManager::instance().getMeshData(mesh).Vj()}, + m_xj{MeshDataManager::instance().getMeshData(mesh).xj()}, + m_xr{mesh.xr()} +{ + if constexpr (MeshType::Dimension == 2) { + SmallArray<double> antiderivative_coef(m_polynomial_degree + 1); + for (size_t k = 0; k < antiderivative_coef.size(); ++k) { + antiderivative_coef[k] = ((1. * k) / (k + 1)); + } + + m_antiderivative_coef = antiderivative_coef; + } +} + +template void PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<Mesh<2>>::build( + const CellId, + ShrinkMatrixView<SmallMatrix<double>>&); + +template void PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<PolynomialMesh<2>>::build( + const CellId, + ShrinkMatrixView<SmallMatrix<double>>&); + +template PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< + Mesh<2>>::BoundaryIntegralReconstructionMatrixBuilder(const MeshType&, + const size_t, + const SmallArray<const Rd>&, + const SmallArray<const Rd>&, + const CellToCellStencilArray&); + +template PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< + PolynomialMesh<2>>::BoundaryIntegralReconstructionMatrixBuilder(const MeshType&, + const size_t, + const SmallArray<const Rd>&, + const SmallArray<const Rd>&, + const CellToCellStencilArray&); diff --git a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp new file mode 100644 index 0000000000000000000000000000000000000000..df24d76a73097d13f6379681780d90aed6f49e1d --- /dev/null +++ b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp @@ -0,0 +1,83 @@ +#ifndef BOUNDARY_INTEGRAL_RECONSTRUCTION_MATRIX_BUILDER_HPP +#define BOUNDARY_INTEGRAL_RECONSTRUCTION_MATRIX_BUILDER_HPP + +#include <algebra/ShrinkMatrixView.hpp> +#include <algebra/SmallMatrix.hpp> +#include <analysis/QuadratureFormula.hpp> +#include <geometry/LineTransformation.hpp> +#include <mesh/ItemValue.hpp> +#include <mesh/StencilArray.hpp> +#include <mesh/SubItemValuePerItem.hpp> +#include <scheme/PolynomialReconstruction.hpp> +#include <utils/SmallArray.hpp> + +template <MeshConcept MeshTypeT> +class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder +{ + public: + using MeshType = MeshTypeT; + + constexpr static bool handles_high_degrees = true; + + private: + using Rd = TinyVector<MeshType::Dimension>; + + const MeshType& m_mesh; + const size_t m_basis_dimension; + const size_t m_polynomial_degree; + + const SmallArray<double> m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek; + SmallArray<double> m_mean_j_of_ejk; + SmallArray<double> m_mean_i_of_ejk; + + const ItemToItemMatrix<ItemType::cell, ItemType::face> m_cell_to_face_matrix; + const ItemToItemMatrix<ItemType::face, ItemType::node> m_face_to_node_matrix; + const FaceValuePerCell<const bool> m_cell_face_is_reversed; + + const CellToCellStencilArray& m_stencil_array; + + const SmallArray<const Rd> m_symmetry_origin_list; + const SmallArray<const Rd> m_symmetry_normal_list; + + const CellValue<const double> m_Vj; + const CellValue<const Rd> m_xj; + const NodeValue<const Rd> m_xr; + + SmallArray<const double> m_antiderivative_coef; + + template <typename ConformTransformationT> + void _computeEjkBoundaryMean(const QuadratureFormula<MeshType::Dimension - 1>& quadrature, + const ConformTransformationT& T, + const Rd& Xj, + const double inv_Vi, + SmallArray<double>& mean_of_ejk); + + void _computeEjkMeanByBoundary(const Rd& Xj, const CellId& cell_id, SmallArray<double>& mean_of_ejk); + + void _computeEjkMeanByBoundaryInSymmetricCell(const Rd& origin, + const Rd& normal, + const Rd& Xj, + const CellId& cell_id, + SmallArray<double>& mean_of_ejk); + + public: + PUGS_INLINE + SmallArray<const double> + meanjOfEjk() const + { + return m_mean_j_of_ejk; + } + + void build(const CellId cell_j_id, ShrinkMatrixView<SmallMatrix<double>>& A); + + BoundaryIntegralReconstructionMatrixBuilder(const MeshType& mesh, + const size_t polynomial_degree, + const SmallArray<const Rd>& symmetry_origin_list, + const SmallArray<const Rd>& symmetry_normal_list, + const CellToCellStencilArray& stencil_array); + + BoundaryIntegralReconstructionMatrixBuilder() = default; + ~BoundaryIntegralReconstructionMatrixBuilder() = default; +}; + +#endif // BOUNDARY_INTEGRAL_RECONSTRUCTION_MATRIX_BUILDER_HPP diff --git a/src/scheme/reconstruction_utils/CMakeLists.txt b/src/scheme/reconstruction_utils/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..1d4ebcb48c3d47c9c19421151257deb8a31bf9a9 --- /dev/null +++ b/src/scheme/reconstruction_utils/CMakeLists.txt @@ -0,0 +1,17 @@ +# ------------------- Source files -------------------- + +add_library(PugsSchemeReconstructionUtils + BoundaryIntegralReconstructionMatrixBuilder.cpp + CellCenterReconstructionMatrixBuilder.cpp + ElementIntegralReconstructionMatrixBuilder.cpp +) + +add_dependencies( + PugsUtils + PugsMesh +) + +target_link_libraries( + PugsSchemeReconstructionUtils + ${HIGHFIVE_TARGET} +) diff --git a/src/scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.cpp b/src/scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ca06fdca5e5f5366879776c8630a74f17fe994cf --- /dev/null +++ b/src/scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.cpp @@ -0,0 +1,104 @@ +#include <scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.hpp> + +#include <geometry/SymmetryUtils.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/PolynomialMesh.hpp> +#include <scheme/DiscreteFunctionDPk.hpp> + +template <MeshConcept MeshTypeT> +void +PolynomialReconstruction::CellCenterReconstructionMatrixBuilder<MeshTypeT>::build( + const CellId cell_j_id, + ShrinkMatrixView<SmallMatrix<double>>& A) +{ + const auto& stencil_cell_list = m_stencil_array[cell_j_id]; + + const Rd& Xj = m_xj[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]; + const Rd Xi_Xj = m_xj[cell_i_id] - Xj; + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = Xi_Xj[l]; + } + } + for (size_t i_symmetry = 0; i_symmetry < m_stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry) { + auto& ghost_stencil = m_stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + + const Rd& origin = m_symmetry_origin_list[i_symmetry]; + const Rd& normal = m_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]; + const Rd Xi_Xj = symmetrize_coordinates(origin, normal, m_xj[cell_i_id]) - Xj; + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = Xi_Xj[l]; + } + } + } +} + +template <MeshConcept MeshTypeT> +PolynomialReconstruction::CellCenterReconstructionMatrixBuilder<MeshTypeT>::CellCenterReconstructionMatrixBuilder( + const MeshType& mesh, + const size_t polynomial_degree, + const SmallArray<const Rd>& symmetry_origin_list, + const SmallArray<const Rd>& symmetry_normal_list, + const CellToCellStencilArray& stencil_array) + : m_basis_dimension{DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree( + polynomial_degree)}, + m_symmetry_origin_list{symmetry_origin_list}, + m_symmetry_normal_list{symmetry_normal_list}, + m_stencil_array{stencil_array}, + m_xj{MeshDataManager::instance().getMeshData(mesh).xj()} +{ + if (polynomial_degree != 1) { + throw NormalError("cell center based reconstruction is only valid for first order"); + } +} + +template void PolynomialReconstruction::CellCenterReconstructionMatrixBuilder<Mesh<1>>::build( + const CellId, + ShrinkMatrixView<SmallMatrix<double>>&); + +template void PolynomialReconstruction::CellCenterReconstructionMatrixBuilder<Mesh<2>>::build( + const CellId, + ShrinkMatrixView<SmallMatrix<double>>&); + +template void PolynomialReconstruction::CellCenterReconstructionMatrixBuilder<Mesh<3>>::build( + const CellId, + ShrinkMatrixView<SmallMatrix<double>>&); + +template void PolynomialReconstruction::CellCenterReconstructionMatrixBuilder<PolynomialMesh<2>>::build( + const CellId, + ShrinkMatrixView<SmallMatrix<double>>&); + +template PolynomialReconstruction::CellCenterReconstructionMatrixBuilder< + Mesh<1>>::CellCenterReconstructionMatrixBuilder(const MeshType&, + const size_t, + const SmallArray<const Rd>&, + const SmallArray<const Rd>&, + const CellToCellStencilArray&); + +template PolynomialReconstruction::CellCenterReconstructionMatrixBuilder< + Mesh<2>>::CellCenterReconstructionMatrixBuilder(const MeshType&, + const size_t, + const SmallArray<const Rd>&, + const SmallArray<const Rd>&, + const CellToCellStencilArray&); + +template PolynomialReconstruction::CellCenterReconstructionMatrixBuilder< + Mesh<3>>::CellCenterReconstructionMatrixBuilder(const MeshType&, + const size_t, + const SmallArray<const Rd>&, + const SmallArray<const Rd>&, + const CellToCellStencilArray&); + +template PolynomialReconstruction::CellCenterReconstructionMatrixBuilder< + PolynomialMesh<2>>::CellCenterReconstructionMatrixBuilder(const MeshType&, + const size_t, + const SmallArray<const Rd>&, + const SmallArray<const Rd>&, + const CellToCellStencilArray&); diff --git a/src/scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.hpp b/src/scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a8205a625dc0d62e35a33469fe2864719f8147a6 --- /dev/null +++ b/src/scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.hpp @@ -0,0 +1,41 @@ +#ifndef CELL_CENTER_RECONSTRUCTION_MATRIX_BUILDER_HPP +#define CELL_CENTER_RECONSTRUCTION_MATRIX_BUILDER_HPP + +#include <algebra/ShrinkMatrixView.hpp> +#include <algebra/SmallMatrix.hpp> +#include <mesh/ItemValue.hpp> +#include <mesh/StencilArray.hpp> +#include <scheme/PolynomialReconstruction.hpp> +#include <utils/SmallArray.hpp> + +template <MeshConcept MeshTypeT> +class PolynomialReconstruction::CellCenterReconstructionMatrixBuilder +{ + public: + using MeshType = MeshTypeT; + + constexpr static bool handles_high_degrees = false; + + private: + using Rd = TinyVector<MeshType::Dimension>; + + const size_t m_basis_dimension; + const SmallArray<const Rd> m_symmetry_origin_list; + const SmallArray<const Rd> m_symmetry_normal_list; + + const CellToCellStencilArray& m_stencil_array; + const CellValue<const Rd> m_xj; + + public: + void build(const CellId cell_j_id, ShrinkMatrixView<SmallMatrix<double>>& A); + + CellCenterReconstructionMatrixBuilder(const MeshType& mesh, + const size_t polynomial_degree, + const SmallArray<const Rd>& symmetry_origin_list, + const SmallArray<const Rd>& symmetry_normal_list, + const CellToCellStencilArray& stencil_array); + + ~CellCenterReconstructionMatrixBuilder() = default; +}; + +#endif // CELL_CENTER_RECONSTRUCTION_MATRIX_BUILDER_HPP diff --git a/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.cpp b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..198dba9afe1faf1c324a8c03ae68376dc27ac799 --- /dev/null +++ b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.cpp @@ -0,0 +1,523 @@ +#include <scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp> + +#include <analysis/GaussLegendreQuadratureDescriptor.hpp> +#include <analysis/GaussQuadratureDescriptor.hpp> +#include <geometry/CubeTransformation.hpp> +#include <geometry/LineTransformation.hpp> +#include <geometry/PrismTransformation.hpp> +#include <geometry/PyramidTransformation.hpp> +#include <geometry/SquareTransformation.hpp> +#include <geometry/SymmetryUtils.hpp> +#include <geometry/TetrahedronTransformation.hpp> +#include <geometry/TriangleTransformation.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshDataManager.hpp> +#include <mesh/PolynomialMesh.hpp> +#include <scheme/DiscreteFunctionDPk.hpp> + +template <MeshConcept MeshTypeT> +template <typename ConformTransformationT> +void +PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder<MeshTypeT>::_computeEjkMean( + const QuadratureFormula<MeshType::Dimension>& quadrature, + const ConformTransformationT& T, + const Rd& Xj, + const double Vi, + SmallArray<double>& mean_of_ejk) noexcept(NO_ASSERT) +{ + mean_of_ejk.fill(0); + + for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { + const double wq = quadrature.weight(i_q); + const Rd& xi_q = quadrature.point(i_q); + + const Rd X_Xj = T(xi_q) - Xj; + + if constexpr (MeshType::Dimension == 1) { + const double detT = T.jacobianDeterminant(); + + const double x_xj = X_Xj[0]; + + { + m_wq_detJ_ek[0] = wq * detT; + for (size_t k = 1; k <= m_polynomial_degree; ++k) { + m_wq_detJ_ek[k] = x_xj * m_wq_detJ_ek[k - 1]; + } + } + + } else if constexpr (MeshType::Dimension == 2) { + const double detT = [&] { + if constexpr (std::is_same_v<TriangleTransformation<2>, std::decay_t<decltype(T)>>) { + return T.jacobianDeterminant(); + } else { + return T.jacobianDeterminant(xi_q); + } + }(); + + const double x_xj = X_Xj[0]; + const double y_yj = X_Xj[1]; + + { + size_t k = 0; + m_wq_detJ_ek[k++] = wq * detT; + for (; k <= m_polynomial_degree; ++k) { + m_wq_detJ_ek[k] = x_xj * m_wq_detJ_ek[k - 1]; + } + + for (size_t i_y = 1; i_y <= m_polynomial_degree; ++i_y) { + const size_t begin_i_y_1 = m_y_row_index[i_y - 1]; + for (size_t l = 0; l <= m_polynomial_degree - i_y; ++l, ++k) { + m_wq_detJ_ek[k] = y_yj * m_wq_detJ_ek[begin_i_y_1 + l]; + } + } + } + + } else if constexpr (MeshType::Dimension == 3) { + static_assert(MeshType::Dimension == 3); + + const double detT = [&] { + if constexpr (std::is_same_v<TetrahedronTransformation, std::decay_t<decltype(T)>>) { + return T.jacobianDeterminant(); + } else { + return T.jacobianDeterminant(xi_q); + } + }(); + + const double x_xj = X_Xj[0]; + const double y_yj = X_Xj[1]; + const double z_zj = X_Xj[2]; + + { + size_t k = 0; + m_wq_detJ_ek[k++] = wq * detT; + for (; k <= m_polynomial_degree; ++k) { + m_wq_detJ_ek[k] = x_xj * m_wq_detJ_ek[k - 1]; + } + + for (size_t i_y = 1; i_y <= m_polynomial_degree; ++i_y) { + const size_t begin_i_y_1 = m_yz_row_index[i_y - 1]; + const size_t nb_monoms = m_yz_row_size[i_y]; + for (size_t l = 0; l < nb_monoms; ++l, ++k) { + m_wq_detJ_ek[k] = y_yj * m_wq_detJ_ek[begin_i_y_1 + l]; + } + } + + for (size_t i_z = 1; i_z <= m_polynomial_degree; ++i_z) { + const size_t nb_y = m_yz_row_size[m_z_triangle_index[i_z]]; + const size_t index_z = m_z_triangle_index[i_z]; + const size_t index_z_1 = m_z_triangle_index[i_z - 1]; + for (size_t i_y = 0; i_y < nb_y; ++i_y) { + const size_t begin_i_yz_1 = m_yz_row_index[index_z_1 + i_y]; + const size_t nb_monoms = m_yz_row_size[index_z + i_y]; + for (size_t l = 0; l < nb_monoms; ++l, ++k) { + m_wq_detJ_ek[k] = z_zj * m_wq_detJ_ek[begin_i_yz_1 + l]; + } + } + } + } + } + + for (size_t k = 1; k < m_basis_dimension; ++k) { + mean_of_ejk[k - 1] += m_wq_detJ_ek[k]; + } + } + + const double inv_Vi = 1. / Vi; + for (size_t k = 0; k < mean_of_ejk.size(); ++k) { + mean_of_ejk[k] *= inv_Vi; + } +} + +template <MeshConcept MeshTypeT> +void +PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder<MeshTypeT>::_computeEjkMean( + const Rd& Xj, + const CellId& cell_i_id, + SmallArray<double>& mean_of_ejk) +{ + const CellType cell_type = m_cell_type[cell_i_id]; + const auto node_list = m_cell_to_node_matrix[cell_i_id]; + const double Vi = m_Vj[cell_i_id]; + + if constexpr (MeshType::Dimension == 1) { + if (m_cell_type[cell_i_id] == CellType::Line) { + const LineTransformation<1> T{m_xr[node_list[0]], m_xr[node_list[1]]}; + + const auto& quadrature = + QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + + } else { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } + } else if constexpr (MeshType::Dimension == 2) { + switch (cell_type) { + case CellType::Triangle: { + const TriangleTransformation<2> T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]]}; + const auto& quadrature = + QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{m_polynomial_degree}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + case CellType::Quadrangle: { + const SquareTransformation<2> T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]], m_xr[node_list[3]]}; + const auto& quadrature = + QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree + 1}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + default: { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } + } + } else { + static_assert(MeshType::Dimension == 3); + + switch (cell_type) { + case CellType::Tetrahedron: { + const TetrahedronTransformation T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]], m_xr[node_list[3]]}; + + const auto& quadrature = + QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{m_polynomial_degree}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + + break; + } + case CellType::Prism: { + const PrismTransformation T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]], // + m_xr[node_list[3]], m_xr[node_list[4]], m_xr[node_list[5]]}; + + const auto& quadrature = + QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{m_polynomial_degree + 1}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + + break; + } + case CellType::Pyramid: { + const PyramidTransformation T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]], m_xr[node_list[3]], + m_xr[node_list[4]]}; + + const auto& quadrature = + QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{m_polynomial_degree + 1}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + case CellType::Hexahedron: { + const CubeTransformation T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]], m_xr[node_list[3]], + m_xr[node_list[4]], m_xr[node_list[5]], m_xr[node_list[6]], m_xr[node_list[7]]}; + + const auto& quadrature = + QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree + 1}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + default: { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } + } + } +} + +template <MeshConcept MeshTypeT> +void +PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder<MeshTypeT>::_computeEjkMeanInSymmetricCell( + const Rd& origin, + const Rd& normal, + const Rd& Xj, + const CellId& cell_i_id, + SmallArray<double>& mean_of_ejk) +{ + if constexpr (MeshType::Dimension == 1) { + auto node_list = m_cell_to_node_matrix[cell_i_id]; + const CellType cell_type = m_cell_type[cell_i_id]; + const double Vi = m_Vj[cell_i_id]; + + if (cell_type == CellType::Line) { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); + + const LineTransformation<1> T{x0, x1}; + + const auto& quadrature = + QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + + } else { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } + } else if constexpr (MeshType::Dimension == 2) { + auto node_list = m_cell_to_node_matrix[cell_i_id]; + const CellType cell_type = m_cell_type[cell_i_id]; + const double Vi = m_Vj[cell_i_id]; + + switch (cell_type) { + case CellType::Triangle: { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); + const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); + + const TriangleTransformation<2> T{x0, x1, x2}; + const auto& quadrature = + QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{m_polynomial_degree}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + case CellType::Quadrangle: { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[3]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); + const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); + const auto x3 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); + + const SquareTransformation<2> T{x0, x1, x2, x3}; + const auto& quadrature = + QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree + 1}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + default: { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } + } + } else { + static_assert(MeshType::Dimension == 3); + auto node_list = m_cell_to_node_matrix[cell_i_id]; + const CellType cell_type = m_cell_type[cell_i_id]; + const double Vi = m_Vj[cell_i_id]; + switch (cell_type) { + case CellType::Tetrahedron: { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); + const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); + const auto x3 = symmetrize_coordinates(origin, normal, m_xr[node_list[3]]); + + const TetrahedronTransformation T{x0, x1, x2, x3}; + + const auto& quadrature = + QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{m_polynomial_degree}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + case CellType::Prism: { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); + const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); + const auto x3 = symmetrize_coordinates(origin, normal, m_xr[node_list[4]]); + const auto x4 = symmetrize_coordinates(origin, normal, m_xr[node_list[3]]); + const auto x5 = symmetrize_coordinates(origin, normal, m_xr[node_list[5]]); + + const PrismTransformation T{x0, x1, x2, // + x3, x4, x5}; + + const auto& quadrature = + QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{m_polynomial_degree + 1}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + case CellType::Pyramid: { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[3]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); + const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); + const auto x3 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); + const auto x4 = symmetrize_coordinates(origin, normal, m_xr[node_list[4]]); + const PyramidTransformation T{x0, x1, x2, x3, x4}; + + const auto& quadrature = + QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{m_polynomial_degree + 1}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + case CellType::Hexahedron: { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[3]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); + const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); + const auto x3 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); + const auto x4 = symmetrize_coordinates(origin, normal, m_xr[node_list[7]]); + const auto x5 = symmetrize_coordinates(origin, normal, m_xr[node_list[6]]); + const auto x6 = symmetrize_coordinates(origin, normal, m_xr[node_list[5]]); + const auto x7 = symmetrize_coordinates(origin, normal, m_xr[node_list[4]]); + + const CubeTransformation T{x0, x1, x2, x3, x4, x5, x6, x7}; + + const auto& quadrature = + QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree + 1}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + default: { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } + } + } +} + +template <MeshConcept MeshTypeT> +void +PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder<MeshTypeT>::build( + const CellId cell_j_id, + ShrinkMatrixView<SmallMatrix<double>>& A) +{ + const auto& stencil_cell_list = m_stencil_array[cell_j_id]; + + const Rd& Xj = m_xj[cell_j_id]; + + this->_computeEjkMean(Xj, cell_j_id, m_mean_j_of_ejk); + + 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]; + + this->_computeEjkMean(Xj, cell_i_id, m_mean_i_of_ejk); + + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; + } + } + + for (size_t i_symmetry = 0; i_symmetry < m_stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry) { + auto& ghost_stencil = m_stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + + const Rd& origin = m_symmetry_origin_list[i_symmetry]; + const Rd& normal = m_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]; + + this->_computeEjkMeanInSymmetricCell(origin, normal, Xj, cell_i_id, m_mean_i_of_ejk); + + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; + } + } + } +} + +template <MeshConcept MeshTypeT> +PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder< + MeshTypeT>::ElementIntegralReconstructionMatrixBuilder(const MeshType& mesh, + const size_t polynomial_degree, + const SmallArray<const Rd>& symmetry_origin_list, + const SmallArray<const Rd>& symmetry_normal_list, + const CellToCellStencilArray& stencil_array) + : m_basis_dimension{DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree( + polynomial_degree)}, + m_polynomial_degree{polynomial_degree}, + + m_wq_detJ_ek{m_basis_dimension}, + m_mean_j_of_ejk{m_basis_dimension - 1}, + m_mean_i_of_ejk{m_basis_dimension - 1}, + + m_cell_to_node_matrix{mesh.connectivity().cellToNodeMatrix()}, + m_stencil_array{stencil_array}, + m_symmetry_origin_list{symmetry_origin_list}, + m_symmetry_normal_list{symmetry_normal_list}, + m_cell_type{mesh.connectivity().cellType()}, + m_Vj{MeshDataManager::instance().getMeshData(mesh).Vj()}, + m_xj{MeshDataManager::instance().getMeshData(mesh).xj()}, + m_xr{mesh.xr()} +{ + if constexpr (is_polynomial_mesh_v<MeshType>) { + if (polynomial_degree != 1) { + throw NotImplementedError("cannot reconstruct polynomials of degree != 1 on polynomial meshes"); + } + } + + if constexpr (MeshType::Dimension == 2) { + SmallArray<size_t> y_row_index(m_polynomial_degree + 1); + + size_t i_y = 0; + + y_row_index[i_y++] = 0; + for (ssize_t n = m_polynomial_degree + 1; n > 1; --n, ++i_y) { + y_row_index[i_y] = y_row_index[i_y - 1] + n; + } + + m_y_row_index = y_row_index; + + } else if constexpr (MeshType::Dimension == 3) { + SmallArray<size_t> yz_row_index((m_polynomial_degree + 2) * (m_polynomial_degree + 1) / 2 + 1); + SmallArray<size_t> z_triangle_index(m_polynomial_degree + 1); + + { + size_t i_z = 0; + size_t i_yz = 0; + + yz_row_index[i_yz++] = 0; + for (ssize_t n = m_polynomial_degree + 1; n >= 1; --n) { + z_triangle_index[i_z++] = i_yz - 1; + for (ssize_t i = n; i >= 1; --i) { + yz_row_index[i_yz] = yz_row_index[i_yz - 1] + i; + ++i_yz; + } + } + } + + SmallArray<size_t> yz_row_size{yz_row_index.size() - 1}; + for (size_t i = 0; i < yz_row_size.size(); ++i) { + yz_row_size[i] = yz_row_index[i + 1] - yz_row_index[i]; + } + + m_yz_row_index = yz_row_index; + m_z_triangle_index = z_triangle_index; + m_yz_row_size = yz_row_size; + } +} + +template void PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder<Mesh<1>>::build( + const CellId, + ShrinkMatrixView<SmallMatrix<double>>&); + +template void PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder<Mesh<2>>::build( + const CellId, + ShrinkMatrixView<SmallMatrix<double>>&); + +template void PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder<Mesh<3>>::build( + const CellId, + ShrinkMatrixView<SmallMatrix<double>>&); + +template void PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder<PolynomialMesh<2>>::build( + const CellId, + ShrinkMatrixView<SmallMatrix<double>>&); + +template PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder< + Mesh<1>>::ElementIntegralReconstructionMatrixBuilder(const MeshType&, + const size_t, + const SmallArray<const Rd>&, + const SmallArray<const Rd>&, + const CellToCellStencilArray&); + +template PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder< + Mesh<2>>::ElementIntegralReconstructionMatrixBuilder(const MeshType&, + const size_t, + const SmallArray<const Rd>&, + const SmallArray<const Rd>&, + const CellToCellStencilArray&); + +template PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder< + Mesh<3>>::ElementIntegralReconstructionMatrixBuilder(const MeshType&, + const size_t, + const SmallArray<const Rd>&, + const SmallArray<const Rd>&, + const CellToCellStencilArray&); + +template PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder< + PolynomialMesh<2>>::ElementIntegralReconstructionMatrixBuilder(const MeshType&, + const size_t, + const SmallArray<const Rd>&, + const SmallArray<const Rd>&, + const CellToCellStencilArray&); diff --git a/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1f4573438260e321dbd1461d2566f854236b25f4 --- /dev/null +++ b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp @@ -0,0 +1,87 @@ +#ifndef ELEMENT_INTEGRAL_RECONSTRUCTION_MATRIX_BUILDER_HPP +#define ELEMENT_INTEGRAL_RECONSTRUCTION_MATRIX_BUILDER_HPP + +#include <algebra/ShrinkMatrixView.hpp> +#include <algebra/SmallMatrix.hpp> +#include <analysis/QuadratureFormula.hpp> +#include <analysis/QuadratureManager.hpp> +#include <mesh/CellType.hpp> +#include <mesh/ItemValue.hpp> +#include <mesh/StencilArray.hpp> +#include <scheme/PolynomialReconstruction.hpp> +#include <utils/SmallArray.hpp> + +template <MeshConcept MeshTypeT> +class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder +{ + public: + using MeshType = MeshTypeT; + + constexpr static bool handles_high_degrees = true; + + private: + using Rd = TinyVector<MeshType::Dimension>; + + const size_t m_basis_dimension; + const size_t m_polynomial_degree; + + const SmallArray<double> m_wq_detJ_ek; + SmallArray<double> m_mean_j_of_ejk; + SmallArray<double> m_mean_i_of_ejk; + + const ItemToItemMatrix<ItemType::cell, ItemType::node> m_cell_to_node_matrix; + const CellToCellStencilArray& m_stencil_array; + + const SmallArray<const Rd> m_symmetry_origin_list; + const SmallArray<const Rd> m_symmetry_normal_list; + + const CellValue<const CellType> m_cell_type; + const CellValue<const double> m_Vj; + const CellValue<const Rd> m_xj; + const NodeValue<const Rd> m_xr; + + // 2D + SmallArray<const size_t> m_y_row_index; + + // 3D + SmallArray<const size_t> m_yz_row_index; + SmallArray<const size_t> m_z_triangle_index; + SmallArray<const size_t> m_yz_row_size; + + template <typename ConformTransformationT> + void _computeEjkMean(const QuadratureFormula<MeshType::Dimension>& quadrature, + const ConformTransformationT& T, + const Rd& Xj, + const double Vi, + SmallArray<double>& mean_of_ejk) noexcept(NO_ASSERT); + + void _computeEjkMean(const TinyVector<MeshType::Dimension>& Xj, + const CellId& cell_i_id, + SmallArray<double>& mean_of_ejk); + + void _computeEjkMeanInSymmetricCell(const Rd& origin, + const Rd& normal, + const Rd& Xj, + const CellId& cell_i_id, + SmallArray<double>& mean_of_ejk); + + public: + PUGS_INLINE + SmallArray<const double> + meanjOfEjk() const + { + return m_mean_j_of_ejk; + } + + void build(const CellId cell_j_id, ShrinkMatrixView<SmallMatrix<double>>& A); + + ElementIntegralReconstructionMatrixBuilder(const MeshType& mesh, + const size_t polynomial_degree, + const SmallArray<const Rd>& symmetry_origin_list, + const SmallArray<const Rd>& symmetry_normal_list, + const CellToCellStencilArray& stencil_array); + + ~ElementIntegralReconstructionMatrixBuilder() = default; +}; + +#endif // ELEMENT_INTEGRAL_RECONSTRUCTION_MATRIX_BUILDER_HPP diff --git a/src/scheme/reconstruction_utils/MutableDiscreteFunctionDPkVariant.hpp b/src/scheme/reconstruction_utils/MutableDiscreteFunctionDPkVariant.hpp new file mode 100644 index 0000000000000000000000000000000000000000..761f3c1129b00609b1be9a23b5d256db4b48c084 --- /dev/null +++ b/src/scheme/reconstruction_utils/MutableDiscreteFunctionDPkVariant.hpp @@ -0,0 +1,130 @@ +#ifndef MUTABLE_DISCRETE_FUNCTION_D_PK_VARIANT_HPP +#define MUTABLE_DISCRETE_FUNCTION_D_PK_VARIANT_HPP + +#include <scheme/DiscreteFunctionDPk.hpp> +#include <scheme/PolynomialReconstruction.hpp> + +#include <variant> + +class PolynomialReconstruction::MutableDiscreteFunctionDPkVariant +{ + public: + using Variant = std::variant<DiscreteFunctionDPk<1, double>, + DiscreteFunctionDPk<1, TinyVector<1>>, + DiscreteFunctionDPk<1, TinyVector<2>>, + DiscreteFunctionDPk<1, TinyVector<3>>, + DiscreteFunctionDPk<1, TinyMatrix<1>>, + DiscreteFunctionDPk<1, TinyMatrix<2>>, + DiscreteFunctionDPk<1, TinyMatrix<3>>, + + DiscreteFunctionDPk<2, double>, + DiscreteFunctionDPk<2, TinyVector<1>>, + DiscreteFunctionDPk<2, TinyVector<2>>, + DiscreteFunctionDPk<2, TinyVector<3>>, + DiscreteFunctionDPk<2, TinyMatrix<1>>, + DiscreteFunctionDPk<2, TinyMatrix<2>>, + DiscreteFunctionDPk<2, TinyMatrix<3>>, + + DiscreteFunctionDPk<3, double>, + DiscreteFunctionDPk<3, TinyVector<1>>, + DiscreteFunctionDPk<3, TinyVector<2>>, + DiscreteFunctionDPk<3, TinyVector<3>>, + DiscreteFunctionDPk<3, TinyMatrix<1>>, + DiscreteFunctionDPk<3, TinyMatrix<2>>, + 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<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; + + public: + PUGS_INLINE + const Variant& + mutableDiscreteFunctionDPk() const + { + return m_mutable_discrete_function_dpk; + } + + template <typename DiscreteFunctionDPkT> + PUGS_INLINE auto&& + get() const + { + static_assert(is_discrete_function_dpk_v<DiscreteFunctionDPkT>, "invalid template argument"); +#ifndef NDEBUG + if (not std::holds_alternative<DiscreteFunctionDPkT>(this->m_mutable_discrete_function_dpk)) { + std::ostringstream error_msg; + error_msg << "invalid discrete function type\n"; + error_msg << "- required " << rang::fgB::red << demangle<DiscreteFunctionDPkT>() << rang::fg::reset << '\n'; + error_msg << "- contains " << rang::fgB::yellow + << std::visit([](auto&& f) -> std::string { return demangle<decltype(f)>(); }, + this->m_mutable_discrete_function_dpk) + << rang::fg::reset; + throw NormalError(error_msg.str()); + } +#endif // NDEBUG + + return std::get<DiscreteFunctionDPkT>(this->mutableDiscreteFunctionDPk()); + } + + template <size_t Dimension, typename DataType> + MutableDiscreteFunctionDPkVariant(const DiscreteFunctionDPk<Dimension, DataType>& discrete_function_dpk) + : m_mutable_discrete_function_dpk{discrete_function_dpk} + { + 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>>, + "DiscreteFunctionDPk with this DataType is not allowed in variant"); + } + + template <size_t Dimension, typename DataType> + MutableDiscreteFunctionDPkVariant(const DiscreteFunctionDPkVector<Dimension, DataType>& discrete_function_dpk) + : m_mutable_discrete_function_dpk{discrete_function_dpk} + { + 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"); + } + + MutableDiscreteFunctionDPkVariant& operator=(MutableDiscreteFunctionDPkVariant&&) = default; + MutableDiscreteFunctionDPkVariant& operator=(const MutableDiscreteFunctionDPkVariant&) = default; + + MutableDiscreteFunctionDPkVariant(const MutableDiscreteFunctionDPkVariant&) = default; + MutableDiscreteFunctionDPkVariant(MutableDiscreteFunctionDPkVariant&&) = default; + + MutableDiscreteFunctionDPkVariant() = delete; + ~MutableDiscreteFunctionDPkVariant() = default; +}; + +#endif // MUTABLE_DISCRETE_FUNCTION_D_PK_VARIANT_HPP diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 31e9149852a79c0cd4c2c5fc6e181f3a295231dd..a000a642063b8e68dcec6758b7b5950a4ca69595 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -289,6 +289,8 @@ add_executable (mpi_unit_tests test_ParallelChecker_read.cpp test_Partitioner.cpp test_PolynomialReconstruction_degree_1.cpp + test_PolynomialReconstruction_degree_2.cpp + test_PolynomialReconstruction_degree_3.cpp test_PolynomialReconstructionDescriptor.cpp test_RandomEngine.cpp test_StencilBuilder_cell2cell.cpp @@ -324,6 +326,7 @@ target_link_libraries (unit_tests PugsAlgebra PugsAnalysis PugsScheme + PugsSchemeReconstructionUtils PugsOutput PugsUtils PugsCheckpointing @@ -354,6 +357,7 @@ target_link_libraries (mpi_unit_tests PugsUtils PugsLanguageUtils PugsScheme + PugsSchemeReconstructionUtils PugsOutput PugsUtils PugsCheckpointing diff --git a/tests/DiscreteFunctionDPkForTests.hpp b/tests/DiscreteFunctionDPkForTests.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5b51861d811b2d8a35c91375bc9e8dd368e2b281 --- /dev/null +++ b/tests/DiscreteFunctionDPkForTests.hpp @@ -0,0 +1,365 @@ +#ifndef DISCRETE_FUNCTION_DPK_FOR_TESTS_HPP +#define DISCRETE_FUNCTION_DPK_FOR_TESTS_HPP + +#include <analysis/GaussQuadratureDescriptor.hpp> +#include <analysis/QuadratureFormula.hpp> +#include <analysis/QuadratureManager.hpp> +#include <geometry/CubeTransformation.hpp> +#include <geometry/LineTransformation.hpp> +#include <geometry/PrismTransformation.hpp> +#include <geometry/PyramidTransformation.hpp> +#include <geometry/SquareTransformation.hpp> +#include <geometry/TetrahedronTransformation.hpp> +#include <geometry/TriangleTransformation.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshDataManager.hpp> +#include <type_traits> + +namespace test_only +{ + +template <MeshConcept MeshType, typename DataType> +DiscreteFunctionP0<std::remove_const_t<DataType>> +exact_projection(const MeshType& mesh, + size_t degree, + std::function<DataType(const TinyVector<MeshType::Dimension>&)> exact_function) +{ + DiscreteFunctionP0<std::remove_const_t<DataType>> P0_function{mesh.meshVariant()}; + + auto Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + + auto xr = mesh.xr(); + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + auto cell_type = mesh.connectivity().cellType(); + + auto sum = [&exact_function, &Vj](const CellId cell_id, const auto& T, + const auto& qf) -> std::remove_const_t<DataType> { + std::remove_const_t<DataType> integral = + (qf.weight(0) * T.jacobianDeterminant(qf.point(0))) * exact_function(T(qf.point(0))); + for (size_t i_quadrarture = 1; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + integral += (qf.weight(i_quadrarture) * T.jacobianDeterminant(qf.point(i_quadrarture))) * + exact_function(T(qf.point(i_quadrarture))); + } + return 1. / Vj[cell_id] * integral; + }; + + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + auto cell_nodes = cell_to_node_matrix[cell_id]; + if constexpr (MeshType::Dimension == 1) { + LineTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]]}; + auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor{degree + 1}); + P0_function[cell_id] = sum(cell_id, T, qf); + } else if constexpr (MeshType::Dimension == 2) { + switch (cell_type[cell_id]) { + case CellType::Triangle: { + TriangleTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]]}; + auto qf = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{degree + 2}); + P0_function[cell_id] = sum(cell_id, T, qf); + break; + } + case CellType::Quadrangle: { + SquareTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], + xr[cell_nodes[3]]}; + auto qf = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor{degree + 2}); + P0_function[cell_id] = sum(cell_id, T, qf); + break; + } + default: { + throw UnexpectedError("unexpected cell type"); + } + } + } else if constexpr (MeshType::Dimension == 3) { + switch (cell_type[cell_id]) { + case CellType::Tetrahedron: { + TetrahedronTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]]}; + auto qf = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{degree + 3}); + P0_function[cell_id] = sum(cell_id, T, qf); + break; + } + case CellType::Pyramid: { + PyramidTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]], + xr[cell_nodes[4]]}; + auto qf = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{degree + 3}); + P0_function[cell_id] = sum(cell_id, T, qf); + break; + } + case CellType::Prism: { + PrismTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], + xr[cell_nodes[3]], xr[cell_nodes[4]], xr[cell_nodes[5]]}; + auto qf = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{degree + 3}); + P0_function[cell_id] = sum(cell_id, T, qf); + break; + } + case CellType::Hexahedron: { + CubeTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]], + xr[cell_nodes[4]], xr[cell_nodes[5]], xr[cell_nodes[6]], xr[cell_nodes[7]]}; + auto qf = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor{degree + 3}); + P0_function[cell_id] = sum(cell_id, T, qf); + break; + } + default: { + throw UnexpectedError("unexpected cell type"); + } + } + } else { + throw UnexpectedError("invalid mesh dimension"); + } + } + + return P0_function; +} + +template <MeshConcept MeshType, typename DataType, size_t NbComponents> +DiscreteFunctionP0Vector<std::remove_const_t<DataType>> +exact_projection( + const MeshType& mesh, + size_t degree, + const std::array<std::function<DataType(const TinyVector<MeshType::Dimension>&)>, NbComponents>& vector_exact) +{ + DiscreteFunctionP0Vector<std::remove_const_t<DataType>> P0_function_vector{mesh.meshVariant(), vector_exact.size()}; + + for (size_t i_component = 0; i_component < vector_exact.size(); ++i_component) { + auto exact_function = vector_exact[i_component]; + + DiscreteFunctionP0 P0_function = exact_projection(mesh, degree, vector_exact[i_component]); + + parallel_for( + mesh.numberOfCells(), + PUGS_LAMBDA(const CellId cell_id) { P0_function_vector[cell_id][i_component] = P0_function[cell_id]; }); + } + + return P0_function_vector; +} + +template <typename DataType> +PUGS_INLINE double +get_max_error(const DataType& x, const DataType& y) +{ + if constexpr (is_tiny_matrix_v<DataType>) { + return frobeniusNorm(x - y); + } else if constexpr (is_tiny_vector_v<DataType>) { + return l2Norm(x - y); + } else { + static_assert(std::is_arithmetic_v<DataType>, "expecting arithmetic type"); + return std::abs(x - y); + } +} + +template <MeshConcept MeshType, typename DataType> +double +max_reconstruction_error(const MeshType& mesh, + DiscreteFunctionDPk<MeshType::Dimension, const DataType> dpk_f, + std::function<DataType(const TinyVector<MeshType::Dimension>&)> exact) +{ + auto xr = mesh.xr(); + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + auto cell_type = mesh.connectivity().cellType(); + + double max_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + auto cell_nodes = cell_to_node_matrix[cell_id]; + if constexpr (MeshType::Dimension == 1) { + Assert(cell_type[cell_id] == CellType::Line); + LineTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]]}; + auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); + } + } else if constexpr (MeshType::Dimension == 2) { + switch (cell_type[cell_id]) { + case CellType::Triangle: { + TriangleTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]]}; + auto qf = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); + } + break; + } + case CellType::Quadrangle: { + SquareTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], + xr[cell_nodes[3]]}; + auto qf = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); + } + break; + } + default: { + throw UnexpectedError("unexpected cell type"); + } + } + } else if constexpr (MeshType::Dimension == 3) { + switch (cell_type[cell_id]) { + case CellType::Tetrahedron: { + TetrahedronTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]]}; + auto qf = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); + } + break; + } + case CellType::Pyramid: { + PyramidTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]], + xr[cell_nodes[4]]}; + auto qf = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); + } + break; + } + case CellType::Prism: { + PrismTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], + xr[cell_nodes[3]], xr[cell_nodes[4]], xr[cell_nodes[5]]}; + auto qf = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); + } + break; + } + case CellType::Hexahedron: { + CubeTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]], + xr[cell_nodes[4]], xr[cell_nodes[5]], xr[cell_nodes[6]], xr[cell_nodes[7]]}; + auto qf = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); + } + break; + } + default: { + throw UnexpectedError("unexpected cell type"); + } + } + } + } + return max_error; +} + +template <MeshConcept MeshType, typename DataType, size_t NbComponents> +double +max_reconstruction_error( + const MeshType& mesh, + DiscreteFunctionDPkVector<MeshType::Dimension, const DataType> dpk_v, + const std::array<std::function<DataType(const TinyVector<MeshType::Dimension>&)>, NbComponents>& vector_exact) +{ + auto xr = mesh.xr(); + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + double max_error = 0; + auto cell_type = mesh.connectivity().cellType(); + + REQUIRE(NbComponents == dpk_v.numberOfComponents()); + + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + auto cell_nodes = cell_to_node_matrix[cell_id]; + if constexpr (MeshType::Dimension == 1) { + Assert(cell_type[cell_id] == CellType::Line); + LineTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]]}; + auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + for (size_t i_component = 0; i_component < NbComponents; ++i_component) { + max_error = std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); + } + } + } else if constexpr (MeshType::Dimension == 2) { + switch (cell_type[cell_id]) { + case CellType::Triangle: { + TriangleTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]]}; + auto qf = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + for (size_t i_component = 0; i_component < NbComponents; ++i_component) { + max_error = + std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); + } + } + break; + } + case CellType::Quadrangle: { + SquareTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], + xr[cell_nodes[3]]}; + auto qf = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + for (size_t i_component = 0; i_component < NbComponents; ++i_component) { + max_error = + std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); + } + } + break; + } + default: { + throw UnexpectedError("unexpected cell type"); + } + } + } else if constexpr (MeshType::Dimension == 3) { + switch (cell_type[cell_id]) { + case CellType::Tetrahedron: { + TetrahedronTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]]}; + auto qf = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + for (size_t i_component = 0; i_component < NbComponents; ++i_component) { + max_error = + std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); + } + } + break; + } + case CellType::Pyramid: { + PyramidTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]], + xr[cell_nodes[4]]}; + auto qf = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + for (size_t i_component = 0; i_component < NbComponents; ++i_component) { + max_error = + std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); + } + } + break; + } + case CellType::Prism: { + PrismTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], + xr[cell_nodes[3]], xr[cell_nodes[4]], xr[cell_nodes[5]]}; + auto qf = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + for (size_t i_component = 0; i_component < NbComponents; ++i_component) { + max_error = + std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); + } + } + break; + } + case CellType::Hexahedron: { + CubeTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]], + xr[cell_nodes[4]], xr[cell_nodes[5]], xr[cell_nodes[6]], xr[cell_nodes[7]]}; + auto qf = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + for (size_t i_component = 0; i_component < NbComponents; ++i_component) { + max_error = + std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); + } + } + break; + } + default: { + throw UnexpectedError("unexpected cell type"); + } + } + } + } + return max_error; +} + +} // namespace test_only + +#endif // DISCRETE_FUNCTION_DPK_FOR_TESTS_HPP diff --git a/tests/MeshDataBaseForTests.cpp b/tests/MeshDataBaseForTests.cpp index 1f2b485edcb7f7b3fadf35d845b59f23c1a03274..1615673d0ba28d7328ab5faef7830a774d5c75f4 100644 --- a/tests/MeshDataBaseForTests.cpp +++ b/tests/MeshDataBaseForTests.cpp @@ -1,11 +1,15 @@ #include <MeshDataBaseForTests.hpp> + #include <mesh/CartesianMeshBuilder.hpp> #include <mesh/Connectivity.hpp> #include <mesh/GmshReader.hpp> #include <mesh/MeshVariant.hpp> +#include <utils/GlobalVariableManager.hpp> #include <utils/Messenger.hpp> #include <utils/PugsAssert.hpp> +#include <NbGhostLayersTester.hpp> + #include <filesystem> #include <fstream> @@ -13,17 +17,22 @@ const MeshDataBaseForTests* MeshDataBaseForTests::m_instance = nullptr; MeshDataBaseForTests::MeshDataBaseForTests() { - m_cartesian_1d_mesh = CartesianMeshBuilder{TinyVector<1>{-1}, TinyVector<1>{3}, TinyVector<1, size_t>{23}}.mesh(); + for (size_t nb_ghost_layers = 1; nb_ghost_layers <= m_max_nb_ghost_layers; ++nb_ghost_layers) { + NbGhostLayersTester t{nb_ghost_layers}; - m_cartesian_2d_mesh = - CartesianMeshBuilder{TinyVector<2>{0, -1}, TinyVector<2>{3, 2}, TinyVector<2, size_t>{6, 7}}.mesh(); + m_cartesian_1d_mesh.push_back( + CartesianMeshBuilder{TinyVector<1>{-1}, TinyVector<1>{3}, TinyVector<1, size_t>{23}}.mesh()); - m_cartesian_3d_mesh = - CartesianMeshBuilder{TinyVector<3>{0, 1, 0}, TinyVector<3>{2, -1, 3}, TinyVector<3, size_t>{6, 7, 4}}.mesh(); + m_cartesian_2d_mesh.push_back( + CartesianMeshBuilder{TinyVector<2>{0, -1}, TinyVector<2>{3, 2}, TinyVector<2, size_t>{6, 7}}.mesh()); - m_unordered_1d_mesh = _buildUnordered1dMesh(); - m_hybrid_2d_mesh = _buildHybrid2dMesh(); - m_hybrid_3d_mesh = _buildHybrid3dMesh(); + m_cartesian_3d_mesh.push_back( + CartesianMeshBuilder{TinyVector<3>{0, 1, 0}, TinyVector<3>{2, -1, 3}, TinyVector<3, size_t>{6, 7, 4}}.mesh()); + + m_unordered_1d_mesh.push_back(_buildUnordered1dMesh()); + m_hybrid_2d_mesh.push_back(_buildHybrid2dMesh()); + m_hybrid_3d_mesh.push_back(_buildHybrid3dMesh()); + } } const MeshDataBaseForTests& @@ -50,37 +59,49 @@ MeshDataBaseForTests::destroy() std::shared_ptr<const MeshVariant> MeshDataBaseForTests::cartesian1DMesh() const { - return m_cartesian_1d_mesh; + const size_t nb_ghost_layers = GlobalVariableManager::instance().getNumberOfGhostLayers(); + Assert((nb_ghost_layers >= 1) and (nb_ghost_layers <= m_max_nb_ghost_layers)); + return m_cartesian_1d_mesh[nb_ghost_layers - 1]; } std::shared_ptr<const MeshVariant> MeshDataBaseForTests::cartesian2DMesh() const { - return m_cartesian_2d_mesh; + const size_t nb_ghost_layers = GlobalVariableManager::instance().getNumberOfGhostLayers(); + Assert((nb_ghost_layers >= 1) and (nb_ghost_layers <= m_max_nb_ghost_layers)); + return m_cartesian_2d_mesh[nb_ghost_layers - 1]; } std::shared_ptr<const MeshVariant> MeshDataBaseForTests::cartesian3DMesh() const { - return m_cartesian_3d_mesh; + const size_t nb_ghost_layers = GlobalVariableManager::instance().getNumberOfGhostLayers(); + Assert((nb_ghost_layers >= 1) and (nb_ghost_layers <= m_max_nb_ghost_layers)); + return m_cartesian_3d_mesh[nb_ghost_layers - 1]; } std::shared_ptr<const MeshVariant> MeshDataBaseForTests::unordered1DMesh() const { - return m_unordered_1d_mesh; + const size_t nb_ghost_layers = GlobalVariableManager::instance().getNumberOfGhostLayers(); + Assert((nb_ghost_layers >= 1) and (nb_ghost_layers <= m_max_nb_ghost_layers)); + return m_unordered_1d_mesh[nb_ghost_layers - 1]; } std::shared_ptr<const MeshVariant> MeshDataBaseForTests::hybrid2DMesh() const { - return m_hybrid_2d_mesh; + const size_t nb_ghost_layers = GlobalVariableManager::instance().getNumberOfGhostLayers(); + Assert((nb_ghost_layers >= 1) and (nb_ghost_layers <= m_max_nb_ghost_layers)); + return m_hybrid_2d_mesh[nb_ghost_layers - 1]; } std::shared_ptr<const MeshVariant> MeshDataBaseForTests::hybrid3DMesh() const { - return m_hybrid_3d_mesh; + const size_t nb_ghost_layers = GlobalVariableManager::instance().getNumberOfGhostLayers(); + Assert((nb_ghost_layers >= 1) and (nb_ghost_layers <= m_max_nb_ghost_layers)); + return m_hybrid_3d_mesh[nb_ghost_layers - 1]; } std::shared_ptr<const MeshVariant> diff --git a/tests/MeshDataBaseForTests.hpp b/tests/MeshDataBaseForTests.hpp index 2b092d544271fbd1b1d0488ed73c9c5a401d548b..f0ab29fa9a927c01f16f94925fba7eb750ffd591 100644 --- a/tests/MeshDataBaseForTests.hpp +++ b/tests/MeshDataBaseForTests.hpp @@ -4,6 +4,7 @@ #include <array> #include <memory> #include <string> +#include <vector> class MeshVariant; @@ -35,15 +36,17 @@ class MeshDataBaseForTests private: explicit MeshDataBaseForTests(); + constexpr static size_t m_max_nb_ghost_layers = 3; + static const MeshDataBaseForTests* m_instance; - std::shared_ptr<const MeshVariant> m_cartesian_1d_mesh; - std::shared_ptr<const MeshVariant> m_cartesian_2d_mesh; - std::shared_ptr<const MeshVariant> m_cartesian_3d_mesh; + std::vector<std::shared_ptr<const MeshVariant>> m_cartesian_1d_mesh; + std::vector<std::shared_ptr<const MeshVariant>> m_cartesian_2d_mesh; + std::vector<std::shared_ptr<const MeshVariant>> m_cartesian_3d_mesh; - std::shared_ptr<const MeshVariant> m_unordered_1d_mesh; - std::shared_ptr<const MeshVariant> m_hybrid_2d_mesh; - std::shared_ptr<const MeshVariant> m_hybrid_3d_mesh; + std::vector<std::shared_ptr<const MeshVariant>> m_unordered_1d_mesh; + std::vector<std::shared_ptr<const MeshVariant>> m_hybrid_2d_mesh; + std::vector<std::shared_ptr<const MeshVariant>> m_hybrid_3d_mesh; std::shared_ptr<const MeshVariant> _buildUnordered1dMesh(); std::shared_ptr<const MeshVariant> _buildHybrid2dMesh(); diff --git a/tests/test_PolynomialReconstruction_degree_1.cpp b/tests/test_PolynomialReconstruction_degree_1.cpp index 7c4f07266514eaf8b277af5f26751c2ec17bc8ae..fb2c7b2ec4a56239e37314704b2cc30122182e8c 100644 --- a/tests/test_PolynomialReconstruction_degree_1.cpp +++ b/tests/test_PolynomialReconstruction_degree_1.cpp @@ -2,37 +2,30 @@ #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 <DiscreteFunctionDPkForTests.hpp> #include <MeshDataBaseForTests.hpp> // clazy:excludeall=non-pod-global-static TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") { + constexpr size_t degree = 1; + SECTION("without symmetries") { std::vector<PolynomialReconstructionDescriptor> descriptor_list = - {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, 1}, - PolynomialReconstructionDescriptor{IntegrationMethodType::element, 1}, - PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, 1}}; + {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, degree}, + PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree}}; for (auto descriptor : descriptor_list) { SECTION(name(descriptor.integrationMethodType())) @@ -49,55 +42,16 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") 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(); + auto R_exact = [](const R1& x) { return 2.3 + 1.7 * x[0]; }; - DiscreteFunctionP0<double> fh{p_mesh}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); 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)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -112,41 +66,20 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); auto& mesh = *p_mesh; - auto R3_affine = [](const R1& x) -> R3 { + auto R3_exact = [](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]); }); + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); 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)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -161,106 +94,45 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); auto& mesh = *p_mesh; - auto R3x3_affine = [](const R1& x) -> R3x3 { + auto R3x3_exact = [](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]); }); + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R3x3_exact)); 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)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R3x3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } } } 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; + std::array<std::function<double(const R1&)>, 3> vector_exact = + {[](const R1& x) -> double { return +2.3 + 1.7 * x[0]; }, + [](const R1& x) -> double { return -1.7 + 2.1 * x[0]; }, + [](const R1& x) -> double { return +1.4 - 0.6 * 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(); - - 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]; - } - }); + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const double>>(); - 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)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -275,68 +147,22 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") 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]}; - }; + std::array<std::function<R3(const R1&)>, 2> vector_exact = + {[](const R1& x) -> R3 { + return R3{+2.3 + 1.7 * x[0], -1.7 + 2.1 * x[0], +1.4 - 0.6 * x[0]}; + }, + [](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]); - }); + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); 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)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -352,15 +178,15 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") 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 R_exact = [](const R1& x) { return 2.3 + 1.7 * x[0]; }; - auto R3_affine = [](const R1& x) -> R3 { + auto R3_exact = [](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 { + auto R3x3_exact = [](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], @@ -368,135 +194,43 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") }; }; - 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]); }); + std::array<std::function<double(const R1&)>, 3> vector_exact = + {[](const R1& x) -> double { return +2.3 + 1.7 * x[0]; }, + [](const R1& x) -> double { return -1.7 + 2.1 * x[0]; }, + [](const R1& x) -> double { return +1.4 - 0.6 * x[0]; }}; - 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]; - } - }); + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R3x3_exact)); + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); 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_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_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)); + auto dpk_uh = reconstructions[1]->get<DiscreteFunctionDPk<1, const R3>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_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_Ah = reconstructions[2]->get<DiscreteFunctionDPk<1, const R3x3>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R3x3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } - 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)); + auto dpk_Vh = reconstructions[3]->get<DiscreteFunctionDPkVector<1, const double>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -515,48 +249,16 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") 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}; + auto R_exact = [](const R2& x) { return 2.3 + 1.7 * x[0] - 1.3 * x[1]; }; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); 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)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -571,52 +273,20 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); auto& mesh = *p_mesh; - auto R3_affine = [](const R2& x) -> R3 { + auto R3_exact = [](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]); }); + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); 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)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -631,127 +301,43 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); auto& mesh = *p_mesh; - auto R2x2_affine = [](const R2& x) -> R2x2 { + auto R2x2_exact = [](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]); }); + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); 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)); - } + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R2x2_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } } } 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}; + std::array<std::function<double(const R2&)>, 4> vector_exact = + {[](const R2& x) -> double { return +2.3 + 1.7 * x[0] + 1.2 * x[1]; }, + [](const R2& x) -> double { return -1.7 + 2.1 * x[0] - 2.2 * x[1]; }, + [](const R2& x) -> double { return +1.4 - 0.6 * x[0] - 2.1 * x[1]; }, + [](const R2& x) -> double { return +2.4 - 2.3 * x[0] + 1.3 * x[1]; }}; - 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]; - } - }); + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); 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)); - } + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const double>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -769,62 +355,16 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") 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}; + auto R_exact = [](const R3& x) { return 2.3 + 1.7 * x[0] - 1.3 * x[1] + 2.1 * x[2]; }; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); 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)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } } } @@ -837,63 +377,20 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); auto& mesh = *p_mesh; - auto R3_affine = [](const R3& x) -> R3 { + auto R3_exact = [](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]); }); + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); 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)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } } } @@ -908,158 +405,47 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); auto& mesh = *p_mesh; - auto R2x2_affine = [](const R3& x) -> R2x2 { + auto R2x2_exact = [](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]); }); + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); 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)); - } + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R2x2_exact)); + REQUIRE(parallel::allReduceMax(max_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}; + std::array<std::function<double(const R3&)>, 4> vector_exact = + {[](const R3& x) -> double { return +2.3 + 1.7 * x[0] + 1.2 * x[1] - 1.3 * x[2]; }, + [](const R3& x) -> double { return -1.7 + 2.1 * x[0] - 2.2 * x[1] - 2.4 * x[2]; }, + [](const R3& x) -> double { return +2.4 - 2.3 * x[0] + 1.3 * x[1] + 1.4 * x[2]; }, + [](const R3& x) -> double { return -0.2 + 3.1 * x[0] + 0.8 * x[1] - 1.8 * x[2]; }}; - 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]; - } - }); + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); 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)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } } } @@ -1088,7 +474,7 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") { auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); - PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::element, 1, + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::element, degree, std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ std::make_shared<NamedBoundaryDescriptor>("XMIN")}}; @@ -1125,7 +511,7 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") { auto p_mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); - PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::element, 1, + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::element, degree, std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ std::make_shared<NamedBoundaryDescriptor>("XMIN")}}; @@ -1162,7 +548,7 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") { auto p_mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); - PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::element, 1, + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::element, degree, std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ std::make_shared<NamedBoundaryDescriptor>("XMIN")}}; @@ -1196,61 +582,40 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") } } - 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") + SECTION("1D") + { + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, degree, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}}; + + using R1 = TinyVector<1>; + + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) { - 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(); + auto R1_exact = [](const R1& x) { return R1{1.7 * (x[0] + 1)}; }; - DiscreteFunctionP0<R1> fh{p_mesh}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R1_affine(xj[cell_id]); }); + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R1_exact)); 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)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R1_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } SECTION("R1 vector data") @@ -1261,650 +626,173 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") 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)}; }; + std::array<std::function<R1(const R1&)>, 2> vector_exact // + = {[](const R1& x) -> R1 { return R1{+1.7 * (x[0] + 1)}; }, + [](const R1& x) -> R1 { return R1{-0.3 * (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]); - }); + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); 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)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } } } } + } + } - SECTION("2D") + SECTION("2D") + { + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, degree, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX")}}}; + + using R2 = TinyVector<2>; + + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) { -#warning not done - using R2 = TinyVector<2>; + SECTION("R^2 data") + { + auto p_mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + auto& mesh = *p_mesh; - // 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)); - // } - // } - // } - // } + auto R2_exact = [](const R2& x) -> R2 { return R2{2.3 * (x[0] - 2), -1.3 * (x[1] - 1)}; }; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R2_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R2_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("vector of R2") + { + auto p_mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + auto& mesh = *p_mesh; + + std::array<std::function<R2(const R2&)>, 2> vector_exact // + = {[](const R2& x) -> R2 { + return R2{+1.7 * (x[0] - 2), -0.6 * (x[1] - 1)}; + }, + [](const R2& x) -> R2 { + return R2{-2.3 * (x[0] - 2), +1.1 * (x[1] - 1)}; + }}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const R2>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } } + } + } - SECTION("3D") + SECTION("3D") + { + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, degree, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "ZMAX")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "ZMAX")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "ZMAX")}}}; + + using R3 = TinyVector<3>; + + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) { - 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)); - // } - // } - // } - // } + SECTION("R^3 data") + { + auto p_mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + auto& mesh = *p_mesh; + + auto R3_exact = [](const R3& x) -> R3 { return R3{2.3 * (x[0] - 2), -1.3 * (x[1] - 1), 1.4 * (x[2] - 1)}; }; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("vector of R3") + { + auto p_mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + auto& mesh = *p_mesh; + + std::array<std::function<R3(const R3&)>, 2> vector_exact // + = {[](const R3& x) -> R3 { + return R3{+1.7 * (x[0] - 2), -0.6 * (x[1] - 1), +1.2 * (x[2] - 1)}; + }, + [](const R3& x) -> R3 { + return R3{-2.3 * (x[0] - 2), +1.1 * (x[1] - 1), -0.3 * (x[2] - 1)}; + }}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const R3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } } } } diff --git a/tests/test_PolynomialReconstruction_degree_2.cpp b/tests/test_PolynomialReconstruction_degree_2.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a1c5c058975c17f9a5ec7e40c41f28b9abb18a66 --- /dev/null +++ b/tests/test_PolynomialReconstruction_degree_2.cpp @@ -0,0 +1,1043 @@ +#include <catch2/catch_approx.hpp> +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <utils/PugsAssert.hpp> + +#include <mesh/Mesh.hpp> +#include <mesh/NamedBoundaryDescriptor.hpp> +#include <scheme/DiscreteFunctionDPkVariant.hpp> +#include <scheme/DiscreteFunctionP0.hpp> +#include <scheme/DiscreteFunctionVariant.hpp> +#include <scheme/PolynomialReconstruction.hpp> + +#include <DiscreteFunctionDPkForTests.hpp> +#include <MeshDataBaseForTests.hpp> + +#include <NbGhostLayersTester.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") +{ + constexpr size_t degree = 2; + + constexpr size_t nb_ghost_layers = 2; + NbGhostLayersTester t{nb_ghost_layers}; + + SECTION("without symmetries") + { + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree}}; + + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) + { + SECTION("1D") + { + using R1 = TinyVector<1>; + + auto p0 = [](const R1& x) { return +2.3 + 1.7 * x[0] - 2.3 * x[0] * x[0]; }; + auto p1 = [](const R1& x) { return -1.7 + 2.1 * x[0] + 1.2 * x[0] * x[0]; }; + auto p2 = [](const R1& x) { return +1.4 - 0.6 * x[0] - 2.0 * x[0] * x[0]; }; + auto p3 = [](const R1& x) { return +2.4 - 2.3 * x[0] + 1.1 * x[0] * x[0]; }; + auto p4 = [](const R1& x) { return -0.2 + 3.1 * x[0] - 0.7 * x[0] * x[0]; }; + auto p5 = [](const R1& x) { return -3.2 - 3.6 * x[0] + 0.1 * x[0] * x[0]; }; + auto p6 = [](const R1& x) { return -4.1 + 3.1 * x[0] - 0.2 * x[0] * x[0]; }; + auto p7 = [](const R1& x) { return +0.8 + 2.9 * x[0] + 4.1 * x[0] * x[0]; }; + auto p8 = [](const R1& x) { return -1.6 + 2.3 * x[0] - 1.7 * x[0] * x[0]; }; + auto p9 = [](const R1& x) { return +2.3 + 1.7 * x[0] - 1.4 * x[0] * x[0]; }; + + 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_exact = p0; + + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_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_exact = [&](const R1& x) -> R3 { return R3{p2(x), p4(x), p1(x)}; }; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_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_exact = [&](const R1& x) -> R3x3 { + return R3x3{p1(x), p2(x), p3(x), // + p4(x), p5(x), p6(x), // + p7(x), p8(x), p9(x)}; + }; + + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R3x3_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3x3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R3x3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); + } + } + } + + SECTION("R 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; + + std::array<std::function<double(const R1&)>, 3> vector_exact = {p1, p7, p9}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_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; + + std::array<std::function<R3(const R1&)>, 3> vector_exact // + = {[&](const R1& x) -> R3 { + return R3{p1(x), p2(x), p3(x)}; + }, + [&](const R1& x) -> R3 { + return R3{p5(x), p7(x), p0(x)}; + }, + [&](const R1& x) -> R3 { + return R3{p9(x), p8(x), p4(x)}; + }}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const R3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_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_exact = p0; + + auto R3_exact = [&](const R1& x) -> R3 { return R3{p9(x), p4(x), p7(x)}; }; + + auto R3x3_exact = [&](const R1& x) -> R3x3 { + return R3x3{p2(x), p1(x), p0(x), // + p3(x), p2(x), p4(x), // + p6(x), p5(x), p9(x)}; + }; + + std::array<std::function<double(const R1&)>, 3> vector_exact = {p1, p8, p7}; + + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R3x3_exact)); + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + 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_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); + } + + { + auto dpk_uh = reconstructions[1]->get<DiscreteFunctionDPk<1, const R3>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); + } + + { + auto dpk_Ah = reconstructions[2]->get<DiscreteFunctionDPk<1, const R3x3>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R3x3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); + } + + { + auto dpk_Vh = reconstructions[3]->get<DiscreteFunctionDPkVector<1, const double>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); + } + } + } + } + } + + SECTION("2D") + { + using R2 = TinyVector<2>; + auto p0 = [](const R2& x) { + return +2.3 + 1.7 * x[0] - 1.3 * x[1] + 1.2 * x[0] * x[0] + 1.3 * x[0] * x[1] - 3.2 * x[1] * x[1]; + }; + + auto p1 = [](const R2& x) { + return +2.3 + 1.7 * x[0] - 2.2 * x[1] - 2.1 * x[0] * x[0] - 2.3 * x[0] * x[1] - 3.2 * x[1] * x[1]; + }; + auto p2 = [](const R2& x) { + return +1.4 - 0.6 * x[0] + 1.3 * x[1] + 2.3 * x[0] * x[0] - 1.3 * x[0] * x[1] + 1.2 * x[1] * x[1]; + }; + auto p3 = [](const R2& x) { + return -0.2 + 3.1 * x[0] - 1.1 * x[1] - 2.1 * x[0] * x[0] + 1.3 * x[0] * x[1] - 1.1 * x[1] * x[1]; + }; + + 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_exact = p0; + + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + 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_exact = [&](const R2& x) -> R3 { return R3{p1(x), p2(x), p3(x)}; }; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + 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_exact = [&](const R2& x) -> R2x2 { + return R2x2{p0(x), p1(x), // + p2(x), p3(x)}; + }; + + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R2x2_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + SECTION("vector 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; + + std::array<std::function<double(const R2&)>, 4> vector_exact = {p0, p1, p2, p3}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const double>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + } + + SECTION("3D") + { + using R3 = TinyVector<3>; + + auto p0 = [](const R3& x) { + return 2.3 + 1.7 * x[0] - 1.3 * x[1] + 2.1 * x[2] // + + 1.7 * x[0] * x[0] + 1.4 * x[1] * x[1] + 1.7 * x[2] * x[2] // + - 2.3 * x[0] * x[1] + 1.6 * x[0] * x[2] - 1.9 * x[1] * x[2]; + }; + + auto p1 = [](const R3& x) { + return +2.3 + 1.7 * x[0] - 2.2 * x[1] + 1.8 * x[2] // + + 1.7 * x[0] * x[0] - 2.4 * x[1] * x[1] - 2.3 * x[2] * x[2] // + - 2.1 * x[0] * x[1] + 2.6 * x[0] * x[2] + 1.6 * x[1] * x[2]; + }; + + auto p2 = [](const R3& x) { + return +1.4 - 0.6 * x[0] + 1.3 * x[1] - 3.7 * x[2] // + + 3.1 * x[0] * x[0] - 1.1 * x[1] * x[1] + 1.7 * x[2] * x[2] // + - 2.3 * x[0] * x[1] - 2.6 * x[0] * x[2] - 1.9 * x[1] * x[2]; + }; + + auto p3 = [](const R3& x) { + return -0.2 + 3.1 * x[0] - 1.1 * x[1] + 1.9 * x[2] // + - 1.5 * x[0] * x[0] + 1.4 * x[1] * x[1] - 1.2 * x[2] * x[2] // + - 1.7 * x[0] * x[1] - 1.3 * x[0] * x[2] + 2.1 * x[1] * x[2]; + }; + + 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_exact = p0; + + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_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_exact = [&](const R3& x) -> R3 { return R3{p1(x), p2(x), p3(x)}; }; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_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_exact = [&](const R3& x) -> R2x2 { + return R2x2{p0(x), p1(x), // + p2(x), p3(x)}; + }; + + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); + + descriptor.setRowWeighting(false); + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R2x2_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + SECTION("vector 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; + + std::array<std::function<double(const R3&)>, 4> vector_exact = {p0, p1, p2, p3}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + descriptor.setPreconditioning(false); + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + } + } + } + } + + SECTION("with symmetries") + { + SECTION("1D") + { + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}}; + using R1 = TinyVector<1>; + + auto p0 = [](const R1& x) { return +1.7 * (x[0] + 1) * (x[0] + 1) - 1.1; }; + auto p1 = [](const R1& x) { return -1.2 * (x[0] + 1) * (x[0] + 1) + 1.3; }; + auto p2 = [](const R1& x) { return +1.4 * (x[0] + 1) * (x[0] + 1) - 0.6; }; + + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) + { + SECTION("R data") + { + auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + auto& mesh = *p_mesh; + + auto R_exact = p0; + + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("R1x1 data") + { + using R1x1 = TinyMatrix<1>; + + auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + auto& mesh = *p_mesh; + + auto R1x1_exact = [&](const R1& x) { return R1x1{p0(x)}; }; + + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R1x1_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R1x1>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R1x1_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("R vector data") + { + auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + auto& mesh = *p_mesh; + + std::array<std::function<double(const R1&)>, 3> vector_exact // + = {p0, p1, p2}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("R1x1 vector data") + { + using R1x1 = TinyMatrix<1>; + + auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + auto& mesh = *p_mesh; + + std::array<std::function<R1x1(const R1&)>, 3> vector_exact // + = {[&](const R1& x) { return R1x1{p2(x)}; }, // + [&](const R1& x) { return R1x1{p0(x)}; }, // + [&](const R1& x) { return R1x1{p1(x)}; }}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const R1x1>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + } + + SECTION("2D") + { + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX")}}}; + + using R2 = TinyVector<2>; + + auto p_initial_mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + auto& initial_mesh = *p_initial_mesh; + + constexpr double theta = 1; + TinyMatrix<2> T{std::cos(theta), -std::sin(theta), // + std::sin(theta), std::cos(theta)}; + + auto xr = initial_mesh.xr(); + + NodeValue<R2> new_xr{initial_mesh.connectivity()}; + parallel_for( + initial_mesh.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { new_xr[node_id] = T * xr[node_id]; }); + + std::shared_ptr p_mesh = std::make_shared<const Mesh<2>>(initial_mesh.shared_connectivity(), new_xr); + const Mesh<2>& mesh = *p_mesh; + // inverse rotation + TinyMatrix<2> inv_T{std::cos(theta), std::sin(theta), // + -std::sin(theta), std::cos(theta)}; + + auto p0 = [&inv_T](const R2& X) { + const R2 Y = inv_T * X; + const double x = Y[0] - 2; + const double y = Y[1] - 1; + return +1.7 * x * x + 2 * y * y - 1.1; + }; + + auto p1 = [&inv_T](const R2& X) { + const R2 Y = inv_T * X; + const double x = Y[0] - 2; + const double y = Y[1] - 1; + return -1.3 * x * x - 0.2 * y * y + 0.7; + }; + + auto p2 = [&inv_T](const R2& X) { + const R2 Y = inv_T * X; + const double x = Y[0] - 2; + const double y = Y[1] - 1; + return +2.6 * x * x - 1.4 * y * y - 1.9; + }; + + auto p3 = [&inv_T](const R2& X) { + const R2 Y = inv_T * X; + const double x = Y[0] - 2; + const double y = Y[1] - 1; + return -0.6 * x * x + 1.4 * y * y + 2.3; + }; + + auto q0 = [&inv_T](const R2& X) { + const R2 Y = inv_T * X; + const double x = Y[0] - 2; + const double y = Y[1] - 1; + return 3 * x * y; + }; + + auto q1 = [&inv_T](const R2& X) { + const R2 Y = inv_T * X; + const double x = Y[0] - 2; + const double y = Y[1] - 1; + return -1.3 * x * y; + }; + + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) + { + SECTION("R data") + { + auto R_exact = p0; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("R2x2 data") + { + using R2x2 = TinyMatrix<2>; + auto R2x2_exact = [&](const R2& X) -> R2x2 { + return T * TinyMatrix<2>{p0(X), q0(X), q1(X), p1(X)} * inv_T; + }; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R2x2_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("vector of R") + { + std::array<std::function<double(const R2&)>, 4> vector_exact // + = {p0, p1, p2, p3}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("vector of R2x2") + { + using R2x2 = TinyMatrix<2>; + + std::array<std::function<R2x2(const R2&)>, 2> vector_R2x2_exact // + = {[&](const R2& X) { + return T * R2x2{p0(X), q0(X), q1(X), p1(X)} * inv_T; + }, + [&](const R2& X) { + return T * R2x2{p0(X), q1(X), 0, p1(X)} * inv_T; + }}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_R2x2_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const R2x2>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_R2x2_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("list") + { + using R2x2 = TinyMatrix<2>; + + auto R_exact = p0; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + + std::array<std::function<double(const R2&)>, 4> vector_exact // + = {p0, p1, p2, p3}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + std::array<std::function<R2x2(const R2&)>, 2> vector_R2x2_exact // + = {[&](const R2& X) { + return T * R2x2{p0(X), q0(X), q1(X), p1(X)} * inv_T; + }, + [&](const R2& X) { + return T * R2x2{p2(X), q1(X), 0, p3(X)} * inv_T; + }}; + + DiscreteFunctionP0Vector Wh = test_only::exact_projection(mesh, degree, vector_R2x2_exact); + + auto R2x2_exact = [&](const R2& X) -> R2x2 { + return T * TinyMatrix<2>{p0(X), q1(X), q0(X), p3(X)} * inv_T; + }; + + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh, Vh, Wh, Ah); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>(); + auto dpk_Vh = reconstructions[1]->get<DiscreteFunctionDPkVector<2, const double>>(); + auto dpk_Wh = reconstructions[2]->get<DiscreteFunctionDPkVector<2, const R2x2>>(); + auto dpk_Ah = reconstructions[3]->get<DiscreteFunctionDPk<2, const R2x2>>(); + + { + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + { + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + { + double max_error = test_only::max_reconstruction_error(mesh, dpk_Wh, vector_R2x2_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + { + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R2x2_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + } + } + + SECTION("3D") + { + using R3 = TinyVector<3>; + + auto p_initial_mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + auto& initial_mesh = *p_initial_mesh; + + constexpr double theta = 1; + TinyMatrix<3> T{std::cos(theta), -std::sin(theta), 0, + // + std::sin(theta), std::cos(theta), 0, + // + 0, 0, 1}; + + auto xr = initial_mesh.xr(); + + NodeValue<R3> new_xr{initial_mesh.connectivity()}; + parallel_for( + initial_mesh.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { new_xr[node_id] = T * xr[node_id]; }); + + std::shared_ptr p_mesh = std::make_shared<const Mesh<3>>(initial_mesh.shared_connectivity(), new_xr); + const Mesh<3>& mesh = *p_mesh; + // inverse rotation + TinyMatrix<3> inv_T{std::cos(theta), std::sin(theta), 0, + // + -std::sin(theta), std::cos(theta), 0, + // + 0, 0, 1}; + + auto p0 = [&inv_T](const R3& X) { + const R3 Y = inv_T * X; + const double x = Y[0] - 2; + const double y = Y[1] - 1; + const double z = Y[2] - 1; + return +1.7 * x * x + 2 * y * y + 1.3 * z * z - 1.1; + }; + + auto p1 = [&inv_T](const R3& X) { + const R3 Y = inv_T * X; + const double x = Y[0] - 2; + const double y = Y[1] - 1; + const double z = Y[2] - 1; + return +2.1 * x * x - 1.4 * y * y - 3.1 * z * z + 2.2; + }; + + auto p2 = [&inv_T](const R3& X) { + const R3 Y = inv_T * X; + const double x = Y[0] - 2; + const double y = Y[1] - 1; + const double z = Y[2] - 1; + return -1.1 * x * x - 1.2 * y * y + 1.3 * z * z - 1.7; + }; + + auto p3 = [&inv_T](const R3& X) { + const R3 Y = inv_T * X; + const double x = Y[0] - 2; + const double y = Y[1] - 1; + const double z = Y[2] - 1; + return 1.9 * x * x + 2.1 * y * y - 3.1 * z * z + 1.6; + }; + + auto p4 = [&inv_T](const R3& X) { + const R3 Y = inv_T * X; + const double x = Y[0] - 2; + const double y = Y[1] - 1; + const double z = Y[2] - 1; + return -2.4 * x * x + 3.3 * y * y - 1.7 * z * z + 2.1; + }; + + SECTION("3 symmetries") + { + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "ZMAX")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "ZMAX")}}}; + + for (auto descriptor : descriptor_list) { + SECTION("R data") + { + auto R_exact = p0; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("vector of R") + { + std::array<std::function<double(const R3&)>, 4> vector_exact // + = {p0, p1, p2, p3}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + SECTION("1 symmetry") + { + // Matrix and their transformations are kept simple to + // derive exact solutions + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMAX")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMAX")}}}; + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) + { + SECTION("R3x3 data") + { + // Matrix and their transformations are kept simple to + // derive exact solutions + + using R3x3 = TinyMatrix<3>; + auto R2x2_exact = [&](const R3& X) -> R3x3 { + return T * TinyMatrix<3>{p0(X), 0, 0, // + 0, p1(X), p2(X), // + 0, p3(X), p4(X)} * + inv_T; + }; + + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3x3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R2x2_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("vector of R3x3") + { + using R3x3 = TinyMatrix<3>; + + std::array<std::function<R3x3(const R3&)>, 2> vector_R3x3_exact // + = {[&](const R3& X) { + return T * R3x3{p0(X), 0, 0, // + 0, p1(X), p2(X), // + 0, p3(X), p4(X)} * + inv_T; + }, + [&](const R3& X) { + return T * R3x3{p0(X), 0, 0, // + 0, p2(X), p4(X), // + 0, p3(X), p1(X)} * + inv_T; + }}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_R3x3_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const R3x3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_R3x3_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("list") + { + using R3x3 = TinyMatrix<3>; + + auto R_exact = p0; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + + std::array<std::function<double(const R3&)>, 4> vector_exact // + = {p0, p1, p2, p3}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + std::array<std::function<R3x3(const R3&)>, 2> vector_R3x3_exact // + = {[&](const R3& X) { + return T * R3x3{p1(X), 0, 0, // + 0, p3(X), p0(X), // + 0, p2(X), p4(X)} * + inv_T; + }, + [&](const R3& X) { + return T * R3x3{p2(X), 0, 0, // + 0, p0(X), p1(X), // + 0, p3(X), p4(X)} * + inv_T; + }}; + + DiscreteFunctionP0Vector Wh = test_only::exact_projection(mesh, degree, vector_R3x3_exact); + + auto R3x3_exact = [&](const R3& X) -> R3x3 { + return T * R3x3{p0(X), 0, 0, // + 0, p1(X), p2(X), // + 0, p3(X), p4(X)} * + inv_T; + }; + + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R3x3_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh, Vh, Wh, Ah); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); + auto dpk_Vh = reconstructions[1]->get<DiscreteFunctionDPkVector<3, const double>>(); + auto dpk_Wh = reconstructions[2]->get<DiscreteFunctionDPkVector<3, const R3x3>>(); + auto dpk_Ah = reconstructions[3]->get<DiscreteFunctionDPk<3, const R3x3>>(); + + { + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + { + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + { + double max_error = test_only::max_reconstruction_error(mesh, dpk_Wh, vector_R3x3_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + { + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R3x3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + } + } + } + } +} diff --git a/tests/test_PolynomialReconstruction_degree_3.cpp b/tests/test_PolynomialReconstruction_degree_3.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e20fa89a3bdcba29f61a771deacba273c536f28b --- /dev/null +++ b/tests/test_PolynomialReconstruction_degree_3.cpp @@ -0,0 +1,766 @@ +#include <catch2/catch_approx.hpp> +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <utils/PugsAssert.hpp> + +#include <mesh/Mesh.hpp> +#include <mesh/NamedBoundaryDescriptor.hpp> +#include <scheme/DiscreteFunctionDPkVariant.hpp> +#include <scheme/DiscreteFunctionP0.hpp> +#include <scheme/DiscreteFunctionVariant.hpp> +#include <scheme/PolynomialReconstruction.hpp> + +#include <mesh/CartesianMeshBuilder.hpp> + +#include <DiscreteFunctionDPkForTests.hpp> +#include <MeshDataBaseForTests.hpp> + +#include <NbGhostLayersTester.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("PolynomialReconstruction_degree_3", "[scheme]") +{ + constexpr size_t degree = 3; + + constexpr size_t nb_ghost_layers = 3; + NbGhostLayersTester t{nb_ghost_layers}; + + SECTION("without symmetries") + { + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree}}; + + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) + { + SECTION("1D") + { + using R1 = TinyVector<1>; + + auto p0 = [](const R1& X) { + const double x = X[0]; + return +2.3 + (1.4 + (1.7 - 2.3 * x) * x) * x; + }; + auto p1 = [](const R1& X) { + const double x = X[0]; + return -1.2 - (2.3 - (1.1 + 2.1 * x) * x) * x; + }; + auto p2 = [](const R1& X) { + const double x = X[0]; + return +2.1 + (2.1 + (3.1 - 1.7 * x) * x) * x; + }; + auto p3 = [](const R1& X) { + const double x = X[0]; + return -1.7 + (1.4 + (1.6 - 3.1 * x) * x) * x; + }; + auto p4 = [](const R1& X) { + const double x = X[0]; + return +1.1 - (1.3 - (2.1 + 1.5 * x) * x) * x; + }; + auto p5 = [](const R1& X) { + const double x = X[0]; + return +1.9 - (2.1 + (1.6 - 2.7 * x) * x) * x; + }; + auto p6 = [](const R1& X) { + const double x = X[0]; + return -0.7 + (1.4 + (2.1 + 1.1 * x) * x) * x; + }; + auto p7 = [](const R1& X) { + const double x = X[0]; + return -1.4 - (1.2 + (1.5 - 2.1 * x) * x) * x; + }; + auto p8 = [](const R1& X) { + const double x = X[0]; + return -2.1 + (1.1 - (1.7 + 1.2 * x) * x) * x; + }; + auto p9 = [](const R1& X) { + const double x = X[0]; + return +1.8 - (3.1 + (2.1 - 2.4 * x) * x) * x; + }; + + 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_exact = p0; + + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + 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_exact = [&](const R1& x) -> R3 { return R3{p2(x), p4(x), p1(x)}; }; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + 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_exact = [&](const R1& x) -> R3x3 { + return R3x3{p1(x), p2(x), p3(x), // + p4(x), p5(x), p6(x), // + p7(x), p8(x), p9(x)}; + }; + + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R3x3_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3x3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R3x3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + SECTION("R 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; + + std::array<std::function<double(const R1&)>, 3> vector_exact = {p1, p7, p9}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + 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; + + std::array<std::function<R3(const R1&)>, 3> vector_exact // + = {[&](const R1& x) -> R3 { return R3{p1(x), p2(x), p3(x)}; }, + [&](const R1& x) -> R3 { return R3{p5(x), p7(x), p0(x)}; }, + [&](const R1& x) -> R3 { return R3{p9(x), p8(x), p4(x)}; }}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const R3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + 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_exact = p0; + + auto R3_exact = [&](const R1& x) -> R3 { return R3{p9(x), p4(x), p7(x)}; }; + + auto R3x3_exact = [&](const R1& x) -> R3x3 { + return R3x3{p2(x), p1(x), p0(x), // + p3(x), p2(x), p4(x), // + p6(x), p5(x), p9(x)}; + }; + + std::array<std::function<double(const R1&)>, 3> vector_exact = {p1, p8, p7}; + + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R3x3_exact)); + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + 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_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + { + auto dpk_uh = reconstructions[1]->get<DiscreteFunctionDPk<1, const R3>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + { + auto dpk_Ah = reconstructions[2]->get<DiscreteFunctionDPk<1, const R3x3>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R3x3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + { + auto dpk_Vh = reconstructions[3]->get<DiscreteFunctionDPkVector<1, const double>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + } + } + + SECTION("2D") + { + using R2 = TinyVector<2>; + auto p0 = [](const R2& X) { + const double x = X[0]; + const double y = X[1]; + const double x2 = x * x; + const double xy = x * y; + const double y2 = y * y; + const double x3 = x2 * x; + const double x2y = x2 * y; + const double xy2 = x * y2; + const double y3 = y * y2; + + return +2.3 // + + 1.7 * x - 1.3 * y // + + 1.2 * x2 + 1.3 * xy - 3.2 * y2 // + - 1.3 * x3 + 2.1 * x2y - 1.6 * xy2 + 2.1 * y3; + }; + + auto p1 = [](const R2& X) { + const double x = X[0]; + const double y = X[1]; + const double x2 = x * x; + const double xy = x * y; + const double y2 = y * y; + const double x3 = x2 * x; + const double x2y = x2 * y; + const double xy2 = x * y2; + const double y3 = y * y2; + + return +1.4 // + + 1.6 * x - 2.1 * y // + + 1.3 * x2 + 2.6 * xy - 1.4 * y2 // + - 1.2 * x3 - 1.7 * x2y + 2.1 * xy2 - 2.2 * y3; + }; + + auto p2 = [](const R2& X) { + const double x = X[0]; + const double y = X[1]; + const double x2 = x * x; + const double xy = x * y; + const double y2 = y * y; + const double x3 = x2 * x; + const double x2y = x2 * y; + const double xy2 = x * y2; + const double y3 = y * y2; + + return -1.2 // + + 2.3 * x - 1.6 * y // + - 1.2 * x2 + 2.4 * xy - 1.9 * y2 // + + 0.9 * x3 + 1.5 * x2y - 2.3 * xy2 - 1.6 * y3; + }; + + auto p3 = [](const R2& X) { + const double x = X[0]; + const double y = X[1]; + const double x2 = x * x; + const double xy = x * y; + const double y2 = y * y; + const double x3 = x2 * x; + const double x2y = x2 * y; + const double xy2 = x * y2; + const double y3 = y * y2; + + return +2.4 // + + 2.5 * x + 1.4 * y // + - 2.7 * x2 + 1.9 * xy - 2.2 * y2 // + - 1.3 * x3 + 2.3 * x2y - 1.4 * xy2 + 2.2 * y3; + }; + + 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_exact = p0; + + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree + 1, std::function(R_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + 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_exact = [&](const R2& x) -> R3 { return R3{p1(x), p2(x), p3(x)}; }; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree + 1, std::function(R3_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + 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_exact = [&](const R2& x) -> R2x2 { + return R2x2{p0(x), p1(x), // + p2(x), p3(x)}; + }; + + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree + 1, std::function(R2x2_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R2x2_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + SECTION("vector 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; + + std::array<std::function<double(const R2&)>, 4> vector_exact = {p0, p1, p2, p3}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree + 1, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const double>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + } + + SECTION("3D") + { + using R3 = TinyVector<3>; + + auto p = [](const R3& X, const std::array<double, 20>& a) -> double { + const double x = X[0]; + const double y = X[1]; + const double z = X[2]; + const double xy = x * y; + const double xz = x * z; + const double yz = y * z; + const double x2 = x * x; + const double y2 = y * y; + const double z2 = z * z; + const double xyz = x * y * z; + const double x2y = x2 * y; + const double x2z = x2 * z; + const double xy2 = x * y2; + const double y2z = y2 * z; + const double xz2 = x * z2; + const double yz2 = y * z2; + const double x3 = x2 * x; + const double y3 = y2 * y; + const double z3 = z2 * z; + + return a[0] + a[1] * x + a[2] * y + a[3] * z // + + a[4] * x2 + a[5] * y2 + a[6] * z2 // + + a[7] * xy + a[8] * xz + a[9] * yz // + + a[10] * x3 + a[11] * y3 + a[12] * z3 // + + a[13] * x2y + a[14] * x2z + a[15] * xy2 // + + a[16] * y2z + a[17] * xz2 + a[18] * yz2 // + + a[19] * xyz // + ; + }; + + constexpr std::array<double, 20> a0 = {+2.3, +1.7, -1.3, +2.1, +1.7, +1.4, +1.7, -2.3, +1.6, -1.9, + +1.2, -2.1, -1.1, -1.7, -1.3, +0.9, -0.7, +1.5, -0.7, +2.8}; + + auto p0 = [&p, &a0](const R3& X) -> double { return p(X, a0); }; + + constexpr std::array<double, 20> a1 = {-1.3, +2.2, +0.1, -2.5, +0.2, -2.3, -1.4, +0.9, +0.2, -0.3, + +2.4, -1.2, +1.7, -2.2, +0.6, +1.9, +1.0, -0.8, +2.4, +2.4}; + + auto p1 = [&p, &a1](const R3& X) -> double { return p(X, a1); }; + + constexpr std::array<double, 20> a2 = {+1.9, -1.2, -0.4, -1.2, -0.8, +1.4, +0.5, -1.6, +1.1, -0.7, + +0.6, +2.3, -1.8, -1.9, -0.3, -2.4, -1.7, +0.2, -2.4, +1.9}; + + auto p2 = [&p, &a2](const R3& X) -> double { return p(X, a2); }; + + constexpr std::array<double, 20> a3 = {+0.8, +0.5, +1.3, -2.3, +0.9, -0.4, -2.0, +1.8, +0.5, +0.7, + +1.0, -0.4, +1.1, +1.8, -0.4, +1.1, -0.0, +1.4, +1.9, -2.2}; + + auto p3 = [&p, &a3](const R3& X) -> double { return p(X, a3); }; + + auto p_mesh = CartesianMeshBuilder{TinyVector<3>{-0.5, -0.5, -0.5}, TinyVector<3>{3.5, 3.5, 3.5}, + TinyVector<3, size_t>{4, 4, 4}} + .mesh() + ->get<Mesh<3>>(); + const auto& mesh = *p_mesh; + + SECTION("R data") + { + auto R_exact = p0; + + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree + 3, std::function(R_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("R^3 data") + { + auto R3_exact = [&](const R3& x) -> R3 { return R3{p1(x), p2(x), p3(x)}; }; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("R^2x2 data") + { + using R2x2 = TinyMatrix<2, 2>; + + auto R2x2_exact = [&](const R3& x) -> R2x2 { + return R2x2{p0(x), p1(x), // + p2(x), p3(x)}; + }; + + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); + + descriptor.setRowWeighting(false); + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R2x2_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("vector data") + { + std::array<std::function<double(const R3&)>, 4> vector_exact = {p0, p1, p2, p3}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + descriptor.setPreconditioning(false); + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + } + } + + SECTION("with symmetries") + { + SECTION("1D") + { + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}}; + using R1 = TinyVector<1>; + + auto p0 = [](const R1& x) -> R1 { return R1{+1.7 * (x[0] + 1) * (x[0] + 1) * (x[0] + 1)}; }; + auto p1 = [](const R1& x) -> R1 { return R1{-1.2 * (x[0] + 1) * (x[0] + 1) * (x[0] + 1)}; }; + auto p2 = [](const R1& x) -> R1 { return R1{+1.4 * (x[0] + 1) * (x[0] + 1) * (x[0] + 1)}; }; + + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) + { + SECTION("R1 data") + { + auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + auto& mesh = *p_mesh; + + auto R1_exact = p0; + + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R1_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R1>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R1_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("R1 vector data") + { + auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + auto& mesh = *p_mesh; + + std::array<std::function<R1(const R1&)>, 3> vector_exact // + = {p0, p1, p2}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const R1>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + } + + SECTION("2D") + { + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX")}}}; + + using R2 = TinyVector<2>; + + auto p_mesh = CartesianMeshBuilder{TinyVector<2>{0, 0}, TinyVector<2>{2, 1}, TinyVector<2, size_t>{3, 3}} + .mesh() + ->get<Mesh<2>>(); + + auto& mesh = *p_mesh; + + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) + { + SECTION("R^2 data") + { + auto R2_exact = [](const R2& x) -> R2 { + return R2{+2.3 * (x[0] - 2) * (x[0] - 2) * (x[0] - 2), // + -1.3 * (x[1] - 1) * (x[1] - 1) * (x[1] - 1)}; + }; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R2_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R2_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("vector of R2") + { + std::array<std::function<R2(const R2&)>, 2> vector_exact // + = {[](const R2& x) -> R2 { + return R2{+1.7 * (x[0] - 2) * (x[0] - 2) * (x[0] - 2), // + -0.6 * (x[1] - 1) * (x[1] - 1) * (x[1] - 1)}; + }, + [](const R2& x) -> R2 { + return R2{-2.3 * (x[0] - 2) * (x[0] - 2) * (x[0] - 2), // + +1.1 * (x[1] - 1) * (x[1] - 1) * (x[1] - 1)}; + }}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const R2>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + } + + SECTION("3D") + { + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "ZMAX")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "ZMAX")}}}; + + using R3 = TinyVector<3>; + + auto p_mesh = CartesianMeshBuilder{TinyVector<3>{0, 0, 0}, TinyVector<3>{2, 1, 1}, TinyVector<3, size_t>{3, 3, 3}} + .mesh() + ->get<Mesh<3>>(); + + auto& mesh = *p_mesh; + + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) + { + SECTION("R^3 data") + { + auto R3_exact = [](const R3& x) -> R3 { + return R3{+2.3 * (x[0] - 2) * (x[0] - 2) * (x[0] - 2), // + -1.3 * (x[1] - 1) * (x[1] - 1) * (x[1] - 1), // + +1.4 * (x[2] - 1) * (x[2] - 1) * (x[2] - 1)}; + }; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("vector of R3") + { + std::array<std::function<R3(const R3&)>, 2> vector_exact // + = {[](const R3& x) -> R3 { + return R3{+1.7 * (x[0] - 2) * (x[0] - 2) * (x[0] - 2), // + -0.6 * (x[1] - 1) * (x[1] - 1) * (x[1] - 1), // + +1.2 * (x[2] - 1) * (x[2] - 1) * (x[2] - 1)}; + }, + [](const R3& x) -> R3 { + return R3{-2.3 * (x[0] - 2) * (x[0] - 2) * (x[0] - 2), // + +1.1 * (x[1] - 1) * (x[1] - 1) * (x[1] - 1), // + -0.3 * (x[2] - 1) * (x[2] - 1) * (x[2] - 1)}; + }}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const R3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + } + } +}