diff --git a/CMakeLists.txt b/CMakeLists.txt index 3159d9471f1d59fd8e573c4f85576156c243c3df..46126f9121cfafeb028c9d6f32a2ddf826c9fcf1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -741,6 +741,7 @@ target_link_libraries( PugsMesh PugsAlgebra PugsScheme + PugsSchemeReconstructionUtils PugsUtils PugsOutput PugsLanguageUtils @@ -778,6 +779,7 @@ target_link_libraries( PugsLanguageModules PugsLanguageUtils PugsScheme + PugsSchemeReconstructionUtils PugsDev PugsAnalysis PugsAlgebra @@ -820,6 +822,7 @@ install(TARGETS PugsMesh PugsOutput PugsScheme + PugsSchemeReconstructionUtils kokkos Catch2 diff --git a/src/geometry/SquareTransformation.hpp b/src/geometry/SquareTransformation.hpp index d6474f33e4ecbaa4bcfba525dadc9fd38e95962d..b0e970c6eda673b48c5cf5c0d0a1ad7e73f8ef74 100644 --- a/src/geometry/SquareTransformation.hpp +++ b/src/geometry/SquareTransformation.hpp @@ -84,6 +84,25 @@ class SquareTransformation return m_coefficients * X + m_shift; } + PUGS_INLINE + TinyVector<Dimension> + areaVariation(const TinyVector<2>& X) const + { + const auto& T = m_coefficients; + const double& x = X[0]; + const double& y = X[1]; + + const TinyVector<Dimension> dxT{T(0, 0) + T(0, 2) * y, // + T(1, 0) + T(1, 2) * y, // + T(2, 0) + T(2, 2) * y}; + + const TinyVector<Dimension> dyT{T(0, 1) + T(0, 2) * x, // + T(1, 1) + T(1, 2) * x, // + T(2, 1) + T(2, 2) * x}; + + return crossProduct(dxT, dyT); + } + PUGS_INLINE double areaVariationNorm(const TinyVector<2>& X) const { 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/TriangleTransformation.hpp b/src/geometry/TriangleTransformation.hpp index df8444d894a7ed56c0fe3825ffbf1cc14a742531..c49db368684d8c96aeb5061041828799e8509fac 100644 --- a/src/geometry/TriangleTransformation.hpp +++ b/src/geometry/TriangleTransformation.hpp @@ -66,6 +66,7 @@ class TriangleTransformation private: TinyMatrix<Dimension, 2> m_jacobian; TinyVector<Dimension> m_shift; + TinyVector<Dimension> m_area_variation; double m_area_variation_norm; public: @@ -76,6 +77,21 @@ class TriangleTransformation return m_jacobian * x + m_shift; } + PUGS_INLINE + TinyVector<Dimension> + areaVariation() const + { + return m_area_variation; + } + + PUGS_INLINE + TinyVector<Dimension> + areaVariation(const TinyVector<2>&) const + { + return m_area_variation; + } + + PUGS_INLINE double areaVariationNorm() const { @@ -92,7 +108,8 @@ class TriangleTransformation m_shift = a; - m_area_variation_norm = l2Norm(crossProduct(b - a, c - a)); + m_area_variation = crossProduct(b - a, c - a); + m_area_variation_norm = l2Norm(m_area_variation); } ~TriangleTransformation() = default; diff --git a/src/scheme/CMakeLists.txt b/src/scheme/CMakeLists.txt index 8385e55ccbc7ebcfedfd6880d7def1df5a381847..c03b412af73bb25d0b71eeb060e33484c02eb824 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 @@ -30,3 +32,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 cf4784a285a85435ddd650d44ceac9d80ff06032..f3d3768d9dd4a880386c66c20ca69fd5847be8e2 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -2,19 +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/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> @@ -24,702 +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); + 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; } - }(); - - 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 l = 0; l < B.numberOfColumns(); ++l) { + B(index, l) *= weight; } - - 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]; + } + 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 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]; + 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); - { - 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)); + g += Ail * Ail; } + G[l] = std::sqrt(g); + } - 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 l = 0; l < A.numberOfColumns(); ++l) { + const double Gl = G[l]; + for (size_t i = 0; i < A.numberOfRows(); ++i) { + A(i, l) *= Gl; } } - for (size_t k = 1; k < dimension; ++k) { - mean_of_ejk[k - 1] += inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k]; - } - } -} + Givens::solveCollectionInPlace(A, X, B); -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) -{ - 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); - } - } -} - -void -_computeEjkMeanByBoundaryInSymmetricCell(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) -{ - 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); + 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( @@ -836,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>; @@ -863,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]; @@ -926,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( @@ -951,544 +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(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[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]; - } - } - } - - 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); } @@ -1509,6 +813,30 @@ 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: { + return this->_build<BoundaryIntegralReconstructionMatrixBuilder<MeshType>>(p_mesh, discrete_function_variant_list); + } + 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..58b8fcd5ea34a64bd8d5808c41ea95079c2fcf84 --- /dev/null +++ b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.cpp @@ -0,0 +1,553 @@ +#include <scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp> + +#include <analysis/GaussLegendreQuadratureDescriptor.hpp> +#include <analysis/GaussQuadratureDescriptor.hpp> +#include <analysis/QuadratureFormula.hpp> +#include <analysis/QuadratureManager.hpp> +#include <geometry/LineTransformation.hpp> +#include <geometry/SquareTransformation.hpp> +#include <geometry/SymmetryUtils.hpp> +#include <geometry/TriangleTransformation.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshDataManager.hpp> +#include <scheme/DiscreteFunctionDPk.hpp> + +template <MeshConcept MeshTypeT> +class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<MeshTypeT>::SpecificDimensionalData +{ + private: + 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; + + public: + PUGS_INLINE const auto& + cellToFaceMatrix() const noexcept + { + return m_cell_to_face_matrix; + } + + PUGS_INLINE + const auto& + faceToNodeMatrix() const noexcept + { + return m_face_to_node_matrix; + } + + PUGS_INLINE + const auto& + cellFaceIsReversed() const noexcept + { + return m_cell_face_is_reversed; + } + + SpecificDimensionalData(const Connectivity<MeshType::Dimension>& connectivity) + : m_cell_to_face_matrix{connectivity.cellToFaceMatrix()}, + m_face_to_node_matrix{connectivity.faceToNodeMatrix()}, + m_cell_face_is_reversed{connectivity.cellFaceIsReversed()} + {} + + ~SpecificDimensionalData() = default; +}; + +template <> +class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<Mesh<1>>::SpecificDimensionalData +{ + private: + const ItemToItemMatrix<ItemType::cell, ItemType::node> m_cell_to_node_matrix; + + public: + PUGS_INLINE + const auto& + cellToNodeMatrix() const noexcept + { + return m_cell_to_node_matrix; + } + + SpecificDimensionalData(const Connectivity<1>& connectivity) : m_cell_to_node_matrix{connectivity.cellToNodeMatrix()} + {} + + ~SpecificDimensionalData() = default; +}; + +template <> +template <typename ConformTransformationT> +void +PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<Mesh<3>>::_computeEjkBoundaryMean( + const QuadratureFormula<MeshType::Dimension - 1>& quadrature, + const ConformTransformationT& T, + const Rd& Xj, + const double inv_Vi, + SmallArray<double>& mean_of_ejk) +{ + for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { + const double wq = quadrature.weight(i_q); + const TinyVector<2> xi_q = quadrature.point(i_q); + + const double area_variation_e1 = T.areaVariation(xi_q)[0] * inv_Vi; + + 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; + m_tmp_array[k++] = x_xj * wq * area_variation_e1; + for (; k <= m_polynomial_degree; ++k) { + m_tmp_array[k] // + = x_xj * m_tmp_array[k - 1] * m_antiderivative_correction_coef[k]; // ((1. * k) / (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_tmp_array[k] = y_yj * m_tmp_array[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_tmp_array[k] = z_zj * m_tmp_array[begin_i_yz_1 + l]; + } + } + } + } + + for (size_t k = 1; k < m_basis_dimension; ++k) { + mean_of_ejk[k - 1] += m_tmp_array[k]; + } + } +} + +template <> +template <typename ConformTransformationT> +void +PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<Mesh<2>>::_computeEjkBoundaryMean( + const QuadratureFormula<MeshType::Dimension - 1>& quadrature, + const ConformTransformationT& T, + const Rd& Xj, + const double inv_Vi, + SmallArray<double>& mean_of_ejk) +{ + 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_tmp_array[k++] = x_xj * wq * velocity_perp_e1; + for (; k <= m_polynomial_degree; ++k) { + m_tmp_array[k] // + = x_xj * m_tmp_array[k - 1] * m_antiderivative_correction_coef[k]; // ((1. * k) / (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) { + m_tmp_array[k++] = y_yj * m_tmp_array[begin_i_y_1 + l]; + } + } + } + + for (size_t k = 1; k < m_basis_dimension; ++k) { + mean_of_ejk[k - 1] += m_tmp_array[k]; + } + } +} + +template <MeshConcept MeshTypeT> +void +PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<MeshTypeT>::_computeEjkMeanByBoundary( + const Rd& Xj, + const CellId& cell_id, + SmallArray<double>& mean_of_ejk) +{ + const double inv_Vi = 1. / m_Vj[cell_id]; + + const auto& face_is_reversed = m_dimensional_data_ptr->cellFaceIsReversed()[cell_id]; + const auto& cell_face_list = m_dimensional_data_ptr->cellToFaceMatrix()[cell_id]; + const auto& face_to_node_matrix = m_dimensional_data_ptr->faceToNodeMatrix(); + + mean_of_ejk.fill(0); + + if constexpr (MeshType::Dimension == 2) { + const auto& quadrature = + QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(m_polynomial_degree + 1)); + + for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { + const FaceId face_id = cell_face_list[i_face]; + auto face_node_list = face_to_node_matrix[face_id]; + if (face_is_reversed[i_face]) { + 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(MeshType::Dimension == 3); + + const auto& triangle_quadrature = + QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(m_polynomial_degree + 1)); + const auto& square_quadrature = + QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor(m_polynomial_degree + 1)); + + for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { + const FaceId face_id = cell_face_list[i_face]; + auto face_node_list = face_to_node_matrix[face_id]; + switch (face_node_list.size()) { + case 3: { + if (face_is_reversed[i_face]) { + const TriangleTransformation<3> T{m_xr[face_node_list[2]], m_xr[face_node_list[1]], m_xr[face_node_list[0]]}; + _computeEjkBoundaryMean(triangle_quadrature, T, Xj, inv_Vi, mean_of_ejk); + } else { + const TriangleTransformation<3> T{m_xr[face_node_list[0]], m_xr[face_node_list[1]], m_xr[face_node_list[2]]}; + _computeEjkBoundaryMean(triangle_quadrature, T, Xj, inv_Vi, mean_of_ejk); + } + break; + } + case 4: { + if (face_is_reversed[i_face]) { + const SquareTransformation<3> T{m_xr[face_node_list[3]], m_xr[face_node_list[2]], m_xr[face_node_list[1]], + m_xr[face_node_list[0]]}; + _computeEjkBoundaryMean(square_quadrature, T, Xj, inv_Vi, mean_of_ejk); + } else { + const SquareTransformation<3> T{m_xr[face_node_list[0]], m_xr[face_node_list[1]], m_xr[face_node_list[2]], + m_xr[face_node_list[3]]}; + _computeEjkBoundaryMean(square_quadrature, T, Xj, inv_Vi, mean_of_ejk); + } + break; + } + default: { + throw NotImplementedError("invalid face type"); + } + } + } + } +} + +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) +{ + const double inv_Vi = 1. / m_Vj[cell_id]; + + const auto& face_is_reversed = m_dimensional_data_ptr->cellFaceIsReversed()[cell_id]; + const auto& cell_face_list = m_dimensional_data_ptr->cellToFaceMatrix()[cell_id]; + const auto& face_to_node_matrix = m_dimensional_data_ptr->faceToNodeMatrix(); + + mean_of_ejk.fill(0); + if constexpr (MeshType::Dimension == 2) { + const auto& quadrature = + QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(m_polynomial_degree + 1)); + + for (size_t i_face = 0; i_face < cell_face_list.size(); ++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, m_xr[face_node_list[1]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[0]]); + + if (face_is_reversed[i_face]) { + 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(MeshType::Dimension == 3); + + const auto& triangle_quadrature = + QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(m_polynomial_degree + 1)); + const auto& square_quadrature = + QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor(m_polynomial_degree + 1)); + + for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { + const FaceId face_id = cell_face_list[i_face]; + auto face_node_list = face_to_node_matrix[face_id]; + switch (face_node_list.size()) { + case 3: { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[2]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[1]]); + const auto x2 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[0]]); + + if (face_is_reversed[i_face]) { + const TriangleTransformation<3> T{x2, x1, x0}; + _computeEjkBoundaryMean(triangle_quadrature, T, Xj, inv_Vi, mean_of_ejk); + } else { + const TriangleTransformation<3> T{x0, x1, x2}; + _computeEjkBoundaryMean(triangle_quadrature, T, Xj, inv_Vi, mean_of_ejk); + } + break; + } + case 4: { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[3]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[2]]); + const auto x2 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[1]]); + const auto x3 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[0]]); + + if (face_is_reversed[i_face]) { + const SquareTransformation<3> T{x3, x2, x1, x0}; + _computeEjkBoundaryMean(square_quadrature, T, Xj, inv_Vi, mean_of_ejk); + } else { + const SquareTransformation<3> T{x0, x1, x2, x3}; + _computeEjkBoundaryMean(square_quadrature, T, Xj, inv_Vi, mean_of_ejk); + } + break; + } + default: { + throw NotImplementedError("invalid face type"); + } + } + } + } +} + +template <> +void +PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<Mesh<1>>::_computeEjkMeanByBoundary( + const Rd& Xj, + const CellId& cell_id, + SmallArray<double>& mean_of_ejk) +{ + const double inv_Vi = 1. / m_Vj[cell_id]; + + const auto& cell_node_list = m_dimensional_data_ptr->cellToNodeMatrix()[cell_id]; + + const double xr1_xj = (m_xr[cell_node_list[1]] - Xj)[0]; + + m_tmp_array[0] = xr1_xj * inv_Vi; + for (size_t k = 1; k <= m_polynomial_degree; ++k) { + m_tmp_array[k] // + = xr1_xj * m_tmp_array[k - 1] * m_antiderivative_correction_coef[k]; // ((1. * k) / (k + 1)); + } + + for (size_t k = 1; k < m_basis_dimension; ++k) { + mean_of_ejk[k - 1] = m_tmp_array[k]; + } + + const double xr0_xj = (m_xr[cell_node_list[0]] - Xj)[0]; + + m_tmp_array[0] = xr0_xj * inv_Vi; + for (size_t k = 1; k <= m_polynomial_degree; ++k) { + m_tmp_array[k] // + = xr0_xj * m_tmp_array[k - 1] * m_antiderivative_correction_coef[k]; // ((1. * k) / (k + 1)); + } + + for (size_t k = 1; k < m_basis_dimension; ++k) { + mean_of_ejk[k - 1] -= m_tmp_array[k]; + } +} + +template <> +void +PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< + Mesh<1>>::_computeEjkMeanByBoundaryInSymmetricCell(const Rd& origin, + const Rd& normal, + const Rd& Xj, + const CellId& cell_id, + SmallArray<double>& mean_of_ejk) +{ + const double inv_Vi = 1. / m_Vj[cell_id]; + + const auto& cell_node_list = m_dimensional_data_ptr->cellToNodeMatrix()[cell_id]; + + const double xr1_xj = (symmetrize_coordinates(origin, normal, m_xr[cell_node_list[0]]) - Xj)[0]; + + m_tmp_array[0] = xr1_xj * inv_Vi; + for (size_t k = 1; k <= m_polynomial_degree; ++k) { + m_tmp_array[k] // + = xr1_xj * m_tmp_array[k - 1] * m_antiderivative_correction_coef[k]; // ((1. * k) / (k + 1)); + } + + for (size_t k = 1; k < m_basis_dimension; ++k) { + mean_of_ejk[k - 1] = m_tmp_array[k]; + } + + const double xr0_xj = (symmetrize_coordinates(origin, normal, m_xr[cell_node_list[1]]) - Xj)[0]; + + m_tmp_array[0] = xr0_xj * inv_Vi; + for (size_t k = 1; k <= m_polynomial_degree; ++k) { + m_tmp_array[k] // + = xr0_xj * m_tmp_array[k - 1] * m_antiderivative_correction_coef[k]; // ((1. * k) / (k + 1)); + } + + for (size_t k = 1; k < m_basis_dimension; ++k) { + mean_of_ejk[k - 1] -= m_tmp_array[k]; + } +} + +template <MeshConcept MeshTypeT> +void +PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<MeshTypeT>::build( + const CellId cell_j_id, + ShrinkMatrixView<SmallMatrix<double>>& A) +{ + if constexpr (MeshType::Dimension <= 3) { + 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_tmp_array{m_basis_dimension}, + m_mean_j_of_ejk{m_basis_dimension - 1}, + m_mean_i_of_ejk{m_basis_dimension - 1}, + + m_dimensional_data_ptr{std::make_shared<SpecificDimensionalData>(mesh.connectivity())}, + + 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()} +{ + SmallArray<double> antiderivative_correction_coef(m_polynomial_degree + 1); + for (size_t k = 0; k < antiderivative_correction_coef.size(); ++k) { + // The antiderivative of x^k is k/(k+1) times the antiderivative of x^(k-1) + antiderivative_correction_coef[k] = ((1. * k) / (k + 1)); + } + + m_antiderivative_correction_coef = antiderivative_correction_coef; + + 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::BoundaryIntegralReconstructionMatrixBuilder<Mesh<1>>::build( + const CellId, + ShrinkMatrixView<SmallMatrix<double>>&); + +template void PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<Mesh<2>>::build( + const CellId, + ShrinkMatrixView<SmallMatrix<double>>&); + +template void PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<Mesh<3>>::build( + const CellId, + ShrinkMatrixView<SmallMatrix<double>>&); + +template PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< + Mesh<1>>::BoundaryIntegralReconstructionMatrixBuilder(const MeshType&, + const size_t, + const SmallArray<const Rd>&, + const SmallArray<const Rd>&, + const CellToCellStencilArray&); + +template PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< + Mesh<2>>::BoundaryIntegralReconstructionMatrixBuilder(const MeshType&, + const size_t, + const SmallArray<const Rd>&, + const SmallArray<const Rd>&, + const CellToCellStencilArray&); + +template PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< + Mesh<3>>::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..60707189b909e9d5f17ae2852be655cbfb877351 --- /dev/null +++ b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp @@ -0,0 +1,89 @@ +#ifndef BOUNDARY_INTEGRAL_RECONSTRUCTION_MATRIX_BUILDER_HPP +#define BOUNDARY_INTEGRAL_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 <size_t Dimension> +class QuadratureFormula; + +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_tmp_array; + SmallArray<double> m_mean_j_of_ejk; + SmallArray<double> m_mean_i_of_ejk; + + class SpecificDimensionalData; + std::shared_ptr<SpecificDimensionalData> m_dimensional_data_ptr; + + 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; + + // 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; + + SmallArray<const double> m_antiderivative_correction_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; +}; + +#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..94d1ef9a79ac1fb8f7e3cc0ece4c8eb21672bef7 --- /dev/null +++ b/src/scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.cpp @@ -0,0 +1,92 @@ +#include <scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.hpp> + +#include <geometry/SymmetryUtils.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.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 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&); 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..09e410811ed203610926bd605be3618e4219f396 --- /dev/null +++ b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.cpp @@ -0,0 +1,501 @@ +#include <scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp> + +#include <analysis/GaussLegendreQuadratureDescriptor.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/SymmetryUtils.hpp> +#include <geometry/TetrahedronTransformation.hpp> +#include <geometry/TriangleTransformation.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshDataManager.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 = 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 (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 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&); diff --git a/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp new file mode 100644 index 0000000000000000000000000000000000000000..7a3e8f7fc04d6d4ba21d4a36ed02ba24dd0d67d0 --- /dev/null +++ b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp @@ -0,0 +1,88 @@ +#ifndef ELEMENT_INTEGRAL_RECONSTRUCTION_MATRIX_BUILDER_HPP +#define ELEMENT_INTEGRAL_RECONSTRUCTION_MATRIX_BUILDER_HPP + +#include <algebra/ShrinkMatrixView.hpp> +#include <algebra/SmallMatrix.hpp> +#include <mesh/CellType.hpp> +#include <mesh/ItemValue.hpp> +#include <mesh/StencilArray.hpp> +#include <scheme/PolynomialReconstruction.hpp> +#include <utils/SmallArray.hpp> + +template <size_t Dimension> +class QuadratureFormula; + +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/src/utils/SignalManager.cpp b/src/utils/SignalManager.cpp index 4b2046a98796fc72cef406f77e7498930089f533..5054622c0d9959c089ddfa3f3a323820614c5806 100644 --- a/src/utils/SignalManager.cpp +++ b/src/utils/SignalManager.cpp @@ -52,17 +52,27 @@ SignalManager::signalName(int signal) void SignalManager::pauseForDebug(int signal) { - if (std::string(PUGS_BUILD_TYPE) != "Release") { - if (s_pause_on_error) { - // Each failing process must write - std::cerr.clear(); + if (s_pause_on_error) { + // Each failing process must write + std::cerr.clear(); + if (std::string(PUGS_BUILD_TYPE) != "Release") { + char hostname[HOST_NAME_MAX + 1]; + gethostname(hostname, HOST_NAME_MAX + 1); std::cerr << "\n======================================\n" - << rang::style::reset << rang::fg::reset << rang::style::bold << "to attach gdb to this process run\n" + << rang::style::reset << rang::fg::reset << rang::style::bold << "Process paused on host \"" + << rang::fg::yellow << hostname << rang::fg::reset << "\"\n" + << "to attach gdb to this process run\n" << "\tgdb -pid " << rang::fg::red << getpid() << rang::fg::reset << '\n' << "else press Control-C to exit\n" << rang::style::reset << "======================================\n" << std::flush; pause(); + } else { + std::cerr << '\n' + << rang::style::bold + << "Pausing is useless for Release version.\n" + "To attach debugger use Debug built type." + << rang::style::reset << '\n'; } } std::exit(signal); @@ -73,56 +83,52 @@ SignalManager::handler(int signal) { static std::mutex mutex; - if (mutex.try_lock()) { - std::signal(SIGTERM, SIG_DFL); - std::signal(SIGINT, SIG_DFL); - std::signal(SIGABRT, SIG_DFL); - - // Each failing process must write - std::cerr.clear(); + std::lock_guard<std::mutex> lock(mutex); - std::cerr << BacktraceManager{} << '\n'; + std::signal(SIGINT, SIG_BLOCK); - std::cerr << "\n *** " << rang::style::reset << rang::fg::reset << rang::style::bold << "Signal " << rang::fgB::red - << signalName(signal) << rang::fg::reset << " caught" << rang::style::reset << " ***\n\n"; + // Each failing process must write + std::cerr.clear(); - std::exception_ptr eptr = std::current_exception(); - try { - if (eptr) { - std::rethrow_exception(eptr); - } else { - std::ostringstream error_msg; - error_msg << "received " << signalName(signal); - std::cerr << ASTExecutionStack::getInstance().errorMessageAt(error_msg.str()) << '\n'; - } - } - catch (const IBacktraceError& backtrace_error) { - auto source_location = backtrace_error.sourceLocation(); - std::cerr << rang::fgB::cyan << source_location.file_name() << ':' << source_location.line() << ':' - << source_location.column() << ':' << rang::fg::reset << rang::fgB::yellow - << " threw the following exception" << rang::fg::reset << "\n\n"; - std::cerr << ASTExecutionStack::getInstance().errorMessageAt(backtrace_error.what()) << '\n'; - } - catch (const ParseError& parse_error) { - auto p = parse_error.positions().front(); - std::cerr << rang::style::bold << p.source << ':' << p.line << ':' << p.column << ':' << rang::style::reset - << rang::fgB::red << " error: " << rang::fg::reset << rang::style::bold << parse_error.what() - << rang::style::reset << '\n'; - } - catch (const IExitError& exit_error) { - std::cerr << ASTExecutionStack::getInstance().errorMessageAt(exit_error.what()) << '\n'; - } - catch (const AssertError& assert_error) { - std::cerr << assert_error << '\n'; - } - catch (...) { - std::cerr << "Unknown exception!\n"; - } + std::cerr << BacktraceManager{} << '\n'; - SignalManager::pauseForDebug(signal); + std::cerr << "\n *** " << rang::style::reset << rang::fg::reset << rang::style::bold << "Signal " << rang::fgB::red + << signalName(signal) << rang::fg::reset << " caught" << rang::style::reset << " ***\n\n"; - mutex.unlock(); + std::exception_ptr eptr = std::current_exception(); + try { + if (eptr) { + std::rethrow_exception(eptr); + } else { + std::ostringstream error_msg; + error_msg << "received " << signalName(signal); + std::cerr << ASTExecutionStack::getInstance().errorMessageAt(error_msg.str()) << '\n'; + } + } + catch (const IBacktraceError& backtrace_error) { + auto source_location = backtrace_error.sourceLocation(); + std::cerr << rang::fgB::cyan << source_location.file_name() << ':' << source_location.line() << ':' + << source_location.column() << ':' << rang::fg::reset << rang::fgB::yellow + << " threw the following exception" << rang::fg::reset << "\n\n"; + std::cerr << ASTExecutionStack::getInstance().errorMessageAt(backtrace_error.what()) << '\n'; } + catch (const ParseError& parse_error) { + auto p = parse_error.positions().front(); + std::cerr << rang::style::bold << p.source << ':' << p.line << ':' << p.column << ':' << rang::style::reset + << rang::fgB::red << " error: " << rang::fg::reset << rang::style::bold << parse_error.what() + << rang::style::reset << '\n'; + } + catch (const IExitError& exit_error) { + std::cerr << ASTExecutionStack::getInstance().errorMessageAt(exit_error.what()) << '\n'; + } + catch (const AssertError& assert_error) { + std::cerr << assert_error << '\n'; + } + catch (...) { + std::cerr << "Unknown exception!\n"; + } + + SignalManager::pauseForDebug(signal); } void diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 52d3a91ecb06c63b44bb6b391eb1c51bde95ed83..8771bb4cc0bae2587948acbe5eb6e49c737690b4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -285,6 +285,7 @@ add_executable (mpi_unit_tests 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 @@ -320,6 +321,7 @@ target_link_libraries (unit_tests PugsAlgebra PugsAnalysis PugsScheme + PugsSchemeReconstructionUtils PugsOutput PugsUtils PugsCheckpointing @@ -350,6 +352,7 @@ target_link_libraries (mpi_unit_tests PugsUtils PugsLanguageUtils PugsScheme + PugsSchemeReconstructionUtils PugsOutput PugsUtils PugsCheckpointing diff --git a/tests/test_PolynomialReconstruction_degree_2.cpp b/tests/test_PolynomialReconstruction_degree_2.cpp index 435084af00385cda388588fb21860fe32680c73e..db13612c1d8c5efad94a6f890251e366826d014b 100644 --- a/tests/test_PolynomialReconstruction_degree_2.cpp +++ b/tests/test_PolynomialReconstruction_degree_2.cpp @@ -118,7 +118,7 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") 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)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } } } @@ -139,7 +139,7 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") 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)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } } } @@ -172,7 +172,7 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") 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)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } } } @@ -219,7 +219,7 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") { 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)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } { @@ -231,7 +231,7 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") { 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)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } } } @@ -477,201 +477,567 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") SECTION("with symmetries") { -#warning continue here - // 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>; - - // for (auto descriptor : descriptor_list) { - // SECTION(name(descriptor.integrationMethodType()))// { - // SECTION("R^1 data") - // { - // auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); - - // auto& mesh = *p_mesh; - - // auto R1_exact = [](const R1& x) { return R1{1.7 * (x[0] + 1)}; }; - - // 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") - // { - // 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<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)}; }}; - - // 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>; - - // for (auto descriptor : descriptor_list) { - // SECTION(name(descriptor.integrationMethodType())) - // { - // SECTION("R^2 data") - // { - // auto p_mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); - // auto& mesh = *p_mesh; - - // 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") - // { - // 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>; - - // for (auto descriptor : descriptor_list) { - // SECTION(name(descriptor.integrationMethodType())) - // { - // 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)); - // } - // } - // } - // } + 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)); + } + } + } + } + } +}