diff --git a/CHANGELOG.md b/CHANGELOG.md index d2383d3664d98652eacad4fcf3b2dcd408c2aa37..f7e12d67471c9afdeb35f602c84243b4792c4348 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ Differences Between DuMu<sup>x</sup> 3.3 and DuMu<sup>x</sup> 3.2 ### Deleted classes/files, property names, constants/enums: - Everything that has been deprecated before release 3.2 has been removed. +- All of the geometry headers previously saved in `dumux/common/geometry` have been relocated to `dumux/geometry`. + The headers in `dumux/common/geometry` are deprecated and will be removed in 3.3. The geometry tests have been moved from `test/common/geometry` + and `test/common/boundingboxtree` to `test/geometry`. ### Other noteworthy changes: - after release 3.2, DuMu<sup>x</sup> releases earlier than 2.12 will no longer be automatically tested or supported. diff --git a/dumux/CMakeLists.txt b/dumux/CMakeLists.txt index 4e865412d8a2e1453310874040348c86257e1295..819d019ad5f37d819bd128854c2892abba2684c9 100644 --- a/dumux/CMakeLists.txt +++ b/dumux/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory(discretization) add_subdirectory(flux) add_subdirectory(freeflow) add_subdirectory(geomechanics) +add_subdirectory(geometry) add_subdirectory(io) add_subdirectory(linear) add_subdirectory(material) diff --git a/dumux/common/geometry/boundingboxtree.hh b/dumux/common/geometry/boundingboxtree.hh index 3541867287b4d2425bafdce860e2e313daf5d201..9ca148086ac58187cbb96f0f75dca404da2cc74c 100644 --- a/dumux/common/geometry/boundingboxtree.hh +++ b/dumux/common/geometry/boundingboxtree.hh @@ -18,399 +18,15 @@ * \file * \ingroup Geometry * \brief An axis-aligned bounding box volume hierarchy for dune grids - * - * Dumux implementation of an AABB tree - * Inspired by the AABB tree implementation in DOLFIN by Anders Logg which has the - * following license info: DOLFIN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * DEPRECATED will be removed once this header is removed */ -#ifndef DUMUX_BOUNDINGBOXTREE_HH -#define DUMUX_BOUNDINGBOXTREE_HH +#ifndef DUMUX_COMMON_GEOMETRY_BOUNDINGBOXTREE_HH +#define DUMUX_COMMON_GEOMETRY_BOUNDINGBOXTREE_HH -#include <vector> -#include <array> -#include <algorithm> -#include <memory> -#include <numeric> -#include <type_traits> -#include <iostream> +#warning "This header is deprecated and will be removed after release 3.3. Please use dumux/geometry/boundingboxtree.hh" -#include <dune/common/promotiontraits.hh> -#include <dune/common/timer.hh> -#include <dune/common/fvector.hh> - -namespace Dumux { - -/*! - * \ingroup Geometry - * \brief An axis-aligned bounding box volume tree implementation - * - * The class constructs a hierarchical structure of bounding box volumes around - * grid entities. This class can be used to efficiently compute intersections - * between a grid and other geometrical object. It only implements the intersection - * of two of such bounding box trees, so that two independent grids can be intersected. - * \tparam GeometricEntitySet has the following requirements - * * export dimensionworld, ctype - * * a size() member function returning the number of entities - * * begin() and end() member function returning at least forward iterators to entities - * * an index() method returning a consecutive index given an entity - * * an entity() method returning an entity given the consecutive index - * * entities have the following requirements: - * * a member function geometry() returning a geometry with the member functions - * * corner() and corners() returning global coordinates and number of corners - */ -template <class GeometricEntitySet> -class BoundingBoxTree -{ - enum { dimworld = GeometricEntitySet::dimensionworld }; - using ctype = typename GeometricEntitySet::ctype; - - /*! - * \brief Bounding box node data structure - * leaf nodes are indicated by setting child0 to - * the node itself and child1 to the index of the entity in the bounding box. - */ - struct BoundingBoxNode - { - std::size_t child0; - std::size_t child1; - }; - -public: - //! the type of entity set this tree was built with - using EntitySet = GeometricEntitySet; - - //! Default Constructor - BoundingBoxTree() = default; - - //! Constructor with gridView - BoundingBoxTree(std::shared_ptr<const GeometricEntitySet> set) - { build(set); } - - //! Build up bounding box tree for a grid with leafGridView - void build(std::shared_ptr<const GeometricEntitySet> set) - { - // set the pointer to the entity set - entitySet_ = set; - - // clear all internal data - boundingBoxNodes_.clear(); - boundingBoxCoordinates_.clear(); - - // start the timer - Dune::Timer timer; - - // Create bounding boxes for all elements - const auto numLeaves = set->size(); - - // reserve enough space for the nodes and the coordinates - const auto numNodes = 2*numLeaves - 1; - boundingBoxNodes_.reserve(numNodes); - boundingBoxCoordinates_.reserve(numNodes*2*dimworld); - - // create a vector for leaf boxes (min and max for all dims) - std::vector<ctype> leafBoxes(2*dimworld*numLeaves); - - for (const auto& geometricEntity : *set) - computeEntityBoundingBox_(leafBoxes.data() + 2*dimworld*set->index(geometricEntity), geometricEntity); - - // create the leaf partition, the set of available indices (to be sorted) - std::vector<std::size_t> leafPartition(numLeaves); - std::iota(leafPartition.begin(), leafPartition.end(), 0); - - // Recursively build the bounding box tree - build_(leafBoxes, leafPartition.begin(), leafPartition.end()); - - // We are done, log output - std::cout << "Computed bounding box tree with " << numBoundingBoxes() - << " nodes for " << numLeaves << " grid entites in " - << timer.stop() << " seconds." << std::endl; - } - - //! the entity set this tree was built with - const EntitySet& entitySet() const - { return *entitySet_; } - - ///////////////////////////////////////////////////// - //! Interface to be used by other bounding box trees - ///////////////////////////////////////////////////// - - //! Get an existing bounding box for a given node - const BoundingBoxNode& getBoundingBoxNode(std::size_t nodeIdx) const - { return boundingBoxNodes_[nodeIdx]; } - - //! Get an existing bounding box for a given node - const ctype* getBoundingBoxCoordinates(std::size_t nodeIdx) const - { return boundingBoxCoordinates_.data() + 2*dimworld*nodeIdx; } - - //! Get the number of bounding boxes currently in the tree - std::size_t numBoundingBoxes() const - { return boundingBoxNodes_.size(); } - - //! Check whether a bounding box node is a leaf node - //! Leaf nodes have itself as child0 - bool isLeaf(const BoundingBoxNode& node, std::size_t nodeIdx) const - { return node.child0 == nodeIdx; } - -private: - - //! vector of bounding box nodes - std::vector<BoundingBoxNode> boundingBoxNodes_; - - //! vector of bounding box coordinates - std::vector<ctype> boundingBoxCoordinates_; - - //! a pointer to the entity set - std::shared_ptr<const EntitySet> entitySet_; - - //! Compute the bounding box of a grid entity - template <class Entity> - void computeEntityBoundingBox_(ctype* b, const Entity& entity) const - { - // get the bounding box coordinates - ctype* xMin = b; - ctype* xMax = b + dimworld; - - // get mesh entity data - auto geometry = entity.geometry(); - - // Get coordinates of first vertex - auto corner = geometry.corner(0); - for (std::size_t dimIdx = 0; dimIdx < dimworld; ++dimIdx) - xMin[dimIdx] = xMax[dimIdx] = corner[dimIdx]; - - // Compute the min and max over the remaining vertices - for (std::size_t cornerIdx = 1; cornerIdx < geometry.corners(); ++cornerIdx) - { - corner = geometry.corner(cornerIdx); - for (std::size_t dimIdx = 0; dimIdx < dimworld; ++dimIdx) - { - using std::max; - using std::min; - xMin[dimIdx] = min(xMin[dimIdx], corner[dimIdx]); - xMax[dimIdx] = max(xMax[dimIdx], corner[dimIdx]); - } - } - } - - //! Build bounding box tree for all entities recursively - std::size_t build_(const std::vector<ctype>& leafBoxes, - const std::vector<std::size_t>::iterator& begin, - const std::vector<std::size_t>::iterator& end) - { - assert(begin < end); - - // If we reached the end of the recursion, i.e. only a leaf box is left - if (end - begin == 1) - { - // Get the bounding box coordinates for the leaf - const std::size_t leafNodeIdx = *begin; - const auto beginCoords = leafBoxes.begin() + 2*dimworld*leafNodeIdx; - const auto endCoords = beginCoords + 2*dimworld; - - // Store the data in the bounding box - // leaf nodes are indicated by setting child0 to - // the node itself and child1 to the index of the entity in the bounding box. - return addBoundingBox_(BoundingBoxNode{numBoundingBoxes(), leafNodeIdx}, beginCoords, endCoords); - } - - // Compute the bounding box of all bounding boxes in the range [begin, end] - const auto bCoords = computeBBoxOfBBoxes_(leafBoxes, begin, end); - - // sort bounding boxes along the longest axis - const auto axis = computeLongestAxis_(bCoords); - - // nth_element sorts the range to make sure that middle points to the coordinate median in axis direction - // this is the most expensive part of the algorithm - auto middle = begin + (end - begin)/2; - std::nth_element(begin, middle, end, [&leafBoxes, axis](std::size_t i, std::size_t j) - { - const ctype* bi = leafBoxes.data() + 2*dimworld*i; - const ctype* bj = leafBoxes.data() + 2*dimworld*j; - return bi[axis] + bi[axis + dimworld] < bj[axis] + bj[axis + dimworld]; - }); - - // split the bounding boxes into two at the middle iterator and call build recursively, each - // call resulting in a new node of this bounding box, i.e. the root will be added at the end of the process. - return addBoundingBox_(BoundingBoxNode{build_(leafBoxes, begin, middle), build_(leafBoxes, middle, end)}, - bCoords.begin(), bCoords.end()); - } - - //! Add a new bounding box to the tree - template <class Iterator> - std::size_t addBoundingBox_(BoundingBoxNode&& node, - const Iterator& coordBegin, - const Iterator& coordEnd) - { - // Add the bounding box - boundingBoxNodes_.emplace_back(node); - - // Add the bounding box's coordinates - boundingBoxCoordinates_.insert(boundingBoxCoordinates_.end(), coordBegin, coordEnd); - - // return the index of the new node - return boundingBoxNodes_.size() - 1; - } - - //! Compute the bounding box of a vector of bounding boxes - std::array<ctype, 2*dimworld> - computeBBoxOfBBoxes_(const std::vector<ctype>& leafBoxes, - const std::vector<std::size_t>::iterator& begin, - const std::vector<std::size_t>::iterator& end) - { - std::array<ctype, 2*dimworld> bBoxCoords; - - // copy the iterator and get coordinates of first box - auto it = begin; - const auto* bFirst = leafBoxes.data() + 2*dimworld*(*it); - - for (int coordIdx = 0; coordIdx < 2*dimworld; ++coordIdx) - bBoxCoords[coordIdx] = bFirst[coordIdx]; - - // Compute min and max with the remaining boxes - for (; it != end; ++it) - { - const auto* b = leafBoxes.data() + 2*dimworld*(*it); - for (int coordIdx = 0; coordIdx < dimworld; ++coordIdx) - if (b[coordIdx] < bBoxCoords[coordIdx]) - bBoxCoords[coordIdx] = b[coordIdx]; - for (int coordIdx = dimworld; coordIdx < 2*dimworld; ++coordIdx) - if (b[coordIdx] > bBoxCoords[coordIdx]) - bBoxCoords[coordIdx] = b[coordIdx]; - } - - return bBoxCoords; - } - - //! Compute the bounding box of a vector of bounding boxes - std::size_t computeLongestAxis_(const std::array<ctype, 2*dimworld>& bCoords) - { - std::array<ctype, dimworld> axisLength; - for (int coordIdx = 0; coordIdx < dimworld; ++coordIdx) - axisLength[coordIdx] = bCoords[dimworld + coordIdx] - bCoords[coordIdx]; - - return std::distance(axisLength.begin(), std::max_element(axisLength.begin(), axisLength.end())); - } -}; - -/*! - * \brief Check whether a point is intersectin a bounding box (dimworld == 3) - * \param point The point - * \param b Pointer to bounding box coordinates - */ -template<class ctype, int dimworld, typename std::enable_if_t<dimworld == 3, int> = 0> -inline bool intersectsPointBoundingBox(const Dune::FieldVector<ctype, dimworld>& point, const ctype* b) -{ - static constexpr ctype eps_ = 1.0e-7; - - using std::max; - const auto dx = b[3] - b[0]; - const auto dy = b[4] - b[1]; - const auto dz = b[5] - b[2]; - const ctype eps = max({dx, dy, dz})*eps_; - return (b[0] - eps <= point[0] && point[0] <= b[3] + eps && - b[1] - eps <= point[1] && point[1] <= b[4] + eps && - b[2] - eps <= point[2] && point[2] <= b[5] + eps); -} - -/*! - * \brief Check whether a point is intersectin a bounding box (dimworld == 2) - * \param point The point - * \param b Pointer to bounding box coordinates - */ -template<class ctype, int dimworld, typename std::enable_if_t<dimworld == 2, int> = 0> -inline bool intersectsPointBoundingBox(const Dune::FieldVector<ctype, dimworld>& point, const ctype* b) -{ - static constexpr ctype eps_ = 1.0e-7; - - using std::max; - const auto dx = b[2] - b[0]; - const auto dy = b[3] - b[1]; - const ctype eps = max(dx, dy)*eps_; - return (b[0] - eps <= point[0] && point[0] <= b[2] + eps && - b[1] - eps <= point[1] && point[1] <= b[3] + eps); -} - -/*! - * \brief Check whether a point is intersectin a bounding box (dimworld == 1) - * \param point The point - * \param b Pointer to bounding box coordinates - */ -template<class ctype, int dimworld, typename std::enable_if_t<dimworld == 1, int> = 0> -inline bool intersectsPointBoundingBox(const Dune::FieldVector<ctype, dimworld>& point, const ctype* b) -{ - static constexpr ctype eps_ = 1.0e-7; - const ctype eps0 = eps_*(b[1] - b[0]); - return b[0] - eps0 <= point[0] && point[0] <= b[1] + eps0; -} - -/*! - * \ingroup Geometry - * \brief Determine if a point intersects an axis-aligned bounding box - * The bounding box is given by the lower left corner (min) and the upper right corner (max) - */ -template<class ctype, int dimworld> -inline bool intersectsPointBoundingBox(const Dune::FieldVector<ctype, dimworld>& point, - const Dune::FieldVector<ctype, dimworld>& min, - const Dune::FieldVector<ctype, dimworld>& max) -{ - std::array<ctype, 2*dimworld> bBox; - std::copy(min.begin(), min.end(), bBox.begin()); - std::copy(max.begin(), max.end(), bBox.begin()+dimworld); - return intersectsPointBoundingBox(point, bBox.data()); -} - -/*! - * \brief Check whether a bounding box is intersecting another bounding box (dimworld == 3) - * \param a Pointer to first bounding box coordinates - * \param b Pointer to second bounding box coordinates - */ -template<int dimworld, class ctypea, class ctypeb, typename std::enable_if_t<dimworld == 3, int> = 0> -inline bool intersectsBoundingBoxBoundingBox(const ctypea* a, const ctypeb* b) -{ - using ctype = typename Dune::PromotionTraits<ctypea, ctypeb>::PromotedType; - static constexpr ctype eps_ = 1.0e-7; - const ctype eps0 = eps_*std::max(b[3]-b[0], a[3]-a[0]); - const ctype eps1 = eps_*std::max(b[4]-b[1], a[4]-a[1]); - const ctype eps2 = eps_*std::max(b[5]-b[2], a[5]-a[2]); - return (b[0] - eps0 <= a[3] && a[0] <= b[3] + eps0 && - b[1] - eps1 <= a[4] && a[1] <= b[4] + eps1 && - b[2] - eps2 <= a[5] && a[2] <= b[5] + eps2); - -} - -/*! - * \brief Check whether a bounding box is intersecting another bounding box (dimworld == 2) - * \param a Pointer to first bounding box coordinates - * \param b Pointer to second bounding box coordinates - */ -template<int dimworld, class ctypea, class ctypeb, typename std::enable_if_t<dimworld == 2, int> = 0> -inline bool intersectsBoundingBoxBoundingBox(const ctypea* a, const ctypeb* b) -{ - using ctype = typename Dune::PromotionTraits<ctypea, ctypeb>::PromotedType; - static constexpr ctype eps_ = 1.0e-7; - const ctype eps0 = eps_*std::max(b[2]-b[0], a[2]-a[0]); - const ctype eps1 = eps_*std::max(b[3]-b[1], a[3]-a[1]); - return (b[0] - eps0 <= a[2] && a[0] <= b[2] + eps0 && - b[1] - eps1 <= a[3] && a[1] <= b[3] + eps1); -} - -/*! - * \brief Check whether a bounding box is intersecting another bounding box (dimworld == 1) - * \param a Pointer to first bounding box coordinates - * \param b Pointer to second bounding box coordinates - */ -template<int dimworld, class ctypea, class ctypeb, typename std::enable_if_t<dimworld == 1, int> = 0> -inline bool intersectsBoundingBoxBoundingBox(const ctypea* a, const ctypeb* b) -{ - using ctype = typename Dune::PromotionTraits<ctypea, ctypeb>::PromotedType; - static constexpr ctype eps_ = 1.0e-7; - const ctype eps0 = eps_*std::max(b[1]-b[0], a[1]-a[0]); - return b[0] - eps0 <= a[1] && a[0] <= b[1] + eps0; -} - -} // end namespace Dumux +// This header, and all other geometry headers have been moved to their own folder. +// Please use the geometry headers in dumux/geometry/, as this will be removed after release 3.3. +#include <dumux/geometry/boundingboxtree.hh> #endif diff --git a/dumux/common/geometry/diameter.hh b/dumux/common/geometry/diameter.hh index 8f4eed920295cb1676755fca486d511e6a3f3aaa..fe4045bec6dd0ead48fcfe6361111dae6247db1b 100644 --- a/dumux/common/geometry/diameter.hh +++ b/dumux/common/geometry/diameter.hh @@ -19,31 +19,15 @@ * \ingroup Geometry * \brief A function to compute a geometry's diameter, i.e. * the longest distance between points of a geometry + * DEPRECATED will be removed once this header is removed */ -#ifndef DUMUX_GEOMETRY_DIAMETER_HH -#define DUMUX_GEOMETRY_DIAMETER_HH +#ifndef DUMUX_COMMON_GEOMETRY_DIAMETER_HH +#define DUMUX_COMMON_GEOMETRY_DIAMETER_HH -#include <algorithm> +#warning "This header is deprecated and will be removed after release 3.3. Please use dumux/geometry/diameter.hh" -namespace Dumux { - -/*! - * \ingroup Geometry - * \brief Computes the longest distance between points of a geometry - * \note Useful e.g. to compute the maximum cell diameter of a grid - */ -template<class Geometry> -typename Geometry::ctype diameter(const Geometry& geo) -{ - using std::max; - typename Geometry::ctype h = 0.0; - for (std::size_t i = 0; i < geo.corners(); ++i) - for (std::size_t j = i + 1; j < geo.corners(); ++j) - h = max(h, (geo.corner(i)-geo.corner(j)).two_norm()); - - return h; -} - -} // end namespace Dumux +// This header, and all other geometry headers have been moved to their own folder. +// Please use the geometry headers in dumux/geometry/, as this will be removed after release 3.3. +#include <dumux/geometry/diameter.hh> #endif diff --git a/dumux/common/geometry/distance.hh b/dumux/common/geometry/distance.hh index 47fccf32ea9057aed73b1a2b5ce77c33783d20b5..e87ee09b163d88dc1b41baa9222422bdf23fdc9b 100644 --- a/dumux/common/geometry/distance.hh +++ b/dumux/common/geometry/distance.hh @@ -18,164 +18,15 @@ * \file * \ingroup Geometry * \brief Helper functions for distance queries + * DEPRECATED will be removed once this header is removed */ -#ifndef DUMUX_GEOMETRY_DISTANCE_HH -#define DUMUX_GEOMETRY_DISTANCE_HH +#ifndef DUMUX_COMMON_GEOMETRY_DISTANCE_HH +#define DUMUX_COMMON_GEOMETRY_DISTANCE_HH -#include <dune/common/fvector.hh> -#include <dune/geometry/quadraturerules.hh> +#warning "This header is deprecated and will be removed after release 3.3. Please use dumux/geometry/distance.hh" -namespace Dumux { - -/*! - * \ingroup Geometry - * \brief Compute the average distance from a point to a geometry by integration - */ -template<class Geometry> -inline typename Geometry::ctype -averageDistancePointGeometry(const typename Geometry::GlobalCoordinate& p, - const Geometry& geometry, - std::size_t integrationOrder = 2) -{ - typename Geometry::ctype avgDist = 0.0; - const auto& quad = Dune::QuadratureRules<typename Geometry::ctype, Geometry::mydimension>::rule(geometry.type(), integrationOrder); - for (const auto& qp : quad) - avgDist += (geometry.global(qp.position())-p).two_norm()*qp.weight()*geometry.integrationElement(qp.position()); - return avgDist/geometry.volume(); -} - -/*! - * \ingroup Geometry - * \brief Compute the distance from a point to a line through the points a and b - */ -template<class Point> -inline typename Point::value_type -distancePointLine(const Point& p, const Point& a, const Point& b) -{ - const auto ab = b - a; - const auto t = (p - a)*ab/ab.two_norm2(); - auto proj = a; - proj.axpy(t, ab); - return (proj - p).two_norm(); -} - -/*! - * \ingroup Geometry - * \brief Compute the distance from a point to a line given by a geometry with two corners - * \note We currently lack the a representation of a line geometry. This convenience function - * assumes a segment geometry (with two corners) is passed which represents a line geometry. - */ -template<class Geometry> -inline typename Geometry::ctype -distancePointLine(const typename Geometry::GlobalCoordinate& p, const Geometry& geometry) -{ - static_assert(Geometry::mydimension == 1, "Geometry has to be a line"); - const auto& a = geometry.corner(0); - const auto& b = geometry.corner(1); - return distancePointLine(p, a, b); -} - -/*! - * \ingroup Geometry - * \brief Compute the distance from a point to the segment connecting the points a and b - */ -template<class Point> -inline typename Point::value_type -distancePointSegment(const Point& p, const Point& a, const Point& b) -{ - const auto ab = b - a; - auto t = (p - a)*ab; - - if (t <= 0.0) - return (a - p).two_norm(); - - const auto lengthSq = ab.two_norm2(); - if (t >= lengthSq) - return (b - p).two_norm(); - - auto proj = a; - proj.axpy(t/lengthSq, ab); - return (proj - p).two_norm(); -} - -/*! - * \ingroup Geometry - * \brief Compute the distance from a point to a given segment geometry - */ -template<class Geometry> -inline typename Geometry::ctype -distancePointSegment(const typename Geometry::GlobalCoordinate& p, const Geometry& geometry) -{ - static_assert(Geometry::mydimension == 1, "Geometry has to be a segment"); - const auto& a = geometry.corner(0); - const auto& b = geometry.corner(1); - return distancePointSegment(p, a, b); -} - -/*! - * \ingroup Geometry - * \brief Compute the shortest distance between two points - */ -template<class ctype, int dimWorld> -inline ctype distance(const Dune::FieldVector<ctype, dimWorld>& a, - const Dune::FieldVector<ctype, dimWorld>& b) -{ return (a-b).two_norm(); } - - - -namespace Detail { - -// helper struct to compute distance between two geometries, specialized below -template<class Geo1, class Geo2, - int dimWorld = Geo1::coorddimension, - int dim1 = Geo1::mydimension, int dim2 = Geo2::mydimension> -struct GeometryDistance -{ - static_assert(Geo1::coorddimension == Geo2::coorddimension, "Geometries have to have the same coordinate dimensions"); - static auto distance(const Geo1& geo1, const Geo2& geo2) - { - DUNE_THROW(Dune::NotImplemented, "Geometry distance computation not implemented for dimworld = " - << dimWorld << ", dim1 = " << dim1 << ", dim2 = " << dim2); - } -}; - -// distance point-point -template<class Geo1, class Geo2, int dimWorld> -struct GeometryDistance<Geo1, Geo2, dimWorld, 0, 0> -{ - static_assert(Geo1::coorddimension == Geo2::coorddimension, "Geometries have to have the same coordinate dimensions"); - static auto distance(const Geo1& geo1, const Geo2& geo2) - { return Dumux::distance(geo1.corner(0), geo2.corner(0)); } -}; - -// distance segment-point -template<class Geo1, class Geo2, int dimWorld> -struct GeometryDistance<Geo1, Geo2, dimWorld, 1, 0> -{ - static_assert(Geo1::coorddimension == Geo2::coorddimension, "Geometries have to have the same coordinate dimensions"); - static auto distance(const Geo1& geo1, const Geo2& geo2) - { return distancePointSegment(geo2.corner(0), geo1); } -}; - -// distance point-segment -template<class Geo1, class Geo2, int dimWorld> -struct GeometryDistance<Geo1, Geo2, dimWorld, 0, 1> -{ - static_assert(Geo1::coorddimension == Geo2::coorddimension, "Geometries have to have the same coordinate dimensions"); - static auto distance(const Geo1& geo1, const Geo2& geo2) - { return distancePointSegment(geo1.corner(0), geo2); } -}; - -} // end namespace Detail - -/*! - * \ingroup Geometry - * \brief Compute the shortest distance between two geometries - */ -template<class Geo1, class Geo2> -inline auto distance(const Geo1& geo1, const Geo2& geo2) -{ return Detail::GeometryDistance<Geo1, Geo2>::distance(geo1, geo2); } - -} // end namespace Dumux +// This header, and all other geometry headers have been moved to their own folder. +// Please use the geometry headers in dumux/geometry/, as this will be removed after release 3.3. +#include <dumux/geometry/distance.hh> #endif diff --git a/dumux/common/geometry/geometricentityset.hh b/dumux/common/geometry/geometricentityset.hh index 012ace5fccdaefdc3531ec55e49cb379a201a6e1..c17166fe0a99e943365e7e42d8d3a98d51f1ff10 100644 --- a/dumux/common/geometry/geometricentityset.hh +++ b/dumux/common/geometry/geometricentityset.hh @@ -18,222 +18,15 @@ * \file * \ingroup Geometry * \brief An interface for a set of geometric entities - * \note This can be used e.g. to contruct a bounding box volume hierarchy of a grid - * It defines the minimum requirement for such a set + * DEPRECATED will be removed once this header is removed */ -#ifndef DUMUX_GEOMETRIC_ENTITY_SET_HH -#define DUMUX_GEOMETRIC_ENTITY_SET_HH +#ifndef DUMUX_COMMON_GEOMETRY_GEOMETRIC_ENTITY_SET_HH +#define DUMUX_COMMON_GEOMETRY_GEOMETRIC_ENTITY_SET_HH -#include <memory> -#include <dune/grid/common/mcmgmapper.hh> -#include <dune/geometry/multilineargeometry.hh> -#include <dumux/common/entitymap.hh> +#warning "This header is deprecated and will be removed after release 3.3. Please use dumux/geometry/geometricentityset.hh" -namespace Dumux { - -/*! - * \ingroup Geometry - * \brief An interface for a set of geometric entities based on a GridView - * \note This can be used e.g. to contruct a bounding box volume hierarchy of a grid - * It defines the minimum requirement for such a set - */ -template <class GridView, int codim = 0, class Mapper = Dune::MultipleCodimMultipleGeomTypeMapper<GridView>> -class GridViewGeometricEntitySet -{ - using EntityMap = Dumux::EntityMap<GridView, codim>; -public: - using Entity = typename GridView::template Codim<codim>::Entity; - - GridViewGeometricEntitySet(const GridView& gridView) - : GridViewGeometricEntitySet(gridView, Mapper(gridView, Dune::mcmgLayout(Dune::Codim<codim>()))) - {} - - GridViewGeometricEntitySet(const GridView& gridView, const Mapper& mapper) - : gridView_(gridView) - , mapper_(mapper) - , entityMap_(std::make_shared<EntityMap>(gridView.grid(), mapper_)) - {} - - GridViewGeometricEntitySet(const GridView& gridView, - const Mapper& mapper, - std::shared_ptr<const EntityMap> entityMap) - : gridView_(gridView) - , mapper_(mapper) - , entityMap_(entityMap) - {} - - /*! - * \brief The world dimension of the entity set - */ - enum { dimensionworld = GridView::dimensionworld }; - - /*! - * \brief the coordinate type - */ - using ctype = typename GridView::ctype; - - /*! - * \brief the number of entities in this set - */ - decltype(auto) size() const - { return gridView_.size(codim); } - - /*! - * \brief begin iterator to enable range-based for iteration - */ - decltype(auto) begin() const - { return entities(gridView_, Dune::Codim<codim>()).begin(); } - - /*! - * \brief end iterator to enable range-based for iteration - */ - decltype(auto) end() const - { return entities(gridView_, Dune::Codim<codim>()).end(); } - - /*! - * \brief get an entities index - */ - std::size_t index(const Entity& e) const - { return mapper_.index(e); } - - /*! - * \brief get an entity from an index - */ - Entity entity(std::size_t index) const - { return (*entityMap_)[index]; } - -private: - GridView gridView_; - Mapper mapper_; - std::shared_ptr<const EntityMap> entityMap_; - -}; - -/*! - * \ingroup Geometry - * \brief An interface for a set of geometric entities - * \note This can be used e.g. to contruct a bounding box volume hierarchy of a grid - * It defines the minimum requirement for such a set - */ -template<class GeoType> -class GeometriesEntitySet -{ - /*! - * \brief Wrapper to turn a geometry into a geometric entity - */ - class EntityWrapper - { - public: - using Geometry = GeoType; - - /*! - * \brief Constructor - */ - EntityWrapper(const Geometry& geo, const std::size_t index) : geo_(geo), index_(index) {} - - /*! - * \brief Constructor - */ - EntityWrapper(Geometry&& geo, const std::size_t index) : geo_(std::move(geo)), index_(index) {} - - /*! - * \brief Returns the geometry - */ - const Geometry& geometry() const - { return geo_; } - - /*! - * \brief Returns the index of the geometry - */ - std::size_t index() const - { return index_; } - - private: - Geometry geo_; - std::size_t index_; - }; - -public: - using Entity = EntityWrapper; - - /*! - * \brief Constructor for initializer_list - */ - GeometriesEntitySet(std::initializer_list<typename Entity::Geometry>&& geometries) - { - std::size_t index = 0; - // note: std::initializer_list::begin() returns const T*, - // thus no moving will be performed and only the copying ctor of - // EntityWrapper can be called - for (auto&& g : geometries) - entities_.emplace_back(g, index++); - } - - /*! - * \brief Constructor for a vector of geometries - */ - GeometriesEntitySet(const std::vector<typename Entity::Geometry>& geometries) - { - std::size_t index = 0; - for (auto&& g : geometries) - entities_.emplace_back(g, index++); - } - - /*! - * \brief Constructor for a vector of geometries - */ - GeometriesEntitySet(std::vector<typename Entity::Geometry>&& geometries) - { - std::size_t index = 0; - for (auto&& g : geometries) - entities_.emplace_back(std::move(g), index++); - } - - /*! - * \brief The world dimension of the entity set - */ - enum { dimensionworld = Entity::Geometry::coorddimension }; - - /*! - * \brief the coordinate type - */ - using ctype = typename Entity::Geometry::ctype; - - /*! - * \brief the number of entities in this set - */ - decltype(auto) size() const - { return entities_.size(); } - - /*! - * \brief begin iterator to enable range-based for iteration - */ - decltype(auto) begin() const - { return entities_.begin(); } - - /*! - * \brief end iterator to enable range-based for iteration - */ - decltype(auto) end() const - { return entities_.end(); } - - /*! - * \brief get an entities index - */ - template<class Entity> - std::size_t index(const Entity& e) const - { return e.index(); } - - /*! - * \brief get an entity from an index - */ - Entity entity(std::size_t index) const - { return entities_[index]; } - -private: - std::vector<Entity> entities_; -}; - -} // end namespace Dumux +// This header, and all other geometry headers have been moved to their own folder. +// Please use the geometry headers in dumux/geometry/, as this will be removed after release 3.3. +#include <dumux/geometry/geometricentityset.hh> #endif diff --git a/dumux/common/geometry/geometryintersection.hh b/dumux/common/geometry/geometryintersection.hh index 31ce8ea999756f07a1d6c52c94d64c7ca3f377c7..97b350cd78e63dd9b24e3d4a0cb24773226c269b 100644 --- a/dumux/common/geometry/geometryintersection.hh +++ b/dumux/common/geometry/geometryintersection.hh @@ -19,1369 +19,15 @@ * \ingroup Geometry * \brief A class for collision detection of two geometries * and computation of intersection corners + * DEPRECATED will be removed once this header is removed */ -#ifndef DUMUX_GEOMETRY_INTERSECTION_HH -#define DUMUX_GEOMETRY_INTERSECTION_HH +#ifndef DUMUX_COMMON_GEOMETRY_INTERSECTION_HH +#define DUMUX_COMMON_GEOMETRY_INTERSECTION_HH -#include <tuple> +#warning "This header is deprecated and will be removed after release 3.3. Please use dumux/geometry/geometryintersection.hh" -#include <dune/common/exceptions.hh> -#include <dune/common/promotiontraits.hh> -#include <dune/geometry/multilineargeometry.hh> +// This header, and all other geometry headers have been moved to their own folder. +// Please use the geometry headers in dumux/geometry/, as this will be removed after release 3.3. +#include <dumux/geometry/geometryintersection.hh> -#include <dumux/common/math.hh> -#include <dumux/common/geometry/intersectspointgeometry.hh> -#include <dumux/common/geometry/grahamconvexhull.hh> -#include <dumux/common/geometry/boundingboxtree.hh> - -namespace Dumux { -namespace IntersectionPolicy { - -//! Policy structure for point-like intersections -template<class ct, int dw> -struct PointPolicy -{ - using ctype = ct; - using Point = Dune::FieldVector<ctype, dw>; - - static constexpr int dimWorld = dw; - static constexpr int dimIntersection = 0; - using Intersection = Point; -}; - -//! Policy structure for segment-like intersections -template<class ct, int dw> -struct SegmentPolicy -{ - using ctype = ct; - using Point = Dune::FieldVector<ctype, dw>; - using Segment = std::array<Point, 2>; - - static constexpr int dimWorld = dw; - static constexpr int dimIntersection = 1; - using Intersection = Segment; -}; - -//! Policy structure for polygon-like intersections -template<class ct, int dw> -struct PolygonPolicy -{ - using ctype = ct; - using Point = Dune::FieldVector<ctype, dw>; - using Polygon = std::vector<Point>; - - static constexpr int dimWorld = dw; - static constexpr int dimIntersection = 2; - using Intersection = Polygon; -}; - -//! Policy structure for polyhedron-like intersections -template<class ct, int dw> -struct PolyhedronPolicy -{ - using ctype = ct; - using Point = Dune::FieldVector<ctype, dw>; - using Polyhedron = std::vector<Point>; - - static constexpr int dimWorld = dw; - static constexpr int dimIntersection = 3; - using Intersection = Polyhedron; -}; - -//! default policy chooser class -template<class Geometry1, class Geometry2> -class DefaultPolicyChooser -{ - static constexpr int dimworld = Geometry1::coorddimension; - static constexpr int isDim = std::min( int(Geometry1::mydimension), int(Geometry2::mydimension) ); - static_assert(dimworld == int(Geometry2::coorddimension), - "Geometries must have the same coordinate dimension!"); - static_assert(int(Geometry1::mydimension) <= 3 && int(Geometry2::mydimension) <= 3, - "Geometries must have dimension 3 or less."); - using ctype = typename Dune::PromotionTraits<typename Geometry1::ctype, typename Geometry2::ctype>::PromotedType; - - using DefaultPolicies = std::tuple<PointPolicy<ctype, dimworld>, - SegmentPolicy<ctype, dimworld>, - PolygonPolicy<ctype, dimworld>, - PolyhedronPolicy<ctype, dimworld>>; -public: - using type = std::tuple_element_t<isDim, DefaultPolicies>; -}; - -//! Helper alias to define the default intersection policy -template<class Geometry1, class Geometry2> -using DefaultPolicy = typename DefaultPolicyChooser<Geometry1, Geometry2>::type; - -} // end namespace IntersectionPolicy - -namespace Detail { - -/*! - * \ingroup Geometry - * \brief Algorithm to find segment-like intersections of a polgon/polyhedron with a - * segment. The result is stored in the form of the local coordinates tfirst - * and tlast on the segment geo1. - * \param geo1 the first geometry - * \param geo2 the second geometry (dimGeo2 < dimGeo1) - * \param baseEps the base epsilon used for floating point comparisons - * \param tfirst stores the local coordinate of beginning of intersection segment - * \param tlast stores the local coordinate of the end of intersection segment - * \param getFacetCornerIndices Function to obtain the facet corner indices on geo1 - * \param computeNormal Function to obtain the normal vector on a facet on geo1 - */ -template< class Geo1, class Geo2, class ctype, - class GetFacetCornerIndices, class ComputeNormalFunction > -bool computeSegmentIntersection(const Geo1& geo1, const Geo2& geo2, ctype baseEps, - ctype& tfirst, ctype& tlast, - const GetFacetCornerIndices& getFacetCornerIndices, - const ComputeNormalFunction& computeNormal) -{ - static_assert(int(Geo2::mydimension) == 1, "Geometry2 must be a segment"); - static_assert(int(Geo1::mydimension) > int(Geo2::mydimension), - "Dimension of Geometry1 must be higher than that of Geometry2"); - - const auto a = geo2.corner(0); - const auto b = geo2.corner(1); - const auto d = b - a; - - // The initial interval is the whole segment. - // Afterwards we start clipping the interval - // at the intersections with the facets of geo1 - tfirst = 0.0; - tlast = 1.0; - - const auto facets = getFacetCornerIndices(geo1); - for (const auto& f : facets) - { - const auto n = computeNormal(f); - - const auto c0 = geo1.corner(f[0]); - const ctype denom = n*d; - const ctype dist = n*(a-c0); - - // use first edge of the facet to scale eps - const auto edge1 = geo1.corner(f[1]) - geo1.corner(f[0]); - const ctype eps = baseEps*edge1.two_norm(); - - // if denominator is zero the segment in parallel to the edge. - // If the distance is positive there is no intersection - using std::abs; - if (abs(denom) < eps) - { - if (dist > eps) - return false; - } - else // not parallel: compute line-line intersection - { - const ctype t = -dist / denom; - // if entering half space cut tfirst if t is larger - using std::signbit; - if (signbit(denom)) - { - if (t > tfirst) - tfirst = t; - } - // if exiting half space cut tlast if t is smaller - else - { - if (t < tlast) - tlast = t; - } - // there is no intersection of the interval is empty - // use unscaled epsilon since t is in local coordinates - if (tfirst > tlast - baseEps) - return false; - } - } - - // If we made it until here an intersection exists. - return true; -} - -} // end namespace Detail - -/*! - * \ingroup Geometry - * \brief A class for geometry collision detection and intersection calculation - * The class can be specialized for combinations of dimworld, dim1, dim2, where - * dimworld is the world dimension embedding a grid of dim1 and a grid of dim2. - */ -template -<class Geometry1, class Geometry2, - class Policy = IntersectionPolicy::DefaultPolicy<Geometry1, Geometry2>, - int dimworld = Geometry1::coorddimension, - int dim1 = Geometry1::mydimension, - int dim2 = Geometry2::mydimension> -class GeometryIntersection -{ - static constexpr int dimWorld = Policy::dimWorld; - -public: - using ctype = typename Policy::ctype; - using Point = typename Policy::Point; - using Intersection = typename Policy::Intersection; - - //! Determine if the two geometries intersect and compute the intersection geometry - static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) - { - static_assert(dimworld == Geometry2::coorddimension, "Can only intersect geometries of same coordinate dimension"); - DUNE_THROW(Dune::NotImplemented, "Geometry intersection detection with intersection computation not implemented for dimworld = " - << dimworld << ", dim1 = " << dim1 << ", dim2 = " << dim2); - } -}; - -/*! - * \ingroup Geometry - * \brief A class for segment--segment intersection in 2d space - */ -template <class Geometry1, class Geometry2, class Policy> -class GeometryIntersection<Geometry1, Geometry2, Policy, 2, 1, 1> -{ - enum { dimworld = 2 }; - enum { dim1 = 1 }; - enum { dim2 = 1 }; - - // base epsilon for floating point comparisons - static constexpr typename Policy::ctype eps_ = 1.5e-7; - -public: - using ctype = typename Policy::ctype; - using Point = typename Policy::Point; - using Intersection = typename Policy::Intersection; - - /*! - * \brief Colliding two segments - * \param geo1/geo2 The geometries to intersect - * \param intersection The intersection point - * \note This overload is used when point-like intersections are seeked - */ - template<class P = Policy, std::enable_if_t<P::dimIntersection == 0, int> = 0> - static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) - { - static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); - - const auto v1 = geo1.corner(1) - geo1.corner(0); - const auto v2 = geo2.corner(1) - geo2.corner(0); - const auto ac = geo2.corner(0) - geo1.corner(0); - - auto n2 = Point({-1.0*v2[1], v2[0]}); - n2 /= n2.two_norm(); - - // compute distance of first corner on geo2 to line1 - const auto dist12 = n2*ac; - - // first check parallel segments - using std::abs; - using std::sqrt; - - const auto v1Norm2 = v1.two_norm2(); - const auto eps = eps_*sqrt(v1Norm2); - const auto eps2 = eps_*v1Norm2; - - const auto sp = n2*v1; - if (abs(sp) < eps) - { - if (abs(dist12) > eps) - return false; - - // point intersection can only be one of the corners - if ( (v1*v2) < 0.0 ) - { - if ( ac.two_norm2() < eps2 ) - { intersection = geo2.corner(0); return true; } - - if ( (geo2.corner(1) - geo1.corner(1)).two_norm2() < eps2 ) - { intersection = geo2.corner(1); return true; } - } - else - { - if ( (geo2.corner(1) - geo1.corner(0)).two_norm2() < eps2 ) - { intersection = geo2.corner(1); return true; } - - if ( (geo2.corner(0) - geo1.corner(1)).two_norm2() < eps2 ) - { intersection = geo2.corner(0); return true; } - } - - // no intersection - return false; - } - - // intersection point on v1 in local coords - const auto t1 = dist12 / sp; - - // check if the local coords are valid - if (t1 < -1.0*eps_ || t1 > 1.0 + eps_) - return false; - - // compute global coordinates - auto isPoint = geo1.global(t1); - - // check if point is in bounding box of v2 - const auto c = geo2.corner(0); - const auto d = geo2.corner(1); - - using std::min; using std::max; - std::array<ctype, 4> bBox({ min(c[0], d[0]), min(c[1], d[1]), max(c[0], d[0]), max(c[1], d[1]) }); - - if ( intersectsPointBoundingBox(isPoint, bBox.data()) ) - { - intersection = std::move(isPoint); - return true; - } - - return false; - } - - /*! - * \brief Colliding two segments - * \param geo1/geo2 The geometries to intersect - * \param intersection Container to store corners of intersection segment - * \note this overload is used when segment-like intersections are seeked - */ - template<class P = Policy, std::enable_if_t<P::dimIntersection == 1, int> = 0> - static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) - { - static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); - - const auto& a = geo1.corner(0); - const auto& b = geo1.corner(1); - const auto ab = b - a; - - const auto& p = geo2.corner(0); - const auto& q = geo2.corner(1); - const auto pq = q - p; - Dune::FieldVector<ctype, dimworld> n2{-pq[1], pq[0]}; - - using std::max; - const auto abNorm2 = ab.two_norm2(); - const auto pqNorm2 = pq.two_norm2(); - const auto eps2 = eps_*max(abNorm2, pqNorm2); - - // non-parallel segments do not intersect in a segment. - using std::abs; - if (abs(n2*ab) > eps2) - return false; - - // check if the segments lie on the same line - const auto ap = p - a; - if (abs(ap*n2) > eps2) - return false; - - // compute scaled local coordinates of corner 1/2 of segment2 on segment1 - auto t1 = ab*ap; - auto t2 = ab*(q - a); - - using std::swap; - if (t1 > t2) - swap(t1, t2); - - using std::clamp; - t1 = clamp(t1, 0.0, abNorm2); - t2 = clamp(t2, 0.0, abNorm2); - - if (abs(t2-t1) < eps2) - return false; - - intersection = Intersection({geo1.global(t1/abNorm2), geo1.global(t2/abNorm2)}); - return true; - } -}; - -/*! - * \ingroup Geometry - * \brief A class for polygon--segment intersection in 2d space - */ -template <class Geometry1, class Geometry2, class Policy> -class GeometryIntersection<Geometry1, Geometry2, Policy, 2, 2, 1> -{ - enum { dimworld = 2 }; - enum { dim1 = 2 }; - enum { dim2 = 1 }; - -public: - using ctype = typename Policy::ctype; - using Point = typename Policy::Point; - using Intersection = typename Policy::Intersection; - -private: - static constexpr ctype eps_ = 1.5e-7; // base epsilon for floating point comparisons - -public: - /*! - * \brief Colliding segment and convex polygon - * \param geo1/geo2 The geometries to intersect - * \param intersection If the geometries collide intersection holds the - * corner points of the intersection object in global coordinates. - * \note This overload is used when segment-like intersections are seeked. - */ - template<class P = Policy, std::enable_if_t<P::dimIntersection == 1, int> = 0> - static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) - { - static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); - - ctype tfirst, tlast; - if (intersect_(geo1, geo2, tfirst, tlast)) - { - // the intersection exists. Export the intersections geometry now: - // s(t) = a + t(b-a) in [tfirst, tlast] - intersection = Intersection({geo2.global(tfirst), geo2.global(tlast)}); - return true; - } - - return false; - } - - /*! - * \brief Colliding segment and convex polygon - * \param geo1/geo2 The geometries to intersect - * \param intersection If the geometries collide intersection holds the - * corner points of the intersection object in global coordinates. - * \note This overload is used when point-like intersections are seeked. - */ - template<class P = Policy, std::enable_if_t<P::dimIntersection == 0, int> = 0> - static bool intersection(const Geometry1& geo1, const Geometry2& geo2, typename P::Intersection& intersection) - { - static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); - - ctype tfirst, tlast; - if (!intersect_(geo1, geo2, tfirst, tlast)) - { - // if start & end point are same, the intersection is a point - using std::abs; - if ( tfirst > tlast - eps_ ) - { - intersection = Intersection(geo2.global(tfirst)); - return true; - } - } - - return false; - } - -private: - /*! - * \brief Obtain local coordinates of start/end point of the intersecting segment. - */ - static bool intersect_(const Geometry1& geo1, const Geometry2& geo2, ctype& tfirst, ctype& tlast) - { - // lambda to obtain the facet corners on geo1 - auto getFacetCorners = [] (const Geometry1& geo1) - { - std::vector< std::array<int, 2> > facetCorners; - switch (geo1.corners()) - { - case 4: // quadrilateral - facetCorners = {{0, 2}, {3, 1}, {1, 0}, {2, 3}}; - break; - case 3: // triangle - facetCorners = {{1, 0}, {0, 2}, {2, 1}}; - break; - default: - DUNE_THROW(Dune::NotImplemented, "Collision of segment and geometry of type " - << geo1.type() << " with "<< geo1.corners() << " corners."); - } - - return facetCorners; - }; - - // lambda to obtain the normal on a facet - const auto center1 = geo1.center(); - auto computeNormal = [&geo1, ¢er1] (const std::array<int, 2>& facetCorners) - { - const auto c0 = geo1.corner(facetCorners[0]); - const auto c1 = geo1.corner(facetCorners[1]); - const auto edge = c1 - c0; - - Dune::FieldVector<ctype, dimworld> n({-1.0*edge[1], edge[0]}); - n /= n.two_norm(); - - // orientation of the element is not known - // make sure normal points outwards of element - if ( n*(center1-c0) > 0.0 ) - n *= -1.0; - - return n; - }; - - return Detail::computeSegmentIntersection(geo1, geo2, eps_, tfirst, tlast, getFacetCorners, computeNormal); - } -}; - -/*! - * \ingroup Geometry - * \brief A class for segment--polygon intersection in 2d space - */ -template <class Geometry1, class Geometry2, class Policy> -class GeometryIntersection<Geometry1, Geometry2, Policy, 2, 1, 2> -: public GeometryIntersection<Geometry2, Geometry1, Policy, 2, 2, 1> -{ - using Base = GeometryIntersection<Geometry2, Geometry1, Policy, 2, 2, 1>; - -public: - /*! - * \brief Colliding segment and convex polygon - * \param geo1/geo2 The geometries to intersect - * \param intersection If the geometries collide intersection holds the - * corner points of the intersection object in global coordinates. - * \note This forwards to the polygon-segment specialization with swapped arguments. - */ - template<class P = Policy> - static bool intersection(const Geometry1& geo1, const Geometry2& geo2, typename Base::Intersection& intersection) - { - return Base::intersection(geo2, geo1, intersection); - } -}; - -/*! - * \ingroup Geometry - * \brief A class for polygon--polygon intersection in 2d space - */ -template <class Geometry1, class Geometry2, class Policy> -class GeometryIntersection<Geometry1, Geometry2, Policy, 2, 2, 2> -{ - enum { dimworld = 2 }; - enum { dim1 = 2 }; - enum { dim2 = 2 }; - -public: - using ctype = typename Policy::ctype; - using Point = typename Policy::Point; - using Intersection = typename Policy::Intersection; - -private: - static constexpr ctype eps_ = 1.5e-7; // base epsilon for floating point comparisons - -public: - /*! - * \brief Colliding two polygons - * \note First we find the vertex candidates for the intersection region as follows: - * Add polygon vertices that are inside the other polygon - * Add intersections of polygon edges - * Remove duplicate points from the list - * Compute the convex hull polygon - * Return a triangulation of that polygon as intersection - * \param geo1/geo2 The geometries to intersect - * \param intersection Container to store the corner points of the polygon (as convex hull) - * \note This overload is used when polygon like intersections are seeked - */ - template<class P = Policy, std::enable_if_t<P::dimIntersection == 2, int> = 0> - static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) - { - static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); - - // the candidate intersection points - std::vector<Point> points; points.reserve(6); - - // add polygon1 corners that are inside polygon2 - for (int i = 0; i < geo1.corners(); ++i) - if (intersectsPointGeometry(geo1.corner(i), geo2)) - points.emplace_back(geo1.corner(i)); - - const auto numPoints1 = points.size(); - if (numPoints1 != geo1.corners()) - { - // add polygon2 corners that are inside polygon1 - for (int i = 0; i < geo2.corners(); ++i) - if (intersectsPointGeometry(geo2.corner(i), geo1)) - points.emplace_back(geo2.corner(i)); - - if (points.empty()) - return false; - - if (points.size() - numPoints1 != geo2.corners()) - { - const auto refElement1 = referenceElement(geo1); - const auto refElement2 = referenceElement(geo2); - - // add intersections of edges - using SegGeometry = Dune::MultiLinearGeometry<ctype, 1, dimworld>; - using PointPolicy = IntersectionPolicy::PointPolicy<ctype, dimworld>; - for (int i = 0; i < refElement1.size(dim1-1); ++i) - { - const auto localEdgeGeom1 = refElement1.template geometry<dim1-1>(i); - const auto edge1 = SegGeometry( Dune::GeometryTypes::line, - std::vector<Point>( {geo1.global(localEdgeGeom1.corner(0)), - geo1.global(localEdgeGeom1.corner(1))} )); - - for (int j = 0; j < refElement2.size(dim2-1); ++j) - { - const auto localEdgeGeom2 = refElement2.template geometry<dim2-1>(j); - const auto edge2 = SegGeometry( Dune::GeometryTypes::line, - std::vector<Point>( {geo2.global(localEdgeGeom2.corner(0)), - geo2.global(localEdgeGeom2.corner(1))} )); - - using EdgeTest = GeometryIntersection<SegGeometry, SegGeometry, PointPolicy>; - typename EdgeTest::Intersection edgeIntersection; - if (EdgeTest::intersection(edge1, edge2, edgeIntersection)) - points.emplace_back(edgeIntersection); - } - } - } - } - - if (points.empty()) - return false; - - // remove duplicates - const auto eps = (geo1.corner(0) - geo1.corner(1)).two_norm()*eps_; - std::sort(points.begin(), points.end(), [&eps](const auto& a, const auto& b) -> bool - { - using std::abs; - return (abs(a[0]-b[0]) > eps ? a[0] < b[0] : a[1] < b[1]); - }); - - auto removeIt = std::unique(points.begin(), points.end(), [&eps](const auto& a, const auto&b) - { - return (b-a).two_norm() < eps; - }); - - points.erase(removeIt, points.end()); - - // return false if we don't have at least three unique points - if (points.size() < 3) - return false; - - // intersection polygon is convex hull of above points - intersection = grahamConvexHull<2>(points); - assert(!intersection.empty()); - return true; - } - - /*! - * \brief Colliding two polygons - * \param geo1/geo2 The geometries to intersect - * \param intersection Container to store the corners of intersection segment - * \note this overload is used when segment-like intersections are seeked - * \todo implement this query - */ - template<class P = Policy, std::enable_if_t<P::dimIntersection == 1, int> = 0> - static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) - { - static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); - DUNE_THROW(Dune::NotImplemented, "Polygon-polygon intersection detection for segment-like intersections"); - } - - /*! - * \brief Colliding two polygons - * \param geo1/geo2 The geometries to intersect - * \param intersection The intersection point - * \note this overload is used when point-like intersections are seeked - * \todo implement this query - */ - template<class P = Policy, std::enable_if_t<P::dimIntersection == 0, int> = 0> - static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) - { - static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); - DUNE_THROW(Dune::NotImplemented, "Polygon-polygon intersection detection for touching points"); - } -}; - -/*! - * \ingroup Geometry - * \brief A class for polyhedron--segment intersection in 3d space - */ -template <class Geometry1, class Geometry2, class Policy> -class GeometryIntersection<Geometry1, Geometry2, Policy, 3, 3, 1> -{ - enum { dimworld = 3 }; - enum { dim1 = 3 }; - enum { dim2 = 1 }; - -public: - using ctype = typename Policy::ctype; - using Point = typename Policy::Point; - using Intersection = typename Policy::Intersection; - -private: - static constexpr ctype eps_ = 1.5e-7; // base epsilon for floating point comparisons - -public: - /*! - * \brief Colliding segment and convex polyhedron - * \note Algorithm based on the one from "Real-Time Collision Detection" by Christer Ericson, - * published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc. - * Basis is the theorem that for any two non-intersecting convex polyhedrons - * a separating plane exists. - * \param geo1/geo2 The geometries to intersect - * \param intersection If the geometries collide intersection holds the corner points of - * the intersection object in global coordinates. - * \note This overload is used when segment-like intersections are seeked. - */ - template<class P = Policy, std::enable_if_t<P::dimIntersection == 1, int> = 0> - static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) - { - static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); - - ctype tfirst, tlast; - if (intersect_(geo1, geo2, tfirst, tlast)) - { - // Intersection exists. Export the intersections geometry now: - // s(t) = a + t(b-a) in [tfirst, tlast] - intersection = Intersection({geo2.global(tfirst), geo2.global(tlast)}); - return true; - } - - return false; - } - - /*! - * \brief Colliding segment and convex polyhedron - * \note Algorithm based on the one from "Real-Time Collision Detection" by Christer Ericson, - * published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc. - * Basis is the theorem that for any two non-intersecting convex polyhedrons - * a separating plane exists. - * \param geo1/geo2 The geometries to intersect - * \param intersection If the geometries collide intersection holds the corner points of - * the intersection object in global coordinates. - * \note This overload is used when point-like intersections are seeked. - */ - template<class P = Policy, std::enable_if_t<P::dimIntersection == 0, int> = 0> - static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) - { - static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); - - ctype tfirst, tlast; - if (intersect_(geo1, geo2, tfirst, tlast)) - { - // if start & end point are same, the intersection is a point - using std::abs; - if ( abs(tfirst - tlast) < eps_ ) - { - intersection = Intersection(geo2.global(tfirst)); - return true; - } - } - - return false; - } - -private: - /*! - * \brief Obtain local coordinates of start/end point of the intersecting segment. - */ - static bool intersect_(const Geometry1& geo1, const Geometry2& geo2, ctype& tfirst, ctype& tlast) - { - // lambda to obtain facet corners on geo1 - auto getFacetCorners = [] (const Geometry1& geo1) - { - std::vector< std::vector<int> > facetCorners; - // sort facet corner so that normal n = (p1-p0)x(p2-p0) always points outwards - switch (geo1.corners()) - { - // todo compute intersection geometries - case 8: // hexahedron - facetCorners = {{2, 0, 6, 4}, {1, 3, 5, 7}, {0, 1, 4, 5}, - {3, 2, 7, 6}, {1, 0, 3, 2}, {4, 5, 6, 7}}; - break; - case 4: // tetrahedron - facetCorners = {{1, 0, 2}, {0, 1, 3}, {0, 3, 2}, {1, 2, 3}}; - break; - default: - DUNE_THROW(Dune::NotImplemented, "Collision of segment and geometry of type " - << geo1.type() << ", "<< geo1.corners() << " corners."); - } - - return facetCorners; - }; - - // lambda to obtain the normal on a facet - auto computeNormal = [&geo1] (const std::vector<int>& facetCorners) - { - const auto v0 = geo1.corner(facetCorners[1]) - geo1.corner(facetCorners[0]); - const auto v1 = geo1.corner(facetCorners[2]) - geo1.corner(facetCorners[0]); - - auto n = crossProduct(v0, v1); - n /= n.two_norm(); - - return n; - }; - - return Detail::computeSegmentIntersection(geo1, geo2, eps_, tfirst, tlast, getFacetCorners, computeNormal); - } -}; - -/*! - * \ingroup Geometry - * \brief A class for segment--polyhedron intersection in 3d space - */ -template <class Geometry1, class Geometry2, class Policy> -class GeometryIntersection<Geometry1, Geometry2, Policy, 3, 1, 3> -: public GeometryIntersection<Geometry2, Geometry1, Policy, 3, 3, 1> -{ - using Base = GeometryIntersection<Geometry2, Geometry1, Policy, 3, 3, 1>; -public: - /*! - * \brief Colliding segment and convex polyhedron - * \param geo1/geo2 The geometries to intersect - * \param intersection If the geometries collide intersection holds the - * corner points of the intersection object in global coordinates. - * \note This forwards to the polyhedron-segment specialization with swapped arguments. - */ - template<class P = Policy> - static bool intersection(const Geometry1& geo1, const Geometry2& geo2, typename Base::Intersection& intersection) - { - return Base::intersection(geo2, geo1, intersection); - } -}; - -/*! - * \ingroup Geometry - * \brief A class for polyhedron--polygon intersection in 3d space - */ -template <class Geometry1, class Geometry2, class Policy> -class GeometryIntersection<Geometry1, Geometry2, Policy, 3, 3, 2> -{ - enum { dimworld = 3 }; - enum { dim1 = 3 }; - enum { dim2 = 2 }; - -public: - using ctype = typename Policy::ctype; - using Point = typename Policy::Point; - using Intersection = typename Policy::Intersection; - -private: - static constexpr ctype eps_ = 1.5e-7; // base epsilon for floating point comparisons - -public: - /*! - * \brief Colliding polygon and convex polyhedron - * \note First we find the vertex candidates for the intersection region as follows: - * Add triangle vertices that are inside the tetrahedron - * Add tetrahedron vertices that are inside the triangle - * Add all intersection points of tetrahedron edges (codim 2) with the triangle (codim 0) (6*1 tests) - * Add all intersection points of triangle edges (codim 1) with tetrahedron faces (codim 1) (3*4 tests) - * Remove duplicate points from the list - * Compute the convex hull polygon - * Return a triangulation of that polygon as intersection - * \param geo1/geo2 The geometries to intersect - * \param intersection Container to store the corner points of the polygon (as convex hull) - * \note This overload is used when polygon like intersections are seeked - */ - template<class P = Policy, std::enable_if_t<P::dimIntersection == 2, int> = 0> - static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) - { - static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); - - // the candidate intersection points - std::vector<Point> points; points.reserve(10); - - // add 3d geometry corners that are inside the 2d geometry - for (int i = 0; i < geo1.corners(); ++i) - if (intersectsPointGeometry(geo1.corner(i), geo2)) - points.emplace_back(geo1.corner(i)); - - // add 2d geometry corners that are inside the 3d geometry - for (int i = 0; i < geo2.corners(); ++i) - if (intersectsPointGeometry(geo2.corner(i), geo1)) - points.emplace_back(geo2.corner(i)); - - // get some geometry types - using PolyhedronFaceGeometry = Dune::MultiLinearGeometry<ctype, 2, dimworld>; - using SegGeometry = Dune::MultiLinearGeometry<ctype, 1, dimworld>; - - const auto refElement1 = referenceElement(geo1); - const auto refElement2 = referenceElement(geo2); - - // intersection policy for point-like intersections (used later) - using PointPolicy = IntersectionPolicy::PointPolicy<ctype, dimworld>; - - // add intersection points of all polyhedron edges (codim dim-1) with the polygon - for (int i = 0; i < refElement1.size(dim1-1); ++i) - { - const auto localEdgeGeom = refElement1.template geometry<dim1-1>(i); - const auto p = geo1.global(localEdgeGeom.corner(0)); - const auto q = geo1.global(localEdgeGeom.corner(1)); - const auto segGeo = SegGeometry(Dune::GeometryTypes::line, std::vector<Point>{p, q}); - - using PolySegTest = GeometryIntersection<Geometry2, SegGeometry, PointPolicy>; - typename PolySegTest::Intersection polySegIntersection; - if (PolySegTest::intersection(geo2, segGeo, polySegIntersection)) - points.emplace_back(polySegIntersection); - } - - // add intersection points of all polygon faces (codim 1) with the polyhedron faces - for (int i = 0; i < refElement1.size(1); ++i) - { - const auto faceGeo = [&]() - { - const auto localFaceGeo = refElement1.template geometry<1>(i); - if (localFaceGeo.corners() == 4) - { - const auto a = geo1.global(localFaceGeo.corner(0)); - const auto b = geo1.global(localFaceGeo.corner(1)); - const auto c = geo1.global(localFaceGeo.corner(2)); - const auto d = geo1.global(localFaceGeo.corner(3)); - - return PolyhedronFaceGeometry(Dune::GeometryTypes::cube(2), std::vector<Point>{a, b, c, d}); - } - else - { - const auto a = geo1.global(localFaceGeo.corner(0)); - const auto b = geo1.global(localFaceGeo.corner(1)); - const auto c = geo1.global(localFaceGeo.corner(2)); - - return PolyhedronFaceGeometry(Dune::GeometryTypes::simplex(2), std::vector<Point>{a, b, c}); - } - }(); - - for (int j = 0; j < refElement2.size(1); ++j) - { - const auto localEdgeGeom = refElement2.template geometry<1>(j); - const auto p = geo2.global(localEdgeGeom.corner(0)); - const auto q = geo2.global(localEdgeGeom.corner(1)); - - const auto segGeo = SegGeometry(Dune::GeometryTypes::line, std::vector<Point>{p, q}); - - using PolySegTest = GeometryIntersection<PolyhedronFaceGeometry, SegGeometry, PointPolicy>; - typename PolySegTest::Intersection polySegIntersection; - if (PolySegTest::intersection(faceGeo, segGeo, polySegIntersection)) - points.emplace_back(polySegIntersection); - } - } - - // return if no intersection points were found - if (points.empty()) return false; - - // remove duplicates - const auto eps = (geo1.corner(0) - geo1.corner(1)).two_norm()*eps_; - std::sort(points.begin(), points.end(), [&eps](const auto& a, const auto& b) -> bool - { - using std::abs; - return (abs(a[0]-b[0]) > eps ? a[0] < b[0] : (abs(a[1]-b[1]) > eps ? a[1] < b[1] : (a[2] < b[2]))); - }); - - auto removeIt = std::unique(points.begin(), points.end(), [&eps](const auto& a, const auto&b) - { - return (b-a).two_norm() < eps; - }); - - points.erase(removeIt, points.end()); - - // return false if we don't have more than three unique points - if (points.size() < 3) return false; - - // intersection polygon is convex hull of above points - intersection = grahamConvexHull<2>(points); - assert(!intersection.empty()); - - return true; - } - - /*! - * \brief Colliding segment and convex polyhedron - * \note First we find the vertex candidates for the intersection region as follows: - * Add triangle vertices that are inside the tetrahedron - * Add tetrahedron vertices that are inside the triangle - * Add all intersection points of tetrahedron edges (codim 2) with the triangle (codim 0) (6*1 tests) - * Add all intersection points of triangle edges (codim 1) with tetrahedron faces (codim 1) (3*4 tests) - * Remove duplicate points from the list - * Compute the convex hull polygon - * Return a triangulation of that polygon as intersection - * \param geo1/geo2 The geometries to intersect - * \param intersection Container to store the intersection result - * \todo implement overloads for segment or point-like intersections - */ - template<class P = Policy, std::enable_if_t<P::dimIntersection != 2, int> = 0> - static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) - { - static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); - DUNE_THROW(Dune::NotImplemented, "Polyhedron-polygon intersection detection only implemented for polygon-like intersections"); - } -}; - -/*! - * \ingroup Geometry - * \brief A class for polygon--polyhedron intersection in 3d space - */ -template <class Geometry1, class Geometry2, class Policy> -class GeometryIntersection<Geometry1, Geometry2, Policy, 3, 2, 3> -: public GeometryIntersection<Geometry2, Geometry1, Policy, 3, 3, 2> -{ - using Base = GeometryIntersection<Geometry2, Geometry1, Policy, 3, 3, 2>; -public: - /*! - * \brief Colliding polygon and convex polyhedron - * \param geo1/geo2 The geometries to intersect - * \param intersection If the geometries collide intersection holds the - * corner points of the intersection object in global coordinates. - * \note This forwards to the polyhedron-polygon specialization with swapped arguments. - */ - template<class P = Policy> - static bool intersection(const Geometry1& geo1, const Geometry2& geo2, typename Base::Intersection& intersection) - { - return Base::intersection(geo2, geo1, intersection); - } -}; - -/*! - * \ingroup Geometry - * \brief A class for polygon--segment intersection in 3d space - */ -template <class Geometry1, class Geometry2, class Policy> -class GeometryIntersection<Geometry1, Geometry2, Policy, 3, 2, 1> -{ - enum { dimworld = 3 }; - enum { dim1 = 2 }; - enum { dim2 = 1 }; - -public: - using ctype = typename Policy::ctype; - using Point = typename Policy::Point; - using Intersection = typename Policy::Intersection; - -private: - static constexpr ctype eps_ = 1.5e-7; // base epsilon for floating point comparisons - -public: - /*! - * \brief Colliding segment and convex polyhedron - * \note Algorithm based on the one from "Real-Time Collision Detection" by Christer Ericson, - * published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc. (Chapter 5.3.6) - * \param geo1/geo2 The geometries to intersect - * \param intersection If the geometries collide, is holds the corner points of - * the intersection object in global coordinates. - * \note This overload is used when point-like intersections are seeked - */ - template<class P = Policy, std::enable_if_t<P::dimIntersection == 1, int> = 0> - static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) - { - static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); - - ctype tfirst, tlast; - if (intersect_(geo1, geo2, tfirst, tlast)) - { - // the intersection exists. Export the intersections geometry now: - // s(t) = a + t(b-a) in [tfirst, tlast] - intersection = Intersection({geo2.global(tfirst), geo2.global(tlast)}); - return true; - } - - return false; - } - - /*! - * \brief Colliding segment and convex polyhedron - * \note Algorithm based on the one from "Real-Time Collision Detection" by Christer Ericson, - * published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc. (Chapter 5.3.6) - * \param geo1/geo2 The geometries to intersect - * \param is If the geometries collide, is holds the corner points of - * the intersection object in global coordinates. - * \note This overload is used when point-like intersections are seeked - */ - template<class P = Policy, std::enable_if_t<P::dimIntersection == 0, int> = 0> - static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& is) - { - static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); - - const auto p = geo2.corner(0); - const auto q = geo2.corner(1); - - const auto a = geo1.corner(0); - const auto b = geo1.corner(1); - const auto c = geo1.corner(2); - - if (geo1.corners() == 3) - return intersect_<Policy>(a, b, c, p, q, is); - - else if (geo1.corners() == 4) - { - Intersection is1, is2; - bool hasSegment1, hasSegment2; - - const auto d = geo1.corner(3); - const bool intersects1 = intersect_<Policy>(a, b, d, p, q, is1, hasSegment1); - const bool intersects2 = intersect_<Policy>(a, d, c, p, q, is2, hasSegment2); - - if (hasSegment1 || hasSegment2) - return false; - - if (intersects1) { is = is1; return true; } - if (intersects2) { is = is2; return true; } - - return false; - } - - else - DUNE_THROW(Dune::NotImplemented, "Collision of segment and geometry of type " - << geo1.type() << ", "<< geo1.corners() << " corners."); - } - -private: - /*! - * \brief Obtain local coordinates of start/end point of the intersecting segment. - */ - template<class P = Policy, std::enable_if_t<P::dimIntersection == 1, int> = 0> - static bool intersect_(const Geometry1& geo1, const Geometry2& geo2, ctype& tfirst, ctype& tlast) - { - // lambda to obtain the facet corners on geo1 - auto getFacetCorners = [] (const Geometry1& geo1) - { - std::vector< std::array<int, 2> > facetCorners; - switch (geo1.corners()) - { - case 4: // quadrilateral - facetCorners = {{0, 2}, {3, 1}, {1, 0}, {2, 3}}; - break; - case 3: // triangle - facetCorners = {{1, 0}, {0, 2}, {2, 1}}; - break; - default: - DUNE_THROW(Dune::NotImplemented, "Collision of segment and geometry of type " - << geo1.type() << " with "<< geo1.corners() << " corners."); - } - - return facetCorners; - }; - - const auto center1 = geo1.center(); - const auto normal1 = crossProduct(geo1.corner(1) - geo1.corner(0), - geo1.corner(2) - geo1.corner(0)); - - // lambda to obtain the normal on a facet - auto computeNormal = [¢er1, &normal1, &geo1] (const std::array<int, 2>& facetCorners) - { - const auto c0 = geo1.corner(facetCorners[0]); - const auto c1 = geo1.corner(facetCorners[1]); - const auto edge = c1 - c0; - - Dune::FieldVector<ctype, dimworld> n = crossProduct(edge, normal1); - n /= n.two_norm(); - - // orientation of the element is not known - // make sure normal points outwards of element - if ( n*(center1-c0) > 0.0 ) - n *= -1.0; - - return n; - }; - - return Detail::computeSegmentIntersection(geo1, geo2, eps_, tfirst, tlast, getFacetCorners, computeNormal); - } - - /*! - * \brief triangle--segment point-like intersection with points as input. - */ - template<class P = Policy, std::enable_if_t<P::dimIntersection == 0, int> = 0> - static bool intersect_(const Point& a, const Point& b, const Point& c, - const Point& p, const Point& q, - Intersection& is) - { - bool hasSegment; - return intersect_(a, b, c, p, q, is, hasSegment); - } - - /*! - * \brief triangle--segment point-like intersection with points as input. Also - * stores if a segment-like intersection was found in the provided boolean. - */ - template<class P = Policy, std::enable_if_t<P::dimIntersection == 0, int> = 0> - static bool intersect_(const Point& a, const Point& b, const Point& c, - const Point& p, const Point& q, - Intersection& is, bool& hasSegmentIs) - { - hasSegmentIs = false; - - const auto ab = b - a; - const auto ac = c - a; - const auto qp = p - q; - - // compute the triangle normal that defines the triangle plane - const auto normal = crossProduct(ab, ac); - - // compute the denominator - // if denom is 0 the segment is parallel and we can return - const auto denom = normal*qp; - const auto abnorm2 = ab.two_norm2(); - const auto eps = eps_*abnorm2*qp.two_norm(); - using std::abs; - if (abs(denom) < eps) - { - const auto pa = a - p; - const auto denom2 = normal*pa; - if (abs(denom2) > eps_*pa.two_norm()*abnorm2) - return false; - - // if we get here, we are in-plane. Check if a - // segment-like intersection with geometry 1 exists. - using SegmentPolicy = typename IntersectionPolicy::SegmentPolicy<ctype, dimworld>; - using Triangle = Dune::AffineGeometry<ctype, 2, dimworld>; - using Segment = Dune::AffineGeometry<ctype, 1, dimworld>; - using SegmentIntersectionAlgorithm = GeometryIntersection<Triangle, Segment, SegmentPolicy>; - using SegmentIntersectionType = typename SegmentIntersectionAlgorithm::Intersection; - SegmentIntersectionType segmentIs; - - Triangle triangle(Dune::GeometryTypes::simplex(2), std::array<Point, 3>({a, b, c})); - Segment segment(Dune::GeometryTypes::simplex(1), std::array<Point, 2>({p, q})); - if (SegmentIntersectionAlgorithm::intersection(triangle, segment, segmentIs)) - { - hasSegmentIs = true; - return false; - } - - // We are in-plane. A point-like - // intersection can only be on the edges - for (const auto& ip : {p, q}) - { - if ( intersectsPointSimplex(ip, a, b) - || intersectsPointSimplex(ip, b, c) - || intersectsPointSimplex(ip, c, a) ) - { - is = ip; - return true; - } - } - - return false; - } - - // compute intersection t value of pq with plane of triangle. - // a segment intersects if and only if 0 <= t <= 1. - const auto ap = p - a; - const auto t = (ap*normal)/denom; - if (t < 0.0 - eps_) return false; - if (t > 1.0 + eps_) return false; - - // compute the barycentric coordinates and check if the intersection point - // is inside the bounds of the triangle - const auto e = crossProduct(qp, ap); - const auto v = (ac*e)/denom; - if (v < -eps_ || v > 1.0 + eps_) return false; - const auto w = -(ab*e)/denom; - if (w < -eps_ || v + w > 1.0 + eps_) return false; - - // Now we are sure there is an intersection points - // Perform delayed division compute the last barycentric coordinate component - const auto u = 1.0 - v - w; - - Point ip(0.0); - ip.axpy(u, a); - ip.axpy(v, b); - ip.axpy(w, c); - is = ip; - return true; - } -}; - -/*! - * \ingroup Geometry - * \brief A class for segment--polygon intersection in 3d space - */ -template <class Geometry1, class Geometry2, class Policy> -class GeometryIntersection<Geometry1, Geometry2, Policy, 3, 1, 2> -: public GeometryIntersection<Geometry2, Geometry1, Policy, 3, 2, 1> -{ - using Base = GeometryIntersection<Geometry2, Geometry1, Policy, 3, 2, 1>; -public: - /*! - * \brief Colliding segment and convex polygon - * \param geo1/geo2 The geometries to intersect - * \param intersection If the geometries collide intersection holds the - * corner points of the intersection object in global coordinates. - * \note This forwards to the polyhedron-polygon specialization with swapped arguments. - */ - template<class P = Policy> - static bool intersection(const Geometry1& geo1, const Geometry2& geo2, typename Base::Intersection& intersection) - { - return Base::intersection(geo2, geo1, intersection); - } -}; - -/*! - * \ingroup Geometry - * \brief A class for segment--segment intersection in 3d space - */ -template <class Geometry1, class Geometry2, class Policy> -class GeometryIntersection<Geometry1, Geometry2, Policy, 3, 1, 1> -{ - enum { dimworld = 3 }; - enum { dim1 = 1 }; - enum { dim2 = 1 }; - -public: - using ctype = typename Policy::ctype; - using Point = typename Policy::Point; - using Intersection = typename Policy::Intersection; - -private: - static constexpr ctype eps_ = 1.5e-7; // base epsilon for floating point comparisons - -public: - /*! - * \brief Colliding two segments - * \param geo1/geo2 The geometries to intersect - * \param intersection Container to store corners of intersection segment - * \note this overload is used when point-like intersections are seeked - * \todo implement this query - */ - template<class P = Policy, std::enable_if_t<P::dimIntersection == 0, int> = 0> - static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) - { - static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); - DUNE_THROW(Dune::NotImplemented, "segment-segment intersection detection for point-like intersections"); - } - - /*! - * \brief Colliding two segments in 3D - * \param geo1/geo2 The geometries to intersect - * \param intersection If the geometries collide, is holds the corner points of - * the intersection object in global coordinates. - * \note This overload is used when segment-like intersections are seeked - */ - template<class P = Policy, std::enable_if_t<P::dimIntersection == 1, int> = 0> - static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) - { - static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); - - const auto& a = geo1.corner(0); - const auto& b = geo1.corner(1); - const auto ab = b-a; - - const auto& p = geo2.corner(0); - const auto& q = geo2.corner(1); - const auto pq = q-p; - - const auto abNorm2 = ab.two_norm2(); - const auto pqNorm2 = pq.two_norm2(); - - using std::max; - const auto eps2 = eps_*max(abNorm2, pqNorm2); - - // if the segment are not parallel there is no segment intersection - using std::abs; - if (crossProduct(ab, pq).two_norm2() > eps2*eps2) - return false; - - const auto ap = (p-a); - const auto aq = (q-a); - - // points have to be colinear - if (crossProduct(ap, aq).two_norm2() > eps2*eps2) - return false; - - // scaled local coordinates - // we save the division for the normalization for now - // and do it in the very end if we find an intersection - auto tp = ap*ab; - auto tq = aq*ab; - - // make sure they are sorted - using std::swap; - if (tp > tq) - swap(tp, tq); - - using std::clamp; - tp = clamp(tp, 0.0, abNorm2); - tq = clamp(tq, 0.0, abNorm2); - - if (abs(tp-tq) < eps2) - return false; - - intersection = Intersection({geo1.global(tp/abNorm2), geo1.global(tq/abNorm2)}); - return true; - } -}; - -} // end namespace Dumux - -# endif +#endif diff --git a/dumux/common/geometry/grahamconvexhull.hh b/dumux/common/geometry/grahamconvexhull.hh index 3ed273d9e8a98772345d5d41ea3b7d5783f89830..b1a3155bfe7b4b89ce44fe2e0d18277b61f5f830 100644 --- a/dumux/common/geometry/grahamconvexhull.hh +++ b/dumux/common/geometry/grahamconvexhull.hh @@ -19,200 +19,15 @@ * \ingroup Geometry * \brief A function to compute the convex hull of a point cloud * and a function to triangulate the polygon area spanned by the convex hull + * DEPRECATED will be removed once this header is removed */ -#ifndef DUMUX_GRAHAM_CONVEX_HULL_HH -#define DUMUX_GRAHAM_CONVEX_HULL_HH +#ifndef DUMUX_COMMON_GEOMETRY_GRAHAM_CONVEX_HULL_HH +#define DUMUX_COMMON_GEOMETRY_GRAHAM_CONVEX_HULL_HH -#include <vector> -#include <array> -#include <algorithm> -#include <iterator> +#warning "This header is deprecated and will be removed after release 3.3. Please use dumux/geometry/grahamconvexhull.hh" -#include <dune/common/exceptions.hh> -#include <dune/common/fvector.hh> +// This header, and all other geometry headers have been moved to their own folder. +// Please use the geometry headers in dumux/geometry/, as this will be removed after release 3.3. +#include <dumux/geometry/grahamconvexhull.hh> -#include <dumux/common/math.hh> -#include <dumux/common/geometry/triangulation.hh> - -namespace Dumux { - -/*! - * \ingroup Geometry - * \brief Returns the orientation of a sequence a-->b-->c in one plane (defined by normal vector) - * \return -1 if a-->b-->c forms a counter-clockwise turn (given the normal vector) - * +1 for a clockwise turn, - * 0 if they are on one line (colinear) - */ -template<class ctype> -int getOrientation(const Dune::FieldVector<ctype, 3>& a, - const Dune::FieldVector<ctype, 3>& b, - const Dune::FieldVector<ctype, 3>& c, - const Dune::FieldVector<ctype, 3>& normal) -{ - const auto d = b-a; - const auto e = c-b; - const auto f = Dumux::crossProduct(d, e); - const auto area = f*normal; - return Dumux::sign(-area); -} - -/*! - * \ingroup Geometry - * \brief Compute the points making up the convex hull around the given set of unordered points - * \note We assume that all points are coplanar and there are no indentical points in the list - * \note This algorithm changes the order of the given points a bit - * as they are unordered anyway this shouldn't matter too much - */ -template<int dim, class ctype, - std::enable_if_t<(dim==2), int> = 0> -std::vector<Dune::FieldVector<ctype, 3>> -grahamConvexHullImpl(std::vector<Dune::FieldVector<ctype, 3>>& points) -{ - using Point = Dune::FieldVector<ctype, 3>; - std::vector<Point> convexHull; - - // return empty convex hull - if (points.size() < 3) - return convexHull; - - // return the points (already just one triangle) - if (points.size() == 3) - return points; - - // try to compute the normal vector of the plane - const auto a = points[1] - points[0]; - auto b = points[2] - points[0]; - auto normal = Dumux::crossProduct(a, b); - - // make sure the normal vector has non-zero length - std::size_t k = 2; - auto norm = normal.two_norm(); - while (norm == 0.0 && k < points.size()-1) - { - b = points[++k]; - normal = Dumux::crossProduct(a, b); - norm = normal.two_norm(); - } - - // if all given points are colinear -> return empty convex hull - if (norm == 0.0) - return convexHull; - - using std::sqrt; - const auto eps = 1e-7*sqrt(norm); - normal /= norm; - - // find the element with the smallest x coordinate (if x is the same, smallest y coordinate, and so on...) - auto minIt = std::min_element(points.begin(), points.end(), [&eps](const auto& a, const auto& b) - { - using std::abs; - return (abs(a[0]-b[0]) > eps ? a[0] < b[0] : (abs(a[1]-b[1]) > eps ? a[1] < b[1] : (a[2] < b[2]))); - }); - - // swap the smallest element to the front - std::iter_swap(minIt, points.begin()); - - // choose the first (min element) as the pivot point - // sort in counter-clockwise order around pivot point - const auto pivot = points[0]; - std::sort(points.begin()+1, points.end(), [&](const auto& a, const auto& b) - { - const int order = getOrientation(pivot, a, b, normal); - if (order == 0) - return (a-pivot).two_norm() < (b-pivot).two_norm(); - else - return (order == -1); - }); - - // push the first three points - convexHull.reserve(50); - convexHull.push_back(points[0]); - convexHull.push_back(points[1]); - convexHull.push_back(points[2]); - - // This is the heart of the algorithm - // pop_back until the last point in the queue forms a counter-clockwise oriented line - // with the next two vertices. Then add these points to the queue. - for (std::size_t i = 3; i < points.size(); ++i) - { - Point p = convexHull.back(); - convexHull.pop_back(); - // keep popping until the orientation a->b->currentp is counter-clockwise - while (getOrientation(convexHull.back(), p, points[i], normal) != -1) - { - // make sure the queue doesn't get empty - if (convexHull.size() == 1) - { - // before we reach i=size-1 there has to be a good candidate - // as not all points are colinear (a non-zero plane normal exists) - assert(i < points.size()-1); - p = points[i++]; - } - else - { - p = convexHull.back(); - convexHull.pop_back(); - } - } - - // add back the last popped point and this point - convexHull.emplace_back(std::move(p)); - convexHull.push_back(points[i]); - } - - return convexHull; -} - -/*! - * \ingroup Geometry - * \brief Compute the points making up the convex hull around the given set of unordered points - * \note This is the specialization for 2d space. Here, we make use of the generic implementation - * for the case of coplanar points in 3d space (a more efficient implementation could be provided). - */ -template<int dim, class ctype, - std::enable_if_t<(dim==2), int> = 0> -std::vector<Dune::FieldVector<ctype, 2>> -grahamConvexHullImpl(const std::vector<Dune::FieldVector<ctype, 2>>& points) -{ - std::vector<Dune::FieldVector<ctype, 3>> points3D; - points3D.reserve(points.size()); - std::transform(points.begin(), points.end(), std::back_inserter(points3D), - [](const auto& p) { return Dune::FieldVector<ctype, 3>({p[0], p[1], 0.0}); }); - - const auto result3D = grahamConvexHullImpl<2>(points3D); - - std::vector<Dune::FieldVector<ctype, 2>> result2D; - result2D.reserve(result3D.size()); - std::transform(result3D.begin(), result3D.end(), std::back_inserter(result2D), - [](const auto& p) { return Dune::FieldVector<ctype, 2>({p[0], p[1]}); }); - - return result2D; -} - -/*! - * \ingroup Geometry - * \brief Compute the points making up the convex hull around the given set of unordered points - * \note We assume that all points are coplanar and there are no indentical points in the list - */ -template<int dim, class ctype, int dimWorld> -std::vector<Dune::FieldVector<ctype, dimWorld>> grahamConvexHull(std::vector<Dune::FieldVector<ctype, dimWorld>>& points) -{ - return grahamConvexHullImpl<dim>(points); -} - -/*! - * \ingroup Geometry - * \brief Compute the points making up the convex hull around the given set of unordered points - * \note We assume that all points are coplanar and there are no indentical points in the list - * \note This is the overload if we are not allowed to write into the given points vector - */ -template<int dim, class ctype, int dimWorld> -std::vector<Dune::FieldVector<ctype, dimWorld>> grahamConvexHull(const std::vector<Dune::FieldVector<ctype, dimWorld>>& points) -{ - auto copyPoints = points; - return grahamConvexHullImpl<dim>(copyPoints); -} - -} // end namespace Dumux - -# endif +#endif diff --git a/dumux/common/geometry/intersectingentities.hh b/dumux/common/geometry/intersectingentities.hh index 15378819faf88a687851de7877fc9414ff4da72f..4d529eab1a94baf82d5b5edfac087330ffaf147e 100644 --- a/dumux/common/geometry/intersectingentities.hh +++ b/dumux/common/geometry/intersectingentities.hh @@ -18,378 +18,15 @@ * \file * \ingroup Geometry * \brief Algorithms that finds which geometric entites intersect + * DEPRECATED will be removed once this header is removed */ -#ifndef DUMUX_INTERSECTING_ENTITIES_HH -#define DUMUX_INTERSECTING_ENTITIES_HH +#ifndef DUMUX_COMMON_GEOMETRY_INTERSECTING_ENTITIES_HH +#define DUMUX_COMMON_GEOMETRY_INTERSECTING_ENTITIES_HH -#include <cmath> -#include <type_traits> -#include <vector> -#include <algorithm> +#warning "This header is deprecated and will be removed after release 3.3. Please use dumux/geometry/intersectingentities.hh" -#include <dune/common/fvector.hh> - -#include <dumux/common/math.hh> -#include <dumux/common/geometry/boundingboxtree.hh> -#include <dumux/common/geometry/intersectspointgeometry.hh> -#include <dumux/common/geometry/geometryintersection.hh> -#include <dumux/common/geometry/triangulation.hh> - -namespace Dumux { - -/*! - * \ingroup Geometry - * \brief An intersection object resulting from the intersection of two primitives in an entity set - * \note this is used as return type for some of the intersectingEntities overloads below - */ -template<int dimworld, class CoordTypeA, class CoordTypeB = CoordTypeA> -class IntersectionInfo -{ -public: - using ctype = typename Dune::PromotionTraits<CoordTypeA, CoordTypeB>::PromotedType; - static constexpr int dimensionworld = dimworld; - using GlobalPosition = Dune::FieldVector<ctype, dimworld>; - - template<class Corners> - explicit IntersectionInfo(std::size_t a, std::size_t b, Corners&& c) - : a_(a) - , b_(b) - , corners_(c.begin(), c.end()) - {} - - //! Get the index of the intersecting entity belonging to this grid - std::size_t first() const - { return a_; } - - //! Get the index of the intersecting entity belonging to the other grid - std::size_t second() const - { return b_; } - - //! Get the corners of the intersection geometry - std::vector<GlobalPosition> corners() const - { return corners_; } - - /*! - * \brief Check if the corners of this intersection match with the given corners - * \note This is useful to check if the intersection geometry of two intersections coincide. - */ - bool cornersMatch(const std::vector<GlobalPosition>& otherCorners) const - { - if (otherCorners.size() != corners_.size()) - return false; - - const auto eps = 1.5e-7*(corners_[1] - corners_[0]).two_norm(); - for (int i = 0; i < corners_.size(); ++i) - if ((corners_[i] - otherCorners[i]).two_norm() > eps) - return false; - - return true; - } - -private: - std::size_t a_, b_; //!< Indices of the intersection elements - std::vector<GlobalPosition> corners_; //!< the corner points of the intersection geometry -}; - -/*! - * \ingroup Geometry - * \brief Compute all intersections between entities and a point - */ -template<class EntitySet, class ctype, int dimworld> -inline std::vector<std::size_t> -intersectingEntities(const Dune::FieldVector<ctype, dimworld>& point, - const BoundingBoxTree<EntitySet>& tree, - bool isCartesianGrid = false) -{ - // Call the recursive find function to find candidates - std::vector<std::size_t> entities; - intersectingEntities(point, tree, tree.numBoundingBoxes() - 1, entities, isCartesianGrid); - return entities; -} - -/*! - * \ingroup Geometry - * \brief Compute intersections with point for all nodes of the bounding box tree recursively - */ -template<class EntitySet, class ctype, int dimworld> -void intersectingEntities(const Dune::FieldVector<ctype, dimworld>& point, - const BoundingBoxTree<EntitySet>& tree, - std::size_t node, - std::vector<std::size_t>& entities, - bool isCartesianGrid = false) -{ - // Get the bounding box for the current node - const auto& bBox = tree.getBoundingBoxNode(node); - - // if the point is not in the bounding box we can stop - if (!intersectsPointBoundingBox(point, tree.getBoundingBoxCoordinates(node))) return; - - // now we know it's inside - // if the box is a leaf do the primitive test. - else if (tree.isLeaf(bBox, node)) - { - const std::size_t entityIdx = bBox.child1; - // for structured cube grids skip the primitive test - if (isCartesianGrid) - entities.push_back(entityIdx); - else - { - const auto geometry = tree.entitySet().entity(entityIdx).geometry(); - // if the primitive is positive it intersects the actual geometry, add the entity to the list - if (intersectsPointGeometry(point, geometry)) - entities.push_back(entityIdx); - } - } - - // No leaf. Check both children nodes. - else - { - intersectingEntities(point, tree, bBox.child0, entities, isCartesianGrid); - intersectingEntities(point, tree, bBox.child1, entities, isCartesianGrid); - } -} - -/*! - * \ingroup Geometry - * \brief Compute all intersections between a geometry and a bounding box tree - */ -template<class Geometry, class EntitySet> -inline std::vector<IntersectionInfo<Geometry::coorddimension, typename Geometry::ctype, typename EntitySet::ctype>> -intersectingEntities(const Geometry& geometry, - const BoundingBoxTree<EntitySet>& tree) -{ - // check if the world dimensions match - static_assert(int(Geometry::coorddimension) == int(EntitySet::dimensionworld), - "Can only intersect geometry and bounding box tree of same world dimension"); - - // Create data structure for return type - std::vector<IntersectionInfo<Geometry::coorddimension, typename Geometry::ctype, typename EntitySet::ctype>> intersections; - using ctype = typename IntersectionInfo<Geometry::coorddimension, typename Geometry::ctype, typename EntitySet::ctype>::ctype; - static constexpr int dimworld = Geometry::coorddimension; - - // compute the bounding box of the given geometry - std::array<ctype, 2*Geometry::coorddimension> bBox; - ctype* xMin = bBox.data(); ctype* xMax = xMin + Geometry::coorddimension; - - // Get coordinates of first vertex - auto corner = geometry.corner(0); - for (std::size_t dimIdx = 0; dimIdx < dimworld; ++dimIdx) - xMin[dimIdx] = xMax[dimIdx] = corner[dimIdx]; - - // Compute the min and max over the remaining vertices - for (std::size_t cornerIdx = 1; cornerIdx < geometry.corners(); ++cornerIdx) - { - corner = geometry.corner(cornerIdx); - for (std::size_t dimIdx = 0; dimIdx < dimworld; ++dimIdx) - { - using std::max; - using std::min; - xMin[dimIdx] = min(xMin[dimIdx], corner[dimIdx]); - xMax[dimIdx] = max(xMax[dimIdx], corner[dimIdx]); - } - } - - // Call the recursive find function to find candidates - intersectingEntities(geometry, tree, - bBox, tree.numBoundingBoxes() - 1, - intersections); - - return intersections; -} - -/*! - * \ingroup Geometry - * \brief Compute intersections with point for all nodes of the bounding box tree recursively - */ -template<class Geometry, class EntitySet> -void intersectingEntities(const Geometry& geometry, - const BoundingBoxTree<EntitySet>& tree, - const std::array<typename Geometry::ctype, 2*Geometry::coorddimension>& bBox, - std::size_t nodeIdx, - std::vector<IntersectionInfo<Geometry::coorddimension, - typename Geometry::ctype, - typename EntitySet::ctype>>& intersections) -{ - // if the two bounding boxes don't intersect we can stop searching - static constexpr int dimworld = Geometry::coorddimension; - if (!intersectsBoundingBoxBoundingBox<dimworld>(bBox.data(), tree.getBoundingBoxCoordinates(nodeIdx))) - return; - - // get node info for current bounding box node - const auto& bBoxNode = tree.getBoundingBoxNode(nodeIdx); - - // if the box is a leaf do the primitive test. - if (tree.isLeaf(bBoxNode, nodeIdx)) - { - // eIdxA is always 0 since we intersect with exactly one geometry - const auto eIdxA = 0; - const auto eIdxB = bBoxNode.child1; - - const auto geometryTree = tree.entitySet().entity(eIdxB).geometry(); - using GeometryTree = std::decay_t<decltype(geometryTree)>; - using Policy = IntersectionPolicy::DefaultPolicy<Geometry, GeometryTree>; - using IntersectionAlgorithm = GeometryIntersection<Geometry, GeometryTree, Policy>; - using Intersection = typename IntersectionAlgorithm::Intersection; - Intersection intersection; - - if (IntersectionAlgorithm::intersection(geometry, geometryTree, intersection)) - { - static constexpr int dimIntersection = Policy::dimIntersection; - if (dimIntersection >= 2) - { - const auto triangulation = triangulate<dimIntersection, dimworld>(intersection); - for (unsigned int i = 0; i < triangulation.size(); ++i) - intersections.emplace_back(eIdxA, eIdxB, std::move(triangulation[i])); - } - else - intersections.emplace_back(eIdxA, eIdxB, intersection); - } - } - - // No leaf. Check both children nodes. - else - { - intersectingEntities(geometry, tree, bBox, bBoxNode.child0, intersections); - intersectingEntities(geometry, tree, bBox, bBoxNode.child1, intersections); - } -} - -/*! - * \ingroup Geometry - * \brief Compute all intersections between two bounding box trees - */ -template<class EntitySet0, class EntitySet1> -inline std::vector<IntersectionInfo<EntitySet0::dimensionworld, typename EntitySet0::ctype, typename EntitySet1::ctype>> -intersectingEntities(const BoundingBoxTree<EntitySet0>& treeA, - const BoundingBoxTree<EntitySet1>& treeB) -{ - // check if the world dimensions match - static_assert(int(EntitySet0::dimensionworld) == int(EntitySet1::dimensionworld), - "Can only intersect bounding box trees of same world dimension"); - - // Create data structure for return type - std::vector<IntersectionInfo<EntitySet0::dimensionworld, typename EntitySet0::ctype, typename EntitySet1::ctype>> intersections; - - // Call the recursive find function to find candidates - intersectingEntities(treeA, treeB, - treeA.numBoundingBoxes() - 1, - treeB.numBoundingBoxes() - 1, - intersections); - - return intersections; -} - -/*! - * \ingroup Geometry - * \brief Compute all intersections between two all bounding box tree nodes recursively - */ -template<class EntitySet0, class EntitySet1> -void intersectingEntities(const BoundingBoxTree<EntitySet0>& treeA, - const BoundingBoxTree<EntitySet1>& treeB, - std::size_t nodeA, std::size_t nodeB, - std::vector<IntersectionInfo<EntitySet0::dimensionworld, - typename EntitySet0::ctype, - typename EntitySet1::ctype>>& intersections) -{ - // Get the bounding box for the current node - const auto& bBoxA = treeA.getBoundingBoxNode(nodeA); - const auto& bBoxB = treeB.getBoundingBoxNode(nodeB); - - // if the two bounding boxes of the current nodes don't intersect we can stop searching - static constexpr int dimworld = EntitySet0::dimensionworld; - if (!intersectsBoundingBoxBoundingBox<dimworld>(treeA.getBoundingBoxCoordinates(nodeA), - treeB.getBoundingBoxCoordinates(nodeB))) - return; - - // Check if we have a leaf in treeA or treeB - const bool isLeafA = treeA.isLeaf(bBoxA, nodeA); - const bool isLeafB = treeB.isLeaf(bBoxB, nodeB); - - // If both boxes are leaves do the primitive test - if (isLeafA && isLeafB) - { - const auto eIdxA = bBoxA.child1; - const auto eIdxB = bBoxB.child1; - - const auto geometryA = treeA.entitySet().entity(eIdxA).geometry(); - const auto geometryB = treeB.entitySet().entity(eIdxB).geometry(); - - using GeometryA = std::decay_t<decltype(geometryA)>; - using GeometryB = std::decay_t<decltype(geometryB)>; - using Policy = IntersectionPolicy::DefaultPolicy<GeometryA, GeometryB>; - using IntersectionAlgorithm = GeometryIntersection<GeometryA, GeometryB, Policy>; - using Intersection = typename IntersectionAlgorithm::Intersection; - Intersection intersection; - - if (IntersectionAlgorithm::intersection(geometryA, geometryB, intersection)) - { - static constexpr int dimIntersection = Policy::dimIntersection; - - if (dimIntersection >= 2) - { - const auto triangulation = triangulate<dimIntersection, dimworld>(intersection); - for (unsigned int i = 0; i < triangulation.size(); ++i) - intersections.emplace_back(eIdxA, eIdxB, std::move(triangulation[i])); - } - else - intersections.emplace_back(eIdxA, eIdxB, intersection); - } - } - - // if we reached the leaf in treeA, just continue in treeB - else if (isLeafA) - { - intersectingEntities(treeA, treeB, nodeA, bBoxB.child0, intersections); - intersectingEntities(treeA, treeB, nodeA, bBoxB.child1, intersections); - } - - // if we reached the leaf in treeB, just continue in treeA - else if (isLeafB) - { - intersectingEntities(treeA, treeB, bBoxA.child0, nodeB, intersections); - intersectingEntities(treeA, treeB, bBoxA.child1, nodeB, intersections); - } - - // we know now that both trees didn't reach the leaf yet so - // we continue with the larger tree first (bigger node number) - else if (nodeA > nodeB) - { - intersectingEntities(treeA, treeB, bBoxA.child0, nodeB, intersections); - intersectingEntities(treeA, treeB, bBoxA.child1, nodeB, intersections); - } - else - { - intersectingEntities(treeA, treeB, nodeA, bBoxB.child0, intersections); - intersectingEntities(treeA, treeB, nodeA, bBoxB.child1, intersections); - } -} - -/*! - * \ingroup Geometry - * \brief Compute the index of the intersecting element of a Cartesian grid with a point - * The grid is given by the lower left corner (min), the upper right corner (max) - * and the number of cells in each direction (cells). - * \note If there are several options the lowest matching cell index will be returned - * \note Returns the index i + I*j + I*J*k for the intersecting element i,j,k of a grid with I,J,K cells - */ -template<class ctype, int dimworld> -inline std::size_t intersectingEntityCartesianGrid(const Dune::FieldVector<ctype, dimworld>& point, - const Dune::FieldVector<ctype, dimworld>& min, - const Dune::FieldVector<ctype, dimworld>& max, - const std::array<int, std::size_t(dimworld)>& cells) -{ - std::size_t index = 0; - for (int i = 0; i < dimworld; ++i) - { - using std::clamp; using std::floor; - ctype dimOffset = clamp<ctype>(floor((point[i]-min[i])*cells[i]/(max[i]-min[i])), 0.0, cells[i]-1); - for (int j = 0; j < i; ++j) - dimOffset *= cells[j]; - index += static_cast<std::size_t>(dimOffset); - } - return index; -} - -} // end namespace Dumux +// This header, and all other geometry headers have been moved to their own folder. +// Please use the geometry headers in dumux/geometry/, as this will be removed after release 3.3. +#include <dumux/geometry/intersectingentities.hh> #endif diff --git a/dumux/common/geometry/intersectionentityset.hh b/dumux/common/geometry/intersectionentityset.hh index 7fad35dd08ab836297d62ac6a3880c064f38dfaa..6aa4b6f66f369b33af20da9948e86065b09e860e 100644 --- a/dumux/common/geometry/intersectionentityset.hh +++ b/dumux/common/geometry/intersectionentityset.hh @@ -20,271 +20,16 @@ * \file * \ingroup Geometry * \brief A class representing the intersection entites two geometric entity sets + * DEPRECATED will be removed once this header is removed */ #ifndef DUMUX_COMMON_GEOMETRY_INTERSECTION_ENTITY_SET_HH #define DUMUX_COMMON_GEOMETRY_INTERSECTION_ENTITY_SET_HH -#include <algorithm> -#include <cassert> -#include <iostream> -#include <memory> -#include <type_traits> -#include <utility> -#include <vector> +#warning "This header is deprecated and will be removed after release 3.3. Please use dumux/geometry/intersectionentityset.hh" -#include <dune/common/indices.hh> -#include <dune/common/timer.hh> -#include <dune/common/iteratorrange.hh> -#include <dune/common/promotiontraits.hh> -#include <dune/common/reservedvector.hh> -#include <dune/geometry/affinegeometry.hh> -#include <dune/geometry/type.hh> - -#include <dumux/common/geometry/boundingboxtree.hh> -#include <dumux/common/geometry/intersectingentities.hh> - -namespace Dumux { - -/*! - * \ingroup Geometry - * \brief A class representing the intersection entites two geometric entity sets - */ -template<class DomainEntitySet, class TargetEntitySet> -class IntersectionEntitySet -{ - using DomainTree = BoundingBoxTree<DomainEntitySet>; - using TargetTree = BoundingBoxTree<TargetEntitySet>; - - 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"); - - 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; - - /*! - * \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 - - // 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 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 - } - - //! 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 - 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 domainTree_.entitySet().entity(std::get<domainIdx>(neighbors_)[n]); } - - //! get the nth target neighbor entity - typename TargetEntitySet::Entity targetEntity(unsigned int n = 0) const - { return targetTree_.entitySet().entity(std::get<targetIdx>(neighbors_)[n]); } - - private: - IndexStorage neighbors_; - std::vector<GlobalPosition> corners_; - - const DomainTree& domainTree_; - const TargetTree& targetTree_; - }; - - using Intersections = std::vector<IntersectionEntity>; - -public: - //! make intersection entity type available - using Entity = IntersectionEntity; - //! make entity iterator type available - using EntityIterator = typename Intersections::const_iterator; - - /*! - * \brief Default constructor - */ - IntersectionEntitySet() = default; - - /*! - * \brief Build intersections - */ - 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_); - } - - /*! - * \brief Build intersections - */ - void build(std::shared_ptr<const DomainTree> domainTree, std::shared_ptr<const TargetTree> targetTree) - { - // make sure the tree don't get out of scope - domainTree_ = domainTree; - targetTree_ = targetTree; - build(*domainTree_, *targetTree_); - } - - /*! - * \brief Build intersections - * \note If you call this, make sure the bounding box tree stays alive for the life-time of this object - */ - void build(const DomainTree& domainTree, const TargetTree& targetTree) - { - Dune::Timer timer; - - // compute raw intersections - const auto rawIntersections = intersectingEntities(domainTree, targetTree); - - // 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- - // dimensional entities which is identical for all geometrically identical intersections. - std::vector<std::vector<std::vector<GlobalPosition>>> intersectionMap; - std::vector<std::vector<std::size_t>> intersectionIndex; - if constexpr (isMixedDimensional) - { - const auto numLowDimEntities = dimTarget < dimDomain ? targetTree.entitySet().size() - : domainTree.entitySet().size(); - intersectionMap.resize(numLowDimEntities); - intersectionIndex.resize(numLowDimEntities); - } - - // reserve memory for storing the intersections. In case of grids of - // different dimensionality this might be an overestimate. We get rid - // of the overhead memory at the end of this function though. - intersections_.clear(); - intersections_.reserve(rawIntersections.size()); - - for (const auto& rawIntersection : rawIntersections) - { - bool add = true; - - // Check if intersection was already inserted. - // In this case we only add new neighbor information as the geometry is identical. - if constexpr (isMixedDimensional) - { - const auto lowDimNeighborIdx = getLowDimNeighborIdx_(rawIntersection); - for (int i = 0; i < intersectionMap[lowDimNeighborIdx].size(); ++i) - { - if (rawIntersection.cornersMatch(intersectionMap[lowDimNeighborIdx][i])) - { - add = false; - // only add the pair of neighbors using the insertionIndex - auto idx = intersectionIndex[lowDimNeighborIdx][i]; - intersections_[idx].addNeighbors(rawIntersection.first(), rawIntersection.second()); - break; - } - } - } - - if(add) - { - // maybe add to the map - if constexpr (isMixedDimensional) - { - intersectionMap[getLowDimNeighborIdx_(rawIntersection)].push_back(rawIntersection.corners()); - intersectionIndex[getLowDimNeighborIdx_(rawIntersection)].push_back(intersections_.size()); - } - - // add new intersection and add the neighbors - intersections_.emplace_back(domainTree, targetTree); - intersections_.back().setCorners(rawIntersection.corners()); - intersections_.back().addNeighbors(rawIntersection.first(), rawIntersection.second()); - } - } - - 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) - { - if constexpr (dimTarget < dimDomain) - return is.second(); - else - return is.first(); - } - - Intersections intersections_; - - std::shared_ptr<const DomainTree> domainTree_; - std::shared_ptr<const TargetTree> targetTree_; -}; - -} // end namespace Dumux +// This header, and all other geometry headers have been moved to their own folder. +// Please use the geometry headers in dumux/geometry/, as this will be removed after release 3.3. +#include <dumux/geometry/intersectionentityset.hh> #endif diff --git a/dumux/common/geometry/intersectspointgeometry.hh b/dumux/common/geometry/intersectspointgeometry.hh index 68f0c8b115da3f04f6722e48973bca68d2efee91..909c6d1b58ae08ed67f3f014b9be2cd50b10b48c 100644 --- a/dumux/common/geometry/intersectspointgeometry.hh +++ b/dumux/common/geometry/intersectspointgeometry.hh @@ -18,104 +18,15 @@ * \file * \ingroup Geometry * \brief Detect if a point intersects a geometry + * DEPRECATED will be removed once this header is removed */ #ifndef DUMUX_INTERSECTS_POINT_GEOMETRY_HH #define DUMUX_INTERSECTS_POINT_GEOMETRY_HH -#include <cmath> -#include <dune/common/exceptions.hh> -#include <dune/common/fvector.hh> +#warning "This header is deprecated and will be removed after release 3.3. Please use dumux/geometry/intersectspointgeometry.hh" -#include "intersectspointsimplex.hh" - -namespace Dumux { - -/*! - * \ingroup Geometry - * \brief Find out whether a point is inside a three-dimensional geometry - */ -template <class ctype, int dimworld, class Geometry, typename std::enable_if_t<(Geometry::mydimension == 3), int> = 0> -bool intersectsPointGeometry(const Dune::FieldVector<ctype, dimworld>& point, const Geometry& g) -{ - // get the g type - const auto type = g.type(); - - // if it's a tetrahedron we can check directly - if (type.isTetrahedron()) - return intersectsPointSimplex(point, g.corner(0), g.corner(1), g.corner(2), g.corner(3)); - - // split hexahedrons into five tetrahedrons - else if (type.isHexahedron()) - { - if (intersectsPointSimplex(point, g.corner(0), g.corner(1), g.corner(3), g.corner(5))) return true; - if (intersectsPointSimplex(point, g.corner(0), g.corner(5), g.corner(6), g.corner(4))) return true; - if (intersectsPointSimplex(point, g.corner(5), g.corner(3), g.corner(6), g.corner(7))) return true; - if (intersectsPointSimplex(point, g.corner(0), g.corner(3), g.corner(2), g.corner(6))) return true; - if (intersectsPointSimplex(point, g.corner(5), g.corner(3), g.corner(0), g.corner(6))) return true; - return false; - } - - // split pyramids into two tetrahedrons - else if (type.isPyramid()) - { - if (intersectsPointSimplex(point, g.corner(0), g.corner(1), g.corner(2), g.corner(4))) return true; - if (intersectsPointSimplex(point, g.corner(1), g.corner(3), g.corner(2), g.corner(4))) return true; - return false; - } - - // split prisms into three tetrahedrons - else if (type.isPrism()) - { - if (intersectsPointSimplex(point, g.corner(0), g.corner(1), g.corner(2), g.corner(4))) return true; - if (intersectsPointSimplex(point, g.corner(3), g.corner(0), g.corner(2), g.corner(4))) return true; - if (intersectsPointSimplex(point, g.corner(2), g.corner(5), g.corner(3), g.corner(4))) return true; - return false; - } - - else - DUNE_THROW(Dune::NotImplemented, - "Intersection for point and geometry type " - << type << " in " << dimworld << "-dimensional world."); -} - -/*! - * \ingroup Geometry - * \brief Find out whether a point is inside a two-dimensional geometry - */ -template <class ctype, int dimworld, class Geometry, typename std::enable_if_t<(Geometry::mydimension == 2), int> = 0> -bool intersectsPointGeometry(const Dune::FieldVector<ctype, dimworld>& point, const Geometry& g) -{ - // get the g type - const auto type = g.type(); - - // if it's a triangle we can check directly - if (type.isTriangle()) - return intersectsPointSimplex(point, g.corner(0), g.corner(1), g.corner(2)); - - // split quadrilaterals into two triangles - else if (type.isQuadrilateral()) - { - if (intersectsPointSimplex(point, g.corner(0), g.corner(1), g.corner(3))) return true; - if (intersectsPointSimplex(point, g.corner(0), g.corner(3), g.corner(2))) return true; - return false; - } - - else - DUNE_THROW(Dune::NotImplemented, - "Intersection for point and geometry type " - << type << " in " << dimworld << "-dimensional world."); -} - -/*! - * \ingroup Geometry - * \brief Find out whether a point is inside a one-dimensional geometry - */ -template <class ctype, int dimworld, class Geometry, typename std::enable_if_t<(Geometry::mydimension == 1), int> = 0> -bool intersectsPointGeometry(const Dune::FieldVector<ctype, dimworld>& point, const Geometry& g) -{ - return intersectsPointSimplex(point, g.corner(0), g.corner(1)); -} - -} // end namespace Dumux +// This header, and all other geometry headers have been moved to their own folder. +// Please use the geometry headers in dumux/geometry/, as this will be removed after release 3.3. +#include <dumux/geometry/intersectspointgeometry.hh> #endif diff --git a/dumux/common/geometry/intersectspointsimplex.hh b/dumux/common/geometry/intersectspointsimplex.hh index d3eb254c37729eee6a5b52b37586fb987f05ff85..cb8639f536f0dc8617ce786f92dfec7ae321a0f8 100644 --- a/dumux/common/geometry/intersectspointsimplex.hh +++ b/dumux/common/geometry/intersectspointsimplex.hh @@ -18,208 +18,14 @@ * \file * \ingroup Geometry * \brief Detect if a point intersects a simplex (including boundary) + * DEPRECATED will be removed once this header is removed */ -#ifndef DUMUX_INTERSECTS_POINT_SIMPLEX_HH -#define DUMUX_INTERSECTS_POINT_SIMPLEX_HH +#ifndef DUMUX_COMMON_GEOMETRY_INTERSECTS_POINT_SIMPLEX_HH +#define DUMUX_COMMON_GEOMETRY_INTERSECTS_POINT_SIMPLEX_HH -#include <cmath> -#include <dune/common/fvector.hh> -#include <dumux/common/math.hh> - -namespace Dumux { - -/*! - * \ingroup Geometry - * \brief Find out whether a point is inside a tetrahedron (p0, p1, p2, p3) (dimworld is 3) - */ -template<class ctype, int dimworld, typename std::enable_if_t<(dimworld == 3), int> = 0> -bool intersectsPointSimplex(const Dune::FieldVector<ctype, dimworld>& point, - const Dune::FieldVector<ctype, dimworld>& p0, - const Dune::FieldVector<ctype, dimworld>& p1, - const Dune::FieldVector<ctype, dimworld>& p2, - const Dune::FieldVector<ctype, dimworld>& p3) -{ - // Algorithm from http://www.blackpawn.com/texts/pointinpoly/ - // See also "Real-Time Collision Detection" by Christer Ericson. - using GlobalPosition = Dune::FieldVector<ctype, dimworld>; - static constexpr ctype eps_ = 1.0e-7; - - // put the tetrahedron points in an array - const GlobalPosition *p[4] = {&p0, &p1, &p2, &p3}; - - // iterate over all faces - for (int i = 0; i < 4; ++i) - { - // compute all the vectors from vertex (local index 0) to the other points - const GlobalPosition v1 = *p[(i + 1)%4] - *p[i]; - const GlobalPosition v2 = *p[(i + 2)%4] - *p[i]; - const GlobalPosition v3 = *p[(i + 3)%4] - *p[i]; - const GlobalPosition v = point - *p[i]; - // compute the normal to the facet (cross product) - GlobalPosition n1 = crossProduct(v1, v2); - n1 /= n1.two_norm(); - // find out on which side of the plane v and v3 are - const auto t1 = n1.dot(v); - const auto t2 = n1.dot(v3); - // If the point is not exactly on the plane the - // points have to be on the same side - const auto eps = eps_ * v1.two_norm(); - if ((t1 > eps || t1 < -eps) && std::signbit(t1) != std::signbit(t2)) - return false; - } - return true; -} - -/*! - * \ingroup Geometry - * \brief Find out whether a point is inside a triangle (p0, p1, p2, p3) (dimworld is 3) - */ -template<class ctype, int dimworld, typename std::enable_if_t<(dimworld == 3), int> = 0> -bool intersectsPointSimplex(const Dune::FieldVector<ctype, dimworld>& point, - const Dune::FieldVector<ctype, dimworld>& p0, - const Dune::FieldVector<ctype, dimworld>& p1, - const Dune::FieldVector<ctype, dimworld>& p2) -{ - // adapted from the algorithm from from "Real-Time Collision Detection" by Christer Ericson, - // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc. (Chapter 5.4.2) - constexpr ctype eps_ = 1.0e-7; - - // compute the normal of the triangle - const auto v1 = p0 - p2; - auto n = crossProduct(v1, p1 - p0); - const ctype nnorm = n.two_norm(); - const ctype eps4 = eps_*nnorm*nnorm; // compute an epsilon for later - n /= nnorm; // normalize - - // first check if we are in the plane of the triangle - // if not we can return early - using std::abs; - auto x = p0 - point; - x /= x.two_norm(); // normalize - - if (abs(x*n) > eps_) - return false; - - // translate the triangle so that 'point' is the origin - const auto a = p0 - point; - const auto b = p1 - point; - const auto c = p2 - point; - - // compute the normal vectors for triangles P->A->B and P->B->C - const auto u = crossProduct(b, c); - const auto v = crossProduct(c, a); - - // they have to point in the same direction or be orthogonal - if (u*v < 0.0 - eps4) - return false; - - // compute the normal vector for triangle P->C->A - const auto w = crossProduct(a, b); - - // it also has to point in the same direction or be orthogonal - if (u*w < 0.0 - eps4) - return false; - - // check if point is on the line of one of the edges - if (u.two_norm2() < eps4) - return b*c < 0.0 + eps_*nnorm; - if (v.two_norm2() < eps4) - return a*c < 0.0 + eps_*nnorm; - - // now the point must be in the triangle (or on the faces) - return true; -} - -/*! - * \ingroup Geometry - * \brief Find out whether a point is inside a triangle (p0, p1, p2, p3) (dimworld is 2) - */ -template<class ctype, int dimworld, typename std::enable_if_t<(dimworld == 2), int> = 0> -bool intersectsPointSimplex(const Dune::FieldVector<ctype, dimworld>& point, - const Dune::FieldVector<ctype, dimworld>& p0, - const Dune::FieldVector<ctype, dimworld>& p1, - const Dune::FieldVector<ctype, dimworld>& p2) -{ - static constexpr ctype eps_ = 1.0e-7; - - // Use barycentric coordinates - const ctype A = 0.5*(-p1[1]*p2[0] + p0[1]*(p2[0] - p1[0]) - +p1[0]*p2[1] + p0[0]*(p1[1] - p2[1])); - const ctype sign = std::copysign(1.0, A); - const ctype s = sign*(p0[1]*p2[0] + point[0]*(p2[1]-p0[1]) - -p0[0]*p2[1] + point[1]*(p0[0]-p2[0])); - const ctype t = sign*(p0[0]*p1[1] + point[0]*(p0[1]-p1[1]) - -p0[1]*p1[0] + point[1]*(p1[0]-p0[0])); - const ctype eps = sign*A*eps_; - - return (s > -eps - && t > -eps - && (s + t) < 2*A*sign + eps); -} - -/*! - * \ingroup Geometry - * \brief Find out whether a point is inside a interval (p0, p1, p2, p3) (dimworld is 3) - * \note We assume the given interval has non-zero length and use it to scale the epsilon - */ -template<class ctype, int dimworld, typename std::enable_if_t<(dimworld == 3 || dimworld == 2), int> = 0> -bool intersectsPointSimplex(const Dune::FieldVector<ctype, dimworld>& point, - const Dune::FieldVector<ctype, dimworld>& p0, - const Dune::FieldVector<ctype, dimworld>& p1) -{ - using GlobalPosition = Dune::FieldVector<ctype, dimworld>; - static constexpr ctype eps_ = 1.0e-7; - - // compute the vectors between p0 and the other points - const GlobalPosition v1 = p1 - p0; - const GlobalPosition v2 = point - p0; - - const ctype v1norm = v1.two_norm(); - const ctype v2norm = v2.two_norm(); - - // check if point and p0 are the same - if (v2norm < v1norm*eps_) - return true; - - return (v1.dot(v2) > v1norm*v2norm*(1.0 - eps_) && v2norm < v1norm*(1.0 + eps_)); -} - -/*! - * \ingroup Geometry - * \brief Find out whether a point is inside a interval (p0, p1, p2, p3) (dimworld is 1) - */ -template<class ctype, int dimworld, typename std::enable_if_t<(dimworld == 1), int> = 0> -bool intersectsPointSimplex(const Dune::FieldVector<ctype, dimworld>& point, - const Dune::FieldVector<ctype, dimworld>& p0, - const Dune::FieldVector<ctype, dimworld>& p1) -{ - static constexpr ctype eps_ = 1.0e-7; - - // sort the interval so interval[1] is the end and interval[0] the start - const ctype *interval[2] = {&p0[0], &p1[0]}; - if (*interval[0] > *interval[1]) - std::swap(interval[0], interval[1]); - - const ctype v1 = point[0] - *interval[0]; - const ctype v2 = *interval[1] - *interval[0]; // always positive - - // the coordinates are the same - using std::abs; - if (abs(v1) < v2*eps_) - return true; - - // the point doesn't coincide with p0 - // so if p0 and p1 are equal it's not inside - if (v2 < 1.0e-30) - return false; - - // the point is inside if the length is - // smaller than the interval length and the - // sign of v1 & v2 are the same - using std::signbit; - return (!signbit(v1) && abs(v1) < v2*(1.0 + eps_)); -} - -} // end namespace Dumux +#warning "This header is deprecated and will be removed after release 3.3. Please use dumux/geometry/intersectspointsimplex.hh" +// This header, and all other geometry headers have been moved to their own folder. +// Please use the geometry headers in dumux/geometry/, as this will be removed after release 3.3. +#include <dumux/geometry/intersectspointsimplex.hh> #endif diff --git a/dumux/common/geometry/makegeometry.hh b/dumux/common/geometry/makegeometry.hh index 68a057e53204c65b3e82614dd0c741245f7b7ca1..2590962b50b9e04ff507b2fe51978f0597e12a08 100644 --- a/dumux/common/geometry/makegeometry.hh +++ b/dumux/common/geometry/makegeometry.hh @@ -18,193 +18,15 @@ * \file * \ingroup Geometry * \brief Create Dune geometries from user-specified points + * DEPRECATED will be removed once this header is removed */ -#ifndef DUMUX_MAKE_GEOMETRY_HH -#define DUMUX_MAKE_GEOMETRY_HH +#ifndef DUMUX_COMMON_GEOMETRY_MAKE_GEOMETRY_HH +#define DUMUX_COMMON_GEOMETRY_MAKE_GEOMETRY_HH -#include <vector> -#include <array> -#include <limits> -#include <dune/common/fvector.hh> -#include <dune/common/fmatrix.hh> -#include <dune/common/exceptions.hh> -#include <dune/geometry/multilineargeometry.hh> -#include <dumux/common/math.hh> -#include <dumux/common/geometry/grahamconvexhull.hh> +#warning "This header is deprecated and will be removed after release 3.3. Please use dumux/geometry/makegeometry.hh" -namespace Dumux { +// This header, and all other geometry headers have been moved to their own folder. +// Please use the geometry headers in dumux/geometry/, as this will be removed after release 3.3. +#include <dumux/geometry/makegeometry.hh> -/*! - * \ingroup Geometry - * \brief Checks if four points lie within the same plane - */ -template<class CoordScalar> -bool pointsAreCoplanar(const std::vector<Dune::FieldVector<CoordScalar, 3>>& points, const CoordScalar scale) -{ - if (points.size() != 4) - DUNE_THROW(Dune::InvalidStateException, "Check only works for 4 points!"); - - // (see "Real-Time Collision Detection" by Christer Ericson) - Dune::FieldMatrix<CoordScalar, 4, 4> M; - for (int i = 0; i < 3; ++i ) - M[i] = {points[0][i], points[1][i], points[2][i], points[3][i]}; - M[3] = {1.0*scale, 1.0*scale, 1.0*scale, 1.0*scale}; - - using std::abs; - return abs(M.determinant()) < 1.5e-7*scale*scale*scale*scale; -} - -/*! - * \ingroup Geometry - * \brief Checks if four points lie within the same plane. - */ -template<class CoordScalar> -bool pointsAreCoplanar(const std::vector<Dune::FieldVector<CoordScalar, 3>>& points) -{ - Dune::FieldVector<CoordScalar, 3> bBoxMin(std::numeric_limits<CoordScalar>::max()); - Dune::FieldVector<CoordScalar, 3> bBoxMax(std::numeric_limits<CoordScalar>::lowest()); - for (const auto& p : points) - { - for (int i=0; i<3; i++) - { - using std::min; - using std::max; - bBoxMin[i] = min(bBoxMin[i], p[i]); - bBoxMax[i] = max(bBoxMax[i], p[i]); - } - } - - const auto size = (bBoxMax - bBoxMin).two_norm(); - - return pointsAreCoplanar(points, size); -} - -/*! - * \ingroup Geometry - * \brief Returns a vector of points following the dune ordering. - * Convenience method that creates a temporary object in case no array of orientations is desired. - * - * \param points The user-specified vector of points (potentially in wrong order). - */ -template<class CoordScalar> -std::vector<Dune::FieldVector<CoordScalar, 3>> getReorderedPoints(const std::vector<Dune::FieldVector<CoordScalar, 3>>& points) -{ - std::array<int, 4> tmp; - return getReorderedPoints(points, tmp); -} - -/*! - * \ingroup Geometry - * \brief Returns a vector of points following the dune ordering. - * - * \param points The user-specified vector of points (potentially in wrong order). - * \param orientations An array of orientations that can be useful for further processing. - */ -template<class CoordScalar> -std::vector<Dune::FieldVector<CoordScalar, 3>> getReorderedPoints(const std::vector<Dune::FieldVector<CoordScalar, 3>>& points, - std::array<int, 4>& orientations) -{ - if(points.size() == 4) - { - auto& p0 = points[0]; - auto& p1 = points[1]; - auto& p2 = points[2]; - auto& p3 = points[3]; - - // check if the points define a proper quadrilateral - const auto normal = crossProduct((p1 - p0), (p2 - p0)); - - orientations = { getOrientation(p0, p3, p2, normal), - getOrientation(p0, p3, p1, normal), - getOrientation(p2, p1, p0, normal), - getOrientation(p2, p1, p3, normal) }; - - - // check if the points follow the dune ordering (see http://www.dcs.gla.ac.uk/~pat/52233/slides/Geometry1x1.pdf) - const bool diagonalsIntersect = (orientations[0] != orientations[1]) && (orientations[2] != orientations[3]); - - // the points conform with the dune ordering - if(diagonalsIntersect) - return points; - - // the points do not conform with the dune ordering, re-order - using GlobalPosition = Dune::FieldVector<CoordScalar, 3>; - if(!diagonalsIntersect && orientations[0] == 1) - return std::vector<GlobalPosition>{p1, p0, p2, p3}; - else if(!diagonalsIntersect && orientations[0] == -1) - return std::vector<GlobalPosition>{p3, p1, p0, p2}; - else - DUNE_THROW(Dune::InvalidStateException, "Could not reorder points"); - } - else - DUNE_THROW(Dune::NotImplemented, "Reorder for " << points.size() << " points."); -} - -/*! - * \ingroup Geometry - * \brief Creates a dune quadrilateral geometry given 4 corner points. - * - * \tparam CoordScalar The CoordScalar type. - * \tparam enableSanityCheck Turn on/off sanity check and reordering of points - * \param points The user-specified vector of points (potentially in wrong order). - */ -template<class CoordScalar, bool enableSanityCheck = true> -auto makeDuneQuadrilaterial(const std::vector<Dune::FieldVector<CoordScalar, 3>>& points) -{ - if (points.size() != 4) - DUNE_THROW(Dune::InvalidStateException, "A quadrilateral needs 4 corner points!"); - - using GlobalPosition = Dune::FieldVector<CoordScalar, 3>; - static constexpr auto coordDim = GlobalPosition::dimension; - static constexpr auto dim = coordDim-1; - using GeometryType = Dune::MultiLinearGeometry<CoordScalar, dim, coordDim>; - - // if no sanity check if desired, use the given points directly to construct the geometry - if (!enableSanityCheck) - return GeometryType(Dune::GeometryTypes::quadrilateral, points); - - // compute size - Dune::FieldVector<CoordScalar, 3> bBoxMin(std::numeric_limits<CoordScalar>::max()); - Dune::FieldVector<CoordScalar, 3> bBoxMax(std::numeric_limits<CoordScalar>::lowest()); - for (const auto& p : points) - { - for (int i = 0; i < 3; i++) - { - using std::min; - using std::max; - bBoxMin[i] = min(bBoxMin[i], p[i]); - bBoxMax[i] = max(bBoxMax[i], p[i]); - } - } - - const auto size = (bBoxMax - bBoxMin).two_norm(); - - // otherwise, perform a number of checks and corrections - if (!pointsAreCoplanar(points, size)) - DUNE_THROW(Dune::InvalidStateException, "Points do not lie within a plane"); - - auto corners = grahamConvexHull<2>(points); - if (corners.size() != 4) - DUNE_THROW(Dune::InvalidStateException, "Points do not span a strictly convex polygon!"); - - // make sure points conform with dune ordering - std::array<int, 4> orientations; - corners = getReorderedPoints(corners, orientations); - - if (std::any_of(orientations.begin(), orientations.end(), [](auto i){ return i == 0; })) - DUNE_THROW(Dune::InvalidStateException, "More than two points lie on the same line."); - - const auto quadrilateral = GeometryType(Dune::GeometryTypes::quadrilateral, corners); - - const auto eps = 1e-7; - if (quadrilateral.volume() < eps*size*size) - DUNE_THROW(Dune::InvalidStateException, "Something went wrong, geometry has area of zero"); - - return quadrilateral; -} - - - -} // end namespace Dumux - -#endif // DUMUX_MAKE_GEOMETRY_HH +#endif diff --git a/dumux/common/geometry/normal.hh b/dumux/common/geometry/normal.hh index a6e95ecb170b8c75a6df877f8667742f85585ee7..712580c5efae33313c3765ecf60e8b3d2535186a 100644 --- a/dumux/common/geometry/normal.hh +++ b/dumux/common/geometry/normal.hh @@ -18,60 +18,15 @@ * \file * \ingroup Geometry * \brief Helper functions for normals + * DEPRECATED will be removed once this header is removed */ -#ifndef DUMUX_GEOMETRY_NORMAL_HH -#define DUMUX_GEOMETRY_NORMAL_HH +#ifndef DUMUX_COMMON_GEOMETRY_NORMAL_HH +#define DUMUX_COMMON_GEOMETRY_NORMAL_HH -#include <algorithm> -#include <dune/common/float_cmp.hh> +#warning "This header is deprecated and will be removed after release 3.3. Please use dumux/geometry/normal.hh" -namespace Dumux { - -/*! - * \ingroup Geometry - * \brief Create a vector normal to the given one (v is expected to be non-zero) - * \note This returns some orthogonal vector with arbitrary length - */ -template<class Vector> -inline Vector normal(const Vector& v) -{ - static_assert(Vector::size() > 1, "normal expects a coordinate dimension > 1"); - - if constexpr (Vector::size() == 2) - return Vector({-v[1], v[0]}); - - const auto it = std::find_if(v.begin(), v.end(), [](const auto& x) { return Dune::FloatCmp::ne(x, 0.0); }); - const auto index = std::distance(v.begin(), it); - if (index != Vector::size()-1) - { - Vector normal(0.0); - normal[index] = -v[index+1]; - normal[index+1] = v[index]; - return normal; - } - else - { - Vector normal(0.0); - normal[index-1] = -v[index]; - normal[index] = v[index-1]; - return normal; - - } -} - -/*! - * \ingroup Geometry - * \brief Create a vector normal to the given one (v is expected to be non-zero) - * \note This returns some orthogonal vector with unit length - */ -template<class Vector> -inline Vector unitNormal(const Vector& v) -{ - auto normal = Dumux::normal(v); - normal /= normal.two_norm(); - return normal; -} - -} // end namespace Dumux +// This header, and all other geometry headers have been moved to their own folder. +// Please use the geometry headers in dumux/geometry/, as this will be removed after release 3.3. +#include <dumux/geometry/normal.hh> #endif diff --git a/dumux/common/geometry/refinementquadraturerule.hh b/dumux/common/geometry/refinementquadraturerule.hh index d2996c623ef0c64b8b7c2ab37c56c12ca80fcc71..0778c474a0d5a9ee06bf920204f048d19ffa15b5 100644 --- a/dumux/common/geometry/refinementquadraturerule.hh +++ b/dumux/common/geometry/refinementquadraturerule.hh @@ -21,74 +21,16 @@ * \ingroup Geometry * \author Timo Koch * \brief A quadrature based on refinement + * DEPRECATED will be removed once this header is removed */ -#ifndef DUMUX_COMMON_REFINEMENT_QUADRATURERULE_HH -#define DUMUX_COMMON_REFINEMENT_QUADRATURERULE_HH +#ifndef DUMUX_COMMON_GEOMETRY_REFINEMENT_QUADRATURERULE_HH +#define DUMUX_COMMON_GEOMETRY_REFINEMENT_QUADRATURERULE_HH -#include <dune/geometry/quadraturerules.hh> -#include <dune/geometry/virtualrefinement.hh> -#include <dumux/common/math.hh> +#warning "This header is deprecated and will be removed after release 3.3. Please use dumux/geometry/refinementquadraturerule.hh" -namespace Dumux { - -/*! - * \ingroup Geometry - * \brief A "quadrature" based on virtual refinement - */ -template<typename ct, int mydim> -class RefinementQuadratureRule final : public Dune::QuadratureRule<ct, mydim> -{ -public: - //! The space dimension - static constexpr int dim = mydim; - - //! The highest quadrature order available - static constexpr int highest_order = 1; - - ~RefinementQuadratureRule() final {} - - RefinementQuadratureRule(Dune::GeometryType type, int levels) - : Dune::QuadratureRule<ct, dim>(type, highest_order) - { - auto& refinement = Dune::buildRefinement<dim, ct>(type, type); - const auto tag = Dune::refinementLevels(levels); - - // get the vertices - const auto numVertices = refinement.nVertices(tag); - std::vector<Dune::FieldVector<ct, dim>> points; points.reserve(numVertices); - auto vIt = refinement.vBegin(tag); - const auto vItEnd = refinement.vEnd(tag); - for (; vIt != vItEnd; ++vIt) - points.emplace_back(vIt.coords()); - - // go over all elements and get center and volume - const auto numElements = refinement.nElements(tag); - this->reserve(numElements); - auto eIt = refinement.eBegin(tag); - const auto eItEnd = refinement.eEnd(tag); - for (; eIt != eItEnd; ++eIt) - { - // quadrature point in the centroid of the sub-element and weight is its volume - const auto weight = computeVolume_(type, points, eIt.vertexIndices()); - this->emplace_back(eIt.coords(), weight); - } - } - -private: - template<class VertexIndices> - ct computeVolume_(Dune::GeometryType t, const std::vector<Dune::FieldVector<ct, dim>>& points, const VertexIndices& indices) const - { - if (t != Dune::GeometryTypes::simplex(2)) - DUNE_THROW(Dune::NotImplemented, "Only implemented for 2d simplices"); - - const auto ab = points[indices[1]] - points[indices[0]]; - const auto ac = points[indices[2]] - points[indices[0]]; - using std::abs; - return abs(crossProduct(ab, ac))*0.5; - } -}; - -} // end namespace Dumux +// This header, and all other geometry headers have been moved to their own folder. +// Please use the geometry headers in dumux/geometry/, as this will be removed after release 3.3. +#include <dumux/geometry/refinementquadraturerule.hh> #endif diff --git a/dumux/common/geometry/triangulation.hh b/dumux/common/geometry/triangulation.hh index 2b04870cd6e7357f7d9ebc18a1fd2323caac982a..1cba28a5832a197fe6d09cbce8d3ec216fe2f7be 100644 --- a/dumux/common/geometry/triangulation.hh +++ b/dumux/common/geometry/triangulation.hh @@ -18,117 +18,15 @@ * \file * \ingroup Geometry * \brief Functionality to triangulate point clouds + * DEPRECATED will be removed once this header is removed */ -#ifndef DUMUX_TRIANGULATION_HH -#define DUMUX_TRIANGULATION_HH +#ifndef DUMUX_COMMON_GEOMETRY_TRIANGULATION_HH +#define DUMUX_COMMON_GEOMETRY_TRIANGULATION_HH -#include <vector> -#include <array> -#include <algorithm> -#include <type_traits> +#warning "This header is deprecated and will be removed after release 3.3. Please use dumux/geometry/triangulation.hh" -#include <dune/common/exceptions.hh> -#include <dune/common/fvector.hh> +// This header, and all other geometry headers have been moved to their own folder. +// Please use the geometry headers in dumux/geometry/, as this will be removed after release 3.3. +#include <dumux/geometry/triangulation.hh> -#include <dumux/common/math.hh> - -namespace Dumux { -namespace TriangulationPolicy { - -//! Policy that expects a point cloud that represents a convex -//! hull. Inserts the mid point and connects it to the points of the hull. -struct MidPointPolicy {}; - -//! Delaunay-type triangulations. -struct DelaunayPolicy {}; - -template<int dim, int dimWorld> -using DefaultPolicy = std::conditional_t< dim >= 2, MidPointPolicy, DelaunayPolicy >; - -} // end namespace TriangulationPolicy - -//! The data type to store triangulations -template<int dim, int dimWorld, class ctype> -using Triangulation = std::vector< std::array<Dune::FieldVector<ctype, dimWorld>, dim+1> >; - -/*! - * \ingroup Geometry - * \brief Triangulate area given points of a convex hull - * \tparam dim Specifies the dimension of the resulting triangulation (2 or 3) - * \tparam dimWorld The dimension of the coordinate space - * \tparam Policy Specifies the algorithm to be used for triangulation - * - * \note this specialization is for 2d triangulations using mid point policy - * \note Assumes all points of the convex hull are coplanar - * \note This inserts a mid point and connects all corners with that point to triangles - */ -template< int dim, int dimWorld, class Policy = TriangulationPolicy::DefaultPolicy<dim, dimWorld>, - class RandomAccessContainer, - std::enable_if_t< std::is_same<Policy, TriangulationPolicy::MidPointPolicy>::value - && dim == 2, int> = 0 > -inline Triangulation<dim, dimWorld, typename RandomAccessContainer::value_type::value_type> -triangulate(const RandomAccessContainer& convexHull) -{ - using ctype = typename RandomAccessContainer::value_type::value_type; - using Point = Dune::FieldVector<ctype, dimWorld>; - using Triangle = std::array<Point, 3>; - - static_assert(std::is_same<typename RandomAccessContainer::value_type, Point>::value, - "Triangulation expects Dune::FieldVector as point type"); - - if (convexHull.size() < 3) - DUNE_THROW(Dune::InvalidStateException, "Try to triangulate point cloud with less than 3 points!"); - - if (convexHull.size() == 3) - return std::vector<Triangle>(1, {convexHull[0], convexHull[1], convexHull[2]}); - - Point midPoint(0.0); - for (const auto p : convexHull) - midPoint += p; - midPoint /= convexHull.size(); - - std::vector<Triangle> triangulation; - triangulation.reserve(convexHull.size()); - - for (std::size_t i = 0; i < convexHull.size()-1; ++i) - triangulation.emplace_back(Triangle{midPoint, convexHull[i], convexHull[i+1]}); - - triangulation.emplace_back(Triangle{midPoint, convexHull[convexHull.size()-1], convexHull[0]}); - - return triangulation; -} - -/*! - * \ingroup Geometry - * \brief Triangulate area given points of a convex hull - * \tparam dim Specifies the dimension of the resulting triangulation (2 or 3) - * \tparam dimWorld The dimension of the coordinate space - * \tparam Policy Specifies the algorithm to be used for triangulation - * - * \note this specialization is for 1d discretization using segments - * \note sorts the points and connects them via segments - */ -template< int dim, int dimWorld, class Policy = TriangulationPolicy::DefaultPolicy<dim, dimWorld>, - class RandomAccessContainer, - std::enable_if_t< std::is_same<Policy, TriangulationPolicy::DelaunayPolicy>::value - && dim == 1, int> = 0 > -inline Triangulation<dim, dimWorld, typename RandomAccessContainer::value_type::value_type> -triangulate(const RandomAccessContainer& points) -{ - using ctype = typename RandomAccessContainer::value_type::value_type; - using Point = Dune::FieldVector<ctype, dimWorld>; - - static_assert(std::is_same<typename RandomAccessContainer::value_type, Point>::value, - "Triangulation expects Dune::FieldVector as point type"); - - if (points.size() == 2) - return Triangulation<dim, dimWorld, ctype>({ {points[0], points[1]} }); - - //! \todo sort points and create polyline - assert(points.size() > 1); - DUNE_THROW(Dune::NotImplemented, "1d triangulation for point cloud size > 2"); -} - -} // end namespace Dumux - -# endif +#endif diff --git a/dumux/common/pointsource.hh b/dumux/common/pointsource.hh index b7a7bf61d312c52c0cce0cefa708f10e14c28c5e..0f0430416ade1f468bbfe095bb9906162e4795b7 100644 --- a/dumux/common/pointsource.hh +++ b/dumux/common/pointsource.hh @@ -31,9 +31,9 @@ #include <dune/common/reservedvector.hh> #include <dumux/common/properties.hh> #include <dumux/common/parameters.hh> -#include <dumux/common/geometry/boundingboxtree.hh> -#include <dumux/common/geometry/intersectspointgeometry.hh> -#include <dumux/common/geometry/intersectingentities.hh> +#include <dumux/geometry/boundingboxtree.hh> +#include <dumux/geometry/intersectspointgeometry.hh> +#include <dumux/geometry/intersectingentities.hh> #include <dumux/discretization/method.hh> diff --git a/dumux/discretization/basegridgeometry.hh b/dumux/discretization/basegridgeometry.hh index 5d8c58ff2c89e909a8c6fe306be7682a9a82f3f7..73821a6a8775bc220b0dd832bc5f631323efc9fd 100644 --- a/dumux/discretization/basegridgeometry.hh +++ b/dumux/discretization/basegridgeometry.hh @@ -30,8 +30,8 @@ #include <dumux/common/entitymap.hh> #include <dumux/common/indextraits.hh> -#include <dumux/common/geometry/boundingboxtree.hh> -#include <dumux/common/geometry/geometricentityset.hh> +#include <dumux/geometry/boundingboxtree.hh> +#include <dumux/geometry/geometricentityset.hh> // make the local view function available whenever we use the grid geometry #include <dumux/discretization/localview.hh> diff --git a/dumux/freeflow/navierstokes/staggered/fluxoversurface.hh b/dumux/freeflow/navierstokes/staggered/fluxoversurface.hh index 2a90e49ebff370619a3240e5ca09b7ccc1c93e83..aa932dd62d2610ea569d36bca69496afab300b54 100644 --- a/dumux/freeflow/navierstokes/staggered/fluxoversurface.hh +++ b/dumux/freeflow/navierstokes/staggered/fluxoversurface.hh @@ -35,8 +35,8 @@ #include <dune/geometry/multilineargeometry.hh> #include <dumux/common/parameters.hh> -#include <dumux/common/geometry/makegeometry.hh> -#include <dumux/common/geometry/intersectspointgeometry.hh> +#include <dumux/geometry/makegeometry.hh> +#include <dumux/geometry/intersectspointgeometry.hh> #include <dumux/discretization/extrusion.hh> namespace Dumux { diff --git a/dumux/geometry/CMakeLists.txt b/dumux/geometry/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..3e8de14da383d4711b96080ca34610e6189d5a77 --- /dev/null +++ b/dumux/geometry/CMakeLists.txt @@ -0,0 +1,16 @@ +install(FILES +boundingboxtree.hh +diameter.hh +distance.hh +geometricentityset.hh +geometryintersection.hh +grahamconvexhull.hh +intersectingentities.hh +intersectionentityset.hh +intersectspointgeometry.hh +intersectspointsimplex.hh +makegeometry.hh +normal.hh +refinementquadraturerule.hh +triangulation.hh +DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dumux/geometry) diff --git a/dumux/geometry/boundingboxtree.hh b/dumux/geometry/boundingboxtree.hh new file mode 100644 index 0000000000000000000000000000000000000000..85888341c424477bc2f1ff3c7d8101223999f34f --- /dev/null +++ b/dumux/geometry/boundingboxtree.hh @@ -0,0 +1,416 @@ +/***************************************************************************** + * 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 Geometry + * \brief An axis-aligned bounding box volume hierarchy for dune grids + * + * Dumux implementation of an AABB tree + * Inspired by the AABB tree implementation in DOLFIN by Anders Logg which has the + * following license info: DOLFIN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ +#ifndef DUMUX_GEOMETRY_BOUNDINGBOXTREE_HH +#define DUMUX_GEOMETRY_BOUNDINGBOXTREE_HH + +#include <vector> +#include <array> +#include <algorithm> +#include <memory> +#include <numeric> +#include <type_traits> +#include <iostream> + +#include <dune/common/promotiontraits.hh> +#include <dune/common/timer.hh> +#include <dune/common/fvector.hh> + +namespace Dumux { + +/*! + * \ingroup Geometry + * \brief An axis-aligned bounding box volume tree implementation + * + * The class constructs a hierarchical structure of bounding box volumes around + * grid entities. This class can be used to efficiently compute intersections + * between a grid and other geometrical object. It only implements the intersection + * of two of such bounding box trees, so that two independent grids can be intersected. + * \tparam GeometricEntitySet has the following requirements + * * export dimensionworld, ctype + * * a size() member function returning the number of entities + * * begin() and end() member function returning at least forward iterators to entities + * * an index() method returning a consecutive index given an entity + * * an entity() method returning an entity given the consecutive index + * * entities have the following requirements: + * * a member function geometry() returning a geometry with the member functions + * * corner() and corners() returning global coordinates and number of corners + */ +template <class GeometricEntitySet> +class BoundingBoxTree +{ + enum { dimworld = GeometricEntitySet::dimensionworld }; + using ctype = typename GeometricEntitySet::ctype; + + /*! + * \brief Bounding box node data structure + * leaf nodes are indicated by setting child0 to + * the node itself and child1 to the index of the entity in the bounding box. + */ + struct BoundingBoxNode + { + std::size_t child0; + std::size_t child1; + }; + +public: + //! the type of entity set this tree was built with + using EntitySet = GeometricEntitySet; + + //! Default Constructor + BoundingBoxTree() = default; + + //! Constructor with gridView + BoundingBoxTree(std::shared_ptr<const GeometricEntitySet> set) + { build(set); } + + //! Build up bounding box tree for a grid with leafGridView + void build(std::shared_ptr<const GeometricEntitySet> set) + { + // set the pointer to the entity set + entitySet_ = set; + + // clear all internal data + boundingBoxNodes_.clear(); + boundingBoxCoordinates_.clear(); + + // start the timer + Dune::Timer timer; + + // Create bounding boxes for all elements + const auto numLeaves = set->size(); + + // reserve enough space for the nodes and the coordinates + const auto numNodes = 2*numLeaves - 1; + boundingBoxNodes_.reserve(numNodes); + boundingBoxCoordinates_.reserve(numNodes*2*dimworld); + + // create a vector for leaf boxes (min and max for all dims) + std::vector<ctype> leafBoxes(2*dimworld*numLeaves); + + for (const auto& geometricEntity : *set) + computeEntityBoundingBox_(leafBoxes.data() + 2*dimworld*set->index(geometricEntity), geometricEntity); + + // create the leaf partition, the set of available indices (to be sorted) + std::vector<std::size_t> leafPartition(numLeaves); + std::iota(leafPartition.begin(), leafPartition.end(), 0); + + // Recursively build the bounding box tree + build_(leafBoxes, leafPartition.begin(), leafPartition.end()); + + // We are done, log output + std::cout << "Computed bounding box tree with " << numBoundingBoxes() + << " nodes for " << numLeaves << " grid entites in " + << timer.stop() << " seconds." << std::endl; + } + + //! the entity set this tree was built with + const EntitySet& entitySet() const + { return *entitySet_; } + + ///////////////////////////////////////////////////// + //! Interface to be used by other bounding box trees + ///////////////////////////////////////////////////// + + //! Get an existing bounding box for a given node + const BoundingBoxNode& getBoundingBoxNode(std::size_t nodeIdx) const + { return boundingBoxNodes_[nodeIdx]; } + + //! Get an existing bounding box for a given node + const ctype* getBoundingBoxCoordinates(std::size_t nodeIdx) const + { return boundingBoxCoordinates_.data() + 2*dimworld*nodeIdx; } + + //! Get the number of bounding boxes currently in the tree + std::size_t numBoundingBoxes() const + { return boundingBoxNodes_.size(); } + + //! Check whether a bounding box node is a leaf node + //! Leaf nodes have itself as child0 + bool isLeaf(const BoundingBoxNode& node, std::size_t nodeIdx) const + { return node.child0 == nodeIdx; } + +private: + + //! vector of bounding box nodes + std::vector<BoundingBoxNode> boundingBoxNodes_; + + //! vector of bounding box coordinates + std::vector<ctype> boundingBoxCoordinates_; + + //! a pointer to the entity set + std::shared_ptr<const EntitySet> entitySet_; + + //! Compute the bounding box of a grid entity + template <class Entity> + void computeEntityBoundingBox_(ctype* b, const Entity& entity) const + { + // get the bounding box coordinates + ctype* xMin = b; + ctype* xMax = b + dimworld; + + // get mesh entity data + auto geometry = entity.geometry(); + + // Get coordinates of first vertex + auto corner = geometry.corner(0); + for (std::size_t dimIdx = 0; dimIdx < dimworld; ++dimIdx) + xMin[dimIdx] = xMax[dimIdx] = corner[dimIdx]; + + // Compute the min and max over the remaining vertices + for (std::size_t cornerIdx = 1; cornerIdx < geometry.corners(); ++cornerIdx) + { + corner = geometry.corner(cornerIdx); + for (std::size_t dimIdx = 0; dimIdx < dimworld; ++dimIdx) + { + using std::max; + using std::min; + xMin[dimIdx] = min(xMin[dimIdx], corner[dimIdx]); + xMax[dimIdx] = max(xMax[dimIdx], corner[dimIdx]); + } + } + } + + //! Build bounding box tree for all entities recursively + std::size_t build_(const std::vector<ctype>& leafBoxes, + const std::vector<std::size_t>::iterator& begin, + const std::vector<std::size_t>::iterator& end) + { + assert(begin < end); + + // If we reached the end of the recursion, i.e. only a leaf box is left + if (end - begin == 1) + { + // Get the bounding box coordinates for the leaf + const std::size_t leafNodeIdx = *begin; + const auto beginCoords = leafBoxes.begin() + 2*dimworld*leafNodeIdx; + const auto endCoords = beginCoords + 2*dimworld; + + // Store the data in the bounding box + // leaf nodes are indicated by setting child0 to + // the node itself and child1 to the index of the entity in the bounding box. + return addBoundingBox_(BoundingBoxNode{numBoundingBoxes(), leafNodeIdx}, beginCoords, endCoords); + } + + // Compute the bounding box of all bounding boxes in the range [begin, end] + const auto bCoords = computeBBoxOfBBoxes_(leafBoxes, begin, end); + + // sort bounding boxes along the longest axis + const auto axis = computeLongestAxis_(bCoords); + + // nth_element sorts the range to make sure that middle points to the coordinate median in axis direction + // this is the most expensive part of the algorithm + auto middle = begin + (end - begin)/2; + std::nth_element(begin, middle, end, [&leafBoxes, axis](std::size_t i, std::size_t j) + { + const ctype* bi = leafBoxes.data() + 2*dimworld*i; + const ctype* bj = leafBoxes.data() + 2*dimworld*j; + return bi[axis] + bi[axis + dimworld] < bj[axis] + bj[axis + dimworld]; + }); + + // split the bounding boxes into two at the middle iterator and call build recursively, each + // call resulting in a new node of this bounding box, i.e. the root will be added at the end of the process. + return addBoundingBox_(BoundingBoxNode{build_(leafBoxes, begin, middle), build_(leafBoxes, middle, end)}, + bCoords.begin(), bCoords.end()); + } + + //! Add a new bounding box to the tree + template <class Iterator> + std::size_t addBoundingBox_(BoundingBoxNode&& node, + const Iterator& coordBegin, + const Iterator& coordEnd) + { + // Add the bounding box + boundingBoxNodes_.emplace_back(node); + + // Add the bounding box's coordinates + boundingBoxCoordinates_.insert(boundingBoxCoordinates_.end(), coordBegin, coordEnd); + + // return the index of the new node + return boundingBoxNodes_.size() - 1; + } + + //! Compute the bounding box of a vector of bounding boxes + std::array<ctype, 2*dimworld> + computeBBoxOfBBoxes_(const std::vector<ctype>& leafBoxes, + const std::vector<std::size_t>::iterator& begin, + const std::vector<std::size_t>::iterator& end) + { + std::array<ctype, 2*dimworld> bBoxCoords; + + // copy the iterator and get coordinates of first box + auto it = begin; + const auto* bFirst = leafBoxes.data() + 2*dimworld*(*it); + + for (int coordIdx = 0; coordIdx < 2*dimworld; ++coordIdx) + bBoxCoords[coordIdx] = bFirst[coordIdx]; + + // Compute min and max with the remaining boxes + for (; it != end; ++it) + { + const auto* b = leafBoxes.data() + 2*dimworld*(*it); + for (int coordIdx = 0; coordIdx < dimworld; ++coordIdx) + if (b[coordIdx] < bBoxCoords[coordIdx]) + bBoxCoords[coordIdx] = b[coordIdx]; + for (int coordIdx = dimworld; coordIdx < 2*dimworld; ++coordIdx) + if (b[coordIdx] > bBoxCoords[coordIdx]) + bBoxCoords[coordIdx] = b[coordIdx]; + } + + return bBoxCoords; + } + + //! Compute the bounding box of a vector of bounding boxes + std::size_t computeLongestAxis_(const std::array<ctype, 2*dimworld>& bCoords) + { + std::array<ctype, dimworld> axisLength; + for (int coordIdx = 0; coordIdx < dimworld; ++coordIdx) + axisLength[coordIdx] = bCoords[dimworld + coordIdx] - bCoords[coordIdx]; + + return std::distance(axisLength.begin(), std::max_element(axisLength.begin(), axisLength.end())); + } +}; + +/*! + * \brief Check whether a point is intersectin a bounding box (dimworld == 3) + * \param point The point + * \param b Pointer to bounding box coordinates + */ +template<class ctype, int dimworld, typename std::enable_if_t<dimworld == 3, int> = 0> +inline bool intersectsPointBoundingBox(const Dune::FieldVector<ctype, dimworld>& point, const ctype* b) +{ + static constexpr ctype eps_ = 1.0e-7; + + using std::max; + const auto dx = b[3] - b[0]; + const auto dy = b[4] - b[1]; + const auto dz = b[5] - b[2]; + const ctype eps = max({dx, dy, dz})*eps_; + return (b[0] - eps <= point[0] && point[0] <= b[3] + eps && + b[1] - eps <= point[1] && point[1] <= b[4] + eps && + b[2] - eps <= point[2] && point[2] <= b[5] + eps); +} + +/*! + * \brief Check whether a point is intersectin a bounding box (dimworld == 2) + * \param point The point + * \param b Pointer to bounding box coordinates + */ +template<class ctype, int dimworld, typename std::enable_if_t<dimworld == 2, int> = 0> +inline bool intersectsPointBoundingBox(const Dune::FieldVector<ctype, dimworld>& point, const ctype* b) +{ + static constexpr ctype eps_ = 1.0e-7; + + using std::max; + const auto dx = b[2] - b[0]; + const auto dy = b[3] - b[1]; + const ctype eps = max(dx, dy)*eps_; + return (b[0] - eps <= point[0] && point[0] <= b[2] + eps && + b[1] - eps <= point[1] && point[1] <= b[3] + eps); +} + +/*! + * \brief Check whether a point is intersectin a bounding box (dimworld == 1) + * \param point The point + * \param b Pointer to bounding box coordinates + */ +template<class ctype, int dimworld, typename std::enable_if_t<dimworld == 1, int> = 0> +inline bool intersectsPointBoundingBox(const Dune::FieldVector<ctype, dimworld>& point, const ctype* b) +{ + static constexpr ctype eps_ = 1.0e-7; + const ctype eps0 = eps_*(b[1] - b[0]); + return b[0] - eps0 <= point[0] && point[0] <= b[1] + eps0; +} + +/*! + * \ingroup Geometry + * \brief Determine if a point intersects an axis-aligned bounding box + * The bounding box is given by the lower left corner (min) and the upper right corner (max) + */ +template<class ctype, int dimworld> +inline bool intersectsPointBoundingBox(const Dune::FieldVector<ctype, dimworld>& point, + const Dune::FieldVector<ctype, dimworld>& min, + const Dune::FieldVector<ctype, dimworld>& max) +{ + std::array<ctype, 2*dimworld> bBox; + std::copy(min.begin(), min.end(), bBox.begin()); + std::copy(max.begin(), max.end(), bBox.begin()+dimworld); + return intersectsPointBoundingBox(point, bBox.data()); +} + +/*! + * \brief Check whether a bounding box is intersecting another bounding box (dimworld == 3) + * \param a Pointer to first bounding box coordinates + * \param b Pointer to second bounding box coordinates + */ +template<int dimworld, class ctypea, class ctypeb, typename std::enable_if_t<dimworld == 3, int> = 0> +inline bool intersectsBoundingBoxBoundingBox(const ctypea* a, const ctypeb* b) +{ + using ctype = typename Dune::PromotionTraits<ctypea, ctypeb>::PromotedType; + static constexpr ctype eps_ = 1.0e-7; + const ctype eps0 = eps_*std::max(b[3]-b[0], a[3]-a[0]); + const ctype eps1 = eps_*std::max(b[4]-b[1], a[4]-a[1]); + const ctype eps2 = eps_*std::max(b[5]-b[2], a[5]-a[2]); + return (b[0] - eps0 <= a[3] && a[0] <= b[3] + eps0 && + b[1] - eps1 <= a[4] && a[1] <= b[4] + eps1 && + b[2] - eps2 <= a[5] && a[2] <= b[5] + eps2); + +} + +/*! + * \brief Check whether a bounding box is intersecting another bounding box (dimworld == 2) + * \param a Pointer to first bounding box coordinates + * \param b Pointer to second bounding box coordinates + */ +template<int dimworld, class ctypea, class ctypeb, typename std::enable_if_t<dimworld == 2, int> = 0> +inline bool intersectsBoundingBoxBoundingBox(const ctypea* a, const ctypeb* b) +{ + using ctype = typename Dune::PromotionTraits<ctypea, ctypeb>::PromotedType; + static constexpr ctype eps_ = 1.0e-7; + const ctype eps0 = eps_*std::max(b[2]-b[0], a[2]-a[0]); + const ctype eps1 = eps_*std::max(b[3]-b[1], a[3]-a[1]); + return (b[0] - eps0 <= a[2] && a[0] <= b[2] + eps0 && + b[1] - eps1 <= a[3] && a[1] <= b[3] + eps1); +} + +/*! + * \brief Check whether a bounding box is intersecting another bounding box (dimworld == 1) + * \param a Pointer to first bounding box coordinates + * \param b Pointer to second bounding box coordinates + */ +template<int dimworld, class ctypea, class ctypeb, typename std::enable_if_t<dimworld == 1, int> = 0> +inline bool intersectsBoundingBoxBoundingBox(const ctypea* a, const ctypeb* b) +{ + using ctype = typename Dune::PromotionTraits<ctypea, ctypeb>::PromotedType; + static constexpr ctype eps_ = 1.0e-7; + const ctype eps0 = eps_*std::max(b[1]-b[0], a[1]-a[0]); + return b[0] - eps0 <= a[1] && a[0] <= b[1] + eps0; +} + +} // end namespace Dumux + +#endif diff --git a/dumux/geometry/diameter.hh b/dumux/geometry/diameter.hh new file mode 100644 index 0000000000000000000000000000000000000000..8f4eed920295cb1676755fca486d511e6a3f3aaa --- /dev/null +++ b/dumux/geometry/diameter.hh @@ -0,0 +1,49 @@ +/***************************************************************************** + * 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 Geometry + * \brief A function to compute a geometry's diameter, i.e. + * the longest distance between points of a geometry + */ +#ifndef DUMUX_GEOMETRY_DIAMETER_HH +#define DUMUX_GEOMETRY_DIAMETER_HH + +#include <algorithm> + +namespace Dumux { + +/*! + * \ingroup Geometry + * \brief Computes the longest distance between points of a geometry + * \note Useful e.g. to compute the maximum cell diameter of a grid + */ +template<class Geometry> +typename Geometry::ctype diameter(const Geometry& geo) +{ + using std::max; + typename Geometry::ctype h = 0.0; + for (std::size_t i = 0; i < geo.corners(); ++i) + for (std::size_t j = i + 1; j < geo.corners(); ++j) + h = max(h, (geo.corner(i)-geo.corner(j)).two_norm()); + + return h; +} + +} // end namespace Dumux + +#endif diff --git a/dumux/geometry/distance.hh b/dumux/geometry/distance.hh new file mode 100644 index 0000000000000000000000000000000000000000..47fccf32ea9057aed73b1a2b5ce77c33783d20b5 --- /dev/null +++ b/dumux/geometry/distance.hh @@ -0,0 +1,181 @@ +/***************************************************************************** + * 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 Geometry + * \brief Helper functions for distance queries + */ +#ifndef DUMUX_GEOMETRY_DISTANCE_HH +#define DUMUX_GEOMETRY_DISTANCE_HH + +#include <dune/common/fvector.hh> +#include <dune/geometry/quadraturerules.hh> + +namespace Dumux { + +/*! + * \ingroup Geometry + * \brief Compute the average distance from a point to a geometry by integration + */ +template<class Geometry> +inline typename Geometry::ctype +averageDistancePointGeometry(const typename Geometry::GlobalCoordinate& p, + const Geometry& geometry, + std::size_t integrationOrder = 2) +{ + typename Geometry::ctype avgDist = 0.0; + const auto& quad = Dune::QuadratureRules<typename Geometry::ctype, Geometry::mydimension>::rule(geometry.type(), integrationOrder); + for (const auto& qp : quad) + avgDist += (geometry.global(qp.position())-p).two_norm()*qp.weight()*geometry.integrationElement(qp.position()); + return avgDist/geometry.volume(); +} + +/*! + * \ingroup Geometry + * \brief Compute the distance from a point to a line through the points a and b + */ +template<class Point> +inline typename Point::value_type +distancePointLine(const Point& p, const Point& a, const Point& b) +{ + const auto ab = b - a; + const auto t = (p - a)*ab/ab.two_norm2(); + auto proj = a; + proj.axpy(t, ab); + return (proj - p).two_norm(); +} + +/*! + * \ingroup Geometry + * \brief Compute the distance from a point to a line given by a geometry with two corners + * \note We currently lack the a representation of a line geometry. This convenience function + * assumes a segment geometry (with two corners) is passed which represents a line geometry. + */ +template<class Geometry> +inline typename Geometry::ctype +distancePointLine(const typename Geometry::GlobalCoordinate& p, const Geometry& geometry) +{ + static_assert(Geometry::mydimension == 1, "Geometry has to be a line"); + const auto& a = geometry.corner(0); + const auto& b = geometry.corner(1); + return distancePointLine(p, a, b); +} + +/*! + * \ingroup Geometry + * \brief Compute the distance from a point to the segment connecting the points a and b + */ +template<class Point> +inline typename Point::value_type +distancePointSegment(const Point& p, const Point& a, const Point& b) +{ + const auto ab = b - a; + auto t = (p - a)*ab; + + if (t <= 0.0) + return (a - p).two_norm(); + + const auto lengthSq = ab.two_norm2(); + if (t >= lengthSq) + return (b - p).two_norm(); + + auto proj = a; + proj.axpy(t/lengthSq, ab); + return (proj - p).two_norm(); +} + +/*! + * \ingroup Geometry + * \brief Compute the distance from a point to a given segment geometry + */ +template<class Geometry> +inline typename Geometry::ctype +distancePointSegment(const typename Geometry::GlobalCoordinate& p, const Geometry& geometry) +{ + static_assert(Geometry::mydimension == 1, "Geometry has to be a segment"); + const auto& a = geometry.corner(0); + const auto& b = geometry.corner(1); + return distancePointSegment(p, a, b); +} + +/*! + * \ingroup Geometry + * \brief Compute the shortest distance between two points + */ +template<class ctype, int dimWorld> +inline ctype distance(const Dune::FieldVector<ctype, dimWorld>& a, + const Dune::FieldVector<ctype, dimWorld>& b) +{ return (a-b).two_norm(); } + + + +namespace Detail { + +// helper struct to compute distance between two geometries, specialized below +template<class Geo1, class Geo2, + int dimWorld = Geo1::coorddimension, + int dim1 = Geo1::mydimension, int dim2 = Geo2::mydimension> +struct GeometryDistance +{ + static_assert(Geo1::coorddimension == Geo2::coorddimension, "Geometries have to have the same coordinate dimensions"); + static auto distance(const Geo1& geo1, const Geo2& geo2) + { + DUNE_THROW(Dune::NotImplemented, "Geometry distance computation not implemented for dimworld = " + << dimWorld << ", dim1 = " << dim1 << ", dim2 = " << dim2); + } +}; + +// distance point-point +template<class Geo1, class Geo2, int dimWorld> +struct GeometryDistance<Geo1, Geo2, dimWorld, 0, 0> +{ + static_assert(Geo1::coorddimension == Geo2::coorddimension, "Geometries have to have the same coordinate dimensions"); + static auto distance(const Geo1& geo1, const Geo2& geo2) + { return Dumux::distance(geo1.corner(0), geo2.corner(0)); } +}; + +// distance segment-point +template<class Geo1, class Geo2, int dimWorld> +struct GeometryDistance<Geo1, Geo2, dimWorld, 1, 0> +{ + static_assert(Geo1::coorddimension == Geo2::coorddimension, "Geometries have to have the same coordinate dimensions"); + static auto distance(const Geo1& geo1, const Geo2& geo2) + { return distancePointSegment(geo2.corner(0), geo1); } +}; + +// distance point-segment +template<class Geo1, class Geo2, int dimWorld> +struct GeometryDistance<Geo1, Geo2, dimWorld, 0, 1> +{ + static_assert(Geo1::coorddimension == Geo2::coorddimension, "Geometries have to have the same coordinate dimensions"); + static auto distance(const Geo1& geo1, const Geo2& geo2) + { return distancePointSegment(geo1.corner(0), geo2); } +}; + +} // end namespace Detail + +/*! + * \ingroup Geometry + * \brief Compute the shortest distance between two geometries + */ +template<class Geo1, class Geo2> +inline auto distance(const Geo1& geo1, const Geo2& geo2) +{ return Detail::GeometryDistance<Geo1, Geo2>::distance(geo1, geo2); } + +} // end namespace Dumux + +#endif diff --git a/dumux/geometry/geometricentityset.hh b/dumux/geometry/geometricentityset.hh new file mode 100644 index 0000000000000000000000000000000000000000..fbd556d61e08e0b9d6e2269930a0ad855738eb7b --- /dev/null +++ b/dumux/geometry/geometricentityset.hh @@ -0,0 +1,239 @@ +/***************************************************************************** + * 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 Geometry + * \brief An interface for a set of geometric entities + * \note This can be used e.g. to contruct a bounding box volume hierarchy of a grid + * It defines the minimum requirement for such a set + */ +#ifndef DUMUX_GEOMETRY_GEOMETRIC_ENTITY_SET_HH +#define DUMUX_GEOMETRY_GEOMETRIC_ENTITY_SET_HH + +#include <memory> +#include <dune/grid/common/mcmgmapper.hh> +#include <dune/geometry/multilineargeometry.hh> +#include <dumux/common/entitymap.hh> + +namespace Dumux { + +/*! + * \ingroup Geometry + * \brief An interface for a set of geometric entities based on a GridView + * \note This can be used e.g. to contruct a bounding box volume hierarchy of a grid + * It defines the minimum requirement for such a set + */ +template <class GridView, int codim = 0, class Mapper = Dune::MultipleCodimMultipleGeomTypeMapper<GridView>> +class GridViewGeometricEntitySet +{ + using EntityMap = Dumux::EntityMap<GridView, codim>; +public: + using Entity = typename GridView::template Codim<codim>::Entity; + + GridViewGeometricEntitySet(const GridView& gridView) + : GridViewGeometricEntitySet(gridView, Mapper(gridView, Dune::mcmgLayout(Dune::Codim<codim>()))) + {} + + GridViewGeometricEntitySet(const GridView& gridView, const Mapper& mapper) + : gridView_(gridView) + , mapper_(mapper) + , entityMap_(std::make_shared<EntityMap>(gridView.grid(), mapper_)) + {} + + GridViewGeometricEntitySet(const GridView& gridView, + const Mapper& mapper, + std::shared_ptr<const EntityMap> entityMap) + : gridView_(gridView) + , mapper_(mapper) + , entityMap_(entityMap) + {} + + /*! + * \brief The world dimension of the entity set + */ + enum { dimensionworld = GridView::dimensionworld }; + + /*! + * \brief the coordinate type + */ + using ctype = typename GridView::ctype; + + /*! + * \brief the number of entities in this set + */ + decltype(auto) size() const + { return gridView_.size(codim); } + + /*! + * \brief begin iterator to enable range-based for iteration + */ + decltype(auto) begin() const + { return entities(gridView_, Dune::Codim<codim>()).begin(); } + + /*! + * \brief end iterator to enable range-based for iteration + */ + decltype(auto) end() const + { return entities(gridView_, Dune::Codim<codim>()).end(); } + + /*! + * \brief get an entities index + */ + std::size_t index(const Entity& e) const + { return mapper_.index(e); } + + /*! + * \brief get an entity from an index + */ + Entity entity(std::size_t index) const + { return (*entityMap_)[index]; } + +private: + GridView gridView_; + Mapper mapper_; + std::shared_ptr<const EntityMap> entityMap_; + +}; + +/*! + * \ingroup Geometry + * \brief An interface for a set of geometric entities + * \note This can be used e.g. to contruct a bounding box volume hierarchy of a grid + * It defines the minimum requirement for such a set + */ +template<class GeoType> +class GeometriesEntitySet +{ + /*! + * \brief Wrapper to turn a geometry into a geometric entity + */ + class EntityWrapper + { + public: + using Geometry = GeoType; + + /*! + * \brief Constructor + */ + EntityWrapper(const Geometry& geo, const std::size_t index) : geo_(geo), index_(index) {} + + /*! + * \brief Constructor + */ + EntityWrapper(Geometry&& geo, const std::size_t index) : geo_(std::move(geo)), index_(index) {} + + /*! + * \brief Returns the geometry + */ + const Geometry& geometry() const + { return geo_; } + + /*! + * \brief Returns the index of the geometry + */ + std::size_t index() const + { return index_; } + + private: + Geometry geo_; + std::size_t index_; + }; + +public: + using Entity = EntityWrapper; + + /*! + * \brief Constructor for initializer_list + */ + GeometriesEntitySet(std::initializer_list<typename Entity::Geometry>&& geometries) + { + std::size_t index = 0; + // note: std::initializer_list::begin() returns const T*, + // thus no moving will be performed and only the copying ctor of + // EntityWrapper can be called + for (auto&& g : geometries) + entities_.emplace_back(g, index++); + } + + /*! + * \brief Constructor for a vector of geometries + */ + GeometriesEntitySet(const std::vector<typename Entity::Geometry>& geometries) + { + std::size_t index = 0; + for (auto&& g : geometries) + entities_.emplace_back(g, index++); + } + + /*! + * \brief Constructor for a vector of geometries + */ + GeometriesEntitySet(std::vector<typename Entity::Geometry>&& geometries) + { + std::size_t index = 0; + for (auto&& g : geometries) + entities_.emplace_back(std::move(g), index++); + } + + /*! + * \brief The world dimension of the entity set + */ + enum { dimensionworld = Entity::Geometry::coorddimension }; + + /*! + * \brief the coordinate type + */ + using ctype = typename Entity::Geometry::ctype; + + /*! + * \brief the number of entities in this set + */ + decltype(auto) size() const + { return entities_.size(); } + + /*! + * \brief begin iterator to enable range-based for iteration + */ + decltype(auto) begin() const + { return entities_.begin(); } + + /*! + * \brief end iterator to enable range-based for iteration + */ + decltype(auto) end() const + { return entities_.end(); } + + /*! + * \brief get an entities index + */ + template<class Entity> + std::size_t index(const Entity& e) const + { return e.index(); } + + /*! + * \brief get an entity from an index + */ + Entity entity(std::size_t index) const + { return entities_[index]; } + +private: + std::vector<Entity> entities_; +}; + +} // end namespace Dumux + +#endif diff --git a/dumux/geometry/geometryintersection.hh b/dumux/geometry/geometryintersection.hh new file mode 100644 index 0000000000000000000000000000000000000000..d07655c5ecd1112bfe5dbee29e963f6995a772c7 --- /dev/null +++ b/dumux/geometry/geometryintersection.hh @@ -0,0 +1,1387 @@ +/***************************************************************************** + * 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 Geometry + * \brief A class for collision detection of two geometries + * and computation of intersection corners + */ +#ifndef DUMUX_GEOMETRY_INTERSECTION_HH +#define DUMUX_GEOMETRY_INTERSECTION_HH + +#include <tuple> + +#include <dune/common/exceptions.hh> +#include <dune/common/promotiontraits.hh> +#include <dune/geometry/multilineargeometry.hh> + +#include <dumux/common/math.hh> +#include <dumux/geometry/intersectspointgeometry.hh> +#include <dumux/geometry/grahamconvexhull.hh> +#include <dumux/geometry/boundingboxtree.hh> + +namespace Dumux { +namespace IntersectionPolicy { + +//! Policy structure for point-like intersections +template<class ct, int dw> +struct PointPolicy +{ + using ctype = ct; + using Point = Dune::FieldVector<ctype, dw>; + + static constexpr int dimWorld = dw; + static constexpr int dimIntersection = 0; + using Intersection = Point; +}; + +//! Policy structure for segment-like intersections +template<class ct, int dw> +struct SegmentPolicy +{ + using ctype = ct; + using Point = Dune::FieldVector<ctype, dw>; + using Segment = std::array<Point, 2>; + + static constexpr int dimWorld = dw; + static constexpr int dimIntersection = 1; + using Intersection = Segment; +}; + +//! Policy structure for polygon-like intersections +template<class ct, int dw> +struct PolygonPolicy +{ + using ctype = ct; + using Point = Dune::FieldVector<ctype, dw>; + using Polygon = std::vector<Point>; + + static constexpr int dimWorld = dw; + static constexpr int dimIntersection = 2; + using Intersection = Polygon; +}; + +//! Policy structure for polyhedron-like intersections +template<class ct, int dw> +struct PolyhedronPolicy +{ + using ctype = ct; + using Point = Dune::FieldVector<ctype, dw>; + using Polyhedron = std::vector<Point>; + + static constexpr int dimWorld = dw; + static constexpr int dimIntersection = 3; + using Intersection = Polyhedron; +}; + +//! default policy chooser class +template<class Geometry1, class Geometry2> +class DefaultPolicyChooser +{ + static constexpr int dimworld = Geometry1::coorddimension; + static constexpr int isDim = std::min( int(Geometry1::mydimension), int(Geometry2::mydimension) ); + static_assert(dimworld == int(Geometry2::coorddimension), + "Geometries must have the same coordinate dimension!"); + static_assert(int(Geometry1::mydimension) <= 3 && int(Geometry2::mydimension) <= 3, + "Geometries must have dimension 3 or less."); + using ctype = typename Dune::PromotionTraits<typename Geometry1::ctype, typename Geometry2::ctype>::PromotedType; + + using DefaultPolicies = std::tuple<PointPolicy<ctype, dimworld>, + SegmentPolicy<ctype, dimworld>, + PolygonPolicy<ctype, dimworld>, + PolyhedronPolicy<ctype, dimworld>>; +public: + using type = std::tuple_element_t<isDim, DefaultPolicies>; +}; + +//! Helper alias to define the default intersection policy +template<class Geometry1, class Geometry2> +using DefaultPolicy = typename DefaultPolicyChooser<Geometry1, Geometry2>::type; + +} // end namespace IntersectionPolicy + +namespace Detail { + +/*! + * \ingroup Geometry + * \brief Algorithm to find segment-like intersections of a polgon/polyhedron with a + * segment. The result is stored in the form of the local coordinates tfirst + * and tlast on the segment geo1. + * \param geo1 the first geometry + * \param geo2 the second geometry (dimGeo2 < dimGeo1) + * \param baseEps the base epsilon used for floating point comparisons + * \param tfirst stores the local coordinate of beginning of intersection segment + * \param tlast stores the local coordinate of the end of intersection segment + * \param getFacetCornerIndices Function to obtain the facet corner indices on geo1 + * \param computeNormal Function to obtain the normal vector on a facet on geo1 + */ +template< class Geo1, class Geo2, class ctype, + class GetFacetCornerIndices, class ComputeNormalFunction > +bool computeSegmentIntersection(const Geo1& geo1, const Geo2& geo2, ctype baseEps, + ctype& tfirst, ctype& tlast, + const GetFacetCornerIndices& getFacetCornerIndices, + const ComputeNormalFunction& computeNormal) +{ + static_assert(int(Geo2::mydimension) == 1, "Geometry2 must be a segment"); + static_assert(int(Geo1::mydimension) > int(Geo2::mydimension), + "Dimension of Geometry1 must be higher than that of Geometry2"); + + const auto a = geo2.corner(0); + const auto b = geo2.corner(1); + const auto d = b - a; + + // The initial interval is the whole segment. + // Afterwards we start clipping the interval + // at the intersections with the facets of geo1 + tfirst = 0.0; + tlast = 1.0; + + const auto facets = getFacetCornerIndices(geo1); + for (const auto& f : facets) + { + const auto n = computeNormal(f); + + const auto c0 = geo1.corner(f[0]); + const ctype denom = n*d; + const ctype dist = n*(a-c0); + + // use first edge of the facet to scale eps + const auto edge1 = geo1.corner(f[1]) - geo1.corner(f[0]); + const ctype eps = baseEps*edge1.two_norm(); + + // if denominator is zero the segment in parallel to the edge. + // If the distance is positive there is no intersection + using std::abs; + if (abs(denom) < eps) + { + if (dist > eps) + return false; + } + else // not parallel: compute line-line intersection + { + const ctype t = -dist / denom; + // if entering half space cut tfirst if t is larger + using std::signbit; + if (signbit(denom)) + { + if (t > tfirst) + tfirst = t; + } + // if exiting half space cut tlast if t is smaller + else + { + if (t < tlast) + tlast = t; + } + // there is no intersection of the interval is empty + // use unscaled epsilon since t is in local coordinates + if (tfirst > tlast - baseEps) + return false; + } + } + + // If we made it until here an intersection exists. + return true; +} + +} // end namespace Detail + +/*! + * \ingroup Geometry + * \brief A class for geometry collision detection and intersection calculation + * The class can be specialized for combinations of dimworld, dim1, dim2, where + * dimworld is the world dimension embedding a grid of dim1 and a grid of dim2. + */ +template +<class Geometry1, class Geometry2, + class Policy = IntersectionPolicy::DefaultPolicy<Geometry1, Geometry2>, + int dimworld = Geometry1::coorddimension, + int dim1 = Geometry1::mydimension, + int dim2 = Geometry2::mydimension> +class GeometryIntersection +{ + static constexpr int dimWorld = Policy::dimWorld; + +public: + using ctype = typename Policy::ctype; + using Point = typename Policy::Point; + using Intersection = typename Policy::Intersection; + + //! Determine if the two geometries intersect and compute the intersection geometry + static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) + { + static_assert(dimworld == Geometry2::coorddimension, "Can only intersect geometries of same coordinate dimension"); + DUNE_THROW(Dune::NotImplemented, "Geometry intersection detection with intersection computation not implemented for dimworld = " + << dimworld << ", dim1 = " << dim1 << ", dim2 = " << dim2); + } +}; + +/*! + * \ingroup Geometry + * \brief A class for segment--segment intersection in 2d space + */ +template <class Geometry1, class Geometry2, class Policy> +class GeometryIntersection<Geometry1, Geometry2, Policy, 2, 1, 1> +{ + enum { dimworld = 2 }; + enum { dim1 = 1 }; + enum { dim2 = 1 }; + + // base epsilon for floating point comparisons + static constexpr typename Policy::ctype eps_ = 1.5e-7; + +public: + using ctype = typename Policy::ctype; + using Point = typename Policy::Point; + using Intersection = typename Policy::Intersection; + + /*! + * \brief Colliding two segments + * \param geo1/geo2 The geometries to intersect + * \param intersection The intersection point + * \note This overload is used when point-like intersections are seeked + */ + template<class P = Policy, std::enable_if_t<P::dimIntersection == 0, int> = 0> + static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) + { + static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); + + const auto v1 = geo1.corner(1) - geo1.corner(0); + const auto v2 = geo2.corner(1) - geo2.corner(0); + const auto ac = geo2.corner(0) - geo1.corner(0); + + auto n2 = Point({-1.0*v2[1], v2[0]}); + n2 /= n2.two_norm(); + + // compute distance of first corner on geo2 to line1 + const auto dist12 = n2*ac; + + // first check parallel segments + using std::abs; + using std::sqrt; + + const auto v1Norm2 = v1.two_norm2(); + const auto eps = eps_*sqrt(v1Norm2); + const auto eps2 = eps_*v1Norm2; + + const auto sp = n2*v1; + if (abs(sp) < eps) + { + if (abs(dist12) > eps) + return false; + + // point intersection can only be one of the corners + if ( (v1*v2) < 0.0 ) + { + if ( ac.two_norm2() < eps2 ) + { intersection = geo2.corner(0); return true; } + + if ( (geo2.corner(1) - geo1.corner(1)).two_norm2() < eps2 ) + { intersection = geo2.corner(1); return true; } + } + else + { + if ( (geo2.corner(1) - geo1.corner(0)).two_norm2() < eps2 ) + { intersection = geo2.corner(1); return true; } + + if ( (geo2.corner(0) - geo1.corner(1)).two_norm2() < eps2 ) + { intersection = geo2.corner(0); return true; } + } + + // no intersection + return false; + } + + // intersection point on v1 in local coords + const auto t1 = dist12 / sp; + + // check if the local coords are valid + if (t1 < -1.0*eps_ || t1 > 1.0 + eps_) + return false; + + // compute global coordinates + auto isPoint = geo1.global(t1); + + // check if point is in bounding box of v2 + const auto c = geo2.corner(0); + const auto d = geo2.corner(1); + + using std::min; using std::max; + std::array<ctype, 4> bBox({ min(c[0], d[0]), min(c[1], d[1]), max(c[0], d[0]), max(c[1], d[1]) }); + + if ( intersectsPointBoundingBox(isPoint, bBox.data()) ) + { + intersection = std::move(isPoint); + return true; + } + + return false; + } + + /*! + * \brief Colliding two segments + * \param geo1/geo2 The geometries to intersect + * \param intersection Container to store corners of intersection segment + * \note this overload is used when segment-like intersections are seeked + */ + template<class P = Policy, std::enable_if_t<P::dimIntersection == 1, int> = 0> + static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) + { + static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); + + const auto& a = geo1.corner(0); + const auto& b = geo1.corner(1); + const auto ab = b - a; + + const auto& p = geo2.corner(0); + const auto& q = geo2.corner(1); + const auto pq = q - p; + Dune::FieldVector<ctype, dimworld> n2{-pq[1], pq[0]}; + + using std::max; + const auto abNorm2 = ab.two_norm2(); + const auto pqNorm2 = pq.two_norm2(); + const auto eps2 = eps_*max(abNorm2, pqNorm2); + + // non-parallel segments do not intersect in a segment. + using std::abs; + if (abs(n2*ab) > eps2) + return false; + + // check if the segments lie on the same line + const auto ap = p - a; + if (abs(ap*n2) > eps2) + return false; + + // compute scaled local coordinates of corner 1/2 of segment2 on segment1 + auto t1 = ab*ap; + auto t2 = ab*(q - a); + + using std::swap; + if (t1 > t2) + swap(t1, t2); + + using std::clamp; + t1 = clamp(t1, 0.0, abNorm2); + t2 = clamp(t2, 0.0, abNorm2); + + if (abs(t2-t1) < eps2) + return false; + + intersection = Intersection({geo1.global(t1/abNorm2), geo1.global(t2/abNorm2)}); + return true; + } +}; + +/*! + * \ingroup Geometry + * \brief A class for polygon--segment intersection in 2d space + */ +template <class Geometry1, class Geometry2, class Policy> +class GeometryIntersection<Geometry1, Geometry2, Policy, 2, 2, 1> +{ + enum { dimworld = 2 }; + enum { dim1 = 2 }; + enum { dim2 = 1 }; + +public: + using ctype = typename Policy::ctype; + using Point = typename Policy::Point; + using Intersection = typename Policy::Intersection; + +private: + static constexpr ctype eps_ = 1.5e-7; // base epsilon for floating point comparisons + +public: + /*! + * \brief Colliding segment and convex polygon + * \param geo1/geo2 The geometries to intersect + * \param intersection If the geometries collide intersection holds the + * corner points of the intersection object in global coordinates. + * \note This overload is used when segment-like intersections are seeked. + */ + template<class P = Policy, std::enable_if_t<P::dimIntersection == 1, int> = 0> + static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) + { + static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); + + ctype tfirst, tlast; + if (intersect_(geo1, geo2, tfirst, tlast)) + { + // the intersection exists. Export the intersections geometry now: + // s(t) = a + t(b-a) in [tfirst, tlast] + intersection = Intersection({geo2.global(tfirst), geo2.global(tlast)}); + return true; + } + + return false; + } + + /*! + * \brief Colliding segment and convex polygon + * \param geo1/geo2 The geometries to intersect + * \param intersection If the geometries collide intersection holds the + * corner points of the intersection object in global coordinates. + * \note This overload is used when point-like intersections are seeked. + */ + template<class P = Policy, std::enable_if_t<P::dimIntersection == 0, int> = 0> + static bool intersection(const Geometry1& geo1, const Geometry2& geo2, typename P::Intersection& intersection) + { + static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); + + ctype tfirst, tlast; + if (!intersect_(geo1, geo2, tfirst, tlast)) + { + // if start & end point are same, the intersection is a point + using std::abs; + if ( tfirst > tlast - eps_ ) + { + intersection = Intersection(geo2.global(tfirst)); + return true; + } + } + + return false; + } + +private: + /*! + * \brief Obtain local coordinates of start/end point of the intersecting segment. + */ + static bool intersect_(const Geometry1& geo1, const Geometry2& geo2, ctype& tfirst, ctype& tlast) + { + // lambda to obtain the facet corners on geo1 + auto getFacetCorners = [] (const Geometry1& geo1) + { + std::vector< std::array<int, 2> > facetCorners; + switch (geo1.corners()) + { + case 4: // quadrilateral + facetCorners = {{0, 2}, {3, 1}, {1, 0}, {2, 3}}; + break; + case 3: // triangle + facetCorners = {{1, 0}, {0, 2}, {2, 1}}; + break; + default: + DUNE_THROW(Dune::NotImplemented, "Collision of segment and geometry of type " + << geo1.type() << " with "<< geo1.corners() << " corners."); + } + + return facetCorners; + }; + + // lambda to obtain the normal on a facet + const auto center1 = geo1.center(); + auto computeNormal = [&geo1, ¢er1] (const std::array<int, 2>& facetCorners) + { + const auto c0 = geo1.corner(facetCorners[0]); + const auto c1 = geo1.corner(facetCorners[1]); + const auto edge = c1 - c0; + + Dune::FieldVector<ctype, dimworld> n({-1.0*edge[1], edge[0]}); + n /= n.two_norm(); + + // orientation of the element is not known + // make sure normal points outwards of element + if ( n*(center1-c0) > 0.0 ) + n *= -1.0; + + return n; + }; + + return Detail::computeSegmentIntersection(geo1, geo2, eps_, tfirst, tlast, getFacetCorners, computeNormal); + } +}; + +/*! + * \ingroup Geometry + * \brief A class for segment--polygon intersection in 2d space + */ +template <class Geometry1, class Geometry2, class Policy> +class GeometryIntersection<Geometry1, Geometry2, Policy, 2, 1, 2> +: public GeometryIntersection<Geometry2, Geometry1, Policy, 2, 2, 1> +{ + using Base = GeometryIntersection<Geometry2, Geometry1, Policy, 2, 2, 1>; + +public: + /*! + * \brief Colliding segment and convex polygon + * \param geo1/geo2 The geometries to intersect + * \param intersection If the geometries collide intersection holds the + * corner points of the intersection object in global coordinates. + * \note This forwards to the polygon-segment specialization with swapped arguments. + */ + template<class P = Policy> + static bool intersection(const Geometry1& geo1, const Geometry2& geo2, typename Base::Intersection& intersection) + { + return Base::intersection(geo2, geo1, intersection); + } +}; + +/*! + * \ingroup Geometry + * \brief A class for polygon--polygon intersection in 2d space + */ +template <class Geometry1, class Geometry2, class Policy> +class GeometryIntersection<Geometry1, Geometry2, Policy, 2, 2, 2> +{ + enum { dimworld = 2 }; + enum { dim1 = 2 }; + enum { dim2 = 2 }; + +public: + using ctype = typename Policy::ctype; + using Point = typename Policy::Point; + using Intersection = typename Policy::Intersection; + +private: + static constexpr ctype eps_ = 1.5e-7; // base epsilon for floating point comparisons + +public: + /*! + * \brief Colliding two polygons + * \note First we find the vertex candidates for the intersection region as follows: + * Add polygon vertices that are inside the other polygon + * Add intersections of polygon edges + * Remove duplicate points from the list + * Compute the convex hull polygon + * Return a triangulation of that polygon as intersection + * \param geo1/geo2 The geometries to intersect + * \param intersection Container to store the corner points of the polygon (as convex hull) + * \note This overload is used when polygon like intersections are seeked + */ + template<class P = Policy, std::enable_if_t<P::dimIntersection == 2, int> = 0> + static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) + { + static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); + + // the candidate intersection points + std::vector<Point> points; points.reserve(6); + + // add polygon1 corners that are inside polygon2 + for (int i = 0; i < geo1.corners(); ++i) + if (intersectsPointGeometry(geo1.corner(i), geo2)) + points.emplace_back(geo1.corner(i)); + + const auto numPoints1 = points.size(); + if (numPoints1 != geo1.corners()) + { + // add polygon2 corners that are inside polygon1 + for (int i = 0; i < geo2.corners(); ++i) + if (intersectsPointGeometry(geo2.corner(i), geo1)) + points.emplace_back(geo2.corner(i)); + + if (points.empty()) + return false; + + if (points.size() - numPoints1 != geo2.corners()) + { + const auto refElement1 = referenceElement(geo1); + const auto refElement2 = referenceElement(geo2); + + // add intersections of edges + using SegGeometry = Dune::MultiLinearGeometry<ctype, 1, dimworld>; + using PointPolicy = IntersectionPolicy::PointPolicy<ctype, dimworld>; + for (int i = 0; i < refElement1.size(dim1-1); ++i) + { + const auto localEdgeGeom1 = refElement1.template geometry<dim1-1>(i); + const auto edge1 = SegGeometry( Dune::GeometryTypes::line, + std::vector<Point>( {geo1.global(localEdgeGeom1.corner(0)), + geo1.global(localEdgeGeom1.corner(1))} )); + + for (int j = 0; j < refElement2.size(dim2-1); ++j) + { + const auto localEdgeGeom2 = refElement2.template geometry<dim2-1>(j); + const auto edge2 = SegGeometry( Dune::GeometryTypes::line, + std::vector<Point>( {geo2.global(localEdgeGeom2.corner(0)), + geo2.global(localEdgeGeom2.corner(1))} )); + + using EdgeTest = GeometryIntersection<SegGeometry, SegGeometry, PointPolicy>; + typename EdgeTest::Intersection edgeIntersection; + if (EdgeTest::intersection(edge1, edge2, edgeIntersection)) + points.emplace_back(edgeIntersection); + } + } + } + } + + if (points.empty()) + return false; + + // remove duplicates + const auto eps = (geo1.corner(0) - geo1.corner(1)).two_norm()*eps_; + std::sort(points.begin(), points.end(), [&eps](const auto& a, const auto& b) -> bool + { + using std::abs; + return (abs(a[0]-b[0]) > eps ? a[0] < b[0] : a[1] < b[1]); + }); + + auto removeIt = std::unique(points.begin(), points.end(), [&eps](const auto& a, const auto&b) + { + return (b-a).two_norm() < eps; + }); + + points.erase(removeIt, points.end()); + + // return false if we don't have at least three unique points + if (points.size() < 3) + return false; + + // intersection polygon is convex hull of above points + intersection = grahamConvexHull<2>(points); + assert(!intersection.empty()); + return true; + } + + /*! + * \brief Colliding two polygons + * \param geo1/geo2 The geometries to intersect + * \param intersection Container to store the corners of intersection segment + * \note this overload is used when segment-like intersections are seeked + * \todo implement this query + */ + template<class P = Policy, std::enable_if_t<P::dimIntersection == 1, int> = 0> + static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) + { + static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); + DUNE_THROW(Dune::NotImplemented, "Polygon-polygon intersection detection for segment-like intersections"); + } + + /*! + * \brief Colliding two polygons + * \param geo1/geo2 The geometries to intersect + * \param intersection The intersection point + * \note this overload is used when point-like intersections are seeked + * \todo implement this query + */ + template<class P = Policy, std::enable_if_t<P::dimIntersection == 0, int> = 0> + static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) + { + static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); + DUNE_THROW(Dune::NotImplemented, "Polygon-polygon intersection detection for touching points"); + } +}; + +/*! + * \ingroup Geometry + * \brief A class for polyhedron--segment intersection in 3d space + */ +template <class Geometry1, class Geometry2, class Policy> +class GeometryIntersection<Geometry1, Geometry2, Policy, 3, 3, 1> +{ + enum { dimworld = 3 }; + enum { dim1 = 3 }; + enum { dim2 = 1 }; + +public: + using ctype = typename Policy::ctype; + using Point = typename Policy::Point; + using Intersection = typename Policy::Intersection; + +private: + static constexpr ctype eps_ = 1.5e-7; // base epsilon for floating point comparisons + +public: + /*! + * \brief Colliding segment and convex polyhedron + * \note Algorithm based on the one from "Real-Time Collision Detection" by Christer Ericson, + * published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc. + * Basis is the theorem that for any two non-intersecting convex polyhedrons + * a separating plane exists. + * \param geo1/geo2 The geometries to intersect + * \param intersection If the geometries collide intersection holds the corner points of + * the intersection object in global coordinates. + * \note This overload is used when segment-like intersections are seeked. + */ + template<class P = Policy, std::enable_if_t<P::dimIntersection == 1, int> = 0> + static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) + { + static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); + + ctype tfirst, tlast; + if (intersect_(geo1, geo2, tfirst, tlast)) + { + // Intersection exists. Export the intersections geometry now: + // s(t) = a + t(b-a) in [tfirst, tlast] + intersection = Intersection({geo2.global(tfirst), geo2.global(tlast)}); + return true; + } + + return false; + } + + /*! + * \brief Colliding segment and convex polyhedron + * \note Algorithm based on the one from "Real-Time Collision Detection" by Christer Ericson, + * published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc. + * Basis is the theorem that for any two non-intersecting convex polyhedrons + * a separating plane exists. + * \param geo1/geo2 The geometries to intersect + * \param intersection If the geometries collide intersection holds the corner points of + * the intersection object in global coordinates. + * \note This overload is used when point-like intersections are seeked. + */ + template<class P = Policy, std::enable_if_t<P::dimIntersection == 0, int> = 0> + static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) + { + static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); + + ctype tfirst, tlast; + if (intersect_(geo1, geo2, tfirst, tlast)) + { + // if start & end point are same, the intersection is a point + using std::abs; + if ( abs(tfirst - tlast) < eps_ ) + { + intersection = Intersection(geo2.global(tfirst)); + return true; + } + } + + return false; + } + +private: + /*! + * \brief Obtain local coordinates of start/end point of the intersecting segment. + */ + static bool intersect_(const Geometry1& geo1, const Geometry2& geo2, ctype& tfirst, ctype& tlast) + { + // lambda to obtain facet corners on geo1 + auto getFacetCorners = [] (const Geometry1& geo1) + { + std::vector< std::vector<int> > facetCorners; + // sort facet corner so that normal n = (p1-p0)x(p2-p0) always points outwards + switch (geo1.corners()) + { + // todo compute intersection geometries + case 8: // hexahedron + facetCorners = {{2, 0, 6, 4}, {1, 3, 5, 7}, {0, 1, 4, 5}, + {3, 2, 7, 6}, {1, 0, 3, 2}, {4, 5, 6, 7}}; + break; + case 4: // tetrahedron + facetCorners = {{1, 0, 2}, {0, 1, 3}, {0, 3, 2}, {1, 2, 3}}; + break; + default: + DUNE_THROW(Dune::NotImplemented, "Collision of segment and geometry of type " + << geo1.type() << ", "<< geo1.corners() << " corners."); + } + + return facetCorners; + }; + + // lambda to obtain the normal on a facet + auto computeNormal = [&geo1] (const std::vector<int>& facetCorners) + { + const auto v0 = geo1.corner(facetCorners[1]) - geo1.corner(facetCorners[0]); + const auto v1 = geo1.corner(facetCorners[2]) - geo1.corner(facetCorners[0]); + + auto n = crossProduct(v0, v1); + n /= n.two_norm(); + + return n; + }; + + return Detail::computeSegmentIntersection(geo1, geo2, eps_, tfirst, tlast, getFacetCorners, computeNormal); + } +}; + +/*! + * \ingroup Geometry + * \brief A class for segment--polyhedron intersection in 3d space + */ +template <class Geometry1, class Geometry2, class Policy> +class GeometryIntersection<Geometry1, Geometry2, Policy, 3, 1, 3> +: public GeometryIntersection<Geometry2, Geometry1, Policy, 3, 3, 1> +{ + using Base = GeometryIntersection<Geometry2, Geometry1, Policy, 3, 3, 1>; +public: + /*! + * \brief Colliding segment and convex polyhedron + * \param geo1/geo2 The geometries to intersect + * \param intersection If the geometries collide intersection holds the + * corner points of the intersection object in global coordinates. + * \note This forwards to the polyhedron-segment specialization with swapped arguments. + */ + template<class P = Policy> + static bool intersection(const Geometry1& geo1, const Geometry2& geo2, typename Base::Intersection& intersection) + { + return Base::intersection(geo2, geo1, intersection); + } +}; + +/*! + * \ingroup Geometry + * \brief A class for polyhedron--polygon intersection in 3d space + */ +template <class Geometry1, class Geometry2, class Policy> +class GeometryIntersection<Geometry1, Geometry2, Policy, 3, 3, 2> +{ + enum { dimworld = 3 }; + enum { dim1 = 3 }; + enum { dim2 = 2 }; + +public: + using ctype = typename Policy::ctype; + using Point = typename Policy::Point; + using Intersection = typename Policy::Intersection; + +private: + static constexpr ctype eps_ = 1.5e-7; // base epsilon for floating point comparisons + +public: + /*! + * \brief Colliding polygon and convex polyhedron + * \note First we find the vertex candidates for the intersection region as follows: + * Add triangle vertices that are inside the tetrahedron + * Add tetrahedron vertices that are inside the triangle + * Add all intersection points of tetrahedron edges (codim 2) with the triangle (codim 0) (6*1 tests) + * Add all intersection points of triangle edges (codim 1) with tetrahedron faces (codim 1) (3*4 tests) + * Remove duplicate points from the list + * Compute the convex hull polygon + * Return a triangulation of that polygon as intersection + * \param geo1/geo2 The geometries to intersect + * \param intersection Container to store the corner points of the polygon (as convex hull) + * \note This overload is used when polygon like intersections are seeked + */ + template<class P = Policy, std::enable_if_t<P::dimIntersection == 2, int> = 0> + static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) + { + static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); + + // the candidate intersection points + std::vector<Point> points; points.reserve(10); + + // add 3d geometry corners that are inside the 2d geometry + for (int i = 0; i < geo1.corners(); ++i) + if (intersectsPointGeometry(geo1.corner(i), geo2)) + points.emplace_back(geo1.corner(i)); + + // add 2d geometry corners that are inside the 3d geometry + for (int i = 0; i < geo2.corners(); ++i) + if (intersectsPointGeometry(geo2.corner(i), geo1)) + points.emplace_back(geo2.corner(i)); + + // get some geometry types + using PolyhedronFaceGeometry = Dune::MultiLinearGeometry<ctype, 2, dimworld>; + using SegGeometry = Dune::MultiLinearGeometry<ctype, 1, dimworld>; + + const auto refElement1 = referenceElement(geo1); + const auto refElement2 = referenceElement(geo2); + + // intersection policy for point-like intersections (used later) + using PointPolicy = IntersectionPolicy::PointPolicy<ctype, dimworld>; + + // add intersection points of all polyhedron edges (codim dim-1) with the polygon + for (int i = 0; i < refElement1.size(dim1-1); ++i) + { + const auto localEdgeGeom = refElement1.template geometry<dim1-1>(i); + const auto p = geo1.global(localEdgeGeom.corner(0)); + const auto q = geo1.global(localEdgeGeom.corner(1)); + const auto segGeo = SegGeometry(Dune::GeometryTypes::line, std::vector<Point>{p, q}); + + using PolySegTest = GeometryIntersection<Geometry2, SegGeometry, PointPolicy>; + typename PolySegTest::Intersection polySegIntersection; + if (PolySegTest::intersection(geo2, segGeo, polySegIntersection)) + points.emplace_back(polySegIntersection); + } + + // add intersection points of all polygon faces (codim 1) with the polyhedron faces + for (int i = 0; i < refElement1.size(1); ++i) + { + const auto faceGeo = [&]() + { + const auto localFaceGeo = refElement1.template geometry<1>(i); + if (localFaceGeo.corners() == 4) + { + const auto a = geo1.global(localFaceGeo.corner(0)); + const auto b = geo1.global(localFaceGeo.corner(1)); + const auto c = geo1.global(localFaceGeo.corner(2)); + const auto d = geo1.global(localFaceGeo.corner(3)); + + return PolyhedronFaceGeometry(Dune::GeometryTypes::cube(2), std::vector<Point>{a, b, c, d}); + } + else + { + const auto a = geo1.global(localFaceGeo.corner(0)); + const auto b = geo1.global(localFaceGeo.corner(1)); + const auto c = geo1.global(localFaceGeo.corner(2)); + + return PolyhedronFaceGeometry(Dune::GeometryTypes::simplex(2), std::vector<Point>{a, b, c}); + } + }(); + + for (int j = 0; j < refElement2.size(1); ++j) + { + const auto localEdgeGeom = refElement2.template geometry<1>(j); + const auto p = geo2.global(localEdgeGeom.corner(0)); + const auto q = geo2.global(localEdgeGeom.corner(1)); + + const auto segGeo = SegGeometry(Dune::GeometryTypes::line, std::vector<Point>{p, q}); + + using PolySegTest = GeometryIntersection<PolyhedronFaceGeometry, SegGeometry, PointPolicy>; + typename PolySegTest::Intersection polySegIntersection; + if (PolySegTest::intersection(faceGeo, segGeo, polySegIntersection)) + points.emplace_back(polySegIntersection); + } + } + + // return if no intersection points were found + if (points.empty()) return false; + + // remove duplicates + const auto eps = (geo1.corner(0) - geo1.corner(1)).two_norm()*eps_; + std::sort(points.begin(), points.end(), [&eps](const auto& a, const auto& b) -> bool + { + using std::abs; + return (abs(a[0]-b[0]) > eps ? a[0] < b[0] : (abs(a[1]-b[1]) > eps ? a[1] < b[1] : (a[2] < b[2]))); + }); + + auto removeIt = std::unique(points.begin(), points.end(), [&eps](const auto& a, const auto&b) + { + return (b-a).two_norm() < eps; + }); + + points.erase(removeIt, points.end()); + + // return false if we don't have more than three unique points + if (points.size() < 3) return false; + + // intersection polygon is convex hull of above points + intersection = grahamConvexHull<2>(points); + assert(!intersection.empty()); + + return true; + } + + /*! + * \brief Colliding segment and convex polyhedron + * \note First we find the vertex candidates for the intersection region as follows: + * Add triangle vertices that are inside the tetrahedron + * Add tetrahedron vertices that are inside the triangle + * Add all intersection points of tetrahedron edges (codim 2) with the triangle (codim 0) (6*1 tests) + * Add all intersection points of triangle edges (codim 1) with tetrahedron faces (codim 1) (3*4 tests) + * Remove duplicate points from the list + * Compute the convex hull polygon + * Return a triangulation of that polygon as intersection + * \param geo1/geo2 The geometries to intersect + * \param intersection Container to store the intersection result + * \todo implement overloads for segment or point-like intersections + */ + template<class P = Policy, std::enable_if_t<P::dimIntersection != 2, int> = 0> + static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) + { + static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); + DUNE_THROW(Dune::NotImplemented, "Polyhedron-polygon intersection detection only implemented for polygon-like intersections"); + } +}; + +/*! + * \ingroup Geometry + * \brief A class for polygon--polyhedron intersection in 3d space + */ +template <class Geometry1, class Geometry2, class Policy> +class GeometryIntersection<Geometry1, Geometry2, Policy, 3, 2, 3> +: public GeometryIntersection<Geometry2, Geometry1, Policy, 3, 3, 2> +{ + using Base = GeometryIntersection<Geometry2, Geometry1, Policy, 3, 3, 2>; +public: + /*! + * \brief Colliding polygon and convex polyhedron + * \param geo1/geo2 The geometries to intersect + * \param intersection If the geometries collide intersection holds the + * corner points of the intersection object in global coordinates. + * \note This forwards to the polyhedron-polygon specialization with swapped arguments. + */ + template<class P = Policy> + static bool intersection(const Geometry1& geo1, const Geometry2& geo2, typename Base::Intersection& intersection) + { + return Base::intersection(geo2, geo1, intersection); + } +}; + +/*! + * \ingroup Geometry + * \brief A class for polygon--segment intersection in 3d space + */ +template <class Geometry1, class Geometry2, class Policy> +class GeometryIntersection<Geometry1, Geometry2, Policy, 3, 2, 1> +{ + enum { dimworld = 3 }; + enum { dim1 = 2 }; + enum { dim2 = 1 }; + +public: + using ctype = typename Policy::ctype; + using Point = typename Policy::Point; + using Intersection = typename Policy::Intersection; + +private: + static constexpr ctype eps_ = 1.5e-7; // base epsilon for floating point comparisons + +public: + /*! + * \brief Colliding segment and convex polyhedron + * \note Algorithm based on the one from "Real-Time Collision Detection" by Christer Ericson, + * published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc. (Chapter 5.3.6) + * \param geo1/geo2 The geometries to intersect + * \param intersection If the geometries collide, is holds the corner points of + * the intersection object in global coordinates. + * \note This overload is used when point-like intersections are seeked + */ + template<class P = Policy, std::enable_if_t<P::dimIntersection == 1, int> = 0> + static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) + { + static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); + + ctype tfirst, tlast; + if (intersect_(geo1, geo2, tfirst, tlast)) + { + // the intersection exists. Export the intersections geometry now: + // s(t) = a + t(b-a) in [tfirst, tlast] + intersection = Intersection({geo2.global(tfirst), geo2.global(tlast)}); + return true; + } + + return false; + } + + /*! + * \brief Colliding segment and convex polyhedron + * \note Algorithm based on the one from "Real-Time Collision Detection" by Christer Ericson, + * published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc. (Chapter 5.3.6) + * \param geo1/geo2 The geometries to intersect + * \param is If the geometries collide, is holds the corner points of + * the intersection object in global coordinates. + * \note This overload is used when point-like intersections are seeked + */ + template<class P = Policy, std::enable_if_t<P::dimIntersection == 0, int> = 0> + static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& is) + { + static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); + + const auto p = geo2.corner(0); + const auto q = geo2.corner(1); + + const auto a = geo1.corner(0); + const auto b = geo1.corner(1); + const auto c = geo1.corner(2); + + if (geo1.corners() == 3) + return intersect_<Policy>(a, b, c, p, q, is); + + else if (geo1.corners() == 4) + { + Intersection is1, is2; + bool hasSegment1, hasSegment2; + + const auto d = geo1.corner(3); + const bool intersects1 = intersect_<Policy>(a, b, d, p, q, is1, hasSegment1); + const bool intersects2 = intersect_<Policy>(a, d, c, p, q, is2, hasSegment2); + + if (hasSegment1 || hasSegment2) + return false; + + if (intersects1) { is = is1; return true; } + if (intersects2) { is = is2; return true; } + + return false; + } + + else + DUNE_THROW(Dune::NotImplemented, "Collision of segment and geometry of type " + << geo1.type() << ", "<< geo1.corners() << " corners."); + } + +private: + /*! + * \brief Obtain local coordinates of start/end point of the intersecting segment. + */ + template<class P = Policy, std::enable_if_t<P::dimIntersection == 1, int> = 0> + static bool intersect_(const Geometry1& geo1, const Geometry2& geo2, ctype& tfirst, ctype& tlast) + { + // lambda to obtain the facet corners on geo1 + auto getFacetCorners = [] (const Geometry1& geo1) + { + std::vector< std::array<int, 2> > facetCorners; + switch (geo1.corners()) + { + case 4: // quadrilateral + facetCorners = {{0, 2}, {3, 1}, {1, 0}, {2, 3}}; + break; + case 3: // triangle + facetCorners = {{1, 0}, {0, 2}, {2, 1}}; + break; + default: + DUNE_THROW(Dune::NotImplemented, "Collision of segment and geometry of type " + << geo1.type() << " with "<< geo1.corners() << " corners."); + } + + return facetCorners; + }; + + const auto center1 = geo1.center(); + const auto normal1 = crossProduct(geo1.corner(1) - geo1.corner(0), + geo1.corner(2) - geo1.corner(0)); + + // lambda to obtain the normal on a facet + auto computeNormal = [¢er1, &normal1, &geo1] (const std::array<int, 2>& facetCorners) + { + const auto c0 = geo1.corner(facetCorners[0]); + const auto c1 = geo1.corner(facetCorners[1]); + const auto edge = c1 - c0; + + Dune::FieldVector<ctype, dimworld> n = crossProduct(edge, normal1); + n /= n.two_norm(); + + // orientation of the element is not known + // make sure normal points outwards of element + if ( n*(center1-c0) > 0.0 ) + n *= -1.0; + + return n; + }; + + return Detail::computeSegmentIntersection(geo1, geo2, eps_, tfirst, tlast, getFacetCorners, computeNormal); + } + + /*! + * \brief triangle--segment point-like intersection with points as input. + */ + template<class P = Policy, std::enable_if_t<P::dimIntersection == 0, int> = 0> + static bool intersect_(const Point& a, const Point& b, const Point& c, + const Point& p, const Point& q, + Intersection& is) + { + bool hasSegment; + return intersect_(a, b, c, p, q, is, hasSegment); + } + + /*! + * \brief triangle--segment point-like intersection with points as input. Also + * stores if a segment-like intersection was found in the provided boolean. + */ + template<class P = Policy, std::enable_if_t<P::dimIntersection == 0, int> = 0> + static bool intersect_(const Point& a, const Point& b, const Point& c, + const Point& p, const Point& q, + Intersection& is, bool& hasSegmentIs) + { + hasSegmentIs = false; + + const auto ab = b - a; + const auto ac = c - a; + const auto qp = p - q; + + // compute the triangle normal that defines the triangle plane + const auto normal = crossProduct(ab, ac); + + // compute the denominator + // if denom is 0 the segment is parallel and we can return + const auto denom = normal*qp; + const auto abnorm2 = ab.two_norm2(); + const auto eps = eps_*abnorm2*qp.two_norm(); + using std::abs; + if (abs(denom) < eps) + { + const auto pa = a - p; + const auto denom2 = normal*pa; + if (abs(denom2) > eps_*pa.two_norm()*abnorm2) + return false; + + // if we get here, we are in-plane. Check if a + // segment-like intersection with geometry 1 exists. + using SegmentPolicy = typename IntersectionPolicy::SegmentPolicy<ctype, dimworld>; + using Triangle = Dune::AffineGeometry<ctype, 2, dimworld>; + using Segment = Dune::AffineGeometry<ctype, 1, dimworld>; + using SegmentIntersectionAlgorithm = GeometryIntersection<Triangle, Segment, SegmentPolicy>; + using SegmentIntersectionType = typename SegmentIntersectionAlgorithm::Intersection; + SegmentIntersectionType segmentIs; + + Triangle triangle(Dune::GeometryTypes::simplex(2), std::array<Point, 3>({a, b, c})); + Segment segment(Dune::GeometryTypes::simplex(1), std::array<Point, 2>({p, q})); + if (SegmentIntersectionAlgorithm::intersection(triangle, segment, segmentIs)) + { + hasSegmentIs = true; + return false; + } + + // We are in-plane. A point-like + // intersection can only be on the edges + for (const auto& ip : {p, q}) + { + if ( intersectsPointSimplex(ip, a, b) + || intersectsPointSimplex(ip, b, c) + || intersectsPointSimplex(ip, c, a) ) + { + is = ip; + return true; + } + } + + return false; + } + + // compute intersection t value of pq with plane of triangle. + // a segment intersects if and only if 0 <= t <= 1. + const auto ap = p - a; + const auto t = (ap*normal)/denom; + if (t < 0.0 - eps_) return false; + if (t > 1.0 + eps_) return false; + + // compute the barycentric coordinates and check if the intersection point + // is inside the bounds of the triangle + const auto e = crossProduct(qp, ap); + const auto v = (ac*e)/denom; + if (v < -eps_ || v > 1.0 + eps_) return false; + const auto w = -(ab*e)/denom; + if (w < -eps_ || v + w > 1.0 + eps_) return false; + + // Now we are sure there is an intersection points + // Perform delayed division compute the last barycentric coordinate component + const auto u = 1.0 - v - w; + + Point ip(0.0); + ip.axpy(u, a); + ip.axpy(v, b); + ip.axpy(w, c); + is = ip; + return true; + } +}; + +/*! + * \ingroup Geometry + * \brief A class for segment--polygon intersection in 3d space + */ +template <class Geometry1, class Geometry2, class Policy> +class GeometryIntersection<Geometry1, Geometry2, Policy, 3, 1, 2> +: public GeometryIntersection<Geometry2, Geometry1, Policy, 3, 2, 1> +{ + using Base = GeometryIntersection<Geometry2, Geometry1, Policy, 3, 2, 1>; +public: + /*! + * \brief Colliding segment and convex polygon + * \param geo1/geo2 The geometries to intersect + * \param intersection If the geometries collide intersection holds the + * corner points of the intersection object in global coordinates. + * \note This forwards to the polyhedron-polygon specialization with swapped arguments. + */ + template<class P = Policy> + static bool intersection(const Geometry1& geo1, const Geometry2& geo2, typename Base::Intersection& intersection) + { + return Base::intersection(geo2, geo1, intersection); + } +}; + +/*! + * \ingroup Geometry + * \brief A class for segment--segment intersection in 3d space + */ +template <class Geometry1, class Geometry2, class Policy> +class GeometryIntersection<Geometry1, Geometry2, Policy, 3, 1, 1> +{ + enum { dimworld = 3 }; + enum { dim1 = 1 }; + enum { dim2 = 1 }; + +public: + using ctype = typename Policy::ctype; + using Point = typename Policy::Point; + using Intersection = typename Policy::Intersection; + +private: + static constexpr ctype eps_ = 1.5e-7; // base epsilon for floating point comparisons + +public: + /*! + * \brief Colliding two segments + * \param geo1/geo2 The geometries to intersect + * \param intersection Container to store corners of intersection segment + * \note this overload is used when point-like intersections are seeked + * \todo implement this query + */ + template<class P = Policy, std::enable_if_t<P::dimIntersection == 0, int> = 0> + static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) + { + static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); + DUNE_THROW(Dune::NotImplemented, "segment-segment intersection detection for point-like intersections"); + } + + /*! + * \brief Colliding two segments in 3D + * \param geo1/geo2 The geometries to intersect + * \param intersection If the geometries collide, is holds the corner points of + * the intersection object in global coordinates. + * \note This overload is used when segment-like intersections are seeked + */ + template<class P = Policy, std::enable_if_t<P::dimIntersection == 1, int> = 0> + static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection) + { + static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension"); + + const auto& a = geo1.corner(0); + const auto& b = geo1.corner(1); + const auto ab = b-a; + + const auto& p = geo2.corner(0); + const auto& q = geo2.corner(1); + const auto pq = q-p; + + const auto abNorm2 = ab.two_norm2(); + const auto pqNorm2 = pq.two_norm2(); + + using std::max; + const auto eps2 = eps_*max(abNorm2, pqNorm2); + + // if the segment are not parallel there is no segment intersection + using std::abs; + if (crossProduct(ab, pq).two_norm2() > eps2*eps2) + return false; + + const auto ap = (p-a); + const auto aq = (q-a); + + // points have to be colinear + if (crossProduct(ap, aq).two_norm2() > eps2*eps2) + return false; + + // scaled local coordinates + // we save the division for the normalization for now + // and do it in the very end if we find an intersection + auto tp = ap*ab; + auto tq = aq*ab; + + // make sure they are sorted + using std::swap; + if (tp > tq) + swap(tp, tq); + + using std::clamp; + tp = clamp(tp, 0.0, abNorm2); + tq = clamp(tq, 0.0, abNorm2); + + if (abs(tp-tq) < eps2) + return false; + + intersection = Intersection({geo1.global(tp/abNorm2), geo1.global(tq/abNorm2)}); + return true; + } +}; + +} // end namespace Dumux + +# endif diff --git a/dumux/geometry/grahamconvexhull.hh b/dumux/geometry/grahamconvexhull.hh new file mode 100644 index 0000000000000000000000000000000000000000..7a3001f12687309bb9edf41bd76088ba07369ad7 --- /dev/null +++ b/dumux/geometry/grahamconvexhull.hh @@ -0,0 +1,218 @@ +/***************************************************************************** + * 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 Geometry + * \brief A function to compute the convex hull of a point cloud + * and a function to triangulate the polygon area spanned by the convex hull + */ +#ifndef DUMUX_GEOMETRY_GRAHAM_CONVEX_HULL_HH +#define DUMUX_GEOMETRY_GRAHAM_CONVEX_HULL_HH + +#include <vector> +#include <array> +#include <algorithm> +#include <iterator> + +#include <dune/common/exceptions.hh> +#include <dune/common/fvector.hh> + +#include <dumux/common/math.hh> +#include <dumux/geometry/triangulation.hh> + +namespace Dumux { + +/*! + * \ingroup Geometry + * \brief Returns the orientation of a sequence a-->b-->c in one plane (defined by normal vector) + * \return -1 if a-->b-->c forms a counter-clockwise turn (given the normal vector) + * +1 for a clockwise turn, + * 0 if they are on one line (colinear) + */ +template<class ctype> +int getOrientation(const Dune::FieldVector<ctype, 3>& a, + const Dune::FieldVector<ctype, 3>& b, + const Dune::FieldVector<ctype, 3>& c, + const Dune::FieldVector<ctype, 3>& normal) +{ + const auto d = b-a; + const auto e = c-b; + const auto f = Dumux::crossProduct(d, e); + const auto area = f*normal; + return Dumux::sign(-area); +} + +/*! + * \ingroup Geometry + * \brief Compute the points making up the convex hull around the given set of unordered points + * \note We assume that all points are coplanar and there are no indentical points in the list + * \note This algorithm changes the order of the given points a bit + * as they are unordered anyway this shouldn't matter too much + */ +template<int dim, class ctype, + std::enable_if_t<(dim==2), int> = 0> +std::vector<Dune::FieldVector<ctype, 3>> +grahamConvexHullImpl(std::vector<Dune::FieldVector<ctype, 3>>& points) +{ + using Point = Dune::FieldVector<ctype, 3>; + std::vector<Point> convexHull; + + // return empty convex hull + if (points.size() < 3) + return convexHull; + + // return the points (already just one triangle) + if (points.size() == 3) + return points; + + // try to compute the normal vector of the plane + const auto a = points[1] - points[0]; + auto b = points[2] - points[0]; + auto normal = Dumux::crossProduct(a, b); + + // make sure the normal vector has non-zero length + std::size_t k = 2; + auto norm = normal.two_norm(); + while (norm == 0.0 && k < points.size()-1) + { + b = points[++k]; + normal = Dumux::crossProduct(a, b); + norm = normal.two_norm(); + } + + // if all given points are colinear -> return empty convex hull + if (norm == 0.0) + return convexHull; + + using std::sqrt; + const auto eps = 1e-7*sqrt(norm); + normal /= norm; + + // find the element with the smallest x coordinate (if x is the same, smallest y coordinate, and so on...) + auto minIt = std::min_element(points.begin(), points.end(), [&eps](const auto& a, const auto& b) + { + using std::abs; + return (abs(a[0]-b[0]) > eps ? a[0] < b[0] : (abs(a[1]-b[1]) > eps ? a[1] < b[1] : (a[2] < b[2]))); + }); + + // swap the smallest element to the front + std::iter_swap(minIt, points.begin()); + + // choose the first (min element) as the pivot point + // sort in counter-clockwise order around pivot point + const auto pivot = points[0]; + std::sort(points.begin()+1, points.end(), [&](const auto& a, const auto& b) + { + const int order = getOrientation(pivot, a, b, normal); + if (order == 0) + return (a-pivot).two_norm() < (b-pivot).two_norm(); + else + return (order == -1); + }); + + // push the first three points + convexHull.reserve(50); + convexHull.push_back(points[0]); + convexHull.push_back(points[1]); + convexHull.push_back(points[2]); + + // This is the heart of the algorithm + // pop_back until the last point in the queue forms a counter-clockwise oriented line + // with the next two vertices. Then add these points to the queue. + for (std::size_t i = 3; i < points.size(); ++i) + { + Point p = convexHull.back(); + convexHull.pop_back(); + // keep popping until the orientation a->b->currentp is counter-clockwise + while (getOrientation(convexHull.back(), p, points[i], normal) != -1) + { + // make sure the queue doesn't get empty + if (convexHull.size() == 1) + { + // before we reach i=size-1 there has to be a good candidate + // as not all points are colinear (a non-zero plane normal exists) + assert(i < points.size()-1); + p = points[i++]; + } + else + { + p = convexHull.back(); + convexHull.pop_back(); + } + } + + // add back the last popped point and this point + convexHull.emplace_back(std::move(p)); + convexHull.push_back(points[i]); + } + + return convexHull; +} + +/*! + * \ingroup Geometry + * \brief Compute the points making up the convex hull around the given set of unordered points + * \note This is the specialization for 2d space. Here, we make use of the generic implementation + * for the case of coplanar points in 3d space (a more efficient implementation could be provided). + */ +template<int dim, class ctype, + std::enable_if_t<(dim==2), int> = 0> +std::vector<Dune::FieldVector<ctype, 2>> +grahamConvexHullImpl(const std::vector<Dune::FieldVector<ctype, 2>>& points) +{ + std::vector<Dune::FieldVector<ctype, 3>> points3D; + points3D.reserve(points.size()); + std::transform(points.begin(), points.end(), std::back_inserter(points3D), + [](const auto& p) { return Dune::FieldVector<ctype, 3>({p[0], p[1], 0.0}); }); + + const auto result3D = grahamConvexHullImpl<2>(points3D); + + std::vector<Dune::FieldVector<ctype, 2>> result2D; + result2D.reserve(result3D.size()); + std::transform(result3D.begin(), result3D.end(), std::back_inserter(result2D), + [](const auto& p) { return Dune::FieldVector<ctype, 2>({p[0], p[1]}); }); + + return result2D; +} + +/*! + * \ingroup Geometry + * \brief Compute the points making up the convex hull around the given set of unordered points + * \note We assume that all points are coplanar and there are no indentical points in the list + */ +template<int dim, class ctype, int dimWorld> +std::vector<Dune::FieldVector<ctype, dimWorld>> grahamConvexHull(std::vector<Dune::FieldVector<ctype, dimWorld>>& points) +{ + return grahamConvexHullImpl<dim>(points); +} + +/*! + * \ingroup Geometry + * \brief Compute the points making up the convex hull around the given set of unordered points + * \note We assume that all points are coplanar and there are no indentical points in the list + * \note This is the overload if we are not allowed to write into the given points vector + */ +template<int dim, class ctype, int dimWorld> +std::vector<Dune::FieldVector<ctype, dimWorld>> grahamConvexHull(const std::vector<Dune::FieldVector<ctype, dimWorld>>& points) +{ + auto copyPoints = points; + return grahamConvexHullImpl<dim>(copyPoints); +} + +} // end namespace Dumux + +# endif diff --git a/dumux/geometry/intersectingentities.hh b/dumux/geometry/intersectingentities.hh new file mode 100644 index 0000000000000000000000000000000000000000..5e69a3e35dc79e310ff8d9efe243f22006a9c66e --- /dev/null +++ b/dumux/geometry/intersectingentities.hh @@ -0,0 +1,395 @@ +/***************************************************************************** + * 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 Geometry + * \brief Algorithms that finds which geometric entites intersect + */ +#ifndef DUMUX_GEOMETRY_INTERSECTING_ENTITIES_HH +#define DUMUX_GEOMETRY_INTERSECTING_ENTITIES_HH + +#include <cmath> +#include <type_traits> +#include <vector> +#include <algorithm> + +#include <dune/common/fvector.hh> + +#include <dumux/common/math.hh> +#include <dumux/geometry/boundingboxtree.hh> +#include <dumux/geometry/intersectspointgeometry.hh> +#include <dumux/geometry/geometryintersection.hh> +#include <dumux/geometry/triangulation.hh> + +namespace Dumux { + +/*! + * \ingroup Geometry + * \brief An intersection object resulting from the intersection of two primitives in an entity set + * \note this is used as return type for some of the intersectingEntities overloads below + */ +template<int dimworld, class CoordTypeA, class CoordTypeB = CoordTypeA> +class IntersectionInfo +{ +public: + using ctype = typename Dune::PromotionTraits<CoordTypeA, CoordTypeB>::PromotedType; + static constexpr int dimensionworld = dimworld; + using GlobalPosition = Dune::FieldVector<ctype, dimworld>; + + template<class Corners> + explicit IntersectionInfo(std::size_t a, std::size_t b, Corners&& c) + : a_(a) + , b_(b) + , corners_(c.begin(), c.end()) + {} + + //! Get the index of the intersecting entity belonging to this grid + std::size_t first() const + { return a_; } + + //! Get the index of the intersecting entity belonging to the other grid + std::size_t second() const + { return b_; } + + //! Get the corners of the intersection geometry + std::vector<GlobalPosition> corners() const + { return corners_; } + + /*! + * \brief Check if the corners of this intersection match with the given corners + * \note This is useful to check if the intersection geometry of two intersections coincide. + */ + bool cornersMatch(const std::vector<GlobalPosition>& otherCorners) const + { + if (otherCorners.size() != corners_.size()) + return false; + + const auto eps = 1.5e-7*(corners_[1] - corners_[0]).two_norm(); + for (int i = 0; i < corners_.size(); ++i) + if ((corners_[i] - otherCorners[i]).two_norm() > eps) + return false; + + return true; + } + +private: + std::size_t a_, b_; //!< Indices of the intersection elements + std::vector<GlobalPosition> corners_; //!< the corner points of the intersection geometry +}; + +/*! + * \ingroup Geometry + * \brief Compute all intersections between entities and a point + */ +template<class EntitySet, class ctype, int dimworld> +inline std::vector<std::size_t> +intersectingEntities(const Dune::FieldVector<ctype, dimworld>& point, + const BoundingBoxTree<EntitySet>& tree, + bool isCartesianGrid = false) +{ + // Call the recursive find function to find candidates + std::vector<std::size_t> entities; + intersectingEntities(point, tree, tree.numBoundingBoxes() - 1, entities, isCartesianGrid); + return entities; +} + +/*! + * \ingroup Geometry + * \brief Compute intersections with point for all nodes of the bounding box tree recursively + */ +template<class EntitySet, class ctype, int dimworld> +void intersectingEntities(const Dune::FieldVector<ctype, dimworld>& point, + const BoundingBoxTree<EntitySet>& tree, + std::size_t node, + std::vector<std::size_t>& entities, + bool isCartesianGrid = false) +{ + // Get the bounding box for the current node + const auto& bBox = tree.getBoundingBoxNode(node); + + // if the point is not in the bounding box we can stop + if (!intersectsPointBoundingBox(point, tree.getBoundingBoxCoordinates(node))) return; + + // now we know it's inside + // if the box is a leaf do the primitive test. + else if (tree.isLeaf(bBox, node)) + { + const std::size_t entityIdx = bBox.child1; + // for structured cube grids skip the primitive test + if (isCartesianGrid) + entities.push_back(entityIdx); + else + { + const auto geometry = tree.entitySet().entity(entityIdx).geometry(); + // if the primitive is positive it intersects the actual geometry, add the entity to the list + if (intersectsPointGeometry(point, geometry)) + entities.push_back(entityIdx); + } + } + + // No leaf. Check both children nodes. + else + { + intersectingEntities(point, tree, bBox.child0, entities, isCartesianGrid); + intersectingEntities(point, tree, bBox.child1, entities, isCartesianGrid); + } +} + +/*! + * \ingroup Geometry + * \brief Compute all intersections between a geometry and a bounding box tree + */ +template<class Geometry, class EntitySet> +inline std::vector<IntersectionInfo<Geometry::coorddimension, typename Geometry::ctype, typename EntitySet::ctype>> +intersectingEntities(const Geometry& geometry, + const BoundingBoxTree<EntitySet>& tree) +{ + // check if the world dimensions match + static_assert(int(Geometry::coorddimension) == int(EntitySet::dimensionworld), + "Can only intersect geometry and bounding box tree of same world dimension"); + + // Create data structure for return type + std::vector<IntersectionInfo<Geometry::coorddimension, typename Geometry::ctype, typename EntitySet::ctype>> intersections; + using ctype = typename IntersectionInfo<Geometry::coorddimension, typename Geometry::ctype, typename EntitySet::ctype>::ctype; + static constexpr int dimworld = Geometry::coorddimension; + + // compute the bounding box of the given geometry + std::array<ctype, 2*Geometry::coorddimension> bBox; + ctype* xMin = bBox.data(); ctype* xMax = xMin + Geometry::coorddimension; + + // Get coordinates of first vertex + auto corner = geometry.corner(0); + for (std::size_t dimIdx = 0; dimIdx < dimworld; ++dimIdx) + xMin[dimIdx] = xMax[dimIdx] = corner[dimIdx]; + + // Compute the min and max over the remaining vertices + for (std::size_t cornerIdx = 1; cornerIdx < geometry.corners(); ++cornerIdx) + { + corner = geometry.corner(cornerIdx); + for (std::size_t dimIdx = 0; dimIdx < dimworld; ++dimIdx) + { + using std::max; + using std::min; + xMin[dimIdx] = min(xMin[dimIdx], corner[dimIdx]); + xMax[dimIdx] = max(xMax[dimIdx], corner[dimIdx]); + } + } + + // Call the recursive find function to find candidates + intersectingEntities(geometry, tree, + bBox, tree.numBoundingBoxes() - 1, + intersections); + + return intersections; +} + +/*! + * \ingroup Geometry + * \brief Compute intersections with point for all nodes of the bounding box tree recursively + */ +template<class Geometry, class EntitySet> +void intersectingEntities(const Geometry& geometry, + const BoundingBoxTree<EntitySet>& tree, + const std::array<typename Geometry::ctype, 2*Geometry::coorddimension>& bBox, + std::size_t nodeIdx, + std::vector<IntersectionInfo<Geometry::coorddimension, + typename Geometry::ctype, + typename EntitySet::ctype>>& intersections) +{ + // if the two bounding boxes don't intersect we can stop searching + static constexpr int dimworld = Geometry::coorddimension; + if (!intersectsBoundingBoxBoundingBox<dimworld>(bBox.data(), tree.getBoundingBoxCoordinates(nodeIdx))) + return; + + // get node info for current bounding box node + const auto& bBoxNode = tree.getBoundingBoxNode(nodeIdx); + + // if the box is a leaf do the primitive test. + if (tree.isLeaf(bBoxNode, nodeIdx)) + { + // eIdxA is always 0 since we intersect with exactly one geometry + const auto eIdxA = 0; + const auto eIdxB = bBoxNode.child1; + + const auto geometryTree = tree.entitySet().entity(eIdxB).geometry(); + using GeometryTree = std::decay_t<decltype(geometryTree)>; + using Policy = IntersectionPolicy::DefaultPolicy<Geometry, GeometryTree>; + using IntersectionAlgorithm = GeometryIntersection<Geometry, GeometryTree, Policy>; + using Intersection = typename IntersectionAlgorithm::Intersection; + Intersection intersection; + + if (IntersectionAlgorithm::intersection(geometry, geometryTree, intersection)) + { + static constexpr int dimIntersection = Policy::dimIntersection; + if (dimIntersection >= 2) + { + const auto triangulation = triangulate<dimIntersection, dimworld>(intersection); + for (unsigned int i = 0; i < triangulation.size(); ++i) + intersections.emplace_back(eIdxA, eIdxB, std::move(triangulation[i])); + } + else + intersections.emplace_back(eIdxA, eIdxB, intersection); + } + } + + // No leaf. Check both children nodes. + else + { + intersectingEntities(geometry, tree, bBox, bBoxNode.child0, intersections); + intersectingEntities(geometry, tree, bBox, bBoxNode.child1, intersections); + } +} + +/*! + * \ingroup Geometry + * \brief Compute all intersections between two bounding box trees + */ +template<class EntitySet0, class EntitySet1> +inline std::vector<IntersectionInfo<EntitySet0::dimensionworld, typename EntitySet0::ctype, typename EntitySet1::ctype>> +intersectingEntities(const BoundingBoxTree<EntitySet0>& treeA, + const BoundingBoxTree<EntitySet1>& treeB) +{ + // check if the world dimensions match + static_assert(int(EntitySet0::dimensionworld) == int(EntitySet1::dimensionworld), + "Can only intersect bounding box trees of same world dimension"); + + // Create data structure for return type + std::vector<IntersectionInfo<EntitySet0::dimensionworld, typename EntitySet0::ctype, typename EntitySet1::ctype>> intersections; + + // Call the recursive find function to find candidates + intersectingEntities(treeA, treeB, + treeA.numBoundingBoxes() - 1, + treeB.numBoundingBoxes() - 1, + intersections); + + return intersections; +} + +/*! + * \ingroup Geometry + * \brief Compute all intersections between two all bounding box tree nodes recursively + */ +template<class EntitySet0, class EntitySet1> +void intersectingEntities(const BoundingBoxTree<EntitySet0>& treeA, + const BoundingBoxTree<EntitySet1>& treeB, + std::size_t nodeA, std::size_t nodeB, + std::vector<IntersectionInfo<EntitySet0::dimensionworld, + typename EntitySet0::ctype, + typename EntitySet1::ctype>>& intersections) +{ + // Get the bounding box for the current node + const auto& bBoxA = treeA.getBoundingBoxNode(nodeA); + const auto& bBoxB = treeB.getBoundingBoxNode(nodeB); + + // if the two bounding boxes of the current nodes don't intersect we can stop searching + static constexpr int dimworld = EntitySet0::dimensionworld; + if (!intersectsBoundingBoxBoundingBox<dimworld>(treeA.getBoundingBoxCoordinates(nodeA), + treeB.getBoundingBoxCoordinates(nodeB))) + return; + + // Check if we have a leaf in treeA or treeB + const bool isLeafA = treeA.isLeaf(bBoxA, nodeA); + const bool isLeafB = treeB.isLeaf(bBoxB, nodeB); + + // If both boxes are leaves do the primitive test + if (isLeafA && isLeafB) + { + const auto eIdxA = bBoxA.child1; + const auto eIdxB = bBoxB.child1; + + const auto geometryA = treeA.entitySet().entity(eIdxA).geometry(); + const auto geometryB = treeB.entitySet().entity(eIdxB).geometry(); + + using GeometryA = std::decay_t<decltype(geometryA)>; + using GeometryB = std::decay_t<decltype(geometryB)>; + using Policy = IntersectionPolicy::DefaultPolicy<GeometryA, GeometryB>; + using IntersectionAlgorithm = GeometryIntersection<GeometryA, GeometryB, Policy>; + using Intersection = typename IntersectionAlgorithm::Intersection; + Intersection intersection; + + if (IntersectionAlgorithm::intersection(geometryA, geometryB, intersection)) + { + static constexpr int dimIntersection = Policy::dimIntersection; + + if (dimIntersection >= 2) + { + const auto triangulation = triangulate<dimIntersection, dimworld>(intersection); + for (unsigned int i = 0; i < triangulation.size(); ++i) + intersections.emplace_back(eIdxA, eIdxB, std::move(triangulation[i])); + } + else + intersections.emplace_back(eIdxA, eIdxB, intersection); + } + } + + // if we reached the leaf in treeA, just continue in treeB + else if (isLeafA) + { + intersectingEntities(treeA, treeB, nodeA, bBoxB.child0, intersections); + intersectingEntities(treeA, treeB, nodeA, bBoxB.child1, intersections); + } + + // if we reached the leaf in treeB, just continue in treeA + else if (isLeafB) + { + intersectingEntities(treeA, treeB, bBoxA.child0, nodeB, intersections); + intersectingEntities(treeA, treeB, bBoxA.child1, nodeB, intersections); + } + + // we know now that both trees didn't reach the leaf yet so + // we continue with the larger tree first (bigger node number) + else if (nodeA > nodeB) + { + intersectingEntities(treeA, treeB, bBoxA.child0, nodeB, intersections); + intersectingEntities(treeA, treeB, bBoxA.child1, nodeB, intersections); + } + else + { + intersectingEntities(treeA, treeB, nodeA, bBoxB.child0, intersections); + intersectingEntities(treeA, treeB, nodeA, bBoxB.child1, intersections); + } +} + +/*! + * \ingroup Geometry + * \brief Compute the index of the intersecting element of a Cartesian grid with a point + * The grid is given by the lower left corner (min), the upper right corner (max) + * and the number of cells in each direction (cells). + * \note If there are several options the lowest matching cell index will be returned + * \note Returns the index i + I*j + I*J*k for the intersecting element i,j,k of a grid with I,J,K cells + */ +template<class ctype, int dimworld> +inline std::size_t intersectingEntityCartesianGrid(const Dune::FieldVector<ctype, dimworld>& point, + const Dune::FieldVector<ctype, dimworld>& min, + const Dune::FieldVector<ctype, dimworld>& max, + const std::array<int, std::size_t(dimworld)>& cells) +{ + std::size_t index = 0; + for (int i = 0; i < dimworld; ++i) + { + using std::clamp; using std::floor; + ctype dimOffset = clamp<ctype>(floor((point[i]-min[i])*cells[i]/(max[i]-min[i])), 0.0, cells[i]-1); + for (int j = 0; j < i; ++j) + dimOffset *= cells[j]; + index += static_cast<std::size_t>(dimOffset); + } + return index; +} + +} // end namespace Dumux + +#endif diff --git a/dumux/geometry/intersectionentityset.hh b/dumux/geometry/intersectionentityset.hh new file mode 100644 index 0000000000000000000000000000000000000000..6fc754c080237e015e5e1fde0f77e5da8167580e --- /dev/null +++ b/dumux/geometry/intersectionentityset.hh @@ -0,0 +1,290 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * 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 Geometry + * \brief A class representing the intersection entites two geometric entity sets + */ + +#ifndef DUMUX_GEOMETRY_INTERSECTION_ENTITY_SET_HH +#define DUMUX_GEOMETRY_INTERSECTION_ENTITY_SET_HH + +#include <algorithm> +#include <cassert> +#include <iostream> +#include <memory> +#include <type_traits> +#include <utility> +#include <vector> + +#include <dune/common/indices.hh> +#include <dune/common/timer.hh> +#include <dune/common/iteratorrange.hh> +#include <dune/common/promotiontraits.hh> +#include <dune/common/reservedvector.hh> +#include <dune/geometry/affinegeometry.hh> +#include <dune/geometry/type.hh> + +#include <dumux/geometry/boundingboxtree.hh> +#include <dumux/geometry/intersectingentities.hh> + +namespace Dumux { + +/*! + * \ingroup Geometry + * \brief A class representing the intersection entites two geometric entity sets + */ +template<class DomainEntitySet, class TargetEntitySet> +class IntersectionEntitySet +{ + using DomainTree = BoundingBoxTree<DomainEntitySet>; + using TargetTree = BoundingBoxTree<TargetEntitySet>; + + 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"); + + 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; + + /*! + * \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 + + // 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 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 + } + + //! 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 + 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 domainTree_.entitySet().entity(std::get<domainIdx>(neighbors_)[n]); } + + //! get the nth target neighbor entity + typename TargetEntitySet::Entity targetEntity(unsigned int n = 0) const + { return targetTree_.entitySet().entity(std::get<targetIdx>(neighbors_)[n]); } + + private: + IndexStorage neighbors_; + std::vector<GlobalPosition> corners_; + + const DomainTree& domainTree_; + const TargetTree& targetTree_; + }; + + using Intersections = std::vector<IntersectionEntity>; + +public: + //! make intersection entity type available + using Entity = IntersectionEntity; + //! make entity iterator type available + using EntityIterator = typename Intersections::const_iterator; + + /*! + * \brief Default constructor + */ + IntersectionEntitySet() = default; + + /*! + * \brief Build intersections + */ + 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_); + } + + /*! + * \brief Build intersections + */ + void build(std::shared_ptr<const DomainTree> domainTree, std::shared_ptr<const TargetTree> targetTree) + { + // make sure the tree don't get out of scope + domainTree_ = domainTree; + targetTree_ = targetTree; + build(*domainTree_, *targetTree_); + } + + /*! + * \brief Build intersections + * \note If you call this, make sure the bounding box tree stays alive for the life-time of this object + */ + void build(const DomainTree& domainTree, const TargetTree& targetTree) + { + Dune::Timer timer; + + // compute raw intersections + const auto rawIntersections = intersectingEntities(domainTree, targetTree); + + // 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- + // dimensional entities which is identical for all geometrically identical intersections. + std::vector<std::vector<std::vector<GlobalPosition>>> intersectionMap; + std::vector<std::vector<std::size_t>> intersectionIndex; + if constexpr (isMixedDimensional) + { + const auto numLowDimEntities = dimTarget < dimDomain ? targetTree.entitySet().size() + : domainTree.entitySet().size(); + intersectionMap.resize(numLowDimEntities); + intersectionIndex.resize(numLowDimEntities); + } + + // reserve memory for storing the intersections. In case of grids of + // different dimensionality this might be an overestimate. We get rid + // of the overhead memory at the end of this function though. + intersections_.clear(); + intersections_.reserve(rawIntersections.size()); + + for (const auto& rawIntersection : rawIntersections) + { + bool add = true; + + // Check if intersection was already inserted. + // In this case we only add new neighbor information as the geometry is identical. + if constexpr (isMixedDimensional) + { + const auto lowDimNeighborIdx = getLowDimNeighborIdx_(rawIntersection); + for (int i = 0; i < intersectionMap[lowDimNeighborIdx].size(); ++i) + { + if (rawIntersection.cornersMatch(intersectionMap[lowDimNeighborIdx][i])) + { + add = false; + // only add the pair of neighbors using the insertionIndex + auto idx = intersectionIndex[lowDimNeighborIdx][i]; + intersections_[idx].addNeighbors(rawIntersection.first(), rawIntersection.second()); + break; + } + } + } + + if(add) + { + // maybe add to the map + if constexpr (isMixedDimensional) + { + intersectionMap[getLowDimNeighborIdx_(rawIntersection)].push_back(rawIntersection.corners()); + intersectionIndex[getLowDimNeighborIdx_(rawIntersection)].push_back(intersections_.size()); + } + + // add new intersection and add the neighbors + intersections_.emplace_back(domainTree, targetTree); + intersections_.back().setCorners(rawIntersection.corners()); + intersections_.back().addNeighbors(rawIntersection.first(), rawIntersection.second()); + } + } + + 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) + { + if constexpr (dimTarget < dimDomain) + return is.second(); + else + return is.first(); + } + + Intersections intersections_; + + std::shared_ptr<const DomainTree> domainTree_; + std::shared_ptr<const TargetTree> targetTree_; +}; + +} // end namespace Dumux + +#endif diff --git a/dumux/geometry/intersectspointgeometry.hh b/dumux/geometry/intersectspointgeometry.hh new file mode 100644 index 0000000000000000000000000000000000000000..d92ade60424fba103bfdde9922e9d4202c7bc795 --- /dev/null +++ b/dumux/geometry/intersectspointgeometry.hh @@ -0,0 +1,121 @@ +/***************************************************************************** + * 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 Geometry + * \brief Detect if a point intersects a geometry + */ +#ifndef DUMUX_GEOMETRY_INTERSECTS_POINT_GEOMETRY_HH +#define DUMUX_GEOMETRY_INTERSECTS_POINT_GEOMETRY_HH + +#include <cmath> +#include <dune/common/exceptions.hh> +#include <dune/common/fvector.hh> + +#include <dumux/geometry/intersectspointsimplex.hh> + +namespace Dumux { + +/*! + * \ingroup Geometry + * \brief Find out whether a point is inside a three-dimensional geometry + */ +template <class ctype, int dimworld, class Geometry, typename std::enable_if_t<(Geometry::mydimension == 3), int> = 0> +bool intersectsPointGeometry(const Dune::FieldVector<ctype, dimworld>& point, const Geometry& g) +{ + // get the g type + const auto type = g.type(); + + // if it's a tetrahedron we can check directly + if (type.isTetrahedron()) + return intersectsPointSimplex(point, g.corner(0), g.corner(1), g.corner(2), g.corner(3)); + + // split hexahedrons into five tetrahedrons + else if (type.isHexahedron()) + { + if (intersectsPointSimplex(point, g.corner(0), g.corner(1), g.corner(3), g.corner(5))) return true; + if (intersectsPointSimplex(point, g.corner(0), g.corner(5), g.corner(6), g.corner(4))) return true; + if (intersectsPointSimplex(point, g.corner(5), g.corner(3), g.corner(6), g.corner(7))) return true; + if (intersectsPointSimplex(point, g.corner(0), g.corner(3), g.corner(2), g.corner(6))) return true; + if (intersectsPointSimplex(point, g.corner(5), g.corner(3), g.corner(0), g.corner(6))) return true; + return false; + } + + // split pyramids into two tetrahedrons + else if (type.isPyramid()) + { + if (intersectsPointSimplex(point, g.corner(0), g.corner(1), g.corner(2), g.corner(4))) return true; + if (intersectsPointSimplex(point, g.corner(1), g.corner(3), g.corner(2), g.corner(4))) return true; + return false; + } + + // split prisms into three tetrahedrons + else if (type.isPrism()) + { + if (intersectsPointSimplex(point, g.corner(0), g.corner(1), g.corner(2), g.corner(4))) return true; + if (intersectsPointSimplex(point, g.corner(3), g.corner(0), g.corner(2), g.corner(4))) return true; + if (intersectsPointSimplex(point, g.corner(2), g.corner(5), g.corner(3), g.corner(4))) return true; + return false; + } + + else + DUNE_THROW(Dune::NotImplemented, + "Intersection for point and geometry type " + << type << " in " << dimworld << "-dimensional world."); +} + +/*! + * \ingroup Geometry + * \brief Find out whether a point is inside a two-dimensional geometry + */ +template <class ctype, int dimworld, class Geometry, typename std::enable_if_t<(Geometry::mydimension == 2), int> = 0> +bool intersectsPointGeometry(const Dune::FieldVector<ctype, dimworld>& point, const Geometry& g) +{ + // get the g type + const auto type = g.type(); + + // if it's a triangle we can check directly + if (type.isTriangle()) + return intersectsPointSimplex(point, g.corner(0), g.corner(1), g.corner(2)); + + // split quadrilaterals into two triangles + else if (type.isQuadrilateral()) + { + if (intersectsPointSimplex(point, g.corner(0), g.corner(1), g.corner(3))) return true; + if (intersectsPointSimplex(point, g.corner(0), g.corner(3), g.corner(2))) return true; + return false; + } + + else + DUNE_THROW(Dune::NotImplemented, + "Intersection for point and geometry type " + << type << " in " << dimworld << "-dimensional world."); +} + +/*! + * \ingroup Geometry + * \brief Find out whether a point is inside a one-dimensional geometry + */ +template <class ctype, int dimworld, class Geometry, typename std::enable_if_t<(Geometry::mydimension == 1), int> = 0> +bool intersectsPointGeometry(const Dune::FieldVector<ctype, dimworld>& point, const Geometry& g) +{ + return intersectsPointSimplex(point, g.corner(0), g.corner(1)); +} + +} // end namespace Dumux + +#endif diff --git a/dumux/geometry/intersectspointsimplex.hh b/dumux/geometry/intersectspointsimplex.hh new file mode 100644 index 0000000000000000000000000000000000000000..4f62a05419a3c2d31713496b79c7fc15cdcc2105 --- /dev/null +++ b/dumux/geometry/intersectspointsimplex.hh @@ -0,0 +1,225 @@ +/***************************************************************************** + * 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 Geometry + * \brief Detect if a point intersects a simplex (including boundary) + */ +#ifndef DUMUX_GEOMETRY_INTERSECTS_POINT_SIMPLEX_HH +#define DUMUX_GEOMETRY_INTERSECTS_POINT_SIMPLEX_HH + +#include <cmath> +#include <dune/common/fvector.hh> +#include <dumux/common/math.hh> + +namespace Dumux { + +/*! + * \ingroup Geometry + * \brief Find out whether a point is inside a tetrahedron (p0, p1, p2, p3) (dimworld is 3) + */ +template<class ctype, int dimworld, typename std::enable_if_t<(dimworld == 3), int> = 0> +bool intersectsPointSimplex(const Dune::FieldVector<ctype, dimworld>& point, + const Dune::FieldVector<ctype, dimworld>& p0, + const Dune::FieldVector<ctype, dimworld>& p1, + const Dune::FieldVector<ctype, dimworld>& p2, + const Dune::FieldVector<ctype, dimworld>& p3) +{ + // Algorithm from http://www.blackpawn.com/texts/pointinpoly/ + // See also "Real-Time Collision Detection" by Christer Ericson. + using GlobalPosition = Dune::FieldVector<ctype, dimworld>; + static constexpr ctype eps_ = 1.0e-7; + + // put the tetrahedron points in an array + const GlobalPosition *p[4] = {&p0, &p1, &p2, &p3}; + + // iterate over all faces + for (int i = 0; i < 4; ++i) + { + // compute all the vectors from vertex (local index 0) to the other points + const GlobalPosition v1 = *p[(i + 1)%4] - *p[i]; + const GlobalPosition v2 = *p[(i + 2)%4] - *p[i]; + const GlobalPosition v3 = *p[(i + 3)%4] - *p[i]; + const GlobalPosition v = point - *p[i]; + // compute the normal to the facet (cross product) + GlobalPosition n1 = crossProduct(v1, v2); + n1 /= n1.two_norm(); + // find out on which side of the plane v and v3 are + const auto t1 = n1.dot(v); + const auto t2 = n1.dot(v3); + // If the point is not exactly on the plane the + // points have to be on the same side + const auto eps = eps_ * v1.two_norm(); + if ((t1 > eps || t1 < -eps) && std::signbit(t1) != std::signbit(t2)) + return false; + } + return true; +} + +/*! + * \ingroup Geometry + * \brief Find out whether a point is inside a triangle (p0, p1, p2, p3) (dimworld is 3) + */ +template<class ctype, int dimworld, typename std::enable_if_t<(dimworld == 3), int> = 0> +bool intersectsPointSimplex(const Dune::FieldVector<ctype, dimworld>& point, + const Dune::FieldVector<ctype, dimworld>& p0, + const Dune::FieldVector<ctype, dimworld>& p1, + const Dune::FieldVector<ctype, dimworld>& p2) +{ + // adapted from the algorithm from from "Real-Time Collision Detection" by Christer Ericson, + // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc. (Chapter 5.4.2) + constexpr ctype eps_ = 1.0e-7; + + // compute the normal of the triangle + const auto v1 = p0 - p2; + auto n = crossProduct(v1, p1 - p0); + const ctype nnorm = n.two_norm(); + const ctype eps4 = eps_*nnorm*nnorm; // compute an epsilon for later + n /= nnorm; // normalize + + // first check if we are in the plane of the triangle + // if not we can return early + using std::abs; + auto x = p0 - point; + x /= x.two_norm(); // normalize + + if (abs(x*n) > eps_) + return false; + + // translate the triangle so that 'point' is the origin + const auto a = p0 - point; + const auto b = p1 - point; + const auto c = p2 - point; + + // compute the normal vectors for triangles P->A->B and P->B->C + const auto u = crossProduct(b, c); + const auto v = crossProduct(c, a); + + // they have to point in the same direction or be orthogonal + if (u*v < 0.0 - eps4) + return false; + + // compute the normal vector for triangle P->C->A + const auto w = crossProduct(a, b); + + // it also has to point in the same direction or be orthogonal + if (u*w < 0.0 - eps4) + return false; + + // check if point is on the line of one of the edges + if (u.two_norm2() < eps4) + return b*c < 0.0 + eps_*nnorm; + if (v.two_norm2() < eps4) + return a*c < 0.0 + eps_*nnorm; + + // now the point must be in the triangle (or on the faces) + return true; +} + +/*! + * \ingroup Geometry + * \brief Find out whether a point is inside a triangle (p0, p1, p2, p3) (dimworld is 2) + */ +template<class ctype, int dimworld, typename std::enable_if_t<(dimworld == 2), int> = 0> +bool intersectsPointSimplex(const Dune::FieldVector<ctype, dimworld>& point, + const Dune::FieldVector<ctype, dimworld>& p0, + const Dune::FieldVector<ctype, dimworld>& p1, + const Dune::FieldVector<ctype, dimworld>& p2) +{ + static constexpr ctype eps_ = 1.0e-7; + + // Use barycentric coordinates + const ctype A = 0.5*(-p1[1]*p2[0] + p0[1]*(p2[0] - p1[0]) + +p1[0]*p2[1] + p0[0]*(p1[1] - p2[1])); + const ctype sign = std::copysign(1.0, A); + const ctype s = sign*(p0[1]*p2[0] + point[0]*(p2[1]-p0[1]) + -p0[0]*p2[1] + point[1]*(p0[0]-p2[0])); + const ctype t = sign*(p0[0]*p1[1] + point[0]*(p0[1]-p1[1]) + -p0[1]*p1[0] + point[1]*(p1[0]-p0[0])); + const ctype eps = sign*A*eps_; + + return (s > -eps + && t > -eps + && (s + t) < 2*A*sign + eps); +} + +/*! + * \ingroup Geometry + * \brief Find out whether a point is inside a interval (p0, p1, p2, p3) (dimworld is 3) + * \note We assume the given interval has non-zero length and use it to scale the epsilon + */ +template<class ctype, int dimworld, typename std::enable_if_t<(dimworld == 3 || dimworld == 2), int> = 0> +bool intersectsPointSimplex(const Dune::FieldVector<ctype, dimworld>& point, + const Dune::FieldVector<ctype, dimworld>& p0, + const Dune::FieldVector<ctype, dimworld>& p1) +{ + using GlobalPosition = Dune::FieldVector<ctype, dimworld>; + static constexpr ctype eps_ = 1.0e-7; + + // compute the vectors between p0 and the other points + const GlobalPosition v1 = p1 - p0; + const GlobalPosition v2 = point - p0; + + const ctype v1norm = v1.two_norm(); + const ctype v2norm = v2.two_norm(); + + // check if point and p0 are the same + if (v2norm < v1norm*eps_) + return true; + + return (v1.dot(v2) > v1norm*v2norm*(1.0 - eps_) && v2norm < v1norm*(1.0 + eps_)); +} + +/*! + * \ingroup Geometry + * \brief Find out whether a point is inside a interval (p0, p1, p2, p3) (dimworld is 1) + */ +template<class ctype, int dimworld, typename std::enable_if_t<(dimworld == 1), int> = 0> +bool intersectsPointSimplex(const Dune::FieldVector<ctype, dimworld>& point, + const Dune::FieldVector<ctype, dimworld>& p0, + const Dune::FieldVector<ctype, dimworld>& p1) +{ + static constexpr ctype eps_ = 1.0e-7; + + // sort the interval so interval[1] is the end and interval[0] the start + const ctype *interval[2] = {&p0[0], &p1[0]}; + if (*interval[0] > *interval[1]) + std::swap(interval[0], interval[1]); + + const ctype v1 = point[0] - *interval[0]; + const ctype v2 = *interval[1] - *interval[0]; // always positive + + // the coordinates are the same + using std::abs; + if (abs(v1) < v2*eps_) + return true; + + // the point doesn't coincide with p0 + // so if p0 and p1 are equal it's not inside + if (v2 < 1.0e-30) + return false; + + // the point is inside if the length is + // smaller than the interval length and the + // sign of v1 & v2 are the same + using std::signbit; + return (!signbit(v1) && abs(v1) < v2*(1.0 + eps_)); +} + +} // end namespace Dumux + +#endif diff --git a/dumux/geometry/makegeometry.hh b/dumux/geometry/makegeometry.hh new file mode 100644 index 0000000000000000000000000000000000000000..7148063a3d6e3ac60e26fa61d7745f3f17e92235 --- /dev/null +++ b/dumux/geometry/makegeometry.hh @@ -0,0 +1,210 @@ +/***************************************************************************** + * 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 Geometry + * \brief Create Dune geometries from user-specified points + */ +#ifndef DUMUX_GEOMETRY_MAKE_GEOMETRY_HH +#define DUMUX_GEOMETRY_MAKE_GEOMETRY_HH + +#include <vector> +#include <array> +#include <limits> +#include <dune/common/fvector.hh> +#include <dune/common/fmatrix.hh> +#include <dune/common/exceptions.hh> +#include <dune/geometry/multilineargeometry.hh> +#include <dumux/common/math.hh> +#include <dumux/geometry/grahamconvexhull.hh> + +namespace Dumux { + +/*! + * \ingroup Geometry + * \brief Checks if four points lie within the same plane + */ +template<class CoordScalar> +bool pointsAreCoplanar(const std::vector<Dune::FieldVector<CoordScalar, 3>>& points, const CoordScalar scale) +{ + if (points.size() != 4) + DUNE_THROW(Dune::InvalidStateException, "Check only works for 4 points!"); + + // (see "Real-Time Collision Detection" by Christer Ericson) + Dune::FieldMatrix<CoordScalar, 4, 4> M; + for (int i = 0; i < 3; ++i ) + M[i] = {points[0][i], points[1][i], points[2][i], points[3][i]}; + M[3] = {1.0*scale, 1.0*scale, 1.0*scale, 1.0*scale}; + + using std::abs; + return abs(M.determinant()) < 1.5e-7*scale*scale*scale*scale; +} + +/*! + * \ingroup Geometry + * \brief Checks if four points lie within the same plane. + */ +template<class CoordScalar> +bool pointsAreCoplanar(const std::vector<Dune::FieldVector<CoordScalar, 3>>& points) +{ + Dune::FieldVector<CoordScalar, 3> bBoxMin(std::numeric_limits<CoordScalar>::max()); + Dune::FieldVector<CoordScalar, 3> bBoxMax(std::numeric_limits<CoordScalar>::lowest()); + for (const auto& p : points) + { + for (int i=0; i<3; i++) + { + using std::min; + using std::max; + bBoxMin[i] = min(bBoxMin[i], p[i]); + bBoxMax[i] = max(bBoxMax[i], p[i]); + } + } + + const auto size = (bBoxMax - bBoxMin).two_norm(); + + return pointsAreCoplanar(points, size); +} + +/*! + * \ingroup Geometry + * \brief Returns a vector of points following the dune ordering. + * Convenience method that creates a temporary object in case no array of orientations is desired. + * + * \param points The user-specified vector of points (potentially in wrong order). + */ +template<class CoordScalar> +std::vector<Dune::FieldVector<CoordScalar, 3>> getReorderedPoints(const std::vector<Dune::FieldVector<CoordScalar, 3>>& points) +{ + std::array<int, 4> tmp; + return getReorderedPoints(points, tmp); +} + +/*! + * \ingroup Geometry + * \brief Returns a vector of points following the dune ordering. + * + * \param points The user-specified vector of points (potentially in wrong order). + * \param orientations An array of orientations that can be useful for further processing. + */ +template<class CoordScalar> +std::vector<Dune::FieldVector<CoordScalar, 3>> getReorderedPoints(const std::vector<Dune::FieldVector<CoordScalar, 3>>& points, + std::array<int, 4>& orientations) +{ + if(points.size() == 4) + { + auto& p0 = points[0]; + auto& p1 = points[1]; + auto& p2 = points[2]; + auto& p3 = points[3]; + + // check if the points define a proper quadrilateral + const auto normal = crossProduct((p1 - p0), (p2 - p0)); + + orientations = { getOrientation(p0, p3, p2, normal), + getOrientation(p0, p3, p1, normal), + getOrientation(p2, p1, p0, normal), + getOrientation(p2, p1, p3, normal) }; + + + // check if the points follow the dune ordering (see http://www.dcs.gla.ac.uk/~pat/52233/slides/Geometry1x1.pdf) + const bool diagonalsIntersect = (orientations[0] != orientations[1]) && (orientations[2] != orientations[3]); + + // the points conform with the dune ordering + if(diagonalsIntersect) + return points; + + // the points do not conform with the dune ordering, re-order + using GlobalPosition = Dune::FieldVector<CoordScalar, 3>; + if(!diagonalsIntersect && orientations[0] == 1) + return std::vector<GlobalPosition>{p1, p0, p2, p3}; + else if(!diagonalsIntersect && orientations[0] == -1) + return std::vector<GlobalPosition>{p3, p1, p0, p2}; + else + DUNE_THROW(Dune::InvalidStateException, "Could not reorder points"); + } + else + DUNE_THROW(Dune::NotImplemented, "Reorder for " << points.size() << " points."); +} + +/*! + * \ingroup Geometry + * \brief Creates a dune quadrilateral geometry given 4 corner points. + * + * \tparam CoordScalar The CoordScalar type. + * \tparam enableSanityCheck Turn on/off sanity check and reordering of points + * \param points The user-specified vector of points (potentially in wrong order). + */ +template<class CoordScalar, bool enableSanityCheck = true> +auto makeDuneQuadrilaterial(const std::vector<Dune::FieldVector<CoordScalar, 3>>& points) +{ + if (points.size() != 4) + DUNE_THROW(Dune::InvalidStateException, "A quadrilateral needs 4 corner points!"); + + using GlobalPosition = Dune::FieldVector<CoordScalar, 3>; + static constexpr auto coordDim = GlobalPosition::dimension; + static constexpr auto dim = coordDim-1; + using GeometryType = Dune::MultiLinearGeometry<CoordScalar, dim, coordDim>; + + // if no sanity check if desired, use the given points directly to construct the geometry + if (!enableSanityCheck) + return GeometryType(Dune::GeometryTypes::quadrilateral, points); + + // compute size + Dune::FieldVector<CoordScalar, 3> bBoxMin(std::numeric_limits<CoordScalar>::max()); + Dune::FieldVector<CoordScalar, 3> bBoxMax(std::numeric_limits<CoordScalar>::lowest()); + for (const auto& p : points) + { + for (int i = 0; i < 3; i++) + { + using std::min; + using std::max; + bBoxMin[i] = min(bBoxMin[i], p[i]); + bBoxMax[i] = max(bBoxMax[i], p[i]); + } + } + + const auto size = (bBoxMax - bBoxMin).two_norm(); + + // otherwise, perform a number of checks and corrections + if (!pointsAreCoplanar(points, size)) + DUNE_THROW(Dune::InvalidStateException, "Points do not lie within a plane"); + + auto corners = grahamConvexHull<2>(points); + if (corners.size() != 4) + DUNE_THROW(Dune::InvalidStateException, "Points do not span a strictly convex polygon!"); + + // make sure points conform with dune ordering + std::array<int, 4> orientations; + corners = getReorderedPoints(corners, orientations); + + if (std::any_of(orientations.begin(), orientations.end(), [](auto i){ return i == 0; })) + DUNE_THROW(Dune::InvalidStateException, "More than two points lie on the same line."); + + const auto quadrilateral = GeometryType(Dune::GeometryTypes::quadrilateral, corners); + + const auto eps = 1e-7; + if (quadrilateral.volume() < eps*size*size) + DUNE_THROW(Dune::InvalidStateException, "Something went wrong, geometry has area of zero"); + + return quadrilateral; +} + + + +} // end namespace Dumux + +#endif // DUMUX_MAKE_GEOMETRY_HH diff --git a/dumux/geometry/normal.hh b/dumux/geometry/normal.hh new file mode 100644 index 0000000000000000000000000000000000000000..a6e95ecb170b8c75a6df877f8667742f85585ee7 --- /dev/null +++ b/dumux/geometry/normal.hh @@ -0,0 +1,77 @@ +/***************************************************************************** + * 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 Geometry + * \brief Helper functions for normals + */ +#ifndef DUMUX_GEOMETRY_NORMAL_HH +#define DUMUX_GEOMETRY_NORMAL_HH + +#include <algorithm> +#include <dune/common/float_cmp.hh> + +namespace Dumux { + +/*! + * \ingroup Geometry + * \brief Create a vector normal to the given one (v is expected to be non-zero) + * \note This returns some orthogonal vector with arbitrary length + */ +template<class Vector> +inline Vector normal(const Vector& v) +{ + static_assert(Vector::size() > 1, "normal expects a coordinate dimension > 1"); + + if constexpr (Vector::size() == 2) + return Vector({-v[1], v[0]}); + + const auto it = std::find_if(v.begin(), v.end(), [](const auto& x) { return Dune::FloatCmp::ne(x, 0.0); }); + const auto index = std::distance(v.begin(), it); + if (index != Vector::size()-1) + { + Vector normal(0.0); + normal[index] = -v[index+1]; + normal[index+1] = v[index]; + return normal; + } + else + { + Vector normal(0.0); + normal[index-1] = -v[index]; + normal[index] = v[index-1]; + return normal; + + } +} + +/*! + * \ingroup Geometry + * \brief Create a vector normal to the given one (v is expected to be non-zero) + * \note This returns some orthogonal vector with unit length + */ +template<class Vector> +inline Vector unitNormal(const Vector& v) +{ + auto normal = Dumux::normal(v); + normal /= normal.two_norm(); + return normal; +} + +} // end namespace Dumux + +#endif diff --git a/dumux/geometry/refinementquadraturerule.hh b/dumux/geometry/refinementquadraturerule.hh new file mode 100644 index 0000000000000000000000000000000000000000..a99649a232d964a78dd646cc3f6b4fe904e37ffe --- /dev/null +++ b/dumux/geometry/refinementquadraturerule.hh @@ -0,0 +1,94 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * 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 Geometry + * \author Timo Koch + * \brief A quadrature based on refinement + */ + +#ifndef DUMUX_GEOMETRY_REFINEMENT_QUADRATURERULE_HH +#define DUMUX_GEOMETRY_REFINEMENT_QUADRATURERULE_HH + +#include <dune/geometry/quadraturerules.hh> +#include <dune/geometry/virtualrefinement.hh> +#include <dumux/common/math.hh> + +namespace Dumux { + +/*! + * \ingroup Geometry + * \brief A "quadrature" based on virtual refinement + */ +template<typename ct, int mydim> +class RefinementQuadratureRule final : public Dune::QuadratureRule<ct, mydim> +{ +public: + //! The space dimension + static constexpr int dim = mydim; + + //! The highest quadrature order available + static constexpr int highest_order = 1; + + ~RefinementQuadratureRule() final {} + + RefinementQuadratureRule(Dune::GeometryType type, int levels) + : Dune::QuadratureRule<ct, dim>(type, highest_order) + { + auto& refinement = Dune::buildRefinement<dim, ct>(type, type); + const auto tag = Dune::refinementLevels(levels); + + // get the vertices + const auto numVertices = refinement.nVertices(tag); + std::vector<Dune::FieldVector<ct, dim>> points; points.reserve(numVertices); + auto vIt = refinement.vBegin(tag); + const auto vItEnd = refinement.vEnd(tag); + for (; vIt != vItEnd; ++vIt) + points.emplace_back(vIt.coords()); + + // go over all elements and get center and volume + const auto numElements = refinement.nElements(tag); + this->reserve(numElements); + auto eIt = refinement.eBegin(tag); + const auto eItEnd = refinement.eEnd(tag); + for (; eIt != eItEnd; ++eIt) + { + // quadrature point in the centroid of the sub-element and weight is its volume + const auto weight = computeVolume_(type, points, eIt.vertexIndices()); + this->emplace_back(eIt.coords(), weight); + } + } + +private: + template<class VertexIndices> + ct computeVolume_(Dune::GeometryType t, const std::vector<Dune::FieldVector<ct, dim>>& points, const VertexIndices& indices) const + { + if (t != Dune::GeometryTypes::simplex(2)) + DUNE_THROW(Dune::NotImplemented, "Only implemented for 2d simplices"); + + const auto ab = points[indices[1]] - points[indices[0]]; + const auto ac = points[indices[2]] - points[indices[0]]; + using std::abs; + return abs(crossProduct(ab, ac))*0.5; + } +}; + +} // end namespace Dumux + +#endif diff --git a/dumux/geometry/triangulation.hh b/dumux/geometry/triangulation.hh new file mode 100644 index 0000000000000000000000000000000000000000..7a537e672d61dbc787591f26548c8419eb0240ce --- /dev/null +++ b/dumux/geometry/triangulation.hh @@ -0,0 +1,134 @@ +/***************************************************************************** + * 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 Geometry + * \brief Functionality to triangulate point clouds + */ +#ifndef DUMUX_GEOMETRY_TRIANGULATION_HH +#define DUMUX_GEOMETRY_TRIANGULATION_HH + +#include <vector> +#include <array> +#include <algorithm> +#include <type_traits> + +#include <dune/common/exceptions.hh> +#include <dune/common/fvector.hh> + +#include <dumux/common/math.hh> + +namespace Dumux { +namespace TriangulationPolicy { + +//! Policy that expects a point cloud that represents a convex +//! hull. Inserts the mid point and connects it to the points of the hull. +struct MidPointPolicy {}; + +//! Delaunay-type triangulations. +struct DelaunayPolicy {}; + +template<int dim, int dimWorld> +using DefaultPolicy = std::conditional_t< dim >= 2, MidPointPolicy, DelaunayPolicy >; + +} // end namespace TriangulationPolicy + +//! The data type to store triangulations +template<int dim, int dimWorld, class ctype> +using Triangulation = std::vector< std::array<Dune::FieldVector<ctype, dimWorld>, dim+1> >; + +/*! + * \ingroup Geometry + * \brief Triangulate area given points of a convex hull + * \tparam dim Specifies the dimension of the resulting triangulation (2 or 3) + * \tparam dimWorld The dimension of the coordinate space + * \tparam Policy Specifies the algorithm to be used for triangulation + * + * \note this specialization is for 2d triangulations using mid point policy + * \note Assumes all points of the convex hull are coplanar + * \note This inserts a mid point and connects all corners with that point to triangles + */ +template< int dim, int dimWorld, class Policy = TriangulationPolicy::DefaultPolicy<dim, dimWorld>, + class RandomAccessContainer, + std::enable_if_t< std::is_same<Policy, TriangulationPolicy::MidPointPolicy>::value + && dim == 2, int> = 0 > +inline Triangulation<dim, dimWorld, typename RandomAccessContainer::value_type::value_type> +triangulate(const RandomAccessContainer& convexHull) +{ + using ctype = typename RandomAccessContainer::value_type::value_type; + using Point = Dune::FieldVector<ctype, dimWorld>; + using Triangle = std::array<Point, 3>; + + static_assert(std::is_same<typename RandomAccessContainer::value_type, Point>::value, + "Triangulation expects Dune::FieldVector as point type"); + + if (convexHull.size() < 3) + DUNE_THROW(Dune::InvalidStateException, "Try to triangulate point cloud with less than 3 points!"); + + if (convexHull.size() == 3) + return std::vector<Triangle>(1, {convexHull[0], convexHull[1], convexHull[2]}); + + Point midPoint(0.0); + for (const auto p : convexHull) + midPoint += p; + midPoint /= convexHull.size(); + + std::vector<Triangle> triangulation; + triangulation.reserve(convexHull.size()); + + for (std::size_t i = 0; i < convexHull.size()-1; ++i) + triangulation.emplace_back(Triangle{midPoint, convexHull[i], convexHull[i+1]}); + + triangulation.emplace_back(Triangle{midPoint, convexHull[convexHull.size()-1], convexHull[0]}); + + return triangulation; +} + +/*! + * \ingroup Geometry + * \brief Triangulate area given points of a convex hull + * \tparam dim Specifies the dimension of the resulting triangulation (2 or 3) + * \tparam dimWorld The dimension of the coordinate space + * \tparam Policy Specifies the algorithm to be used for triangulation + * + * \note this specialization is for 1d discretization using segments + * \note sorts the points and connects them via segments + */ +template< int dim, int dimWorld, class Policy = TriangulationPolicy::DefaultPolicy<dim, dimWorld>, + class RandomAccessContainer, + std::enable_if_t< std::is_same<Policy, TriangulationPolicy::DelaunayPolicy>::value + && dim == 1, int> = 0 > +inline Triangulation<dim, dimWorld, typename RandomAccessContainer::value_type::value_type> +triangulate(const RandomAccessContainer& points) +{ + using ctype = typename RandomAccessContainer::value_type::value_type; + using Point = Dune::FieldVector<ctype, dimWorld>; + + static_assert(std::is_same<typename RandomAccessContainer::value_type, Point>::value, + "Triangulation expects Dune::FieldVector as point type"); + + if (points.size() == 2) + return Triangulation<dim, dimWorld, ctype>({ {points[0], points[1]} }); + + //! \todo sort points and create polyline + assert(points.size() > 1); + DUNE_THROW(Dune::NotImplemented, "1d triangulation for point cloud size > 2"); +} + +} // end namespace Dumux + +# endif diff --git a/dumux/multidomain/boundary/darcydarcy/couplingmapper.hh b/dumux/multidomain/boundary/darcydarcy/couplingmapper.hh index f13d1cc8488e0af12bc86ef39f6bb13b5a82ae15..527ffd0d32c2e52fe0536e33b5b1f54e060bf8f1 100644 --- a/dumux/multidomain/boundary/darcydarcy/couplingmapper.hh +++ b/dumux/multidomain/boundary/darcydarcy/couplingmapper.hh @@ -34,7 +34,7 @@ #include <dune/common/exceptions.hh> #include <dune/common/indices.hh> -#include <dumux/common/geometry/intersectingentities.hh> +#include <dumux/geometry/intersectingentities.hh> #include <dumux/discretization/method.hh> namespace Dumux { diff --git a/dumux/multidomain/embedded/circlepoints.hh b/dumux/multidomain/embedded/circlepoints.hh index 6145915bb08de6c6c0246cc95eda90b8ca9a6091..9c991d21a35d2fef786c82a9b5b5e71e9c411392 100644 --- a/dumux/multidomain/embedded/circlepoints.hh +++ b/dumux/multidomain/embedded/circlepoints.hh @@ -31,7 +31,7 @@ #include <dune/common/exceptions.hh> #include <dumux/common/math.hh> -#include <dumux/common/geometry/normal.hh> +#include <dumux/geometry/normal.hh> namespace Dumux::EmbeddedCoupling { diff --git a/dumux/multidomain/embedded/couplingmanager1d3d.hh b/dumux/multidomain/embedded/couplingmanager1d3d.hh index 53e647e594d54014599d6d30fa6a6e1c62b7ed31..4b8376c06954c5a422b93fd58db36fe98e6b76c9 100644 --- a/dumux/multidomain/embedded/couplingmanager1d3d.hh +++ b/dumux/multidomain/embedded/couplingmanager1d3d.hh @@ -33,7 +33,7 @@ #include <dumux/common/properties.hh> #include <dumux/common/indextraits.hh> -#include <dumux/common/geometry/distance.hh> +#include <dumux/geometry/distance.hh> #include <dumux/multidomain/embedded/pointsourcedata.hh> #include <dumux/multidomain/embedded/integrationpointsource.hh> #include <dumux/multidomain/embedded/couplingmanagerbase.hh> diff --git a/dumux/multidomain/embedded/couplingmanagerbase.hh b/dumux/multidomain/embedded/couplingmanagerbase.hh index 964a95069074461b5eba4249d0e240e8aaad7a6d..7b28d99897b3666302cd7e89f40b186be30329c6 100644 --- a/dumux/multidomain/embedded/couplingmanagerbase.hh +++ b/dumux/multidomain/embedded/couplingmanagerbase.hh @@ -36,8 +36,8 @@ #include <dune/geometry/quadraturerules.hh> #include <dumux/common/properties.hh> -#include <dumux/common/geometry/distance.hh> -#include <dumux/common/geometry/intersectingentities.hh> +#include <dumux/geometry/distance.hh> +#include <dumux/geometry/intersectingentities.hh> #include <dumux/discretization/method.hh> #include <dumux/multidomain/couplingmanager.hh> #include <dumux/multidomain/glue.hh> diff --git a/dumux/multidomain/glue.hh b/dumux/multidomain/glue.hh index d0d54f5c2ed4c5870b6fe71ccf1d5bb1b96453f9..20b79f90171893e484f1a9ca5c81e6de87f40580 100644 --- a/dumux/multidomain/glue.hh +++ b/dumux/multidomain/glue.hh @@ -26,9 +26,9 @@ #ifndef DUMUX_MULTIDOMAIN_GLUE_HH #define DUMUX_MULTIDOMAIN_GLUE_HH -#include <dumux/common/geometry/geometricentityset.hh> -#include <dumux/common/geometry/boundingboxtree.hh> -#include <dumux/common/geometry/intersectionentityset.hh> +#include <dumux/geometry/geometricentityset.hh> +#include <dumux/geometry/boundingboxtree.hh> +#include <dumux/geometry/intersectionentityset.hh> namespace Dumux { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9cec5e3ff866b7f0fa7cd8f49b96a4873354d95f..b6d855fcc0dcabd9bc4072ab6c4d1a76bba629f0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory(common) add_subdirectory(geomechanics) +add_subdirectory(geometry) add_subdirectory(flux) add_subdirectory(freeflow) add_subdirectory(io) diff --git a/test/common/CMakeLists.txt b/test/common/CMakeLists.txt index 80146db8c5a314ffa848258085b72f327ca779b1..68b481a963e0e524456015771aa2e1d31662a037 100644 --- a/test/common/CMakeLists.txt +++ b/test/common/CMakeLists.txt @@ -1,6 +1,4 @@ -add_subdirectory(boundingboxtree) add_subdirectory(functions) -add_subdirectory(geometry) add_subdirectory(integrate) add_subdirectory(math) add_subdirectory(parameters) diff --git a/test/common/geometry/CMakeLists.txt b/test/geometry/CMakeLists.txt similarity index 97% rename from test/common/geometry/CMakeLists.txt rename to test/geometry/CMakeLists.txt index 6e57be7f6d0a318670f5bbc84edc0d64000ddcc0..5e5c273e27c52f628e79f3052dc7226dc1f5305e 100644 --- a/test/common/geometry/CMakeLists.txt +++ b/test/geometry/CMakeLists.txt @@ -1,3 +1,5 @@ +add_subdirectory(boundingboxtree) + 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) @@ -12,11 +14,9 @@ dumux_add_test(SOURCES test_graham_convex_hull.cc LABELS unit) dumux_add_test(SOURCES test_intersectingentity_cartesiangrid.cc LABELS unit) dumux_add_test(SOURCES test_circlepoints.cc LABELS unit) dumux_add_test(SOURCES test_cylinderintegration.cc LABELS unit) - +dumux_add_test(SOURCES test_makegeometry.cc LABELS unit) +dumux_add_test(SOURCES test_refinementquadraturerule.cc LABELS unit) dune_symlink_to_source_files(FILES ball.msh) dumux_add_test(SOURCES test_intersectionentityset.cc CMAKE_GUARD dune-alugrid_FOUND LABELS unit) - -dumux_add_test(SOURCES test_makegeometry.cc LABELS unit) -dumux_add_test(SOURCES test_refinementquadraturerule.cc LABELS unit) diff --git a/test/common/geometry/ball.geo b/test/geometry/ball.geo similarity index 100% rename from test/common/geometry/ball.geo rename to test/geometry/ball.geo diff --git a/test/common/geometry/ball.msh b/test/geometry/ball.msh similarity index 100% rename from test/common/geometry/ball.msh rename to test/geometry/ball.msh diff --git a/test/common/boundingboxtree/CMakeLists.txt b/test/geometry/boundingboxtree/CMakeLists.txt similarity index 100% rename from test/common/boundingboxtree/CMakeLists.txt rename to test/geometry/boundingboxtree/CMakeLists.txt diff --git a/test/common/boundingboxtree/fracture.msh b/test/geometry/boundingboxtree/fracture.msh similarity index 100% rename from test/common/boundingboxtree/fracture.msh rename to test/geometry/boundingboxtree/fracture.msh diff --git a/test/common/boundingboxtree/network1d.geo b/test/geometry/boundingboxtree/network1d.geo similarity index 100% rename from test/common/boundingboxtree/network1d.geo rename to test/geometry/boundingboxtree/network1d.geo diff --git a/test/common/boundingboxtree/network1d.msh b/test/geometry/boundingboxtree/network1d.msh similarity index 100% rename from test/common/boundingboxtree/network1d.msh rename to test/geometry/boundingboxtree/network1d.msh diff --git a/test/common/boundingboxtree/network2d.geo b/test/geometry/boundingboxtree/network2d.geo similarity index 100% rename from test/common/boundingboxtree/network2d.geo rename to test/geometry/boundingboxtree/network2d.geo diff --git a/test/common/boundingboxtree/network2d.msh b/test/geometry/boundingboxtree/network2d.msh similarity index 100% rename from test/common/boundingboxtree/network2d.msh rename to test/geometry/boundingboxtree/network2d.msh diff --git a/test/common/boundingboxtree/test_bboxtree.cc b/test/geometry/boundingboxtree/test_bboxtree.cc similarity index 99% rename from test/common/boundingboxtree/test_bboxtree.cc rename to test/geometry/boundingboxtree/test_bboxtree.cc index 9c6066f7f27119c87195502ac28950a4035db2a6..0427bf3a5926d85202df5c369330439189f04d07 100644 --- a/test/common/boundingboxtree/test_bboxtree.cc +++ b/test/geometry/boundingboxtree/test_bboxtree.cc @@ -16,10 +16,10 @@ #endif #include <dumux/common/exceptions.hh> -#include <dumux/common/geometry/boundingboxtree.hh> -#include <dumux/common/geometry/geometricentityset.hh> -#include <dumux/common/geometry/intersectingentities.hh> -#include <test/common/geometry/writetriangulation.hh> +#include <dumux/geometry/boundingboxtree.hh> +#include <dumux/geometry/geometricentityset.hh> +#include <dumux/geometry/intersectingentities.hh> +#include <test/geometry/writetriangulation.hh> namespace Dumux { diff --git a/test/common/boundingboxtree/test_geometry_fracture.cc b/test/geometry/boundingboxtree/test_geometry_fracture.cc similarity index 93% rename from test/common/boundingboxtree/test_geometry_fracture.cc rename to test/geometry/boundingboxtree/test_geometry_fracture.cc index 4785597b7690524060b138e33f722b06955b5d9a..810553428f7f27d4194276c3110a5da2b6218495 100644 --- a/test/common/boundingboxtree/test_geometry_fracture.cc +++ b/test/geometry/boundingboxtree/test_geometry_fracture.cc @@ -14,10 +14,10 @@ #include <dune/foamgrid/dgffoam.hh> #include <dumux/common/exceptions.hh> -#include <dumux/common/geometry/boundingboxtree.hh> -#include <dumux/common/geometry/geometricentityset.hh> -#include <dumux/common/geometry/intersectingentities.hh> -#include <test/common/geometry/writetriangulation.hh> +#include <dumux/geometry/boundingboxtree.hh> +#include <dumux/geometry/geometricentityset.hh> +#include <dumux/geometry/intersectingentities.hh> +#include <test/geometry/writetriangulation.hh> int main (int argc, char *argv[]) try { diff --git a/test/common/geometry/test_0d1d_intersection.cc b/test/geometry/test_0d1d_intersection.cc similarity index 97% rename from test/common/geometry/test_0d1d_intersection.cc rename to test/geometry/test_0d1d_intersection.cc index 73e1a15f3f2bf747d199a2e7b7326b0f13acfd4d..5051f7b2a4118e366cf3009c8d8e28e35eacfd7a 100644 --- a/test/common/geometry/test_0d1d_intersection.cc +++ b/test/geometry/test_0d1d_intersection.cc @@ -8,7 +8,7 @@ #include <dune/common/parallel/mpihelper.hh> #include <dune/common/fvector.hh> -#include <dumux/common/geometry/intersectspointgeometry.hh> +#include <dumux/geometry/intersectspointgeometry.hh> #ifndef DOXYGEN template<int dimworld = 3> diff --git a/test/common/geometry/test_0d2d_intersection.cc b/test/geometry/test_0d2d_intersection.cc similarity index 100% rename from test/common/geometry/test_0d2d_intersection.cc rename to test/geometry/test_0d2d_intersection.cc diff --git a/test/common/geometry/test_0d3d_intersection.cc b/test/geometry/test_0d3d_intersection.cc similarity index 100% rename from test/common/geometry/test_0d3d_intersection.cc rename to test/geometry/test_0d3d_intersection.cc diff --git a/test/common/geometry/test_1d1d_intersection.cc b/test/geometry/test_1d1d_intersection.cc similarity index 99% rename from test/common/geometry/test_1d1d_intersection.cc rename to test/geometry/test_1d1d_intersection.cc index 034905a2b06dfe110771615ff9f243dd00bc381d..142b96d346ced82dce503fc693119f2ab3e30156 100644 --- a/test/common/geometry/test_1d1d_intersection.cc +++ b/test/geometry/test_1d1d_intersection.cc @@ -7,7 +7,7 @@ #include <dune/common/parallel/mpihelper.hh> #include <dune/common/fvector.hh> -#include <dumux/common/geometry/geometryintersection.hh> +#include <dumux/geometry/geometryintersection.hh> #ifndef DOXYGEN template<int dimWorld> diff --git a/test/common/geometry/test_1d2d_intersection.cc b/test/geometry/test_1d2d_intersection.cc similarity index 99% rename from test/common/geometry/test_1d2d_intersection.cc rename to test/geometry/test_1d2d_intersection.cc index 50b34e2542f19970e8c4d9109e2fd292b0f28fbb..e1c8470a5fc4756d595726f70208ed1b77c291f1 100644 --- a/test/common/geometry/test_1d2d_intersection.cc +++ b/test/geometry/test_1d2d_intersection.cc @@ -10,8 +10,7 @@ #include <dune/common/fvector.hh> #include <dune/geometry/multilineargeometry.hh> -#include <dumux/common/geometry/geometryintersection.hh> - +#include <dumux/geometry/geometryintersection.hh> #ifndef DOXYGEN Dune::MultiLinearGeometry<double, 1, 2> diff --git a/test/common/geometry/test_1d3d_intersection.cc b/test/geometry/test_1d3d_intersection.cc similarity index 99% rename from test/common/geometry/test_1d3d_intersection.cc rename to test/geometry/test_1d3d_intersection.cc index 68fe8d105f48dff100f0975f222b5e2b73f73339..7f0efc3e1ffeeba01fc0fa4f58a0f9edb67b7432 100644 --- a/test/common/geometry/test_1d3d_intersection.cc +++ b/test/geometry/test_1d3d_intersection.cc @@ -9,7 +9,7 @@ #include <dune/common/fvector.hh> #include <dune/geometry/multilineargeometry.hh> -#include <dumux/common/geometry/geometryintersection.hh> +#include <dumux/geometry/geometryintersection.hh> #ifndef DOXYGEN diff --git a/test/common/geometry/test_2d2d_intersection.cc b/test/geometry/test_2d2d_intersection.cc similarity index 99% rename from test/common/geometry/test_2d2d_intersection.cc rename to test/geometry/test_2d2d_intersection.cc index 0042f0ad93f5f12bf4a184154fecf55fc4bbc942..efb6de99123887d0bf6795c7fc4e4cdd414f924a 100644 --- a/test/common/geometry/test_2d2d_intersection.cc +++ b/test/geometry/test_2d2d_intersection.cc @@ -6,7 +6,7 @@ #include <dune/common/fvector.hh> #include <dune/geometry/multilineargeometry.hh> -#include <dumux/common/geometry/geometryintersection.hh> +#include <dumux/geometry/geometryintersection.hh> #ifndef DOXYGEN template<int dimWorld> diff --git a/test/common/geometry/test_2d3d_intersection.cc b/test/geometry/test_2d3d_intersection.cc similarity index 96% rename from test/common/geometry/test_2d3d_intersection.cc rename to test/geometry/test_2d3d_intersection.cc index 0b94c645c35ce30842ea3a39bbd1a146bb082203..4b518874ac1a7a8dfa83895dac0a83c66b649b83 100644 --- a/test/common/geometry/test_2d3d_intersection.cc +++ b/test/geometry/test_2d3d_intersection.cc @@ -10,9 +10,9 @@ #include <dune/common/timer.hh> #include <dune/geometry/multilineargeometry.hh> -#include <dumux/common/geometry/geometryintersection.hh> -#include <dumux/common/geometry/triangulation.hh> -#include <test/common/geometry/writetriangulation.hh> +#include <dumux/geometry/geometryintersection.hh> +#include <dumux/geometry/triangulation.hh> +#include "writetriangulation.hh" template<int dimworld = 3> void testSegTriangle(const Dune::FieldVector<double, dimworld>& a, diff --git a/test/common/geometry/test_circlepoints.cc b/test/geometry/test_circlepoints.cc similarity index 100% rename from test/common/geometry/test_circlepoints.cc rename to test/geometry/test_circlepoints.cc diff --git a/test/common/geometry/test_cylinderintegration.cc b/test/geometry/test_cylinderintegration.cc similarity index 100% rename from test/common/geometry/test_cylinderintegration.cc rename to test/geometry/test_cylinderintegration.cc diff --git a/test/common/geometry/test_distance.cc b/test/geometry/test_distance.cc similarity index 98% rename from test/common/geometry/test_distance.cc rename to test/geometry/test_distance.cc index 3fbed2c96d991718eaf916e83a39bc3f6a2d1557..9f822348f76699d5496046f9d401ba53a8709ce6 100644 --- a/test/common/geometry/test_distance.cc +++ b/test/geometry/test_distance.cc @@ -33,8 +33,8 @@ #include <dune/geometry/affinegeometry.hh> #include <dune/geometry/multilineargeometry.hh> -#include <dumux/common/geometry/distance.hh> -#include <dumux/common/geometry/normal.hh> +#include <dumux/geometry/distance.hh> +#include <dumux/geometry/normal.hh> // helper function to make point geometry from field vector template<class Point, int dimWorld> diff --git a/test/common/geometry/test_graham_convex_hull.cc b/test/geometry/test_graham_convex_hull.cc similarity index 96% rename from test/common/geometry/test_graham_convex_hull.cc rename to test/geometry/test_graham_convex_hull.cc index a59029cc12325393b8006727d82baa7f400cedbb..f70c7881271f5ba26343ebb778f1b56e0e4f8950 100644 --- a/test/common/geometry/test_graham_convex_hull.cc +++ b/test/geometry/test_graham_convex_hull.cc @@ -13,9 +13,10 @@ #include <dune/common/timer.hh> #include <dumux/common/math.hh> -#include <dumux/common/geometry/triangulation.hh> -#include <dumux/common/geometry/grahamconvexhull.hh> -#include <test/common/geometry/writetriangulation.hh> +#include <dumux/geometry/triangulation.hh> +#include <dumux/geometry/grahamconvexhull.hh> + +#include "writetriangulation.hh" template<class Scalar> class UniformDistributedRandomNumber diff --git a/test/common/geometry/test_intersectingentity_cartesiangrid.cc b/test/geometry/test_intersectingentity_cartesiangrid.cc similarity index 93% rename from test/common/geometry/test_intersectingentity_cartesiangrid.cc rename to test/geometry/test_intersectingentity_cartesiangrid.cc index dd7dacc0d9f774577f472f3459da3293f139f7e6..8b97413dff0795647e37eb84ef6a3f373f7b85ba 100644 --- a/test/common/geometry/test_intersectingentity_cartesiangrid.cc +++ b/test/geometry/test_intersectingentity_cartesiangrid.cc @@ -5,8 +5,8 @@ #include <dune/common/exceptions.hh> #include <dune/grid/yaspgrid.hh> -#include <dumux/common/geometry/boundingboxtree.hh> -#include <dumux/common/geometry/intersectingentities.hh> +#include <dumux/geometry/boundingboxtree.hh> +#include <dumux/geometry/intersectingentities.hh> template<int dimworld> void testIntersectingEntityCartesianGrid() diff --git a/test/common/geometry/test_intersection.hh b/test/geometry/test_intersection.hh similarity index 97% rename from test/common/geometry/test_intersection.hh rename to test/geometry/test_intersection.hh index 98fc4a29c7e9cac7c93adc9bdc24d963fdb28f6d..5a3221775da9e875ed1826305d95e48afa295c07 100644 --- a/test/common/geometry/test_intersection.hh +++ b/test/geometry/test_intersection.hh @@ -22,7 +22,7 @@ #ifndef DUMUX_TEST_INTERSECTION_HH #define DUMUX_TEST_INTERSECTION_HH -#include <dumux/common/geometry/intersectspointgeometry.hh> +#include <dumux/geometry/intersectspointgeometry.hh> namespace Dumux { diff --git a/test/common/geometry/test_intersectionentityset.cc b/test/geometry/test_intersectionentityset.cc similarity index 98% rename from test/common/geometry/test_intersectionentityset.cc rename to test/geometry/test_intersectionentityset.cc index f24ffb817e96212e9cf28ee92d5e1869e58b2742..50ef26514214c66d2936a3c497dcacde14b0869b 100644 --- a/test/common/geometry/test_intersectionentityset.cc +++ b/test/geometry/test_intersectionentityset.cc @@ -13,8 +13,8 @@ #include <dune/grid/io/file/gmshreader.hh> #include <dune/alugrid/grid.hh> -#include <dumux/common/geometry/geometricentityset.hh> -#include <dumux/common/geometry/intersectionentityset.hh> +#include <dumux/geometry/geometricentityset.hh> +#include <dumux/geometry/intersectionentityset.hh> int main (int argc, char *argv[]) try { diff --git a/test/common/geometry/test_makegeometry.cc b/test/geometry/test_makegeometry.cc similarity index 98% rename from test/common/geometry/test_makegeometry.cc rename to test/geometry/test_makegeometry.cc index 718db663d5e55b65479866a245d60e9f7a5cb06e..b0cbcdf7b3d9dcccce642a1d95c1d47e320fd06d 100644 --- a/test/common/geometry/test_makegeometry.cc +++ b/test/geometry/test_makegeometry.cc @@ -28,9 +28,9 @@ #include <random> #include <dune/common/float_cmp.hh> -#include <dumux/common/geometry/intersectspointgeometry.hh> -#include <dumux/common/geometry/makegeometry.hh> -#include <dumux/common/geometry/grahamconvexhull.hh> +#include <dumux/geometry/intersectspointgeometry.hh> +#include <dumux/geometry/makegeometry.hh> +#include <dumux/geometry/grahamconvexhull.hh> //! test if the ordering of the input points has an effect on the resulting geometry template<class GlobalPosition> diff --git a/test/common/geometry/test_normal.cc b/test/geometry/test_normal.cc similarity index 98% rename from test/common/geometry/test_normal.cc rename to test/geometry/test_normal.cc index 2a95def1b644225b2042a9a4e3a31b8b6efd397a..f947a92b5cce90159e2c7fde9b6298e6ebc91c48 100644 --- a/test/common/geometry/test_normal.cc +++ b/test/geometry/test_normal.cc @@ -28,7 +28,7 @@ #include <dune/common/exceptions.hh> #include <dune/common/fvector.hh> -#include <dumux/common/geometry/normal.hh> +#include <dumux/geometry/normal.hh> template<class P> void testOrthogonality(const P& normal, const P& vector, double scale, const std::string& caseName = "") diff --git a/test/common/geometry/test_refinementquadraturerule.cc b/test/geometry/test_refinementquadraturerule.cc similarity index 95% rename from test/common/geometry/test_refinementquadraturerule.cc rename to test/geometry/test_refinementquadraturerule.cc index 7511e351a67b87cdce9bd712de69bb261cee1955..ea097c0b175cebeafe6272e090ff82f9f7c5d4da 100644 --- a/test/common/geometry/test_refinementquadraturerule.cc +++ b/test/geometry/test_refinementquadraturerule.cc @@ -6,8 +6,8 @@ #include <dune/common/fvector.hh> #include <dune/common/exceptions.hh> #include <dune/common/float_cmp.hh> -#include <dumux/common/geometry/refinementquadraturerule.hh> #include <dune/geometry/affinegeometry.hh> +#include <dumux/geometry/refinementquadraturerule.hh> int main (int argc, char *argv[]) try { diff --git a/test/common/geometry/transformation.hh b/test/geometry/transformation.hh similarity index 100% rename from test/common/geometry/transformation.hh rename to test/geometry/transformation.hh diff --git a/test/common/geometry/writetriangulation.hh b/test/geometry/writetriangulation.hh similarity index 100% rename from test/common/geometry/writetriangulation.hh rename to test/geometry/writetriangulation.hh diff --git a/test/multidomain/boundary/stokesdarcy/1p2c_1p2c/diffusionlawcomparison/main.cc b/test/multidomain/boundary/stokesdarcy/1p2c_1p2c/diffusionlawcomparison/main.cc index 2580af165d51c8d745d78c1c4dd9ef2b7ffae91d..e457f6ff882058f275e11947a934ec8b95fa9207 100644 --- a/test/multidomain/boundary/stokesdarcy/1p2c_1p2c/diffusionlawcomparison/main.cc +++ b/test/multidomain/boundary/stokesdarcy/1p2c_1p2c/diffusionlawcomparison/main.cc @@ -37,7 +37,7 @@ #include <dumux/common/parameters.hh> #include <dumux/common/partial.hh> #include <dumux/common/dumuxmessage.hh> -#include <dumux/common/geometry/diameter.hh> +#include <dumux/geometry/diameter.hh> #include <dumux/linear/seqsolverbackend.hh> #include <dumux/assembly/fvassembler.hh> #include <dumux/assembly/diffmethod.hh> diff --git a/test/multidomain/boundary/stokesdarcy/1p2c_1p2c/main.cc b/test/multidomain/boundary/stokesdarcy/1p2c_1p2c/main.cc index 56c348c5a53f330167452c25a51496c20f4d293d..4d5e8b956107ae9e9929a2fd6c9bfdd4112dd7fd 100644 --- a/test/multidomain/boundary/stokesdarcy/1p2c_1p2c/main.cc +++ b/test/multidomain/boundary/stokesdarcy/1p2c_1p2c/main.cc @@ -36,7 +36,7 @@ #include <dumux/common/parameters.hh> #include <dumux/common/partial.hh> #include <dumux/common/dumuxmessage.hh> -#include <dumux/common/geometry/diameter.hh> +#include <dumux/geometry/diameter.hh> #include <dumux/linear/seqsolverbackend.hh> #include <dumux/assembly/fvassembler.hh> #include <dumux/assembly/diffmethod.hh> diff --git a/test/multidomain/boundary/stokesdarcy/1p2c_2p2c/main.cc b/test/multidomain/boundary/stokesdarcy/1p2c_2p2c/main.cc index eac50e90273003b026b4986752baf872163c8f6f..50c0de1e6148df7391116ed17612ca7ab41a90af 100644 --- a/test/multidomain/boundary/stokesdarcy/1p2c_2p2c/main.cc +++ b/test/multidomain/boundary/stokesdarcy/1p2c_2p2c/main.cc @@ -36,7 +36,7 @@ #include <dumux/common/parameters.hh> #include <dumux/common/partial.hh> #include <dumux/common/dumuxmessage.hh> -#include <dumux/common/geometry/diameter.hh> +#include <dumux/geometry/diameter.hh> #include <dumux/linear/seqsolverbackend.hh> #include <dumux/assembly/fvassembler.hh> #include <dumux/assembly/diffmethod.hh> diff --git a/test/multidomain/boundary/stokesdarcy/1p3c_1p3c/main.cc b/test/multidomain/boundary/stokesdarcy/1p3c_1p3c/main.cc index 8c45d59943ce3bb2ac7e6089cd066dd7093d5653..a9550162abfa41d2da5c0f34387dd3652b4f0c34 100644 --- a/test/multidomain/boundary/stokesdarcy/1p3c_1p3c/main.cc +++ b/test/multidomain/boundary/stokesdarcy/1p3c_1p3c/main.cc @@ -36,7 +36,7 @@ #include <dumux/common/parameters.hh> #include <dumux/common/partial.hh> #include <dumux/common/dumuxmessage.hh> -#include <dumux/common/geometry/diameter.hh> +#include <dumux/geometry/diameter.hh> #include <dumux/linear/seqsolverbackend.hh> #include <dumux/assembly/fvassembler.hh> #include <dumux/assembly/diffmethod.hh> diff --git a/test/multidomain/boundary/stokesdarcy/1p_2p/main.cc b/test/multidomain/boundary/stokesdarcy/1p_2p/main.cc index 1e0c171ba8af99fd529617ecaac48e0b07749c03..771d54f04ee36879e598239f344267ecb5d3a1f4 100644 --- a/test/multidomain/boundary/stokesdarcy/1p_2p/main.cc +++ b/test/multidomain/boundary/stokesdarcy/1p_2p/main.cc @@ -35,7 +35,7 @@ #include <dumux/common/properties.hh> #include <dumux/common/parameters.hh> #include <dumux/common/dumuxmessage.hh> -#include <dumux/common/geometry/diameter.hh> +#include <dumux/geometry/diameter.hh> #include <dumux/linear/seqsolverbackend.hh> #include <dumux/assembly/fvassembler.hh> #include <dumux/assembly/diffmethod.hh> diff --git a/test/multidomain/embedded/1d3d/1p_1p/main.cc b/test/multidomain/embedded/1d3d/1p_1p/main.cc index 6c90da38eaba11407d25e6fabee96c08c3c32359..9b7427ab04f690d8c360f64fe22397766da2133f 100644 --- a/test/multidomain/embedded/1d3d/1p_1p/main.cc +++ b/test/multidomain/embedded/1d3d/1p_1p/main.cc @@ -36,7 +36,7 @@ #include <dumux/common/properties.hh> #include <dumux/common/parameters.hh> #include <dumux/common/dumuxmessage.hh> -#include <dumux/common/geometry/diameter.hh> +#include <dumux/geometry/diameter.hh> #include <dumux/linear/seqsolverbackend.hh> #include <dumux/assembly/diffmethod.hh> #include <dumux/discretization/method.hh> diff --git a/test/multidomain/embedded/2d3d/1p_1p/main.cc b/test/multidomain/embedded/2d3d/1p_1p/main.cc index 1b7929c0168c41a53c9887ba398c39205462436b..1d1ff9c59db08b453732ad9aadc002ca594554a1 100644 --- a/test/multidomain/embedded/2d3d/1p_1p/main.cc +++ b/test/multidomain/embedded/2d3d/1p_1p/main.cc @@ -36,7 +36,7 @@ #include <dumux/common/properties.hh> #include <dumux/common/parameters.hh> #include <dumux/common/dumuxmessage.hh> -#include <dumux/common/geometry/diameter.hh> +#include <dumux/geometry/diameter.hh> #include <dumux/linear/seqsolverbackend.hh> #include <dumux/assembly/fvassembler.hh> #include <dumux/assembly/diffmethod.hh>