diff --git a/Tests/NumLib/TestShapeFunctions.cpp b/Tests/NumLib/TestShapeFunctions.cpp index 3f53a49bc44c8891ff40050593ff8393050f6984..dee8a5df572b2d9c563ffe9403b568f8a10f183d 100644 --- a/Tests/NumLib/TestShapeFunctions.cpp +++ b/Tests/NumLib/TestShapeFunctions.cpp @@ -11,16 +11,155 @@ #include <gtest/gtest.h> +#include <algorithm> +#include <type_traits> #include <limits> +#include <numeric> #include <valarray> -#include <algorithm> +namespace autocheck{ + +template <typename T, std::size_t N> +std::ostream& operator<<(std::ostream& os, std::array<T, N> const& array) +{ + os << "["; + std::copy(std::begin(array), std::end(array), + std::ostream_iterator<double>(os, ",")); + os << "]"; + return os; +} +} +#include <autocheck/autocheck.hpp> + +#include "MeshLib/Elements/Elements.h" +#include "NumLib/Fem/ShapeFunction/ShapeHex20.h" +#include "NumLib/Fem/ShapeFunction/ShapeHex8.h" +#include "NumLib/Fem/ShapeFunction/ShapeLine2.h" +#include "NumLib/Fem/ShapeFunction/ShapeLine3.h" #include "NumLib/Fem/ShapeFunction/ShapeQuad4.h" +#include "NumLib/Fem/ShapeFunction/ShapeQuad8.h" +#include "NumLib/Fem/ShapeFunction/ShapeQuad9.h" +#include "NumLib/Fem/ShapeFunction/ShapeTet10.h" +#include "NumLib/Fem/ShapeFunction/ShapeTet4.h" +#include "NumLib/Fem/ShapeFunction/ShapeTri3.h" +#include "NumLib/Fem/ShapeFunction/ShapeTri6.h" #include "Tests/TestTools.h" +#include "../MathLib/AutoCheckTools.h" + +namespace autocheck +{ + +template <typename ShapeFunction, + typename Gen = randomTupleGenerator<double, ShapeFunction::DIM>> +struct NaturalPointGenerator +{ + Gen generator; // The default interval is fixed to [-1, 1]. + + using result_type = std::array<double, ShapeFunction::DIM>; + + result_type intervalMap(result_type const& tuple) const + { + if (std::is_same<ShapeFunction, NumLib::ShapeLine2>::value || + std::is_same<ShapeFunction, NumLib::ShapeLine3>::value || + std::is_same<ShapeFunction, NumLib::ShapeQuad4>::value || + std::is_same<ShapeFunction, NumLib::ShapeQuad8>::value || + std::is_same<ShapeFunction, NumLib::ShapeQuad9>::value || + std::is_same<ShapeFunction, NumLib::ShapeHex8>::value || + std::is_same<ShapeFunction, NumLib::ShapeHex20>::value) + return tuple; + if (std::is_same<ShapeFunction, NumLib::ShapeTri3>::value || + std::is_same<ShapeFunction, NumLib::ShapeTri6>::value || + std::is_same<ShapeFunction, NumLib::ShapeTet4>::value || + std::is_same<ShapeFunction, NumLib::ShapeTet10>::value) + { + // Map square (x, y) \in [-1, 1]^2 to a triangle such that x,y + // \in [0, 1], and x+y \in [0, 2/2]. + // Same for three-d case with x + y + z \in [0, 3/2]. + result_type mapped_tuple; + std::transform(std::begin(tuple), std::end(tuple), + std::begin(mapped_tuple), + [](double const& v) { return (v + 1.) / 2.; }); + + if (std::accumulate(std::begin(mapped_tuple), + std::end(mapped_tuple), + 0.) > ShapeFunction::DIM / 2.) + std::transform(std::begin(mapped_tuple), std::end(mapped_tuple), + std::begin(mapped_tuple), + [](double const& v) { return 1. - v; }); + return mapped_tuple; + } + } + + result_type operator()(std::size_t /*size*/ = 0) + { + return intervalMap(fix(1, generator)()); + } +}; + +} + +namespace ac = autocheck; using namespace NumLib; +template <typename ShapeFunction> +struct ShapeFunctionTest : public ::testing::Test +{ + ac::NaturalPointGenerator<ShapeFunction> natural_point_generator; + ac::gtest_reporter gtest_reporter; +}; + +using ShapeFunctionTestTypes = + ::testing::Types<ShapeLine2, ShapeLine3, ShapeTri3, ShapeTri6, ShapeTet4, + ShapeTet10, ShapeQuad4, ShapeQuad8, ShapeQuad9, ShapeHex8, + ShapeHex20>; + +TYPED_TEST_CASE(ShapeFunctionTest, ShapeFunctionTestTypes); + +// TypeParam is the type of the ShapeFunction. +// Access private members via this pointer or TestFixture:: for types +TYPED_TEST(ShapeFunctionTest, PartitionOfUnity) +{ + auto isPartitionOfUnity = [this]( + std::array<double, TypeParam::DIM>& natural_coordinates_point) -> bool { + + // compute shape functions + std::array<double, TypeParam::NPOINTS> N; + TypeParam::computeShapeFunction(natural_coordinates_point, N); + double const sum = std::accumulate(std::begin(N), std::end(N), 0.); + + return std::abs(1. - sum) < 1e-15; + }; + + ac::check<std::array<double, TypeParam::DIM>>( + isPartitionOfUnity, + 1000, + ac::make_arbitrary(this->natural_point_generator), + this->gtest_reporter); +} + +TYPED_TEST(ShapeFunctionTest, SumOfGradientsIsZero) +{ + auto isSumOfGradientsZero = [this]( + std::array<double, TypeParam::DIM>& natural_coordinates_point) -> bool { + + // compute shape functions + std::array<double, TypeParam::DIM * TypeParam::NPOINTS> dNdr; + TypeParam::computeGradShapeFunction(natural_coordinates_point, dNdr); + double const sum = + std::accumulate(std::begin(dNdr), std::end(dNdr), 0.); + + return std::abs(sum) <= 5e-15; + }; + + ac::check<std::array<double, TypeParam::DIM>>( + isSumOfGradientsZero, + 1000, + ac::make_arbitrary(this->natural_point_generator), + this->gtest_reporter); +} + TEST(NumLib, FemShapeQuad4) { static const double eps = std::numeric_limits<double>::epsilon();