From f6b6a93131131e652b97935ae59d96bdf271b8fd Mon Sep 17 00:00:00 2001 From: Timo Koch <timo.koch@iws.uni-stuttgart.de> Date: Wed, 5 May 2021 22:31:28 +0200 Subject: [PATCH] [test][math] Generalize invert cubic root test --- test/common/math/test_math.cc | 112 +++++++++++++++++++++++++--------- 1 file changed, 84 insertions(+), 28 deletions(-) diff --git a/test/common/math/test_math.cc b/test/common/math/test_math.cc index 85ad154791..00d3b92908 100644 --- a/test/common/math/test_math.cc +++ b/test/common/math/test_math.cc @@ -33,6 +33,9 @@ #include <iostream> #include <utility> #include <algorithm> +#include <array> +#include <random> +#include <tuple> #include <dune/common/float_cmp.hh> #include <dune/common/fmatrix.hh> @@ -239,43 +242,96 @@ int main() ///// Dumux::invertCubicPolynomial /////////////////////////////// ////////////////////////////////////////////////////////////////// + const auto cubicCoefficientsFromRoots = [](auto x, auto y, auto z, double scale = 1.0){ + const auto b = -(x + y + z); + const auto c = x*y + x*z + y*z; + const auto d = -x*y*z; + // scaling doesn't change the roots + return std::make_tuple(1.0*scale, b*scale, c*scale, d*scale); + }; + + const auto quadCoefficientsFromRoots = [](auto x, auto y, double scale = 1.0){ + const auto b = -(x + y); + const auto c = x*y; + return std::make_tuple(1.0*scale, b*scale, c*scale); + }; + + const auto linearCoefficientsFromRoots = [](auto x, double scale = 1.0){ + return std::make_tuple(1.0*scale, -x*scale); + }; + + std::mt19937 gen(std::random_device{}()); + std::uniform_real_distribution<> dis(-10.0, 10.0); + auto threeRandomNumbers = [&]() mutable { + const auto x = dis(gen); + auto y = dis(gen); while (x == y) { y = dis(gen); } + auto z = dis(gen); while (x == z || y == z) { z = dis(gen); } + return std::make_tuple(x, y, z); + }; + + const auto checkRoots = [](const std::array<double, 3>& roots, + const std::array<double, 3>& ref, + int n) + { + for (int i = 0; i < n; i++) + { + using std::isfinite; + if (!isfinite(roots[i])) + DUNE_THROW(Dune::Exception, "Expecting finite root"); + if (std::none_of(ref.begin(), ref.end(), [&](auto r){ return Dune::FloatCmp::eq(roots[i], r, 1e-10); })) + DUNE_THROW(Dune::Exception, "[invertCubicPolynomial] Root " + << i << " of " << n << ": " << roots[i] + << " does not match reference"); + } + }; + std::array<double, 3> roots{}; + for (double scaling : {1.0, -1.0, 5.0}) + { + for (int i = 0; i < 1000; ++i) + { + const auto [x, y, z] = threeRandomNumbers(); + + // test for three unique roots + { + const auto [a, b, c, d] = cubicCoefficientsFromRoots(x, y, z, scaling); + int numRoots = Dumux::invertCubicPolynomial(roots.data(), a, b, c, d); + if (numRoots != 3) + DUNE_THROW(Dune::Exception, "Expecting three roots! Got " << numRoots); + checkRoots(roots, {x, y, z}, numRoots); + } + + // test for two unique roots + { + const auto [b, c, d] = quadCoefficientsFromRoots(x, z, scaling); + int numRoots = Dumux::invertCubicPolynomial(roots.data(), 0.0, b, c, d); + if (numRoots != 2) + DUNE_THROW(Dune::Exception, "Expecting two roots! Got " << numRoots); + checkRoots(roots, {x, z}, numRoots); + } + + // test for one unique root + { + const auto [c, d] = linearCoefficientsFromRoots(x, scaling); + int numRoots = Dumux::invertCubicPolynomial(roots.data(), 0.0, 0.0, c, d); + if (numRoots != 1) + DUNE_THROW(Dune::Exception, "Expecting one root! Got " << numRoots); + checkRoots(roots, {x}, numRoots); + } + } + } - // One corner case + // Test one corner case (see MR!2594) int nroots = Dumux::invertCubicPolynomial(roots.data(), - 1.0, - -0.96165703943410097, - 0.30826068470787077, - 0.01340255221155587); + 1.0, -0.96165703943410097, 0.30826068470787077, 0.01340255221155587 + ); if (nroots != 1) DUNE_THROW(Dune::Exception, "Expecting one root"); - using std::isfinite; - if (!isfinite(roots[0])) DUNE_THROW(Dune::Exception, "Expecting finite root"); if (!Dune::FloatCmp::eq(roots[0], -3.863448718244389e-02, 1e-14)) DUNE_THROW(Dune::Exception, "Root of cubic equation does not match reference"); - - // One simple case - std::array<double, 3> known_roots{-10.1, 0.001, 1000.77}; - - nroots = Dumux::invertCubicPolynomial( - roots.data(), - 1.0, - -known_roots[0] - known_roots[1] - known_roots[2], - known_roots[0] * known_roots[1] + known_roots[0] * known_roots[2] + known_roots[1] * known_roots[2], - -known_roots[0] * known_roots[1] * known_roots[2]); - - if (nroots != 3) - DUNE_THROW(Dune::Exception, "Expecting three roots"); - - for (int i = 0; i < 3; i++) - { - if (!isfinite(roots[i])) - DUNE_THROW(Dune::Exception, "Expecting finite root"); - if (!Dune::FloatCmp::eq(roots[i], known_roots[i], 1e-10)) - DUNE_THROW(Dune::Exception, "Root of cubic equation does not match reference"); - } + return 0; } -- GitLab