From aeb147abf6df8c4f2d92e7907a9e59777f10e2bf Mon Sep 17 00:00:00 2001 From: Kilian Weishaupt <kilian.weishaupt@iws.uni-stuttgart.de> Date: Fri, 6 Aug 2021 09:52:24 +0200 Subject: [PATCH] [geometry][test] Add test for circumSphere and distance field --- test/geometry/test_distance.cc | 142 ++++++++++++++++++++++++++++----- 1 file changed, 120 insertions(+), 22 deletions(-) diff --git a/test/geometry/test_distance.cc b/test/geometry/test_distance.cc index e95af41422..76215eddd6 100644 --- a/test/geometry/test_distance.cc +++ b/test/geometry/test_distance.cc @@ -33,8 +33,11 @@ #include <dune/geometry/affinegeometry.hh> #include <dune/geometry/multilineargeometry.hh> +#include <dumux/geometry/circumsphere.hh> #include <dumux/geometry/distance.hh> +#include <dumux/geometry/distancefield.hh> #include <dumux/geometry/normal.hh> +#include <dumux/io/format.hh> #include "transformation.hh" // helper function to make point geometry from field vector @@ -56,6 +59,29 @@ Segment makeSegment(const Dune::FieldVector<typename Segment::ctype, dimWorld>& return { Dune::GeometryTypes::line, Corners{{p1, p2}} }; } +// helper function to make a triangle geometry from field vectors +template<class Triangle, int dimWorld> +Triangle makeTriangle(const Dune::FieldVector<typename Triangle::ctype, dimWorld>& p1, + const Dune::FieldVector<typename Triangle::ctype, dimWorld>& p2, + const Dune::FieldVector<typename Triangle::ctype, dimWorld>& p3) +{ + using GlobalPosition = Dune::FieldVector<typename Triangle::ctype, dimWorld>; + using Corners = std::vector<GlobalPosition>; + return { Dune::GeometryTypes::simplex(2), Corners{{p1, p2, p3}} }; +} + +// helper function to make a quadrilateral geometry from field vectors +template<class Quadrilateral, int dimWorld> +Quadrilateral makeQuadrilateral(const Dune::FieldVector<typename Quadrilateral::ctype, dimWorld>& p1, + const Dune::FieldVector<typename Quadrilateral::ctype, dimWorld>& p2, + const Dune::FieldVector<typename Quadrilateral::ctype, dimWorld>& p3, + const Dune::FieldVector<typename Quadrilateral::ctype, dimWorld>& p4) +{ + using GlobalPosition = Dune::FieldVector<typename Quadrilateral::ctype, dimWorld>; + using Corners = std::vector<GlobalPosition>; + return { Dune::GeometryTypes::quadrilateral, Corners{{p1, p2, p3, p4}} }; +} + // sample a point on a sphere with the given radius template<class Point> Point samplePointOnSphere(typename Point::ctype radius) @@ -86,6 +112,35 @@ void checkGeometryDistance(ctype expected, ctype computed, const std::string& ge DUNE_THROW(Dune::InvalidStateException, "Unexpected " << geometryPairName << " distance "); } +template<class Geometry> +void checkDistanceField(const std::vector<Geometry>& geometries, + const typename Geometry::GlobalCoordinate& p, + typename Geometry::ctype expectedDistance, + int expectedIndex) +{ + using namespace Dumux; + DistanceField<Geometry, false> distanceFieldWithoutBoundingSpheres(geometries); + const auto [d, idx] = distanceFieldWithoutBoundingSpheres.distanceAndIndex(p); + + if (Dune::FloatCmp::ne(d, expectedDistance)) + { + DUNE_THROW(Dune::InvalidStateException, + Fmt::format("Distance field returned wrong minimal distance. Expected {}, got {}", + expectedDistance, d)); + } + + if (idx != expectedIndex) + DUNE_THROW(Dune::InvalidStateException, "Distance field returned wrong index for minimal distance " + + std::to_string(idx) + ", expected " + std::to_string(expectedIndex)); + + DistanceField<Geometry, true> distanceFieldWithBoundingSpheres(geometries); + const auto [d2, idx2] = distanceFieldWithBoundingSpheres.distanceAndIndex(p); + + if (Dune::FloatCmp::ne(d, d2) || idx != idx2) + DUNE_THROW(Dune::InvalidStateException, "Distance field using bounding spheres does" + "not return same result as without bounding spheres"); +} + // checks the distances between various points with points/segments/lines template<template<class, int, int> class PointType, template<class, int, int> class OtherGeometryType, @@ -133,6 +188,11 @@ void runTests() checkGeometryDistance(scale, distancePointLine(p.corner(0), segment2.corner(0), segment2.corner(1)), "point-line"); checkGeometryDistance(scale, distancePointLine(p.corner(0), segment3.corner(0), segment3.corner(1)), "point-line"); + // check distance field class (2D), last segment is closest to p + const auto segment4 = makeSegment<Segment>(origin.corner(0) + 0.5*n, p.corner(0) + 0.5*n); + const std::vector<Segment> segments{segment, segment2, segment3, segment4}; + checkDistanceField(segments, origin.corner(0), 0.5*scale, segments.size() - 1); + // check distance point-triangle if constexpr(GlobalPosition::dimension == 3) { @@ -141,38 +201,76 @@ void runTests() auto rotationAxis = p.corner(0); rotationAxis /= rotationAxis.two_norm(); const auto rotate1 = make3DTransformation(1.0, origin.corner(0), rotationAxis, 2.0/3.0*M_PI, /*verbose*/false); const auto rotate2 = make3DTransformation(1.0, origin.corner(0), rotationAxis, 4.0/3.0*M_PI, /*verbose*/false); - const auto firstTriangle = Points{p.corner(0) + n, - rotate1(n) + p.corner(0), - rotate2(n) + p.corner(0)}; - checkGeometryDistance(scale, distancePointTriangle(origin.corner(0), firstTriangle[0], firstTriangle[1], firstTriangle[2]), "point-triangle"); + const auto firstTrianglePoints = Points{ + p.corner(0) + n, rotate1(n) + p.corner(0), rotate2(n) + p.corner(0) + }; + + checkGeometryDistance( + scale, + distancePointTriangle( + origin.corner(0), firstTrianglePoints[0], firstTrianglePoints[1], firstTrianglePoints[2] + ), + "point-triangle" + ); // check convenience function using Polygon = OtherGeometryType<double, 2, coorddim>; - const auto triangleGeometry = Polygon(Dune::GeometryTypes::simplex(2), std::vector<GlobalPosition>{firstTriangle.begin(), firstTriangle.end()}); - checkGeometryDistance(scale, distance(origin, triangleGeometry), "point-triangle"); - checkGeometryDistance(scale, distance(triangleGeometry, origin), "point-triangle"); + const auto firstTriangle = makeTriangle<Polygon, coorddim>( + firstTrianglePoints[0], firstTrianglePoints[1], firstTrianglePoints[2] + ); + + checkGeometryDistance(scale, distance(origin, firstTriangle), "point-triangle"); + checkGeometryDistance(scale, distance(firstTriangle, origin), "triangle-point"); + + // check circumsphere function + const auto sphere = circumSphereOfTriangle(firstTriangle); + const auto& expectedCenter = firstTriangle.center(); + const auto expectedRadius = (firstTriangle.corner(0) - expectedCenter).two_norm(); + if ((sphere.center() - expectedCenter).two_norm() > 1e-12*expectedRadius) + DUNE_THROW(Dune::InvalidStateException, Fmt::format( + "Wrong circumsphere center. Expected {}, got {}.", + expectedCenter, sphere.center() + )); + + if (Dune::FloatCmp::ne(sphere.radius(), expectedRadius)) + DUNE_THROW(Dune::InvalidStateException, Fmt::format( + "Wrong circumsphere radius Expected {}, got {}", + expectedRadius, sphere.radius() + )); // check quadrilateral - const auto& a = firstTriangle[0]; - const auto& b = firstTriangle[1]; - const auto& c = firstTriangle[2]; + const auto& [a, b, c] = firstTrianglePoints; const auto d = b + (b - a); - const auto quadGeometry = Polygon(Dune::GeometryTypes::quadrilateral, std::vector<GlobalPosition>{a, b, c, d}); - checkGeometryDistance(scale, distance(origin, quadGeometry), "point-quadrilateral"); - checkGeometryDistance(scale, distance(quadGeometry, origin), "point-quadrilateral"); + const auto quadrilateral = makeQuadrilateral<Polygon, coorddim>(a, b, c, d); + checkGeometryDistance(scale, distance(origin, quadrilateral), "point-quadrilateral"); + checkGeometryDistance(scale, distance(quadrilateral, origin), "quadrilateral-point"); // shift the first triangle - const auto secondTriangle = Points{segment.corner(0), - segment.corner(1), - 0.5*(segment.corner(0) + segment.corner(1)) + n}; - checkGeometryDistance(scale, distancePointTriangle(origin.corner(0), secondTriangle[0], secondTriangle[1], secondTriangle[2]), "point-triangle"); - checkGeometryDistance(scale, distancePointTriangle(p.corner(0), secondTriangle[0], secondTriangle[1], secondTriangle[2]), "point-triangle"); + const auto secondTrianglePoints = Points{ + segment.corner(0), segment.corner(1), 0.5*(segment.corner(0) + segment.corner(1)) + n + }; + const auto secondTriangle = makeTriangle<Polygon, coorddim>( + secondTrianglePoints[0], secondTrianglePoints[1], secondTrianglePoints[2] + ); + checkGeometryDistance(scale, distancePointTriangle(origin.corner(0), secondTriangle), "point-triangle"); // create a triangle not touching the sphere - const auto thirdTriangle = Points{segment2.corner(0), - segment2.corner(1), - 0.5*(segment2.corner(0) + segment2.corner(1)) + n}; - checkGeometryDistance(sqrt(2.0)*scale, distancePointTriangle(p.corner(0), thirdTriangle[0], thirdTriangle[1], thirdTriangle[2]), "point-triangle"); + const auto thirdTriangle = Points{ + segment2.corner(0), segment2.corner(1), 0.5*(segment2.corner(0) + segment2.corner(1)) + n + }; + + checkGeometryDistance( + sqrt(2.0)*scale, + distancePointTriangle( + p.corner(0), thirdTriangle[0], thirdTriangle[1], thirdTriangle[2] + ), + "point-triangle" + ); + + // check the distance field class + const auto closestQuadrilateral = makeQuadrilateral<Polygon, coorddim>(0.5*a, 0.5*b, 0.5*c, 0.5*d); + std::vector<Polygon> polygons{firstTriangle, quadrilateral, secondTriangle, closestQuadrilateral}; + checkDistanceField(polygons, origin.corner(0), 0.5*scale, polygons.size() - 1); } } } -- GitLab