diff --git a/dumux/adaptive/CMakeLists.txt b/dumux/adaptive/CMakeLists.txt index a9fe39cd98c6232082fe6da169e535ef117768ff..5f56a25371676e4a1b0700f6431e71e20e7e93e6 100644 --- a/dumux/adaptive/CMakeLists.txt +++ b/dumux/adaptive/CMakeLists.txt @@ -2,5 +2,6 @@ install(FILES adapt.hh griddatatransfer.hh +initializationindicator.hh markelements.hh DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dumux/adaptive) diff --git a/dumux/adaptive/initializationindicator.hh b/dumux/adaptive/initializationindicator.hh new file mode 100644 index 0000000000000000000000000000000000000000..3571568892ef3f86f68a48099467ddaba622980b --- /dev/null +++ b/dumux/adaptive/initializationindicator.hh @@ -0,0 +1,289 @@ +// -*- 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 2 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 +* \brief Class defining an initialization indicator for grid adaption +*/ +#ifndef DUMUX_GRIDADAPTINITIALIZATIONINDICATOR_HH +#define DUMUX_GRIDADAPTINITIALIZATIONINDICATOR_HH + +#include <dune/geometry/type.hh> + +#include <dumux/discretization/methods.hh> + +namespace Dumux +{ + +/*!\ingroup GridAdapt + * \brief Class defining an initialization indicator for grid adaption. + * Accounts for sources and boundaries. Only for grid initialization! + * + * \tparam TypeTag The problem TypeTag + */ +template<class TypeTag> +class GridAdaptInitializationIndicator +{ + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using Element = typename GridView::Traits::template Codim<0>::Entity; + + using GridVariables = typename GET_PROP_TYPE(TypeTag, GridVariables); + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry); + using ElementVolumeVariables = typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables); + using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); + + static constexpr bool isBox = GET_PROP_VALUE(TypeTag, DiscretizationMethod) == DiscretizationMethods::Box; + +public: + + /*! \brief Constructor + * + * \param problem The problem object + * \param fvGridGeometry The finite volume geometry of the grid + * \param gridVariables The secondary variables on the grid + */ + GridAdaptInitializationIndicator(std::shared_ptr<const Problem> problem, + std::shared_ptr<const FVGridGeometry> fvGridGeometry, + std::shared_ptr<const GridVariables> gridVariables) + : problem_(problem) + , fvGridGeometry_(fvGridGeometry) + , gridVariables_(gridVariables) + , minLevel_(getParamFromGroup<int>(GET_PROP_VALUE(TypeTag, ModelParameterGroup), "Adaptive.MinLevel")) + , maxLevel_(getParamFromGroup<int>(GET_PROP_VALUE(TypeTag, ModelParameterGroup), "Adaptive.MaxLevel")) + , refineAtDirichletBC_(getParamFromGroup<bool>(GET_PROP_VALUE(TypeTag, ModelParameterGroup), "Adaptive.RefineAtDirichletBC", true)) + , refineAtFluxBC_(getParamFromGroup<bool>(GET_PROP_VALUE(TypeTag, ModelParameterGroup), "Adaptive.RefineAtFluxBC", true)) + , refineAtSource_(getParamFromGroup<bool>(GET_PROP_VALUE(TypeTag, ModelParameterGroup), "Adaptive.RefineAtSource", true)) + , eps_(getParamFromGroup<Scalar>(GET_PROP_VALUE(TypeTag, ModelParameterGroup), "Adaptive.BCRefinementThreshold", 1e-10)) + {} + + /*! + * \brief Function to set the minimum allowed level. + * + */ + void setMinLevel(std::size_t minLevel) + { + minLevel_ = minLevel; + } + + /*! + * \brief Function to set the maximum allowed level. + * + */ + void setMaxLevel(std::size_t maxLevel) + { + maxLevel_ = maxLevel; + } + + /*! + * \brief Function to set the minumum/maximum allowed levels. + * + */ + void setLevels(std::size_t minLevel, std::size_t maxLevel) + { + minLevel_ = minLevel; + maxLevel_ = maxLevel; + } + + /*! + * \brief Function to set if refinement at Dirichlet boundaries is to be used. + * + */ + void setRefinementAtDirichletBC(bool refine) + { + refineAtDirichletBC_ = refine; + } + + /*! + * \brief Function to set if refinement at sources is to be used. + * + */ + void setRefinementAtSources(bool refine) + { + refineAtSource_ = refine; + } + + /*! + * \brief Function to set if refinement at sources is to be used. + * + */ + void setRefinementAtFluxBoundaries(bool refine) + { + refineAtFluxBC_ = refine; + } + + /*! + * \brief Calculates the indicator used for refinement/coarsening for each grid cell. + * + * \param sol The solution for which indicator is to be calculated + */ + template<class SolutionVector> + void calculate(const SolutionVector& sol) + { + //! prepare an indicator for refinement + indicatorVector_.assign(fvGridGeometry_->gridView().size(0), false); + + for (const auto& element : elements(fvGridGeometry_->gridView())) + { + const auto eIdx = fvGridGeometry_->elementMapper().index(element); + + //! refine any element being below the minimum level + if (element.level() < minLevel_) + { + indicatorVector_[eIdx] = true; + continue; //! proceed to the next element + } + + //! If refinement at sources/BCs etc is deactivated, skip the rest + if (!refineAtSource_ && !refineAtFluxBC_ && !refineAtDirichletBC_) + continue; + + //! if the element is already on the maximum permissive level, skip rest + if (element.level() == maxLevel_) + continue; + + //! get the fvGeometry and elementVolVars needed for the bc and source interfaces + auto fvGeometry = localView(*fvGridGeometry_); + fvGeometry.bindElement(element); + + auto elemVolVars = localView(gridVariables_->curGridVolVars()); + elemVolVars.bindElement(element, fvGeometry, sol); + + //! Check if we have to refine around a source term + if (refineAtSource_) + { + for (const auto& scv : scvs(fvGeometry)) + { + auto source = problem_->source(element, fvGeometry, elemVolVars, scv); + if (source.infinity_norm() > eps_) + { + indicatorVector_[eIdx] = true; + break; //! element is marked, escape scv loop + } + } + } + + //! Check if we have to refine at the boundary + if (!indicatorVector_[eIdx] //! proceed if element is not already marked + && element.hasBoundaryIntersections() //! proceed if element is on boundary + && (refineAtDirichletBC_ || refineAtFluxBC_)) //! proceed if boundary refinement is active + { + //! cell-centered schemes + if (!isBox) + { + for (const auto& scvf : scvfs(fvGeometry)) + { + //! skip non-boundary scvfs + if (!scvf.boundary()) + continue; + + const auto bcTypes = problem_->boundaryTypes(element, scvf); + //! We assume pure BCs, mixed boundary types are not allowed anyway! + if(bcTypes.hasOnlyDirichlet() && refineAtDirichletBC_) + { + indicatorVector_[eIdx] = true; + break; //! element is marked, escape scvf loop + } + + //! we are on a pure Neumann boundary + else if(refineAtFluxBC_) + { + const auto fluxes = problem_->neumann(element, fvGeometry, elemVolVars, scvf); + if (fluxes.infinity_norm() > eps_) + { + indicatorVector_[eIdx] = true; + break; //! element is marked, escape scvf loop + } + } + } + } + //! box-scheme + else + { + //! container to store bcTypes + std::vector<BoundaryTypes> bcTypes(fvGeometry.numScv()); + + //! Get bcTypes and maybe mark for refinement on Dirichlet boundaries + for (const auto& scv : scvs(fvGeometry)) + { + bcTypes[scv.indexInElement()] = problem_->boundaryTypes(element, scv); + if (refineAtDirichletBC_ && bcTypes[scv.indexInElement()].hasDirichlet()) + { + indicatorVector_[eIdx] = true; + break; //! element is marked, escape scv loop + } + } + + //! If element hasn't been marked because of Dirichlet BCS, check Neumann BCs + if (!indicatorVector_[eIdx] && refineAtFluxBC_) + { + for (const auto& scvf : scvfs(fvGeometry)) + { + //! check if scvf is on Neumann boundary + if (scvf.boundary() && bcTypes[scvf.insideScvIdx()].hasNeumann()) + { + const auto fluxes = problem_->neumann(element, fvGeometry, elemVolVars, scvf); + if (fluxes.infinity_norm() > eps_) + { + indicatorVector_[eIdx] = true; + break; //! element is marked, escape scvf loop + } + } + } + } + } + } + } + } + + /*! \brief function call operator to return mark + * + * \return 1 if an element should be refined + * -1 if an element should be coarsened + * 0 otherwise + * + * \note In this initialization indicator implementation + * element coarsening is not considered. + * + * \param element A grid element + */ + int operator() (const Element& element) const + { + if (indicatorVector_[fvGridGeometry_->elementMapper().index(element)]) + return 1; + return 0; + } + +private: + std::shared_ptr<const Problem> problem_; //! The problem to be solved + std::shared_ptr<const FVGridGeometry> fvGridGeometry_; //! The finite volume grid geometry + std::shared_ptr<const GridVariables> gridVariables_; //! The secondary variables on the grid + std::vector<bool> indicatorVector_; //! Indicator for BCs/sources + + int minLevel_; //! The minimum allowed level + int maxLevel_; //! The maximum allowed level + bool refineAtDirichletBC_; //! Specifies if it should be refined at Dirichlet BCs + bool refineAtFluxBC_; //! Specifies if it should be refined at non-zero Neumann BCs + bool refineAtSource_; //! Specifies if it should be refined at sources + Scalar eps_; //! Threshold for refinement at sources/BCS +}; + +} +#endif diff --git a/dumux/porousmediumflow/2p/implicit/gridadaptindicator.hh b/dumux/porousmediumflow/2p/implicit/gridadaptindicator.hh index 23e970d71d8aaffbc706fe782ab6fda4f64bbd29..bb2f8f945f83b19dce51385fd83b51166d810235 100644 --- a/dumux/porousmediumflow/2p/implicit/gridadaptindicator.hh +++ b/dumux/porousmediumflow/2p/implicit/gridadaptindicator.hh @@ -65,24 +65,27 @@ public: , maxLevel_(getParamFromGroup<std::size_t>(GET_PROP_VALUE(TypeTag, ModelParameterGroup), "Adaptive.MaxLevel", 0)) {} - /*! \brief Function to set the minumum allowed levels. - *. + /*! + * \brief Function to set the minimum allowed level. + * */ void setMinLevel(std::size_t minLevel) { minLevel_ = minLevel; } - /*! \brief Function to set the maximum allowed levels. - *. + /*! + * \brief Function to set the maximum allowed level. + * */ void setMaxLevel(std::size_t maxLevel) { maxLevel_ = maxLevel; } - /*! \brief Function to set the minumum/maximum allowed levels. - *. + /*! + * \brief Function to set the minumum/maximum allowed levels. + * */ void setLevels(std::size_t minLevel, std::size_t maxLevel) { @@ -90,7 +93,8 @@ public: maxLevel_ = maxLevel; } - /*! \brief Calculates the indicator used for refinement/coarsening for each grid cell. + /*! + * \brief Calculates the indicator used for refinement/coarsening for each grid cell. * * This standard two-phase indicator is based on the saturation gradient. */ diff --git a/test/porousmediumflow/2p/implicit/adaptive/test_2p_adaptive.input b/test/porousmediumflow/2p/implicit/adaptive/test_2p_adaptive.input index 5e9aa92d80ce192ec095d283290814e0a977df87..296ed5b9e4448153454202e517be99e11879c8e2 100644 --- a/test/porousmediumflow/2p/implicit/adaptive/test_2p_adaptive.input +++ b/test/porousmediumflow/2p/implicit/adaptive/test_2p_adaptive.input @@ -16,8 +16,7 @@ LensUpperRight = 4.0 3.0 # [m] coordinates of the upper right lens corner Name = 2padaptive # name passed to the output routines [Adaptive] -EnableInitializationIndicator = 1 -RefineAtDirichletBC = 1 +RefineAtDirichletBC = 0 RefineAtFluxBC = 1 MinLevel = 0 MaxLevel = 2 diff --git a/test/porousmediumflow/2p/implicit/adaptive/test_2p_adaptive_fv.cc b/test/porousmediumflow/2p/implicit/adaptive/test_2p_adaptive_fv.cc index 8c62d57878cc12829ca6a5e0f458690b5e7cc795..e6a0bd4bd735a29c3e2b04f25a550008235b02bd 100644 --- a/test/porousmediumflow/2p/implicit/adaptive/test_2p_adaptive_fv.cc +++ b/test/porousmediumflow/2p/implicit/adaptive/test_2p_adaptive_fv.cc @@ -55,6 +55,7 @@ #include <dumux/adaptive/adapt.hh> #include <dumux/adaptive/markelements.hh> +#include <dumux/adaptive/initializationindicator.hh> #include <dumux/porousmediumflow/2p/implicit/griddatatransfer.hh> #include <dumux/porousmediumflow/2p/implicit/gridadaptindicator.hh> @@ -129,6 +130,41 @@ int main(int argc, char** argv) try TwoPGridAdaptIndicator<TypeTag> indicator(fvGridGeometry); TwoPGridDataTransfer<TypeTag> dataTransfer(problem, fvGridGeometry, gridVariables, x); + // Do initial refinement around sources/BCs + GridAdaptInitializationIndicator<TypeTag> initIndicator(problem, fvGridGeometry, gridVariables); + + // refine up to the maximum level + const auto maxLevel = getParam<std::size_t>("Adaptive.MaxLevel", 0); + for (std::size_t i = 0; i < maxLevel; ++i) + { + initIndicator.calculate(x); + + bool wasAdapted = false; + if (markElements(GridCreator::grid(), initIndicator)) + wasAdapted = adapt(GridCreator::grid(), dataTransfer); + + // update grid data after adaption + if (wasAdapted) + { + xOld = x; //! Overwrite the old solution with the new (resized & interpolated) one + gridVariables->init(x, xOld); //! Initialize the secondary variables to the new (and "new old") solution + } + } + + // Do refinement for the initial conditions using our indicator + indicator.calculate(x, refineTol, coarsenTol); + + bool wasAdapted = false; + if (markElements(GridCreator::grid(), indicator)) + wasAdapted = adapt(GridCreator::grid(), dataTransfer); + + // update grid data after adaption + if (wasAdapted) + { + xOld = x; //! Overwrite the old solution with the new (resized & interpolated) one + gridVariables->init(x, xOld); //! Initialize the secondary variables to the new (and "new old") solution + } + // get some time loop parameters using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); const auto tEnd = getParam<Scalar>("TimeLoop.TEnd");