diff --git a/dune/fufem/assemblers/basisinterpolationmatrixassembler.hh b/dune/fufem/assemblers/basisinterpolationmatrixassembler.hh index 91c2bd9b8fd424caef4d85c2ceb26c31a940d75e..8478c9b6a9a5a91c2c9723677b7eac43686b954b 100644 --- a/dune/fufem/assemblers/basisinterpolationmatrixassembler.hh +++ b/dune/fufem/assemblers/basisinterpolationmatrixassembler.hh @@ -4,6 +4,8 @@ #define DUNE_FUFEM_ASSEMBLERS_BASIS_INTERPOLATION_MATRIX_ASSEMBLER_HH #include <type_traits> +#include <typeinfo> + #include <dune/common/bitsetvector.hh> #include <dune/istl/matrixindexset.hh> @@ -215,4 +217,281 @@ static void assembleBasisInterpolationMatrix(MatrixType& matrix, const BasisType } } + + + + + +/** \brief Wrapper for evaluating local functions in another geometry */ +template<class Function, class Geometry> +class OtherGeometryWrapper +{ + +public: + + using Traits = typename Function::Traits; + + OtherGeometryWrapper(const Function& function, const Geometry& geometry) + : function_(function) + , geometry_(geometry) + {} + + // forward the evaluation into the other geometry + template<class X, class Y> + void evaluate(const X& x, Y& y) const + { + function_.evaluate(geometry_.global(x), y); + } + +private: + + const Function function_; + const Geometry geometry_; +}; + + +/** \brief Wrapper chaining sequencial father geometries */ +template<class GeometryContainer> +class GeometryInFathers +{ + +public: + + GeometryInFathers(const GeometryContainer& geometryContainer) + : geometryContainer_(geometryContainer) + {} + + // forward the global() method to all single geometries + template<class X> + auto global(const X& x) const + { + auto y = x; + for (const auto& g : geometryContainer_) + { + y = g.global(y); + } + return y; + } + +private: + + const GeometryContainer geometryContainer_; +}; + + + +/** \brief Assemble the transfer matrix for multigrid methods + * + * The coarse and fine basis has to be related: in a way that the fine grid is descendant of the + * coarse grid (not necessarily direct descandant). + * The matrix is assumed to be in block structure with correct block size. + * The two basis are assumed to be scalar dune-functions basis (i.e. leaf nodes in the basis tree). + * + * \param [out] matrix the block matrix corresponding to the basis transfer operator + * \param [in] coarseBasis scalar global basis on the coarse grid + * \param [in] fineasis scalar global basis on the fine grid + * \param [in] tolerance (optional) interpolation threshold. Default is 1e-12. + */ +template<class MatrixType, class CoarseBasis, class FineBasis> +static void assembleGlobalBasisTransferMatrix(MatrixType& matrix, + const CoarseBasis& coarseBasis, + const FineBasis& fineBasis, + const double tolerance = 1e-12) +{ + const auto& coarseGridView = coarseBasis.gridView(); + const auto& fineGridView = fineBasis.gridView(); + + auto coarseLocalView = coarseBasis.localView(); + auto fineLocalView = fineBasis.localView(); + + const auto rows = fineBasis.size(); + const auto cols = coarseBasis.size(); + + if ( not fineLocalView.tree().isLeaf or not coarseLocalView.tree().isLeaf ) + DUNE_THROW(Dune::Exception, "currently, we can handle power nodes only"); + + using CoarseFEType = typename decltype(coarseLocalView)::Tree::FiniteElement; + using FunctionBaseClass = typename Dune::LocalFiniteElementFunctionBase<CoarseFEType>::type; + using LocalBasisWrapper = LocalBasisComponentWrapper<typename CoarseFEType::Traits::LocalBasisType, FunctionBaseClass>; + + matrix.setSize(rows,cols); + matrix.setBuildMode(MatrixType::random); + + + // /////////////////////////////////////////// + // Determine the indices present in the matrix + // /////////////////////////////////////////// + + // Only handle every dof once + Dune::BitSetVector<1> processed(fineBasis.size()); + + Dune::MatrixIndexSet indices(rows, cols); + + // loop over all fine grid elements + for ( const auto& fineElement : elements(fineGridView) ) + { + // find a coarse element that is the parent of the fine element + // start in the fine grid and track the geometry mapping + auto fE = fineElement; + // collect all geometryInFather's on the way + std::vector<decltype(fineElement.geometryInFather())> geometryInFathersVector; + while ( not coarseGridView.contains(fE) and fE.hasFather() ) + { + // add the geometry to the container + geometryInFathersVector.push_back(fE.geometryInFather()); + // step up one level + fE = fE.father(); + } + + // did we really end up in coarseElement? + if ( not coarseGridView.contains(fE) ) + DUNE_THROW( Dune::Exception, "There is a fine element without a parent in the coarse grid!"); + + const auto& coarseElement = fE; + + const auto geometryInFathers = GeometryInFathers<decltype(geometryInFathersVector)>(geometryInFathersVector); + + coarseLocalView.bind(coarseElement); + fineLocalView.bind(fineElement); + + // get the root as reference + const auto& node = fineLocalView.tree(); + + // get the local basis + const auto& localBasis = node.finiteElement().localBasis(); + using Range = typename std::decay_t<decltype(localBasis)>::Traits::RangeType; + + std::vector<Range> values(localBasis.size()); + + LocalBasisWrapper basisFctj(node.finiteElement().localBasis(),0); + + const auto& coarseLocalBasis = coarseLocalView.tree().finiteElement().localBasis(); + + for (size_t j=0; j<coarseLocalBasis.size(); j++) + { + // wrap each local basis function as a local function. + basisFctj.setIndex(j); + + // transform the local fine function to a local coarse function + const auto coarseBaseFctj = OtherGeometryWrapper<decltype(basisFctj),decltype(geometryInFathers)>(basisFctj, geometryInFathers); + + // Interpolate j^th base function by the fine basis + node.finiteElement().localInterpolation().interpolate(coarseBaseFctj, values); + + // get the matrix col index + const auto& globalCoarseIndex = coarseLocalView.index(j); + + // set the matrix indices + for (size_t i=0; i<localBasis.size(); i++) { + + if ( std::abs(values[i]) < tolerance ) + continue; + + // get the matrix row index + const auto& globalFineIndex = fineLocalView.index(i); + + if (processed[globalFineIndex][0]) + continue; + + indices.add(globalFineIndex, globalCoarseIndex); + } + } + + // now the all dof's of this fine element to 'processed' + for (size_t i=0; i<localBasis.size(); i++) + { + const auto& globalFineIndex = fineLocalView.index(i); + processed[globalFineIndex] = true; + } + } + + indices.exportIdx(matrix); + matrix = 0; + + // reset all dof's + processed.unsetAll(); + + ////////////////////// + // Now set the values + ////////////////////// + + for ( const auto& fineElement : elements(fineGridView) ) + { + // find a coarse element that is the parent of the fine element + // start in the fine grid and track the geometry mapping + auto fE = fineElement; + // collect all geometryInFather's on the way + std::vector<decltype(fineElement.geometryInFather())> geometryInFathersVector; + while ( not coarseGridView.contains(fE) and fE.hasFather() ) + { + // add the geometry to the container + geometryInFathersVector.push_back(fE.geometryInFather()); + // step up one level + fE = fE.father(); + } + + // did we really end up in coarseElement? + if ( not coarseGridView.contains(fE) ) + DUNE_THROW( Dune::Exception, "There is a fine element without a parent in the coarse grid!"); + + const auto& coarseElement = fE; + + const auto geometryInFathers = GeometryInFathers<decltype(geometryInFathersVector)>(geometryInFathersVector); + + coarseLocalView.bind(coarseElement); + fineLocalView.bind(fineElement); + + // get the root as reference + const auto& node = fineLocalView.tree(); + + // get the local basis + const auto& localBasis = node.finiteElement().localBasis(); + using Range = typename std::decay_t<decltype(localBasis)>::Traits::RangeType; + + std::vector<Range> values(localBasis.size()); + + LocalBasisWrapper basisFctj(node.finiteElement().localBasis(),0); + + const auto& coarseLocalBasis = coarseLocalView.tree().finiteElement().localBasis(); + + for (size_t j=0; j<coarseLocalBasis.size(); j++) + { + // wrap each local basis function as a local function. + basisFctj.setIndex(j); + + // transform the local fine function to a local coarse function + const auto coarseBaseFctj = OtherGeometryWrapper<decltype(basisFctj),decltype(geometryInFathers)>(basisFctj, geometryInFathers); + + // Interpolate j^th base function by the fine basis + node.finiteElement().localInterpolation().interpolate(coarseBaseFctj, values); + + // get the matrix col index + const auto& globalCoarseIndex = coarseLocalView.index(j); + + // set the matrix indices + for (size_t i=0; i<localBasis.size(); i++) { + + if ( std::abs(values[i]) < tolerance ) + continue; + + // get the matrix row index + const auto& globalFineIndex = fineLocalView.index(i); + + if (processed[globalFineIndex][0]) + continue; + + Dune::MatrixVector::addToDiagonal(matrix[globalFineIndex][globalCoarseIndex],values[i]); + } + } + + // now the all dof's of this fine element to 'processed' + for (size_t i=0; i<localBasis.size(); i++) + { + const auto& globalFineIndex = fineLocalView.index(i); + processed[globalFineIndex] = true; + } + } +} + + #endif diff --git a/dune/fufem/test/CMakeLists.txt b/dune/fufem/test/CMakeLists.txt index a45572682ad4aa17372c51d9e1e59c7488aae60e..2c6685b61159a752c2e51a278403fd4e4ad37ba1 100644 --- a/dune/fufem/test/CMakeLists.txt +++ b/dune/fufem/test/CMakeLists.txt @@ -3,6 +3,7 @@ set(GRID_BASED_TESTS basisgridfunctiontest basisinterpolatortest basisinterpolationmatrixtest + assembletransferoperatortest boundarypatchtest boundarypatchprolongatortest coarsegridfunctionwrappertest diff --git a/dune/fufem/test/assembletransferoperatortest.cc b/dune/fufem/test/assembletransferoperatortest.cc new file mode 100644 index 0000000000000000000000000000000000000000..bbad6a37e9b4e193338ec17f8e664372ec74c5db --- /dev/null +++ b/dune/fufem/test/assembletransferoperatortest.cc @@ -0,0 +1,162 @@ +#include <config.h> + +#include <dune/common/parallel/mpihelper.hh> +#include <dune/common/test/testsuite.hh> +#include <dune/istl/bcrsmatrix.hh> + +#include <dune/functions/functionspacebases/defaultglobalbasis.hh> +#include <dune/functions/functionspacebases/lagrangebasis.hh> +#include <dune/functions/functionspacebases/interpolate.hh> +#include <dune/functions/gridfunctions/discreteglobalbasisfunction.hh> + +#include <dune/fufem/assemblers/basisinterpolationmatrixassembler.hh> + +#include "common.hh" + +using namespace Dune; + +template<int order, class GW0, class GW1> +bool testLagrange(const GW0& gw0, const GW1& gw1) +{ + // our test funcion for interpolation + auto f = [](const auto& x) + { + // const function for order 0 + if ( order == 0 ) + return 1.; + + // for the other cases affine function + auto y = 1.; + for( auto xs : x ) + y += xs; + return y; + }; + + auto coarseLagrange = Functions::BasisFactory::makeBasis(gw0, Functions::BasisFactory::lagrange<order>()); + auto fineLagrange = Functions::BasisFactory::makeBasis(gw1, Functions::BasisFactory::lagrange<order>()); + + BCRSMatrix<FieldMatrix<double,1,1>> matrix; + BlockVector<FieldVector<double,1>> coarseCoeffs, fineCoeffs; + + // assemble the matrix + assembleGlobalBasisTransferMatrix(matrix,coarseLagrange,fineLagrange); + + // interpolate f on the coarse grid + Functions::interpolate(coarseLagrange,coarseCoeffs,f); + + // interpolate f on the fine grid + Functions::interpolate(fineLagrange,fineCoeffs,f); + + // now we should have + // fineCoeffs - matrix * coarseCoeffs == 0 + matrix.mmv(coarseCoeffs,fineCoeffs); + + std::cout << " test lagrage " << GW0::dimension << "D with order " << order << " : error = " << fineCoeffs.two_norm2()<< std::endl; + + return fineCoeffs.two_norm2() < 1e-12; +} + + +template<int order, class GW0, class GW1, class GW2> +bool testTwoLevelLagrange(const GW0& gw0, const GW1& gw1, const GW2& gw2) +{ + + // create 3 global basis + auto coarseLagrange = Functions::BasisFactory::makeBasis(gw0, Functions::BasisFactory::lagrange<order>()); + auto semiLagrange = Functions::BasisFactory::makeBasis(gw1, Functions::BasisFactory::lagrange<order>()); + auto fineLagrange = Functions::BasisFactory::makeBasis(gw2, Functions::BasisFactory::lagrange<order>()); + + // matrices for the conbined jump and two simple jumps + BCRSMatrix<FieldMatrix<double,1,1>> matrixTwoJumps, matrixJumpOne, matrixJumpTwo; + + // assemble the matrices + assembleGlobalBasisTransferMatrix(matrixTwoJumps,coarseLagrange,fineLagrange); + assembleGlobalBasisTransferMatrix(matrixJumpOne,coarseLagrange,semiLagrange); + assembleGlobalBasisTransferMatrix(matrixJumpTwo,semiLagrange,fineLagrange); + + // now check wether the product of two jumps is the conbined jump + // compute matrixTwoJumps -= matrixJumpTwo*matrixJumpOne + for (auto row0 = matrixJumpTwo.begin(); row0 != matrixJumpTwo.end(); row0++) + for (auto entry0 = row0->begin(); entry0 != row0->end(); entry0++) + for (auto row1 = matrixJumpOne.begin(); row1 != matrixJumpOne.end(); row1++) + for(auto entry1 = row1->begin(); entry1 != row1->end(); entry1++) + if (row1.index() == entry0.index() ) + matrixTwoJumps[row0.index()][entry1.index()] -= (*entry0)*(*entry1); + + + std::cout << " test 2-jump " << GW0::dimension << "D with order " << order << " : error = " << matrixTwoJumps.frobenius_norm2()<< std::endl; + + return matrixTwoJumps.frobenius_norm2() < 1e-15; + +} + + + + +// this tests the interpolation porperty of the matrix +template<class GW0, class GW1> +bool checkTransferMatrix(const GW0& gw0, const GW1& gw1) +{ + bool passed = true; + + // test oder 0-3 + passed = passed and testLagrange<0>(gw0,gw1); + passed = passed and testLagrange<1>(gw0,gw1); + passed = passed and testLagrange<2>(gw0,gw1); + // order 3 only for 2D grids + if ( GW0::dimension < 3 ) + passed = passed and testLagrange<3>(gw0,gw1); + + return passed; +} + + +// this test checks a jump over two levels +template<class GW0, class GW1, class GW2> +bool checkTwoLevelJump(const GW0& gw0, const GW1& gw1, const GW2& gw2) +{ + bool passed = true; + + // test oder 0-3 + passed = passed and testTwoLevelLagrange<0>(gw0,gw1,gw2); + passed = passed and testTwoLevelLagrange<1>(gw0,gw1,gw2); + passed = passed and testTwoLevelLagrange<2>(gw0,gw1,gw2); + // order 3 only for 2D grids + if ( GW0::dimension < 3 ) + passed = passed and testTwoLevelLagrange<3>(gw0,gw1,gw2); + + return passed; +} + + +struct Suite +{ + template<class GridType> + bool check(const GridType& grid) + { + bool passed = true; + + auto maxLevel = grid.maxLevel(); + + auto fineGridView = grid.levelGridView(maxLevel); + auto semiGridView = grid.levelGridView(maxLevel-1); + auto coarseGridView = grid.levelGridView(maxLevel-2); + + passed = passed and checkTransferMatrix(semiGridView,fineGridView); + passed = passed and checkTwoLevelJump(coarseGridView,semiGridView,fineGridView); + + return passed; + } +}; + +int main(int argc, char** argv) +{ + Dune::MPIHelper::instance(argc, argv); + + Suite tests; + + bool passed = checkWithStructuredGrid<2>(tests, 3); + passed = passed and checkWithStructuredGrid<3>(tests, 3); + + return passed ? 0 : 1; +}