diff --git a/dumux/geometry/intersectionentityset.hh b/dumux/geometry/intersectionentityset.hh
index 7152297dec2cde2d309547f02517e2f91d85bb51..6f328df4b57bef3556ccdb6b278f836f0b25720c 100644
--- a/dumux/geometry/intersectionentityset.hh
+++ b/dumux/geometry/intersectionentityset.hh
@@ -32,105 +32,123 @@
 #include <dumux/geometry/boundingboxtree.hh>
 #include <dumux/geometry/intersectingentities.hh>
 
-namespace Dumux {
+namespace Dumux::Detail::Intersections {
 
 /*!
- * \ingroup Geometry
- * \brief A class representing the intersection entities two geometric entity sets
+ * \brief A class representing an intersection entity
  */
 template<class DomainEntitySet, class TargetEntitySet>
-class IntersectionEntitySet
+class IntersectionEntity
 {
-    using DomainTree = BoundingBoxTree<DomainEntitySet>;
-    using TargetTree = BoundingBoxTree<TargetEntitySet>;
-
+    static constexpr int dimDomain = DomainEntitySet::Entity::Geometry::mydimension;
+    static constexpr int dimTarget = TargetEntitySet::Entity::Geometry::mydimension;
     using ctype = typename Dune::PromotionTraits<typename DomainEntitySet::ctype, typename TargetEntitySet::ctype>::PromotedType;
 
     static constexpr int dimWorld = DomainEntitySet::dimensionworld;
     static_assert(dimWorld == int(TargetEntitySet::dimensionworld), "Entity sets must have the same world dimension");
 
+    static constexpr int dimIs = std::min(dimDomain, dimTarget);
+
     using GlobalPosition = Dune::FieldVector<ctype, dimWorld>;
+    using Geometry = Dune::AffineGeometry<ctype, dimIs, dimWorld>; // geometries are always simplices
 
-    static constexpr int dimDomain = DomainEntitySet::Entity::Geometry::mydimension;
-    static constexpr int dimTarget = TargetEntitySet::Entity::Geometry::mydimension;
-    static constexpr bool isMixedDimensional = dimDomain != dimTarget;
+    // we can only have multiple neighbors in the mixeddimensional case and then only for the side with the largest dimension
+    using IndexStorage = std::pair<std::conditional_t<dimDomain <= dimTarget, Dune::ReservedVector<std::size_t, 1>, std::vector<std::size_t>>,
+                                std::conditional_t<dimTarget <= dimDomain, Dune::ReservedVector<std::size_t, 1>, std::vector<std::size_t>>>;
 
-    /*!
-     * \brief A class representing an intersection entity
-     */
-    class IntersectionEntity
-    {
-        static constexpr int dimIs = std::min(dimDomain, dimTarget);
-        using Geometry = Dune::AffineGeometry<ctype, dimIs, dimWorld>; // geometries are always simplices
+    static constexpr auto domainIdx = Dune::index_constant<0>{};
+    static constexpr auto targetIdx = Dune::index_constant<1>{};
 
-        // we can only have multiple neighbors in the mixeddimensional case and then only for the side with the largest dimension
-        using IndexStorage = std::pair<std::conditional_t<dimDomain <= dimTarget, Dune::ReservedVector<std::size_t, 1>, std::vector<std::size_t>>,
-                                       std::conditional_t<dimTarget <= dimDomain, Dune::ReservedVector<std::size_t, 1>, std::vector<std::size_t>>>;
-
-        static constexpr auto domainIdx = Dune::index_constant<0>{};
-        static constexpr auto targetIdx = Dune::index_constant<1>{};
+public:
+    IntersectionEntity(const DomainEntitySet& domainEntitySet, const TargetEntitySet& targetEntitySet)
+    : domainEntitySet_(domainEntitySet)
+    , targetEntitySet_(targetEntitySet)
+    {}
 
-    public:
-        IntersectionEntity(const DomainTree& domainTree, const TargetTree& targetTree)
-        : domainTree_(domainTree)
-        , targetTree_(targetTree)
-        {}
+    //! set the intersection geometry corners
+    void setCorners(const std::vector<GlobalPosition>& corners)
+    {
+        corners_ = corners;
+        assert(corners.size() == dimIs + 1); // Only simplex intersections are allowed
+    }
 
-        //! set the intersection geometry corners
-        void setCorners(const std::vector<GlobalPosition>& corners)
+    //! add a pair of neighbor elements
+    void addNeighbors(std::size_t domain, std::size_t target)
+    {
+        if (numDomainNeighbors() == 0 && numTargetNeighbors() == 0)
         {
-            corners_ = corners;
-            assert(corners.size() == dimIs + 1); // Only simplex intersections are allowed
+            std::get<domainIdx>(neighbors_).push_back(domain);
+            std::get<targetIdx>(neighbors_).push_back(target);
         }
+        else if (dimDomain > dimTarget)
+            std::get<domainIdx>(neighbors_).push_back(domain);
 
-        //! add a pair of neighbor elements
-        void addNeighbors(std::size_t domain, std::size_t target)
-        {
-            if (numDomainNeighbors() == 0 && numTargetNeighbors() == 0)
-            {
-                std::get<domainIdx>(neighbors_).push_back(domain);
-                std::get<targetIdx>(neighbors_).push_back(target);
-            }
-            else if (dimDomain > dimTarget)
-                std::get<domainIdx>(neighbors_).push_back(domain);
+        else if (dimTarget > dimDomain)
+            std::get<targetIdx>(neighbors_).push_back(target);
 
-            else if (dimTarget > dimDomain)
-                std::get<targetIdx>(neighbors_).push_back(target);
+        else
+            DUNE_THROW(Dune::InvalidStateException, "Cannot add more than one neighbor per side for equidimensional intersection!");
+    }
 
-            else
-                DUNE_THROW(Dune::InvalidStateException, "Cannot add more than one neighbor per side for equidimensional intersection!");
-        }
+    //! get the intersection geometry
+    Geometry geometry() const
+    { return Geometry(Dune::GeometryTypes::simplex(dimIs), corners_); }
+
+    //! get the number of domain neighbors of this intersection
+    std::size_t numDomainNeighbors() const
+    { return std::get<domainIdx>(neighbors_).size(); }
+
+    //! get the number of target neighbors of this intersection
+    std::size_t numTargetNeighbors() const
+    { return std::get<targetIdx>(neighbors_).size(); }
+
+    //! get the nth domain neighbor entity
+    typename DomainEntitySet::Entity domainEntity(unsigned int n = 0) const
+    { return domainEntitySet_.entity(std::get<domainIdx>(neighbors_)[n]); }
+
+    //! get the nth target neighbor entity
+    typename TargetEntitySet::Entity targetEntity(unsigned int n = 0) const
+    { return targetEntitySet_.entity(std::get<targetIdx>(neighbors_)[n]); }
 
-        //! get the intersection geometry
-        Geometry geometry() const
-        { return Geometry(Dune::GeometryTypes::simplex(dimIs), corners_); }
+private:
+    IndexStorage neighbors_;
+    std::vector<GlobalPosition> corners_;
 
-        //! get the number of domain neighbors of this intersection
-        std::size_t numDomainNeighbors() const
-        { return std::get<domainIdx>(neighbors_).size(); }
+    const DomainEntitySet& domainEntitySet_;
+    const TargetEntitySet& targetEntitySet_;
+};
 
-        //! get the number of target neighbors of this intersection
-        std::size_t numTargetNeighbors() const
-        { return std::get<targetIdx>(neighbors_).size(); }
+} // end namespace Dumux::Detail::Intersections
 
-        //! get the nth domain neighbor entity
-        typename DomainEntitySet::Entity domainEntity(unsigned int n = 0) const
-        { return domainTree_.entitySet().entity(std::get<domainIdx>(neighbors_)[n]); }
+namespace Dumux {
 
-        //! get the nth target neighbor entity
-        typename TargetEntitySet::Entity targetEntity(unsigned int n = 0) const
-        { return targetTree_.entitySet().entity(std::get<targetIdx>(neighbors_)[n]); }
+/*!
+ * \ingroup Geometry
+ * \brief A class representing the intersection entities two geometric entity sets
+ */
+template<class DomainEntitySet, class TargetEntitySet>
+class IntersectionEntitySet
+{
+    using DomainTree = BoundingBoxTree<DomainEntitySet>;
+    using TargetTree = BoundingBoxTree<TargetEntitySet>;
 
-    private:
-        IndexStorage neighbors_;
-        std::vector<GlobalPosition> corners_;
+    using ctype = typename Dune::PromotionTraits<typename DomainEntitySet::ctype, typename TargetEntitySet::ctype>::PromotedType;
 
-        const DomainTree& domainTree_;
-        const TargetTree& targetTree_;
-    };
+    static constexpr int dimWorld = DomainEntitySet::dimensionworld;
+    static_assert(dimWorld == int(TargetEntitySet::dimensionworld), "Entity sets must have the same world dimension");
 
+    using GlobalPosition = Dune::FieldVector<ctype, dimWorld>;
+
+    static constexpr int dimDomain = DomainEntitySet::Entity::Geometry::mydimension;
+    static constexpr int dimTarget = TargetEntitySet::Entity::Geometry::mydimension;
+    static constexpr bool isMixedDimensional = dimDomain != dimTarget;
+
+    using IntersectionEntity = Dumux::Detail::Intersections::IntersectionEntity<DomainEntitySet, TargetEntitySet>;
     using Intersections = std::vector<IntersectionEntity>;
 
+    using DomainGeometry = typename DomainEntitySet::Entity::Geometry;
+    using TargetGeometry = typename TargetEntitySet::Entity::Geometry;
+
 public:
     //! make intersection entity type available
     using Entity = IntersectionEntity;
@@ -147,9 +165,26 @@ public:
      */
     void build(std::shared_ptr<const DomainEntitySet> domainSet, std::shared_ptr<const TargetEntitySet> targetSet)
     {
-        domainTree_ = std::make_shared<DomainTree>(domainSet);
-        targetTree_ = std::make_shared<TargetTree>(targetSet);
-        build(*domainTree_, *targetTree_);
+        // specialized algorithm if the target geometry is only a single entity
+        if (targetSet->size() == 1)
+        {
+            domainTree_ = std::make_shared<DomainTree>(domainSet);
+            targetEntitySet_ = targetSet;
+            build_(*domainTree_, targetEntitySet_->entity(0).geometry());
+        }
+        // specialized algorithm if the domain geometry is only a single entity
+        else if (domainSet->size() == 1)
+        {
+            domainEntitySet_ = domainSet;
+            targetTree_ = std::make_shared<TargetTree>(targetSet);
+            build_(domainEntitySet_->entity(0).geometry(), *targetTree_);
+        }
+        else
+        {
+            domainTree_ = std::make_shared<DomainTree>(domainSet);
+            targetTree_ = std::make_shared<TargetTree>(targetSet);
+            build(*domainTree_, *targetTree_);
+        }
     }
 
     /*!
@@ -171,9 +206,62 @@ public:
     {
         Dune::Timer timer;
 
-        // compute raw intersections
         const auto rawIntersections = intersectingEntities(domainTree, targetTree);
+        build_(domainTree.entitySet(), targetTree.entitySet(), rawIntersections);
+
+        std::cout << "Computed " << size() << " intersection entities in " << timer.elapsed() << std::endl;
+    }
+
+    //! return begin iterator to intersection container
+    typename Intersections::const_iterator ibegin() const
+    { return intersections_.begin(); }
+
+    //! return end iterator to intersection container
+    typename Intersections::const_iterator iend() const
+    { return intersections_.end(); }
+
+    //! the number of intersections
+    std::size_t size() const
+    { return intersections_.size(); }
+
+    /*!
+     * \brief Range generator to iterate with range-based for loops over all intersections
+     *        as follows: for (const auto& is : intersections(glue)) { ... }
+     * \note free function
+     */
+    friend Dune::IteratorRange<EntityIterator> intersections(const IntersectionEntitySet& set)
+    { return {set.ibegin(), set.iend()}; }
+
+private:
+    void build_(const DomainTree& domainTree, const TargetGeometry& targetGeometry)
+    {
+        Dune::Timer timer;
+
+        const auto rawIntersections = intersectingEntities(targetGeometry, domainTree);
+        // because in intersectingEntities geometry always comes first (domain set)
+        // but here we want the geometry to be the second (target set), we need to flip the index sets
+        // of the resulting intersections
+        const bool flipNeighborIndices = true;
+        build_(domainTree.entitySet(), *targetEntitySet_, rawIntersections, flipNeighborIndices);
+
+        std::cout << "Computed " << size() << " intersection entities in " << timer.elapsed() << std::endl;
+    }
+
+    void build_(const DomainGeometry& domainGeometry, const TargetTree& targetTree)
+    {
+        Dune::Timer timer;
+
+        const auto rawIntersections = intersectingEntities(domainGeometry, targetTree);
+        build_(*domainEntitySet_, targetTree.entitySet(), rawIntersections);
+
+        std::cout << "Computed " << size() << " intersection entities in " << timer.elapsed() << std::endl;
+    }
 
+    template<class RawIntersections>
+    void build_(const DomainEntitySet& domainEntitySet, const TargetEntitySet& targetEntitySet,
+                const RawIntersections& rawIntersections,
+                bool flipNeighborIndices = false)
+    {
         // create a map to check whether intersection geometries were already inserted
         // Note that this can only occur if the grids have different dimensionality.
         // If this is the case, we keep track of the intersections using the indices of the lower-
@@ -182,8 +270,7 @@ public:
         std::vector<std::vector<std::size_t>> intersectionIndex;
         if constexpr (isMixedDimensional)
         {
-            const auto numLowDimEntities = dimTarget < dimDomain ? targetTree.entitySet().size()
-                                                                 : domainTree.entitySet().size();
+            const auto numLowDimEntities = dimTarget < dimDomain ? targetEntitySet.size() : domainEntitySet.size();
             intersectionMap.resize(numLowDimEntities);
             intersectionIndex.resize(numLowDimEntities);
         }
@@ -202,7 +289,7 @@ public:
             // In this case we only add new neighbor information as the geometry is identical.
             if constexpr (isMixedDimensional)
             {
-                const auto lowDimNeighborIdx = getLowDimNeighborIdx_(rawIntersection);
+                const auto lowDimNeighborIdx = getLowDimNeighborIndex_(rawIntersection, flipNeighborIndices);
                 for (int i = 0; i < intersectionMap[lowDimNeighborIdx].size(); ++i)
                 {
                     if (rawIntersection.cornersMatch(intersectionMap[lowDimNeighborIdx][i]))
@@ -210,7 +297,7 @@ public:
                         add = false;
                         // only add the pair of neighbors using the insertionIndex
                         auto idx = intersectionIndex[lowDimNeighborIdx][i];
-                        intersections_[idx].addNeighbors(rawIntersection.first(), rawIntersection.second());
+                        intersections_[idx].addNeighbors(domainNeighborIndex_(rawIntersection, flipNeighborIndices), targetNeighborIndex_(rawIntersection, flipNeighborIndices));
                         break;
                     }
                 }
@@ -221,56 +308,45 @@ public:
                 // maybe add to the map
                 if constexpr (isMixedDimensional)
                 {
-                    intersectionMap[getLowDimNeighborIdx_(rawIntersection)].push_back(rawIntersection.corners());
-                    intersectionIndex[getLowDimNeighborIdx_(rawIntersection)].push_back(intersections_.size());
+                    const auto lowDimNeighborIdx = getLowDimNeighborIndex_(rawIntersection, flipNeighborIndices);
+                    intersectionMap[lowDimNeighborIdx].push_back(rawIntersection.corners());
+                    intersectionIndex[lowDimNeighborIdx].push_back(intersections_.size());
                 }
 
                 // add new intersection and add the neighbors
-                intersections_.emplace_back(domainTree, targetTree);
+                intersections_.emplace_back(domainEntitySet, targetEntitySet);
                 intersections_.back().setCorners(rawIntersection.corners());
-                intersections_.back().addNeighbors(rawIntersection.first(), rawIntersection.second());
+                intersections_.back().addNeighbors(domainNeighborIndex_(rawIntersection, flipNeighborIndices), targetNeighborIndex_(rawIntersection, flipNeighborIndices));
             }
         }
 
         intersections_.shrink_to_fit();
-        std::cout << "Computed " << size() << " intersection entities in " << timer.elapsed() << std::endl;
     }
 
-    //! return begin iterator to intersection container
-    typename Intersections::const_iterator ibegin() const
-    { return intersections_.begin(); }
-
-    //! return end iterator to intersection container
-    typename Intersections::const_iterator iend() const
-    { return intersections_.end(); }
-
-    //! the number of intersections
-    std::size_t size() const
-    { return intersections_.size(); }
-
-    /*!
-     * \brief Range generator to iterate with range-based for loops over all intersections
-     *        as follows: for (const auto& is : intersections(glue)) { ... }
-     * \note free function
-     */
-    friend Dune::IteratorRange<EntityIterator> intersections(const IntersectionEntitySet& set)
-    { return {set.ibegin(), set.iend()}; }
-
-private:
     template<class RawIntersection,
              bool enable = isMixedDimensional, std::enable_if_t<enable, int> = 0>
-    auto getLowDimNeighborIdx_(const RawIntersection& is)
+    auto getLowDimNeighborIndex_(const RawIntersection& is, bool flipNeighborIndices)
     {
         if constexpr (dimTarget < dimDomain)
-            return is.second();
+            return targetNeighborIndex_(is, flipNeighborIndices);
         else
-            return is.first();
+            return domainNeighborIndex_(is, flipNeighborIndices);
     }
 
+    template<class RawIntersection>
+    auto domainNeighborIndex_(const RawIntersection& is, bool flipNeighborIndices)
+    { return flipNeighborIndices ? is.second() : is.first(); }
+
+    template<class RawIntersection>
+    auto targetNeighborIndex_(const RawIntersection& is, bool flipNeighborIndices)
+    { return flipNeighborIndices ? is.first() : is.second(); }
+
     Intersections intersections_;
 
     std::shared_ptr<const DomainTree> domainTree_;
     std::shared_ptr<const TargetTree> targetTree_;
+    std::shared_ptr<const DomainEntitySet> domainEntitySet_;
+    std::shared_ptr<const TargetEntitySet> targetEntitySet_;
 };
 
 } // end namespace Dumux