diff --git a/Tests/NumLib/TestIntegrationMethodRegistry.cpp b/Tests/NumLib/TestIntegrationMethodRegistry.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7099b286e8a43092ddee97f727cf99b88f7bb481 --- /dev/null +++ b/Tests/NumLib/TestIntegrationMethodRegistry.cpp @@ -0,0 +1,176 @@ +/** + * \file + * \copyright + * Copyright (c) 2012-2022, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + */ + +#include <gtest/gtest.h> + +#include <unordered_map> + +#include "MeshLib/Elements/Elements.h" +#include "NumLib/Fem/Integration/GaussLegendreIntegrationPolicy.h" +#include "NumLib/Fem/Integration/IntegrationMethodRegistry.h" + +namespace +{ +// helper type for converting lists of types +template <typename InputList, template <typename...> typename NewListType> +struct ConvertListType; +template <template <typename...> typename OldListType, + typename... Ts, + template <typename...> + typename NewListType> +struct ConvertListType<OldListType<Ts...>, NewListType> +{ + using type = NewListType<Ts...>; +}; +template <typename InputList, template <typename...> typename NewListType> +using ConvertListType_t = + typename ConvertListType<InputList, NewListType>::type; +} // namespace + +namespace MathLib +{ +// Used to output helpful information if the unit tests fail. +static std::ostream& operator<<(std::ostream& os, + MathLib::WeightedPoint const& wp) +{ + auto const dim = wp.getDimension(); + os << "WP[" << dim << "D]{{"; + for (decltype(+dim) comp = 0; comp < 3; ++comp) + { + if (comp != 0) + { + os << ", "; + } + os << wp[comp]; + } + os << "}, weight=" << wp.getWeight() << '}'; + return os; +} +} // namespace MathLib + +static std::unordered_map<std::type_index, unsigned> initMapTypeToMaxOrder() +{ + using namespace MeshLib; + + std::unordered_map<std::type_index, unsigned> map_type_to_max_order; + + auto add = [&](std::type_info const& info, unsigned order) + { + auto const [it, inserted] = + map_type_to_max_order.emplace(std::type_index(info), order); + if (!inserted) + { + throw std::runtime_error( + "Maximum integration order already present for the " + "current mesh element type. See file " __FILE__); + } + }; + + add(typeid(Point), 4); + add(typeid(Line), 4); + add(typeid(Line3), 4); + add(typeid(Quad), 4); + add(typeid(Quad8), 4); + add(typeid(Quad9), 4); + add(typeid(Tri), 4); + add(typeid(Tri6), 4); + add(typeid(Hex), 4); + add(typeid(Hex20), 4); + add(typeid(Tet), 4); + add(typeid(Tet10), 4); + add(typeid(Prism), 1); // Actually, there is only one unique integration + // method for prisms, currently. + add(typeid(Prism15), 1); + add(typeid(Pyramid), 3); + add(typeid(Pyramid13), 3); + + if (std::tuple_size_v<AllElementTypes> != map_type_to_max_order.size()) + { + throw std::runtime_error( + "Some element types are missing in the mapping from mesh element " + "type to maximum integration order. See file " __FILE__); + } + + return map_type_to_max_order; +} + +static const std::unordered_map<std::type_index, unsigned> + map_type_to_max_order = initMapTypeToMaxOrder(); + +// Test fixture. +template <typename MeshElementType> +class NumLibIntegrationMethodRegistryTest : public ::testing::Test +{ + std::type_index const type_index = std::type_index(typeid(MeshElementType)); + +protected: + unsigned const max_order = map_type_to_max_order.at(type_index); + + NumLib::GenericIntegrationMethod const& getGenericIntegrationMethod( + unsigned order) const + { + return NumLib::IntegrationMethodRegistry::getIntegrationMethod< + MeshElementType>(order); + } +}; + +using MeshElementTypes = + ConvertListType_t<MeshLib::AllElementTypes, ::testing::Types>; + +TYPED_TEST_SUITE(NumLibIntegrationMethodRegistryTest, MeshElementTypes); + +TYPED_TEST(NumLibIntegrationMethodRegistryTest, HasRightOrder) +{ + for (unsigned order = 1; order <= this->max_order; ++order) + { + auto const& generic_int_meth = this->getGenericIntegrationMethod(order); + + EXPECT_EQ(order, generic_int_meth.getIntegrationOrder()); + } +} + +TYPED_TEST(NumLibIntegrationMethodRegistryTest, HasRightIntegrationPoints) +{ + using MeshElementType = TypeParam; + + for (unsigned order = 1; order <= this->max_order; ++order) + { + auto const& generic_int_meth = this->getGenericIntegrationMethod(order); + + using IntegrationPolicy = + NumLib::GaussLegendreIntegrationPolicy<MeshElementType>; + using NonGenericIntegrationMethod = + typename IntegrationPolicy::IntegrationMethod; + + NonGenericIntegrationMethod int_meth{order}; + + auto const num_int_pts = generic_int_meth.getNumberOfPoints(); + auto const num_int_pts_expected = int_meth.getNumberOfPoints(); + ASSERT_EQ(num_int_pts_expected, num_int_pts) + << "Wrong number of integration points for integration order " + << order << '.'; + + for (unsigned ip = 0; ip < num_int_pts; ++ip) + { + auto const& wp_expected = int_meth.getWeightedPoint(ip); + auto const& wp_actual = generic_int_meth.getWeightedPoint(ip); + EXPECT_EQ(wp_expected, wp_actual) + << "Wrong integration point (#" << ip + << ") for integration order " << order << '.' + << "\nexpected: " << wp_expected << "\nactual: " << wp_actual; + } + } +} + +TYPED_TEST(NumLibIntegrationMethodRegistryTest, OrderZeroForbidden) +{ + unsigned const order = 0; + + ASSERT_ANY_THROW(this->getGenericIntegrationMethod(order)); +}