diff --git a/test/common/geometry/CMakeLists.txt b/test/common/geometry/CMakeLists.txt index de69aced013d170730a8003bc6dceb6c9f4c89db..d31f1ee8bf6a87c0d61720249b47890c90ee4bd0 100644 --- a/test/common/geometry/CMakeLists.txt +++ b/test/common/geometry/CMakeLists.txt @@ -1,4 +1,5 @@ dumux_add_test(SOURCES test_0d1d_intersection.cc LABELS unit) +dumux_add_test(SOURCES test_0d2d_intersection.cc LABELS unit) dumux_add_test(SOURCES test_0d3d_intersection.cc LABELS unit) dumux_add_test(SOURCES test_1d1d_intersection.cc LABELS unit) dumux_add_test(SOURCES test_1d3d_intersection.cc LABELS unit) diff --git a/test/common/geometry/test_0d2d_intersection.cc b/test/common/geometry/test_0d2d_intersection.cc new file mode 100644 index 0000000000000000000000000000000000000000..25e5308fc1edeaaa5115340f69b9578bc3553f69 --- /dev/null +++ b/test/common/geometry/test_0d2d_intersection.cc @@ -0,0 +1,231 @@ +#include <config.h> + +#include <iostream> +#include <algorithm> +#include <functional> +#include <type_traits> + +#include <dune/common/classname.hh> +#include <dune/common/exceptions.hh> +#include <dune/common/fvector.hh> +#include <dune/geometry/type.hh> +#include <dune/geometry/multilineargeometry.hh> + +#include <dumux/common/math.hh> +#include <dumux/common/geometry/intersectspointgeometry.hh> + +#ifndef DOXYGEN +template<class Geometry> +bool testIntersection(const Geometry& geo, + const typename Geometry::GlobalCoordinate& p, + bool foundExpected, bool verbose) +{ + bool found = Dumux::intersectsPointGeometry(p, geo); + if (!found && foundExpected) + { + std::cerr << " Failed detecting intersection of " << geo.type(); + for (int i = 0; i < geo.corners(); ++i) + std::cerr << " (" << geo.corner(i) << ")"; + std::cerr << " with point: " << p << std::endl; + } + else if (found && !foundExpected) + { + std::cerr << " Found false positive: intersection of " << geo.type(); + for (int i = 0; i < geo.corners(); ++i) + std::cerr << " (" << geo.corner(i) << ")"; + std::cerr << " with point: " << p << std::endl; + } + if (verbose) + { + if (found && foundExpected) + std::cout << " Found intersection with " << p << std::endl; + else if (!found && !foundExpected) + std::cout << " No intersection with " << p << std::endl; + } + return (found == foundExpected); +} + +template<int dimworld, class Transformation> +void runIntersectionTest(std::vector<bool>& returns, const Transformation& transform, bool verbose) +{ + using ctype = typename std::decay_t<decltype(transform({0.0}))>::value_type; + using Geo = Dune::MultiLinearGeometry<ctype, 2, dimworld>; + using Points = std::vector<typename Geo::GlobalCoordinate>; + + // test triangle-point intersections + if (verbose) std::cout << "\n -- Test triangle-point intersections" << std::endl; + + if constexpr (dimworld == 2) + { + auto cornersTri = Points ({{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}); + std::transform(cornersTri.begin(), cornersTri.end(), cornersTri.begin(), + [&transform](const auto& p) { return transform(p); }); + auto triangle = Geo(Dune::GeometryTypes::triangle, cornersTri); + + for (const auto& corner : cornersTri) + returns.push_back(testIntersection(triangle, corner, true, verbose)); + + returns.push_back(testIntersection(triangle, transform({0.25, 0.25}), true, verbose)); + returns.push_back(testIntersection(triangle, transform({0.5, 0.5}), true, verbose)); + returns.push_back(testIntersection(triangle, transform({0.0, 0.5}), true, verbose)); + returns.push_back(testIntersection(triangle, transform({1.01, 0.0}), false, verbose)); + returns.push_back(testIntersection(triangle, transform({0.5, 0.51}), false, verbose)); + returns.push_back(testIntersection(triangle, transform({0.0, -0.01}), false, verbose)); + } + + if constexpr (dimworld == 3) + { + auto cornersTri = Points ({{0.0, 0.0, 2.0}, {1.0, 0.0, 2.0}, {0.0, 1.0, 2.0}}); + std::transform(cornersTri.begin(), cornersTri.end(), cornersTri.begin(), + [&transform](const auto& p) { return transform(p); }); + auto triangle = Geo(Dune::GeometryTypes::triangle, cornersTri); + + for (const auto& corner : cornersTri) + returns.push_back(testIntersection(triangle, corner, true, verbose)); + + returns.push_back(testIntersection(triangle, transform({0.25, 0.25, 2.0}), true, verbose)); + returns.push_back(testIntersection(triangle, transform({0.5, 0.5, 2.0}), true, verbose)); + returns.push_back(testIntersection(triangle, transform({0.0, 0.5, 2.0}), true, verbose)); + returns.push_back(testIntersection(triangle, transform({1.01, 0.0, 2.0}), false, verbose)); + returns.push_back(testIntersection(triangle, transform({0.5, 0.51, 2.0}), false, verbose)); + returns.push_back(testIntersection(triangle, transform({0.0, -0.01, 2.0}), false, verbose)); + } + + // test quadrilateral-point intersections + if (verbose) std::cout << "\n -- Test quadrilateral-point intersections" << std::endl; + + if constexpr (dimworld == 2) + { + auto cornersQuad = Points ({{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}); + std::transform(cornersQuad.begin(), cornersQuad.end(), cornersQuad.begin(), + [&transform](const auto& p) { return transform(p); }); + auto quadrilateral = Geo(Dune::GeometryTypes::quadrilateral, cornersQuad); + + for (const auto& corner : cornersQuad) + returns.push_back(testIntersection(quadrilateral, corner, true, verbose)); + + returns.push_back(testIntersection(quadrilateral, transform({0.5, 0.5}), true, verbose)); + returns.push_back(testIntersection(quadrilateral, transform({0.5, 0.0}), true, verbose)); + returns.push_back(testIntersection(quadrilateral, transform({0.5, 1.0}), true, verbose)); + returns.push_back(testIntersection(quadrilateral, transform({1.01, 1.0}), false, verbose)); + returns.push_back(testIntersection(quadrilateral, transform({0.5, 1.01}), false, verbose)); + returns.push_back(testIntersection(quadrilateral, transform({0.0, -0.01}), false, verbose)); + } + + if constexpr (dimworld == 3) + { + auto cornersQuad = Points ({{0.0, 0.0, 2.0}, {1.0, 0.0, 2.0}, {0.0, 1.0, 2.0}, {1.0, 1.0, 2.0}}); + std::transform(cornersQuad.begin(), cornersQuad.end(), cornersQuad.begin(), + [&transform](const auto& p) { return transform(p); }); + auto quadrilateral = Geo(Dune::GeometryTypes::quadrilateral, cornersQuad); + + for (const auto& corner : cornersQuad) + returns.push_back(testIntersection(quadrilateral, corner, true, verbose)); + + returns.push_back(testIntersection(quadrilateral, transform({0.5, 0.5, 2.0}), true, verbose)); + returns.push_back(testIntersection(quadrilateral, transform({0.5, 0.0, 2.0}), true, verbose)); + returns.push_back(testIntersection(quadrilateral, transform({0.5, 1.0, 2.0}), true, verbose)); + returns.push_back(testIntersection(quadrilateral, transform({1.01, 1.0, 2.0}), false, verbose)); + returns.push_back(testIntersection(quadrilateral, transform({0.5, 1.01, 2.0}), false, verbose)); + returns.push_back(testIntersection(quadrilateral, transform({0.0, -0.01, 2.0}), false, verbose)); + } +} + +template<class ctype> +auto create2DTransformation(const ctype scale, + const Dune::FieldVector<ctype, 2>& translate, + const ctype rotationAngle) +{ + std::cout << "Intersection test with transformation:" + << " ctype: " << Dune::className<ctype>() + << ", scaling: " << scale + << ", translation: " << translate + << ", rotationAngle: " << rotationAngle << std::endl; + // Rotation of a vector in two dimensions + // See rotation matrix (2d) at https://en.wikipedia.org/wiki/Rotation_matrix + const ctype sinAngle = std::sin(rotationAngle); + const ctype cosAngle = std::cos(rotationAngle); + return [=](Dune::FieldVector<ctype, 2> p){ + p *= scale; + p.axpy(scale, translate); + auto tp = p; + tp[0] = p[0]*cosAngle-p[1]*sinAngle; + tp[1] = p[0]*sinAngle+p[1]*cosAngle; + return tp; + }; +} + +template<class ctype> +auto create3DTransformation(const ctype scale, + const Dune::FieldVector<ctype, 3>& translate, + const Dune::FieldVector<ctype, 3>& rotationAxis, + const ctype rotationAngle) +{ + std::cout << "Intersection test with transformation:" + << " ctype: " << Dune::className<ctype>() + << ", scaling: " << scale + << ", translation: " << translate + << ", rotationAxis: " << rotationAxis + << ", rotationAngle: " << rotationAngle << std::endl; + // Rotation of a vector in three dimensions + // See Rodrigues' rotation formular at https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula + const ctype sinAngle = std::sin(rotationAngle); + const ctype cosAngle = std::cos(rotationAngle); + return [=](Dune::FieldVector<ctype, 3> p){ + p *= scale; + p.axpy(scale, translate); + auto tp = p; + tp *= cosAngle; + tp.axpy(sinAngle, Dumux::crossProduct({rotationAxis}, p)); + return tp.axpy((1.0-cosAngle)*(rotationAxis*p), rotationAxis); + }; +} + +#endif + +int main (int argc, char *argv[]) try +{ + // collect returns to determine exit code + std::vector<bool> returns; + constexpr bool verbose = false; + + { + constexpr int dimworld = 2; + using Vec = Dune::FieldVector<double, dimworld>; + + std::cout << "Test for point intersection with 2d geometries in 2d corddim" << std::endl; + for (const double scaling : {1.0, 1e3, 1e12, 1e-12}) + for (const auto& translation : {0.0, 1.0}) + for (const double angle : {0.0, 0.2*M_PI, 0.5*M_PI, 0.567576567*M_PI, M_PI}) + runIntersectionTest<dimworld>(returns, create2DTransformation<double>(scaling, Vec(translation), angle), verbose); + } + + { + constexpr int dimworld = 3; + using Vec = Dune::FieldVector<double, dimworld>; + + std::cout << "Test for point intersection with 2d geometries in 3d corddim" << std::endl; + for (const double scaling : {1.0, 1e3, 1e12, 1e-12}) + for (const auto& translation : {0.0, 1.0}) + for (const double angle : {0.0, 0.2*M_PI, 0.5*M_PI, 0.567576567*M_PI, M_PI}) + for (const auto& rotAxis : {Vec(std::sqrt(3.0)/3.0), Vec({std::sqrt(2.0)/2.0, std::sqrt(2.0)/2.0, 0.0})}) + runIntersectionTest<dimworld>(returns, create3DTransformation<double>(scaling, Vec(translation), rotAxis, angle), verbose); + } + + // determine the exit code + if (std::any_of(returns.begin(), returns.end(), std::logical_not<bool>{})) + return 1; + + std::cout << "\n++++++++++++++++++++++\n" + << "All tests passed!" + << "\n++++++++++++++++++++++" << std::endl; + + return 0; +} +// ////////////////////////////////// +// Error handler +// ///////////////////////////////// +catch (const Dune::Exception& e) { + std::cout << e << std::endl; + return 1; +}