From 91ae89cb0f9e97dbbc1d767a6171fbdac754898f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Mon, 6 Dec 2021 18:28:50 +0100 Subject: [PATCH] Add EdgeIntegrator (and associated tests) --- src/scheme/EdgeIntegrator.hpp | 166 +++++++++++++++++++++ tests/CMakeLists.txt | 1 + tests/test_EdgeIntegrator.cpp | 261 ++++++++++++++++++++++++++++++++++ 3 files changed, 428 insertions(+) create mode 100644 src/scheme/EdgeIntegrator.hpp create mode 100644 tests/test_EdgeIntegrator.cpp diff --git a/src/scheme/EdgeIntegrator.hpp b/src/scheme/EdgeIntegrator.hpp new file mode 100644 index 000000000..2bf287c3d --- /dev/null +++ b/src/scheme/EdgeIntegrator.hpp @@ -0,0 +1,166 @@ +#ifndef EDGE_INTEGRATOR_HPP +#define EDGE_INTEGRATOR_HPP + +#include <analysis/IQuadratureDescriptor.hpp> +#include <analysis/QuadratureManager.hpp> +#include <geometry/LineTransformation.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/ItemId.hpp> +#include <utils/Array.hpp> + +#include <functional> + +class EdgeIntegrator +{ + private: + template <size_t Dimension> + class AllEdges + { + private: + const Connectivity<Dimension>& m_connectivity; + + public: + using index_type = EdgeId; + + PUGS_INLINE + EdgeId + edgeId(const EdgeId edge_id) const + { + return edge_id; + } + + PUGS_INLINE + size_t + size() const + { + return m_connectivity.numberOfEdges(); + } + + PUGS_INLINE + AllEdges(const Connectivity<Dimension>& connectivity) : m_connectivity{connectivity} {} + }; + + template <template <typename T> typename ArrayT> + class EdgeList + { + private: + const ArrayT<EdgeId>& m_edge_list; + + public: + using index_type = typename ArrayT<EdgeId>::index_type; + + PUGS_INLINE + EdgeId + edgeId(const index_type index) const + { + return m_edge_list[index]; + } + + PUGS_INLINE + size_t + size() const + { + return m_edge_list.size(); + } + + PUGS_INLINE + EdgeList(const ArrayT<EdgeId>& edge_list) : m_edge_list{edge_list} {} + }; + + template <typename MeshType, typename OutputArrayT, typename ListTypeT, typename OutputType, typename InputType> + static PUGS_INLINE void + _integrateTo(std::function<OutputType(InputType)> function, + const IQuadratureDescriptor& quadrature_descriptor, + const MeshType& mesh, + const ListTypeT& edge_list, + OutputArrayT& value) + { + constexpr size_t Dimension = MeshType::Dimension; + static_assert(Dimension == 3); + + static_assert(std::is_same_v<TinyVector<Dimension>, std::decay_t<InputType>>, "invalid input data type"); + static_assert(std::is_same_v<std::remove_const_t<typename OutputArrayT::data_type>, OutputType>, + "invalid output data type"); + + using execution_space = typename Kokkos::DefaultExecutionSpace::execution_space; + Kokkos::Experimental::UniqueToken<execution_space, Kokkos::Experimental::UniqueTokenScope::Global> tokens; + + if constexpr (std::is_arithmetic_v<OutputType>) { + value.fill(0); + } else if constexpr (is_tiny_vector_v<OutputType> or is_tiny_matrix_v<OutputType>) { + value.fill(zero); + } else { + static_assert(std::is_same_v<OutputType, double>, "unexpected output type"); + } + + const auto& connectivity = mesh.connectivity(); + + const auto edge_to_node_matrix = connectivity.edgeToNodeMatrix(); + const auto xr = mesh.xr(); + + const auto qf = QuadratureManager::instance().getLineFormula(quadrature_descriptor); + + parallel_for(edge_list.size(), [=, &qf, &edge_list](typename ListTypeT::index_type index) { + const EdgeId edge_id = edge_list.edgeId(index); + + const auto edge_to_node = edge_to_node_matrix[edge_id]; + + Assert(edge_to_node.size() == 2); + const LineTransformation<3> T(xr[edge_to_node[0]], xr[edge_to_node[1]]); + + for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) { + const auto xi = qf.point(i_point); + value[index] += qf.weight(i_point) * T.velocityNorm() * function(T(xi)); + } + }); + } + + public: + template <typename MeshType, typename OutputArrayT, typename OutputType, typename InputType> + static PUGS_INLINE void + integrateTo(const std::function<OutputType(InputType)>& function, + const IQuadratureDescriptor& quadrature_descriptor, + const MeshType& mesh, + OutputArrayT& value) + { + constexpr size_t Dimension = MeshType::Dimension; + + _integrateTo<MeshType, OutputArrayT>(function, quadrature_descriptor, mesh, + AllEdges<Dimension>{mesh.connectivity()}, value); + } + + template <typename MeshType, typename OutputArrayT, typename FunctionType> + static PUGS_INLINE void + integrateTo(const FunctionType& function, + const IQuadratureDescriptor& quadrature_descriptor, + const MeshType& mesh, + OutputArrayT& value) + { + integrateTo(std::function(function), quadrature_descriptor, mesh, value); + } + + template <typename MeshType, typename OutputType, typename InputType, template <typename DataType> typename ArrayT> + static PUGS_INLINE ArrayT<OutputType> + integrate(const std::function<OutputType(InputType)>& function, + const IQuadratureDescriptor& quadrature_descriptor, + const MeshType& mesh, + const ArrayT<EdgeId>& edge_list) + { + ArrayT<OutputType> value(size(edge_list)); + _integrateTo<MeshType, ArrayT<OutputType>>(function, quadrature_descriptor, mesh, EdgeList{edge_list}, value); + + return value; + } + + template <typename MeshType, typename FunctionType, template <typename DataType> typename ArrayT> + static PUGS_INLINE auto + integrate(const FunctionType& function, + const IQuadratureDescriptor& quadrature_descriptor, + const MeshType& mesh, + const ArrayT<EdgeId>& edge_list) + { + return integrate(std::function(function), quadrature_descriptor, mesh, edge_list); + } +}; + +#endif // EDGE_INTEGRATOR_HPP diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b10741748..f825a2cfd 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -70,6 +70,7 @@ add_executable (unit_tests test_DiscreteFunctionType.cpp test_DiscreteFunctionUtils.cpp test_DoWhileProcessor.cpp + test_EdgeIntegrator.cpp test_EigenvalueSolver.cpp test_EmbeddedData.cpp test_EmbeddedIDiscreteFunctionUtils.cpp diff --git a/tests/test_EdgeIntegrator.cpp b/tests/test_EdgeIntegrator.cpp new file mode 100644 index 000000000..0cd2feb9d --- /dev/null +++ b/tests/test_EdgeIntegrator.cpp @@ -0,0 +1,261 @@ +#include <catch2/catch_approx.hpp> +#include <catch2/catch_test_macros.hpp> + +#include <analysis/GaussLegendreQuadratureDescriptor.hpp> +#include <analysis/GaussLobattoQuadratureDescriptor.hpp> +#include <analysis/GaussQuadratureDescriptor.hpp> +#include <mesh/DiamondDualMeshManager.hpp> +#include <mesh/ItemValue.hpp> +#include <mesh/Mesh.hpp> +#include <scheme/EdgeIntegrator.hpp> + +#include <MeshDataBaseForTests.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("EdgeIntegrator", "[scheme]") +{ + SECTION("3D") + { + using R3 = TinyVector<3>; + + auto hybrid_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + + auto f = [](const R3& X) -> double { + const double x = X[0]; + const double y = X[1]; + const double z = X[2]; + return x * x + 2 * x * y + 3 * y * y + 2 * z * z - z + 1; + }; + + std::vector<std::pair<std::string, decltype(hybrid_mesh)>> mesh_list; + mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh)); + mesh_list.push_back( + std::make_pair("diamond mesh", DiamondDualMeshManager::instance().getDiamondDualMesh(hybrid_mesh))); + + for (auto mesh_info : mesh_list) { + auto mesh_name = mesh_info.first; + auto mesh = mesh_info.second; + + Array<const double> int_f_per_edge = [=] { + Array<double> int_f(mesh->numberOfEdges()); + auto edge_to_node_matrix = mesh->connectivity().edgeToNodeMatrix(); + + parallel_for( + mesh->numberOfEdges(), PUGS_LAMBDA(const EdgeId edge_id) { + auto edge_node_list = edge_to_node_matrix[edge_id]; + auto xr = mesh->xr(); + double integral = 0; + + LineTransformation<3> T(xr[edge_node_list[0]], xr[edge_node_list[1]]); + auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor(2)); + for (size_t i = 0; i < qf.numberOfPoints(); ++i) { + const auto& xi = qf.point(i); + integral += qf.weight(i) * T.velocityNorm() * f(T(xi)); + } + + int_f[edge_id] = integral; + }); + + return int_f; + }(); + + SECTION(mesh_name) + { + SECTION("direct formula") + { + SECTION("all edges") + { + SECTION("EdgeValue") + { + EdgeValue<double> values(mesh->connectivity()); + EdgeIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussQuadratureDescriptor(4), *mesh, + values); + + double error = 0; + for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) { + error += std::abs(int_f_per_edge[edge_id] - values[edge_id]); + } + + REQUIRE(error == Catch::Approx(0).margin(1E-10)); + } + + SECTION("Array") + { + Array<double> values(mesh->numberOfEdges()); + + EdgeIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values); + + double error = 0; + for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) { + error += std::abs(int_f_per_edge[edge_id] - values[edge_id]); + } + + REQUIRE(error == Catch::Approx(0).margin(1E-10)); + } + + SECTION("SmallArray") + { + SmallArray<double> values(mesh->numberOfEdges()); + EdgeIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values); + + double error = 0; + for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) { + error += std::abs(int_f_per_edge[edge_id] - values[edge_id]); + } + + REQUIRE(error == Catch::Approx(0).margin(1E-10)); + } + } + + SECTION("edge list") + { + SECTION("Array") + { + Array<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2}; + + { + size_t k = 0; + for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) { + edge_list[k] = edge_id; + } + + REQUIRE(k == edge_list.size()); + } + + Array<double> values = EdgeIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, edge_list); + + double error = 0; + for (size_t i = 0; i < edge_list.size(); ++i) { + error += std::abs(int_f_per_edge[edge_list[i]] - values[i]); + } + + REQUIRE(error == Catch::Approx(0).margin(1E-10)); + } + + SECTION("SmallArray") + { + SmallArray<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2}; + + { + size_t k = 0; + for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) { + edge_list[k] = edge_id; + } + + REQUIRE(k == edge_list.size()); + } + + SmallArray<double> values = EdgeIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, edge_list); + + double error = 0; + for (size_t i = 0; i < edge_list.size(); ++i) { + error += std::abs(int_f_per_edge[edge_list[i]] - values[i]); + } + + REQUIRE(error == Catch::Approx(0).margin(1E-10)); + } + } + } + + SECTION("tensorial formula") + { + SECTION("all edges") + { + SECTION("EdgeValue") + { + EdgeValue<double> values(mesh->connectivity()); + EdgeIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussLegendreQuadratureDescriptor(10), + *mesh, values); + + double error = 0; + for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) { + error += std::abs(int_f_per_edge[edge_id] - values[edge_id]); + } + + REQUIRE(error == Catch::Approx(0).margin(1E-10)); + } + + SECTION("Array") + { + Array<double> values(mesh->numberOfEdges()); + + EdgeIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values); + + double error = 0; + for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) { + error += std::abs(int_f_per_edge[edge_id] - values[edge_id]); + } + + REQUIRE(error == Catch::Approx(0).margin(1E-10)); + } + + SECTION("SmallArray") + { + SmallArray<double> values(mesh->numberOfEdges()); + EdgeIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values); + + double error = 0; + for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) { + error += std::abs(int_f_per_edge[edge_id] - values[edge_id]); + } + + REQUIRE(error == Catch::Approx(0).margin(1E-10)); + } + } + + SECTION("edge list") + { + SECTION("Array") + { + Array<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2}; + + { + size_t k = 0; + for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) { + edge_list[k] = edge_id; + } + + REQUIRE(k == edge_list.size()); + } + + Array<double> values = + EdgeIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, edge_list); + + double error = 0; + for (size_t i = 0; i < edge_list.size(); ++i) { + error += std::abs(int_f_per_edge[edge_list[i]] - values[i]); + } + + REQUIRE(error == Catch::Approx(0).margin(1E-10)); + } + + SECTION("SmallArray") + { + SmallArray<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2}; + + { + size_t k = 0; + for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) { + edge_list[k] = edge_id; + } + + REQUIRE(k == edge_list.size()); + } + + SmallArray<double> values = + EdgeIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, edge_list); + + double error = 0; + for (size_t i = 0; i < edge_list.size(); ++i) { + error += std::abs(int_f_per_edge[edge_list[i]] - values[i]); + } + + REQUIRE(error == Catch::Approx(0).margin(1E-10)); + } + } + } + } + } + } +} -- GitLab