From a43b25333fad390e83c875c33096af4348d8b82d Mon Sep 17 00:00:00 2001 From: Christoph Lehmann <christoph.lehmann@ufz.de> Date: Tue, 15 Aug 2017 13:59:03 +0200 Subject: [PATCH] [T] added a further class of polynomials --- Tests/MathLib/TestIntegration2.cpp | 174 ++++++++++++++++++++++++++++- 1 file changed, 173 insertions(+), 1 deletion(-) diff --git a/Tests/MathLib/TestIntegration2.cpp b/Tests/MathLib/TestIntegration2.cpp index 1bca6fc49a4..709cf181d72 100644 --- a/Tests/MathLib/TestIntegration2.cpp +++ b/Tests/MathLib/TestIntegration2.cpp @@ -296,6 +296,108 @@ private: unsigned const _degree; }; +unsigned binomial_coefficient(unsigned n, unsigned k) +{ + EXPECT_GE(n, k); + unsigned res = 1; + + for (unsigned i = n; i > k; --i) + { + res *= i; + } + + for (unsigned i = n - k; i > 0; --i) + { + res /= i; + } + + return res; +} + +/* This function is a polynomial where for each monomial a_ijk x^i y^j z^k + * holds: i + j + k <= n, where n is the overall polynomial degree + */ +struct F3DNonseparablePolynomial final : FBase +{ + // The number of coefficients/monomials are obtained as follows: Compute the + // number of combinations with repititions when drawing + // polynomial_degree times from the set { x, y, z, 1 } + F3DNonseparablePolynomial(unsigned polynomial_degree) + : FBase(binomial_coefficient(4 + polynomial_degree - 1, 4 - 1)), + _degree(polynomial_degree) + { + } + + double operator()(std::array<double, 3> const& coords) const override + { + auto const x = coords[0]; + auto const y = coords[1]; + auto const z = coords[2]; + + double res = 0.0; + unsigned index = 0; + + for (unsigned x_deg = 0; x_deg <= _degree; ++x_deg) + { + for (unsigned y_deg = 0; x_deg + y_deg <= _degree; ++y_deg) + { + for (unsigned z_deg = 0; x_deg + y_deg + z_deg <= _degree; + ++z_deg) + { + EXPECT_GT(coeffs.size(), index); + + res += coeffs[index] * std::pow(x, x_deg) * + std::pow(y, y_deg) * std::pow(z, z_deg); + + ++index; + } + } + } + + EXPECT_EQ(coeffs.size(), index); + + return res; + } + + double getAnalyticalIntegralOverUnitCube() const override + { + double const a = -.5; + double const b = .5; + + double res = 0.0; + unsigned index = 0; + + for (unsigned x_deg = 0; x_deg <= _degree; ++x_deg) + { + for (unsigned y_deg = 0; x_deg + y_deg <= _degree; ++y_deg) + { + for (unsigned z_deg = 0; x_deg + y_deg + z_deg <= _degree; + ++z_deg) + { + EXPECT_GT(coeffs.size(), index); + + res += coeffs[index] * + (std::pow(b, x_deg + 1) - std::pow(a, x_deg + 1)) / + (x_deg + 1) * + (std::pow(b, y_deg + 1) - std::pow(a, y_deg + 1)) / + (y_deg + 1) * + (std::pow(b, z_deg + 1) - std::pow(a, z_deg + 1)) / + (z_deg + 1); + + ++index; + } + } + } + + EXPECT_EQ(coeffs.size(), index); + + return res; + } + +private: + unsigned const _degree; +}; + std::unique_ptr<FBase> getF(unsigned polynomial_order) { std::vector<double> coeffs; @@ -313,6 +415,18 @@ std::unique_ptr<FBase> getF(unsigned polynomial_order) OGS_FATAL("unsupported polynomial order: %d.", polynomial_order); } +/* ***************************************************************************** + * + * The idea behind the tests in this file is to integrate polynomials of + * different degree over the unit cube. + * + * Gauss-Legendre integration should be able to exactly integrate those up to a + * certian degree. + * + * The coefficients of the tested polynomials are chosen randomly. + * + **************************************************************************** */ + TEST(MathLib, IntegrationGaussLegendreTet) { auto const eps = 2 * std::numeric_limits<double>::epsilon(); @@ -367,7 +481,9 @@ TEST(MathLib, IntegrationGaussLegendreHex) } } -TEST(MathLib, IntegrationGaussLegendreTetSeparablePolynomial) +// This test is disabled, because the polynomials involved are too complicated +// to be exactly integrated over tetrahedra using Gauss-Legendre quadrature +TEST(MathLib, DISABLED_IntegrationGaussLegendreTetSeparablePolynomial) { auto const eps = 2 * std::numeric_limits<double>::epsilon(); @@ -422,3 +538,59 @@ TEST(MathLib, IntegrationGaussLegendreHexSeparablePolynomial) } } } + +TEST(MathLib, IntegrationGaussLegendreTetNonSeparablePolynomial) +{ + auto const eps = 2 * std::numeric_limits<double>::epsilon(); + + std::unique_ptr<MeshLib::Mesh> mesh_tet( + MeshLib::IO::VtuInterface::readVTUFile(BaseLib::BuildInfo::data_path + + "/MathLib/unit_cube_tet.vtu")); + + for (unsigned integration_order : {1, 2, 3}) + { + DBUG("\n==== integration order: %u.\n", integration_order); + TestProcess pcs_tet(*mesh_tet, integration_order); + + for (unsigned polynomial_order = 0; + // Gauss-Legendre integration is exact up to this order! + polynomial_order < 2 * integration_order; + ++polynomial_order) + { + DBUG(" == polynomial order: %u.", polynomial_order); + F3DNonseparablePolynomial f(polynomial_order); + + auto const integral_tet = pcs_tet.integrate(f.getClosure()); + EXPECT_NEAR(f.getAnalyticalIntegralOverUnitCube(), integral_tet, + eps); + } + } +} + +TEST(MathLib, IntegrationGaussLegendreHexNonSeparablePolynomial) +{ + auto const eps = 10 * std::numeric_limits<double>::epsilon(); + + std::unique_ptr<MeshLib::Mesh> mesh_hex( + MeshLib::IO::VtuInterface::readVTUFile(BaseLib::BuildInfo::data_path + + "/MathLib/unit_cube_hex.vtu")); + + for (unsigned integration_order : {1, 2, 3, 4}) + { + DBUG("\n==== integration order: %u.\n", integration_order); + TestProcess pcs_hex(*mesh_hex, integration_order); + + for (unsigned polynomial_order = 0; + // Gauss-Legendre integration is exact up to this order! + polynomial_order < 2 * integration_order; + ++polynomial_order) + { + DBUG(" == polynomial order: %u.", polynomial_order); + F3DNonseparablePolynomial f(polynomial_order); + + auto const integral_hex = pcs_hex.integrate(f.getClosure()); + EXPECT_NEAR(f.getAnalyticalIntegralOverUnitCube(), integral_hex, + eps); + } + } +} -- GitLab