From 1a6ef1e368a0df7fb67222ef836637078dfb60ff Mon Sep 17 00:00:00 2001 From: Kilian Weishaupt <kilian.weishaupt@iws.uni-stuttgart.de> Date: Wed, 4 Aug 2021 20:14:01 +0200 Subject: [PATCH] [geometry] Add WallDistance class --- dumux/discretization/walldistance.hh | 297 +++++++++++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 dumux/discretization/walldistance.hh diff --git a/dumux/discretization/walldistance.hh b/dumux/discretization/walldistance.hh new file mode 100644 index 0000000000..d6c9986b4d --- /dev/null +++ b/dumux/discretization/walldistance.hh @@ -0,0 +1,297 @@ +// -*- 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 Discretization + * \copydoc Dumux::WallDistance + */ +#ifndef DUMUX_DISCRETIZATION_WALL_DISTANCE_HH +#define DUMUX_DISCRETIZATION_WALL_DISTANCE_HH + +#include <vector> +#include <dune/common/parallel/mpihelper.hh> +#include <dune/common/shared_ptr.hh> + +#include <dumux/common/tag.hh> +#include <dumux/common/indextraits.hh> +#include <dumux/geometry/distancefield.hh> + +namespace Dumux { + +/*! + * \ingroup Discretization + * \brief Class to calculate the wall distance at every element or vertex of a grid + * \tparam GridGeometry The grid geometry. + * \tparam DistanceField The type of distance field to use (parameterized with the geometry type) + */ +template<class GridGeometry, template<class> class DistanceField = AABBDistanceField> +class WallDistance +{ + using GridView = typename GridGeometry::GridView; + using GridIndexType = typename IndexTraits<GridView>::GridIndex; + using SubControlVolumeFace = typename GridGeometry::SubControlVolumeFace; + using Scalar = typename GridView::Grid::ctype; + using GlobalPosition = typename SubControlVolumeFace::GlobalPosition; + + static constexpr int dim = GridView::dimension; + static constexpr int dimWorld = GridView::dimensionworld; + static_assert(dim == dimWorld, "Wall distances cannot be computed for embedded surface or network domains."); + + using CornerStorage = Dune::ReservedVector<GlobalPosition, (1<<(GridView::dimension-1))>; + + // We use a simplified geometry type here which allows much easier MPI communication + // for parallel runs compared to the Dune geometry types (due to fixed-size storage). + // This type only implements a minimal part of the Geometry interface. + struct SimpleGeometry + { + SimpleGeometry() = default; + SimpleGeometry(CornerStorage&& corners) + : corners_(std::move(corners)) + , center_(0.0) + { + for (int i = 0; i < corners_.size(); ++i) + center_ += corners_[i]; + center_ /= corners_.size(); + } + + using GlobalCoordinate = GlobalPosition; + using ctype = typename GlobalCoordinate::value_type; + static constexpr int coorddimension = GridView::dimensionworld; + static constexpr int mydimension = GridView::dimension-1; + + std::size_t corners() const + { return corners_.size(); } + + const auto& corner(int i) const + { return corners_[i]; } + + const auto& center() const + { return center_; } + + private: + CornerStorage corners_; + GlobalCoordinate center_; + }; + + struct WallDataImpl + { + GridIndexType eIdx; + GridIndexType scvfIdx; + GlobalPosition scvfOuterNormal; + int rank; // for parallel runs + }; + + struct ElementCenters {}; + struct VertexCenters {}; + +public: + static constexpr auto atElementCenters = Utility::Tag<ElementCenters>{}; + static constexpr auto atVertexCenters = Utility::Tag<VertexCenters>{}; + using WallData = WallDataImpl; + + /*! + * \brief Constructs a new wall distance object + * \param gridGeometry the grid geometry of the grid + * \param tag the policy (either WallDistance::atElementCenters or WallDistance::atVertexCenters) + * \param select a selection functor taking an scvf and returning bool signifying whether the scvfs is part of the wall + */ + template<class LocationTag, class ScvfSelectionFunctor> + WallDistance(std::shared_ptr<const GridGeometry> gridGeometry, LocationTag tag, const ScvfSelectionFunctor& select) + : gridGeometry_(gridGeometry) + { + initializeWallDistance_(select, tag); + } + + /*! + * \brief Constructs a new wall distance object + * \param gridGeometry the grid geometry of the grid + * \param tag the policy (either WallDistance::atElementCenters or WallDistance::atVertexCenters) + * \note selects all boundary scvfs as wall faces + */ + template<class LocationTag> + WallDistance(std::shared_ptr<const GridGeometry> gridGeometry, LocationTag tag) + : WallDistance(gridGeometry, tag, [](const SubControlVolumeFace& scvf) { return true; }) {} + + //! caller has to make sure the lifetime of grid geometry exceeds the lifetime of wall distance + template<class LocationTag, class ScvfSelectionFunctor> + WallDistance(const GridGeometry& gridGeometry, LocationTag tag, const ScvfSelectionFunctor& select) + : WallDistance(Dune::stackobject_to_shared_ptr(gridGeometry), tag, select) {} + + //! caller has to make sure the lifetime of grid geometry exceeds the lifetime of wall distance + template<class LocationTag> + WallDistance(const GridGeometry& gridGeometry, LocationTag tag) + : WallDistance(Dune::stackobject_to_shared_ptr(gridGeometry), tag) {} + + /*! + * \brief Returns a vector storing the distance from each DOF location to the nearest wall. + * For the atElementCenter policy, this is the distance from the element center to the nearest wall. + * For the atVertexCenter policy, this is the distance from the vertex to the nearest wall. + */ + const std::vector<Scalar>& wallDistance() const + { return distance_; } + + /*! + * \brief Returns a vector storing additional information about the nearest scvf on the wall (element index and scvf index). + * For the atElementCenter policy, this information is given for each element + * For the atVertexCenter policy, this information is given for each vertex. + */ + const std::vector<WallData>& wallData() const + { return wallData_; } + +private: + /*! + * \brief Perform the actual calculation of wall distances. + * \param considerFace Function object (e.g. a lambda) that determines whether a certain scvf shall be considered + * for the calculation of wall distances. + * \param loc Location a which the distance to the wall shall be calculated (elementCenter or vertex) + */ + template<class ConsiderFaceFunction, class LocationTag> + void initializeWallDistance_(const ConsiderFaceFunction& considerFace, LocationTag loc) + { + const std::size_t numSamplingPoints = (loc == atElementCenters) + ? gridGeometry_->gridView().size(0) + : gridGeometry_->gridView().size(dim); + // Reset the containers. + wallData_.resize(numSamplingPoints); + distance_.resize(numSamplingPoints, std::numeric_limits<Scalar>::max()); + + std::vector<SimpleGeometry> wallGeometries; + wallGeometries.reserve(gridGeometry_->numBoundaryScvf()); + + std::vector<WallData> tempWallData; + tempWallData.reserve(gridGeometry_->numBoundaryScvf()); + + // Loop over all elements: find all wall scvfs. + auto fvGeometry = localView(*gridGeometry_); + for (const auto& element : elements(gridGeometry_->gridView(), Dune::Partitions::interior)) + { + fvGeometry.bindElement(element); + if (!fvGeometry.hasBoundaryScvf()) + continue; + + const auto eIdx = gridGeometry_->elementMapper().index(element); + + for (const auto& scvf : scvfs(fvGeometry)) + { + if (scvf.boundary() && considerFace(scvf)) + { + const auto& geo = scvf.geometry(); + CornerStorage corners; + for (int i = 0; i < geo.corners(); ++i) + corners.push_back(geo.corner(i)); + + wallGeometries.emplace_back(std::move(corners)); + tempWallData.push_back(WallData{ + eIdx, scvf.index(), scvf.unitOuterNormal(), gridGeometry_->gridView().comm().rank() + }); + } + } + } + +#if HAVE_MPI + // Handle parallel runs. We need to prepare a global vector of wall geometries, + // containing the wall geometries of each process in order to get a correct distance field. + const bool isParallel = gridGeometry_->gridView().comm().size() > 1; + std::vector<SimpleGeometry> globalWallGeometries; + std::vector<WallData> globalTempWallData; + const auto distanceField = [&] + { + if (isParallel) + { + const auto& mpiHelper = Dune::MPIHelper::instance(); + const int totalNumberOfBoundaryGeometries = mpiHelper.getCommunication().sum(wallGeometries.size()); + globalWallGeometries.resize(totalNumberOfBoundaryGeometries); + globalTempWallData.resize(totalNumberOfBoundaryGeometries); + + // prepare a displacement vector + std::vector<int> numGeosPerProcLocal{static_cast<int>(wallGeometries.size())}; + std::vector<int> numGeosPerProcGlobal(mpiHelper.getCommunication().size()); + mpiHelper.getCommunication().allgather(numGeosPerProcLocal.data(), 1, numGeosPerProcGlobal.data()); + + std::vector<int> disp(mpiHelper.getCommunication().size(), 0); + disp[1] = numGeosPerProcGlobal[0]; + for (int i = 2; i < numGeosPerProcGlobal.size(); ++i) + disp[i] = disp[i-1] + numGeosPerProcGlobal[i-1]; + + // concatenate the wall geometries and temp scvf data of each process into a global vector + mpiHelper.getCommunication().allgatherv( + wallGeometries.data(), + wallGeometries.size(), + globalWallGeometries.data(), + numGeosPerProcGlobal.data(), + disp.data() + ); + + mpiHelper.getCommunication().allgatherv( + tempWallData.data(), + tempWallData.size(), + globalTempWallData.data(), + numGeosPerProcGlobal.data(), + disp.data() + ); + + // pass the global vector of wall geometries to the distance field + return DistanceField<SimpleGeometry>(globalWallGeometries); + } + else + return DistanceField<SimpleGeometry>(wallGeometries); + }(); +#else + const DistanceField<SimpleGeometry> distanceField(wallGeometries); +#endif + + // get the actual distances + if (loc == atElementCenters) + { + for (const auto& element : elements(gridGeometry_->gridView())) + { + const auto eIdx = gridGeometry_->elementMapper().index(element); + const auto [d, idx] = distanceField.distanceAndIndex(element.geometry().center()); + distance_[eIdx] = d; +#if HAVE_MPI + wallData_[eIdx] = isParallel ? globalTempWallData[idx] : tempWallData[idx]; +#else + wallData_[eIdx] = tempWallData[idx]; +#endif + } + } + else + { + for (const auto& vertex : vertices(gridGeometry_->gridView())) + { + const auto vIdx = gridGeometry_->vertexMapper().index(vertex); + const auto [d, idx] = distanceField.distanceAndIndex(vertex.geometry().center()); + distance_[vIdx] = d; +#if HAVE_MPI + wallData_[vIdx] = isParallel ? globalTempWallData[idx] : tempWallData[idx]; +#else + wallData_[vIdx] = tempWallData[idx]; +#endif + } + } + } + + std::vector<Scalar> distance_; + std::vector<WallData> wallData_; + std::shared_ptr<const GridGeometry> gridGeometry_; +}; + +} // end namespace Dumux + +#endif -- GitLab