diff --git a/test/common/geometry/test_0d2d_intersection.cc b/test/common/geometry/test_0d2d_intersection.cc
index 25e5308fc1edeaaa5115340f69b9578bc3553f69..8c521e60065fd2036c7bafd27de3baff616343a9 100644
--- a/test/common/geometry/test_0d2d_intersection.cc
+++ b/test/common/geometry/test_0d2d_intersection.cc
@@ -12,204 +12,107 @@
 #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);
-}
+#include "transformation.hh"
+#include "test_intersection.hh"
+
+namespace Dumux {
 
 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>;
+    using Points2D = std::vector<Dune::FieldVector<ctype, 2>>;
+    using PointsDimWorld = std::vector<Dune::FieldVector<ctype, dimworld>>;
 
     // 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));
-    }
+    auto cornersTri2D = Points2D ({{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}});
+    auto cornersTri = PointsDimWorld(3);
+    std::transform(cornersTri2D.begin(), cornersTri2D.end(), cornersTri.begin(),
+                   [&](const auto& p) { return transform(p); });
+    auto triangle = Geo(Dune::GeometryTypes::triangle, cornersTri);
 
-    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));
-    }
+    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));
 
     // 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));
-    }
+    auto cornersQuad2D = Points2D ({{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}});
+    auto cornersQuad = PointsDimWorld(4);
+    std::transform(cornersQuad2D.begin(), cornersQuad2D.end(), cornersQuad.begin(),
+                   [&](const auto& p) { return transform(p); });
+    auto quadrilateral = Geo(Dune::GeometryTypes::quadrilateral, cornersQuad);
 
-    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));
-    }
-}
+    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));
 
-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)
+template<class Transformation>
+void run3DIntersectionTest(std::vector<bool>& returns, const Transformation& transform, bool verbose)
 {
-    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);
+    // augment 2d vector by a third coordinate
+    using Point = std::decay_t<decltype(transform({0.0, 0.0, 0.0}))>;
+    const auto transform2D3D = [&](Dune::FieldVector<typename Point::value_type, 2> p2D){
+        return transform({p2D[0], p2D[1], 2.0});
     };
+
+    runIntersectionTest<3>(returns, transform2D3D, verbose);
 }
 
-#endif
+template<class Transformation>
+void run2DIntersectionTest(std::vector<bool>& returns, const Transformation& transform, bool verbose)
+{ runIntersectionTest<2>(returns, transform, verbose); }
+
+} // end namespace Dumux
 
 int main (int argc, char *argv[]) try
 {
+    using namespace Dumux;
+
     // collect returns to determine exit code
     std::vector<bool> returns;
     constexpr bool verbose = false;
 
     {
-        constexpr int dimworld = 2;
-        using Vec = Dune::FieldVector<double, dimworld>;
+        using Vec = Dune::FieldVector<double, 2>;
 
         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);
+                    run2DIntersectionTest(returns,
+                        make2DTransformation<double>(scaling, Vec(translation), angle, true), verbose);
     }
 
     {
-        constexpr int dimworld = 3;
-        using Vec = Dune::FieldVector<double, dimworld>;
+        using Vec = Dune::FieldVector<double, 3>;
 
         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);
+                        run3DIntersectionTest(returns,
+                            make3DTransformation<double>(scaling, Vec(translation), rotAxis, angle, true), verbose);
     }
 
     // determine the exit code
diff --git a/test/common/geometry/test_0d3d_intersection.cc b/test/common/geometry/test_0d3d_intersection.cc
index 16c48998884fa06518e290ec3e4f0529f345fc52..505de29f9f3f885e337fd3ed9ca6044fd01a0502 100644
--- a/test/common/geometry/test_0d3d_intersection.cc
+++ b/test/common/geometry/test_0d3d_intersection.cc
@@ -12,38 +12,11 @@
 #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);
-}
+#include "transformation.hh"
+#include "test_intersection.hh"
+
+namespace Dumux {
 
 template<class Transformation>
 void runIntersectionTest(std::vector<bool>& returns, const Transformation& transform, bool verbose)
@@ -57,7 +30,7 @@ void runIntersectionTest(std::vector<bool>& returns, const Transformation& trans
 
     auto cornersTet = Points({{0.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}});
     std::transform(cornersTet.begin(), cornersTet.end(), cornersTet.begin(),
-                   [&transform](const auto& p) { return transform(p); });
+                   [&](const auto& p) { return transform(p); });
     const auto tetrahedron = Geo(Dune::GeometryTypes::tetrahedron, cornersTet);
 
     for (const auto& corner : cornersTet)
@@ -74,7 +47,7 @@ void runIntersectionTest(std::vector<bool>& returns, const Transformation& trans
     auto cornersHex = Points({{0.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {1.0, 1.0, 0.0},
                               {0.0, 0.0, 1.0}, {1.0, 0.0, 1.0}, {0.0, 1.0, 1.0}, {1.0, 1.0, 1.0}});
     std::transform(cornersHex.begin(), cornersHex.end(), cornersHex.begin(),
-                   [&transform](const auto& p) { return transform(p); });
+                   [&](const auto& p) { return transform(p); });
     auto hexahedron = Geo(Dune::GeometryTypes::hexahedron, cornersHex);
 
     for (const auto& corner : cornersHex)
@@ -91,7 +64,7 @@ void runIntersectionTest(std::vector<bool>& returns, const Transformation& trans
     auto cornersPyramid = Points({{0.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, {0.0, 1.0, 0.0},
                                   {1.0, 1.0, 0.0}, {0.5, 0.5, 1.0}});
     std::transform(cornersPyramid.begin(), cornersPyramid.end(), cornersPyramid.begin(),
-                   [&transform](const auto& p) { return transform(p); });
+                   [&](const auto& p) { return transform(p); });
     auto pyramid = Geo(Dune::GeometryTypes::pyramid, cornersPyramid);
 
     for (const auto& corner : cornersPyramid)
@@ -111,7 +84,7 @@ void runIntersectionTest(std::vector<bool>& returns, const Transformation& trans
     auto cornersPrism = Points({{0.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, {0.0, 1.0, 0.0},
                                 {0.0, 0.0, 1.0}, {1.0, 0.0, 1.0}, {0.0, 1.0, 1.0}});
     std::transform(cornersPrism.begin(), cornersPrism.end(), cornersPrism.begin(),
-                   [&transform](const auto& p) { return transform(p); });
+                   [&](const auto& p) { return transform(p); });
     auto prism = Geo(Dune::GeometryTypes::prism, cornersPrism);
 
     for (const auto& corner : cornersPrism)
@@ -124,34 +97,12 @@ void runIntersectionTest(std::vector<bool>& returns, const Transformation& trans
     returns.push_back(testIntersection(prism, transform({0.25, 0.25, 1.0001}), false, verbose));
 }
 
-template<class ctype>
-auto createTransformation(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;
-    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
+} // end namespace Dumux
 
 int main (int argc, char *argv[]) try
 {
+    using namespace Dumux;
+
     // collect returns to determine exit code
     std::vector<bool> returns;
     constexpr bool verbose = false;
@@ -162,7 +113,7 @@ int main (int argc, char *argv[]) try
             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(returns,
-                        createTransformation<double>(scaling, Vec(translation), rotAxis, angle), verbose);
+                        make3DTransformation<double>(scaling, Vec(translation), rotAxis, angle, true), verbose);
 
     // determine the exit code
     if (std::any_of(returns.begin(), returns.end(), std::logical_not<bool>{}))
diff --git a/test/common/geometry/test_intersection.hh b/test/common/geometry/test_intersection.hh
new file mode 100644
index 0000000000000000000000000000000000000000..98fc4a29c7e9cac7c93adc9bdc24d963fdb28f6d
--- /dev/null
+++ b/test/common/geometry/test_intersection.hh
@@ -0,0 +1,66 @@
+/*****************************************************************************
+ *   See the file COPYING for full copying permissions.                      *
+ *                                                                           *
+ *   This program is free software: you can redistribute it and/or modify    *
+ *   it under the terms of the GNU General Public License as published by    *
+ *   the Free Software Foundation, either version 3 of the License, or       *
+ *   (at your option) any later version.                                     *
+ *                                                                           *
+ *   This program is distributed in the hope that it will be useful,         *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
+ *   GNU General Public License for more details.                            *
+ *                                                                           *
+ *   You should have received a copy of the GNU General Public License       *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.   *
+ *****************************************************************************/
+/*!
+ * \file
+ * \ingroup Common
+ * \brief Intersection test helpers
+ */
+#ifndef DUMUX_TEST_INTERSECTION_HH
+#define DUMUX_TEST_INTERSECTION_HH
+
+#include <dumux/common/geometry/intersectspointgeometry.hh>
+
+namespace Dumux {
+
+/*!
+ * \file
+ * \ingroup Common
+ * \brief Test intersection overload for point and geometry
+ */
+template<class Geometry>
+bool testIntersection(const Geometry& geo,
+                      const typename Geometry::GlobalCoordinate& p,
+                      bool foundExpected, bool verbose)
+{
+    const bool found = 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);
+}
+
+} // end namespace Dumux
+
+#endif
diff --git a/test/common/geometry/transformation.hh b/test/common/geometry/transformation.hh
new file mode 100644
index 0000000000000000000000000000000000000000..a72957687fef34de229bbdbd9f16841f07a48647
--- /dev/null
+++ b/test/common/geometry/transformation.hh
@@ -0,0 +1,123 @@
+/*****************************************************************************
+ *   See the file COPYING for full copying permissions.                      *
+ *                                                                           *
+ *   This program is free software: you can redistribute it and/or modify    *
+ *   it under the terms of the GNU General Public License as published by    *
+ *   the Free Software Foundation, either version 3 of the License, or       *
+ *   (at your option) any later version.                                     *
+ *                                                                           *
+ *   This program is distributed in the hope that it will be useful,         *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
+ *   GNU General Public License for more details.                            *
+ *                                                                           *
+ *   You should have received a copy of the GNU General Public License       *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.   *
+ *****************************************************************************/
+/*!
+ * \file
+ * \ingroup Common
+ * \brief Create a transformation function (translation + rotation)
+ */
+#ifndef DUMUX_TEST_GEOMETRY_TRANSFORMATION_HH
+#define DUMUX_TEST_GEOMETRY_TRANSFORMATION_HH
+
+#include <iostream>
+
+#include <dune/common/classname.hh>
+#include <dune/common/fvector.hh>
+
+#include <dumux/common/math.hh>
+
+namespace Dumux {
+
+/*!
+ * \ingroup Common
+ * \brief Create a transformation function (translation) in 1D
+ */
+template<class ctype>
+auto make1DTransformation(const ctype scale,
+                          const Dune::FieldVector<ctype, 1>& translate,
+                          bool verbose = true)
+{
+    if (verbose)
+        std::cout << "Created 1D transformation with"
+                  << " ctype: " << Dune::className<ctype>()
+                  << ", scaling: " << scale
+                  << ", translation: " << translate << std::endl;
+
+    return [=](Dune::FieldVector<ctype, 1> p){
+        p *= scale;
+        p.axpy(scale, translate);
+        auto tp = p;
+        return p;
+    };
+}
+
+/*!
+ * \ingroup Common
+ * \brief Create a transformation function (translation + rotation) in 2D
+ * \note https://en.wikipedia.org/wiki/Rotation_matrix
+ */
+template<class ctype>
+auto make2DTransformation(const ctype scale,
+                          const Dune::FieldVector<ctype, 2>& translate,
+                          const ctype rotationAngle,
+                          bool verbose = true)
+{
+    if (verbose)
+        std::cout << "Created 2D transformation with"
+                  << " ctype: " << Dune::className<ctype>()
+                  << ", scaling: " << scale
+                  << ", translation: " << translate
+                  << ", rotationAngle: " << rotationAngle << std::endl;
+
+    using std::sin; using std::cos;
+    const ctype sinAngle = sin(rotationAngle);
+    const ctype cosAngle = 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;
+    };
+}
+
+/*!
+ * \ingroup Common
+ * \brief Create a transformation function (translation + rotation) in 3D
+ * \note https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula
+ */
+template<class ctype>
+auto make3DTransformation(const ctype scale,
+                          const Dune::FieldVector<ctype, 3>& translate,
+                          const Dune::FieldVector<ctype, 3>& rotationAxis,
+                          const ctype rotationAngle,
+                          bool verbose = true)
+{
+    if (verbose)
+        std::cout << "Created transformation with"
+                  << " ctype: " << Dune::className<ctype>()
+                  << ", scaling: " << scale
+                  << ", translation: " << translate
+                  << ", rotationAxis: " << rotationAxis
+                  << ", rotationAngle: " << rotationAngle << std::endl;
+
+    using std::sin; using std::cos;
+    const ctype sinAngle = sin(rotationAngle);
+    const ctype cosAngle = 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);
+    };
+}
+
+} // end namespace Dumux
+
+#endif