From a10a415bed310d5cafa9698039c3b52b6df21df6 Mon Sep 17 00:00:00 2001 From: Timo Koch <timo.koch@iws.uni-stuttgart.de> Date: Fri, 31 May 2019 23:14:46 +0200 Subject: [PATCH] [common] Add function to integrate FV-style solutions --- dumux/common/integrate.hh | 120 +++++++++++++++++- test/common/functions/test_function_l2norm.cc | 53 +++++--- 2 files changed, 152 insertions(+), 21 deletions(-) diff --git a/dumux/common/integrate.hh b/dumux/common/integrate.hh index 9ea524a6da..3a68c6d83c 100644 --- a/dumux/common/integrate.hh +++ b/dumux/common/integrate.hh @@ -28,17 +28,117 @@ #include <type_traits> #include <dune/geometry/quadraturerules.hh> +#include <dune/common/concept.hh> + +#if HAVE_DUNE_FUNCTIONS +#include <dune/functions/gridfunctions/gridfunction.hh> +#endif + +#include <dumux/discretization/evalsolution.hh> +#include <dumux/discretization/elementsolution.hh> namespace Dumux { +namespace Impl { + +struct HasLocalFunction +{ + template<class F> + auto require(F&& f) -> decltype( + localFunction(f), + localFunction(f).unbind() + ); +}; + +template<class F> +static constexpr bool hasLocalFunction() +{ return Dune::models<HasLocalFunction, F>(); } + +} // end namespace Impl + +/*! + * \brief Integrate a grid function over a grid view + * \param gv the grid view + * \param f the grid function + * \param order the order of the quadrature rule + */ +template<class GridGeometry, class SolutionVector, + typename std::enable_if_t<!Impl::hasLocalFunction<SolutionVector>(), int> = 0> +auto integrateGridFunction(const GridGeometry& gg, + SolutionVector&& sol, + std::size_t order) +{ + using Scalar = std::decay_t<decltype(sol[0][0])>; + using GridView = typename GridGeometry::GridView; + + Scalar integral = 0.0; + for (const auto& element : elements(gg.gridView())) + { + const auto elemSol = elementSolution(element, sol, gg); + const auto geometry = element.geometry(); + const auto& quad = Dune::QuadratureRules<Scalar, GridView::dimension>::rule(geometry.type(), order); + for (auto&& qp : quad) + { + auto value = evalSolution(element, geometry, gg, elemSol, geometry.global(qp.position())); + value *= qp.weight()*geometry.integrationElement(qp.position()); + integral += value; + } + } + return integral; +} + +/*! + * \brief Integrate a function over a grid view + * \param gv the grid view + * \param f the first function + * \param g the second function + * \param order the order of the quadrature rule + * \note dune functions currently doesn't support composing two functions + */ +template<class GridGeometry, class Sol1, class Sol2, + typename std::enable_if_t<!Impl::hasLocalFunction<Sol1>(), int> = 0> +auto integrateL2Error(const GridGeometry& gg, + const Sol1& sol1, + const Sol2& sol2, + std::size_t order) +{ + using Scalar = std::decay_t<decltype(sol1[0][0])>; + using GridView = typename GridGeometry::GridView; + + Scalar l2norm = 0.0; + for (const auto& element : elements(gg.gridView())) + { + const auto elemSol1 = elementSolution(element, sol1, gg); + const auto elemSol2 = elementSolution(element, sol2, gg); + + const auto geometry = element.geometry(); + const auto& quad = Dune::QuadratureRules<Scalar, GridView::dimension>::rule(geometry.type(), order); + for (auto&& qp : quad) + { + const auto& globalPos = geometry.global(qp.position()); + const auto value1 = evalSolution(element, geometry, gg, elemSol1, globalPos); + const auto value2 = evalSolution(element, geometry, gg, elemSol2, globalPos); + const auto error = (value1 - value2); + l2norm += error*error*qp.weight()*geometry.integrationElement(qp.position()); + } + } + using std::sqrt; + return sqrt(l2norm); +} + +#if HAVE_DUNE_FUNCTIONS /*! * \brief Integrate a grid function over a grid view * \param gv the grid view * \param f the grid function * \param order the order of the quadrature rule + * \note overload for a Dune::Funtions::GridFunction */ -template<class GridView, class F> -auto integrateGridFunction(const GridView& gv, F&& f, std::size_t order) +template<class GridView, class F, + typename std::enable_if_t<Impl::hasLocalFunction<F>(), int> = 0> +auto integrateGridFunction(const GridView& gv, + F&& f, + std::size_t order) { auto fLocal = localFunction(f); @@ -54,7 +154,11 @@ auto integrateGridFunction(const GridView& gv, F&& f, std::size_t order) const auto geometry = element.geometry(); const auto& quad = Dune::QuadratureRules<Scalar, GridView::dimension>::rule(geometry.type(), order); for (auto&& qp : quad) - integral += fLocal(qp.position())*qp.weight()*geometry.integrationElement(qp.position()); + { + auto value = fLocal(qp.position()); + value *= qp.weight()*geometry.integrationElement(qp.position()); + integral += value; + } fLocal.unbind(); } @@ -67,10 +171,15 @@ auto integrateGridFunction(const GridView& gv, F&& f, std::size_t order) * \param f the first function * \param g the second function * \param order the order of the quadrature rule + * \note overload for a Dune::Funtions::GridFunction * \note dune functions currently doesn't support composing two functions */ -template<class GridView, class F, class G> -auto integrateL2Error(const GridView& gv, F&& f, G&& g, std::size_t order) +template<class GridView, class F, class G, + typename std::enable_if_t<Impl::hasLocalFunction<F>(), int> = 0> +auto integrateL2Error(const GridView& gv, + F&& f, + G&& g, + std::size_t order) { auto fLocal = localFunction(f); auto gLocal = localFunction(g); @@ -102,6 +211,7 @@ auto integrateL2Error(const GridView& gv, F&& f, G&& g, std::size_t order) using std::sqrt; return sqrt(l2norm); } +#endif } // end namespace Dumux diff --git a/test/common/functions/test_function_l2norm.cc b/test/common/functions/test_function_l2norm.cc index f4911dda84..baaba12f26 100644 --- a/test/common/functions/test_function_l2norm.cc +++ b/test/common/functions/test_function_l2norm.cc @@ -12,7 +12,8 @@ #include <dumux/common/integrate.hh> #include <dumux/multidomain/glue.hh> #include <dumux/discretization/projection/projector.hh> -#include <dumux/discretization/fem/fegridgeometry.hh> +#include <dumux/discretization/basegridgeometry.hh> +#include <dumux/discretization/box/fvgridgeometry.hh> #include <dumux/io/grid/gridmanager_yasp.hh> int main (int argc, char *argv[]) try @@ -35,38 +36,58 @@ int main (int argc, char *argv[]) try const auto gvFine = gridManagerFine.grid().leafGridView(); const auto gvCoarse = gridManagerCoarse.grid().leafGridView(); - // a quadratic function on the grid - auto f = [](const auto& pos){ return pos.two_norm2(); }; - - // make discrete functions + // get some types using R = Dune::FieldVector<double, 1>; using SolutionVector = Dune::BlockVector<R>; using GridView = Grid::LeafGridView; using namespace Dune::Functions; using P2 = LagrangeBasis<GridView, 2>; - auto basisCoarse = std::make_shared<P2>(gvCoarse), basisFine = std::make_shared<P2>(gvFine); + // construct glue, any grid geometry should be fine here + Dumux::BaseGridGeometry<GridView, Dumux::DefaultMapperTraits<GridView>> ggCoarse(gvCoarse), ggFine(gvFine); + auto glue = makeGlue(ggFine, ggCoarse); + + // a quadratic function on the grid + auto f = [](const auto& pos){ return pos.two_norm2(); }; + + // make bases + P2 basisCoarse(gvCoarse), basisFine(gvFine); SolutionVector solCoarse, solFine; - interpolate(*basisCoarse, solCoarse, f); - interpolate(*basisFine, solFine, f); - auto gfCoarse = makeDiscreteGlobalBasisFunction<R>(*basisCoarse, solCoarse); - auto gfFine = makeDiscreteGlobalBasisFunction<R>(*basisFine, solFine); + interpolate(basisCoarse, solCoarse, f); + interpolate(basisFine, solFine, f); + auto gfCoarse = makeDiscreteGlobalBasisFunction<R>(basisCoarse, solCoarse); + auto gfFine = makeDiscreteGlobalBasisFunction<R>(basisFine, solFine); - // project fine discrete function onto coarse grid - Dumux::FEGridGeometry<P2> ggCoarse(basisCoarse), ggFine(basisFine); - const auto projector = Dumux::makeProjector(*basisFine, *basisCoarse, makeGlue(ggFine, ggCoarse)); + // project fine discrete function onto coarse grid and convert back to grid function + const auto projector = Dumux::makeProjector(basisFine, basisCoarse, glue); SolutionVector solCoarse2; projector.project(solFine, solCoarse2); - auto gfCoarse2 = makeDiscreteGlobalBasisFunction<R>(*basisCoarse, solCoarse); + auto gfCoarse2 = makeDiscreteGlobalBasisFunction<R>(basisCoarse, solCoarse); // check that these functions are identical and that they exactly represent the analytical function auto l2norm = Dumux::integrateL2Error(gvCoarse, gfCoarse, gfCoarse2, 3); if (l2norm > 1e-14) - DUNE_THROW(Dune::MathError, "L2 norm of projection and interpolation is non-zero!"); + DUNE_THROW(Dune::MathError, "L2 norm of projection and interpolation is non-zero (" << l2norm << ")"); l2norm = Dumux::integrateL2Error(gvCoarse, makeAnalyticGridViewFunction(f, gvCoarse), gfCoarse2, 3); if (l2norm > 1e-14) - DUNE_THROW(Dune::MathError, "L2 norm of analytical solution and interpolation is non-zero!"); + DUNE_THROW(Dune::MathError, "L2 norm of analytical solution and interpolation is non-zero (" << l2norm << ")"); + + // check the FV-style interface of integrateL2Error + Dumux::BoxFVGridGeometry<double, GridView> ggBoxCoarse(gvCoarse), ggBoxFine(gvFine); + auto p1BasisCoarse = Dumux::getFunctionSpaceBasis(ggBoxCoarse); + auto p1BasisFine = Dumux::getFunctionSpaceBasis(ggBoxFine); + auto f2 = [](const auto& pos){ return pos[0]; }; + interpolate(p1BasisFine, solFine, f2); + interpolate(p1BasisCoarse, solCoarse, f2); + + // project fine discrete function onto coarse grid + const auto projector2 = Dumux::makeProjector(p1BasisFine, p1BasisCoarse, glue); + projector2.project(solFine, solCoarse2); + + l2norm = Dumux::integrateL2Error(ggBoxCoarse, solCoarse, solCoarse2, 3); + if (l2norm > 1e-14) + DUNE_THROW(Dune::MathError, "L2 norm of FV-style projected solutions is non-zero (" << l2norm << ")"); std::cout << "All tests passed!" << std::endl; return 0; -- GitLab