diff --git a/dumux/discretization/CMakeLists.txt b/dumux/discretization/CMakeLists.txt
index d67d77fbf4c4001da671f855413882ec8399d3f7..812b30de3f33e870c69381002771eca417e2ab0b 100644
--- a/dumux/discretization/CMakeLists.txt
+++ b/dumux/discretization/CMakeLists.txt
@@ -2,6 +2,7 @@ add_subdirectory(box)
add_subdirectory(cellcentered)
add_subdirectory(fem)
add_subdirectory(staggered)
+add_subdirectory(projection)
install(FILES
basefvgridgeometry.hh
@@ -13,6 +14,7 @@ elementsolution.hh
evalgradients.hh
evalsolution.hh
fluxstencil.hh
+functionspacebasis.hh
fvgridvariables.hh
fvproperties.hh
localview.hh
diff --git a/dumux/discretization/functionspacebasis.hh b/dumux/discretization/functionspacebasis.hh
new file mode 100644
index 0000000000000000000000000000000000000000..ec371baecfaed0fd9052b2d531459c23b31cd13d
--- /dev/null
+++ b/dumux/discretization/functionspacebasis.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 . *
+ *****************************************************************************/
+/*!
+ * \file
+ * \ingroup Discretization
+ * \brief Provides helper aliases and functionality to obtain the types
+ * and instances of Dune::Functions function space bases that
+ * underlie different discretization schemes
+ */
+#ifndef DUMUX_DISCRETIZATION_FUNCTION_SPACE_BASIS_HH
+#define DUMUX_DISCRETIZATION_FUNCTION_SPACE_BASIS_HH
+
+#if HAVE_DUNE_FUNCTIONS
+
+#include
+#include
+
+namespace Dumux {
+
+/*!
+ * \ingroup Discretization
+ * \brief Traits class that contains information on the
+ * function space basis used by the discretization
+ * scheme underlying a grid geometry class. Specializations
+ * of the traits for different schemes are provided below.
+ */
+template< class GridGeometry,
+ DiscretizationMethod dm = GridGeometry::discMethod >
+struct FunctionSpaceBasisTraits;
+
+/*!
+ * \ingroup Discretization
+ * \brief Creates a Dune::Functions object of the underlying basis
+ * of a discretization scheme that is not finite elements.
+ */
+template = 0>
+typename FunctionSpaceBasisTraits::GlobalBasis
+getFunctionSpaceBasis(const GridGeometry& gridGeometry)
+{ return {gridGeometry.gridView()}; }
+
+/*!
+ * \ingroup Discretization
+ * \brief Returns the Dune::Functions object for the basis of a finite element scheme.
+ */
+template = 0>
+const typename FunctionSpaceBasisTraits::GlobalBasis&
+getFunctionSpaceBasis(const GridGeometry& gridGeometry)
+{ return gridGeometry.feBasis(); }
+
+
+///////////////////////////////////////////////////////////
+// Specializations of the FunctionSpaceBasisTraits class //
+///////////////////////////////////////////////////////////
+
+//! Traits specialization: box scheme uses lagrange basis of order 1
+template< class GridGeometry >
+struct FunctionSpaceBasisTraits
+{ using GlobalBasis = Dune::Functions::LagrangeBasis; };
+
+//! Traits specialization: cc schemes use lagrange bases of order 0
+template< class GridGeometry >
+struct FunctionSpaceBasisTraits
+{ using GlobalBasis = Dune::Functions::LagrangeBasis; };
+
+//! Traits specialization: cc schemes use lagrange bases of order 0
+template< class GridGeometry >
+struct FunctionSpaceBasisTraits
+{ using GlobalBasis = Dune::Functions::LagrangeBasis; };
+
+//! Traits specialization: fem defines its basis
+template< class GridGeometry >
+struct FunctionSpaceBasisTraits
+{ using GlobalBasis = typename GridGeometry::FEBasis; };
+
+} // end namespace Dumux
+
+#endif // HAVE_DUNE_FUNCTIONS
+#endif // DUMUX_FUNCTION_SPACE_BASIS_HH
diff --git a/dumux/discretization/projection/CMakeLists.txt b/dumux/discretization/projection/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f3f288c353f6a972f27fa5cb6c41534674bb9ff0
--- /dev/null
+++ b/dumux/discretization/projection/CMakeLists.txt
@@ -0,0 +1,3 @@
+install(FILES
+projector.hh
+DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dumux/discretization/projection)
diff --git a/dumux/discretization/projection/projector.hh b/dumux/discretization/projection/projector.hh
new file mode 100644
index 0000000000000000000000000000000000000000..a4a3824d97dcebcfac072fbe81530c8cd2be5c5a
--- /dev/null
+++ b/dumux/discretization/projection/projector.hh
@@ -0,0 +1,342 @@
+// -*- 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 . *
+ *****************************************************************************/
+/*!
+ * \file
+ * \ingroup Discretization
+ * \brief Contains functionality for L2-projections from one function
+ * space into another, which can live both on the same or different
+ * grids of potentially different dimensionality.
+ * \note In the case of the function space bases living on the same grid
+ * an optimized implementation could be implemented. Currently, we
+ * require a glue object containing the intersections between two
+ * grids to be provided to the free factory functions.
+ */
+#ifndef DUMUX_DISCRETIZATION_PROJECTOR_HH
+#define DUMUX_DISCRETIZATION_PROJECTOR_HH
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+namespace Dumux {
+
+/*!
+ * \ingroup Discretization
+ * \brief Does an L2-projection from one discrete function space
+ * into another. The convenience functions makeProjectorPair
+ * or makeProjector can be used to create such a projection.
+ */
+template
+class Projector
+{
+ using BlockType = Dune::FieldMatrix;
+
+public:
+ //! Export the type of the projection matrices
+ using Matrix = Dune::BCRSMatrix< BlockType >;
+
+ //! delete default constructor
+ Projector() = delete;
+
+ /*!
+ * \brief Constructor. Receives the mass and projection
+ * matrix that define the linear system describing
+ * the L2-projection from a function space into another.
+ */
+ Projector(Matrix&& massMatrix, Matrix&& projectionMatrix)
+ : massMat_(std::move(massMatrix))
+ , projMat_(std::move(projectionMatrix))
+ {}
+
+ /*!
+ * \brief Project a solution u into up
+ * \param u The solution living on the domain space
+ * \param up The projection of u into the target space
+ */
+ template< class DomainSolution, class TargetSolution>
+ void project(const DomainSolution& u, TargetSolution& up) const
+ {
+ // be picky about size of u
+ if ( u.size() != projMat_.M())
+ DUNE_THROW(Dune::InvalidStateException, "Vector size mismatch" );
+
+ up.resize(massMat_.N());
+
+ auto rhs = up;
+ projMat_.mv(u, rhs);
+
+ SSORCGBackend solver;
+ solver.solve(massMat_, up, rhs);
+ }
+
+private:
+ Matrix massMat_;
+ Matrix projMat_;
+};
+
+/*!
+ * \brief Traits class stating the type of projector between to bases
+ */
+template
+class ProjectorTraits
+{
+ using FiniteElementDomain = typename FEBasisDomain::LocalView::Tree::FiniteElement;
+ using FiniteElementTarget = typename FEBasisTarget::LocalView::Tree::FiniteElement;
+ using ScalarDomain = typename FiniteElementDomain::Traits::LocalBasisType::Traits::RangeFieldType;
+ using ScalarTarget = typename FiniteElementTarget::Traits::LocalBasisType::Traits::RangeFieldType;
+ using Scalar = typename Dune::PromotionTraits::PromotedType;
+public:
+ using Projector = Dumux::Projector< Scalar >;
+};
+
+
+// Projector construction details
+namespace Impl {
+
+/*!
+ * \brief Creates a projector class between two function space bases
+ * \tparam doBidirectional If false, the backward projection matrix is not assembled
+ */
+template
+std::pair< typename ProjectorTraits::Projector,
+ typename ProjectorTraits::Projector >
+makeProjectorPair(const FEBasisDomain& feBasisDomain,
+ const FEBasisTarget& feBasisTarget,
+ const GlueType& glue)
+{
+ // we assume that target dim <= domain dimension
+ static constexpr int domainDim = FEBasisDomain::GridView::dimension;
+ static constexpr int targetDim = FEBasisTarget::GridView::dimension;
+ static_assert(targetDim <= domainDim, "This expects target dim < domain dim, please swap arguments");
+
+ using ForwardProjector = typename ProjectorTraits::Projector;
+ using BackwardProjector = typename ProjectorTraits::Projector;
+
+ using ForwardProjectionMatrix = typename ForwardProjector::Matrix;
+ using BackwardProjectionMatrix = typename BackwardProjector::Matrix;
+
+ auto domainLocalView = feBasisDomain.localView();
+ auto targetLocalView = feBasisTarget.localView();
+
+ // determine mass matrix patterns (standard FE scheme pattern)
+ Dune::MatrixIndexSet backwardPatternM, forwardPatternM;
+ forwardPatternM = getFEJacobianPattern(feBasisTarget);
+ if (doBidirectional) backwardPatternM = getFEJacobianPattern(feBasisDomain);
+
+ // determine projection matrix patterns
+ Dune::MatrixIndexSet backwardPatternP, forwardPatternP;
+ forwardPatternP.resize(feBasisTarget.size(), feBasisDomain.size());
+ if (doBidirectional) backwardPatternP.resize(feBasisDomain.size(), feBasisTarget.size());
+
+ using std::max;
+ unsigned int maxBasisOrder = 0;
+ for (const auto& is : intersections(glue))
+ {
+ // "outside" element is of target domain
+ // since target dim <= domain dim there is maximum one!
+ targetLocalView.bind( is.outside(0) );
+ const auto& targetLocalBasis = targetLocalView.tree().finiteElement().localBasis();
+
+ for (unsigned int nIdx = 0; nIdx < is.neighbor(/*targetSide*/1); ++nIdx)
+ {
+ domainLocalView.bind( is.inside(nIdx) );
+ const auto& domainLocalBasis = domainLocalView.tree().finiteElement().localBasis();
+
+ // keep track of maximum basis order (used in integration)
+ maxBasisOrder = max(maxBasisOrder, max(domainLocalBasis.order(), targetLocalBasis.order()));
+
+ for (unsigned int i = 0; i < domainLocalBasis.size(); ++i)
+ for (unsigned int j = 0; j < targetLocalBasis.size(); ++j)
+ {
+ forwardPatternP.add(targetLocalView.index(j), domainLocalView.index(i));
+ if (doBidirectional) backwardPatternP.add(domainLocalView.index(i), targetLocalView.index(j));
+ }
+ }
+ }
+
+ // assemble matrices
+ ForwardProjectionMatrix forwardM, forwardP;
+ forwardPatternM.exportIdx(forwardM); forwardM = 0.0;
+ forwardPatternP.exportIdx(forwardP); forwardP = 0.0;
+
+ BackwardProjectionMatrix backwardM, backwardP;
+ if (doBidirectional)
+ {
+ backwardPatternM.exportIdx(backwardM); backwardM = 0.0;
+ backwardPatternP.exportIdx(backwardP); backwardP = 0.0;
+ }
+
+ for (const auto& is : intersections(glue))
+ {
+ const auto& targetElement = is.outside(0);
+ const auto& targetElementGeometry = targetElement.geometry();
+
+ targetLocalView.bind( targetElement );
+ const auto& targetLocalBasis = targetLocalView.tree().finiteElement().localBasis();
+
+ // perform integration over intersection geometry
+ using IsGeometry = typename std::decay_t;
+ using ctype = typename IsGeometry::ctype;
+
+ const auto& isGeometry = is.geometry();
+ const int intOrder = maxBasisOrder + 1;
+ const auto& quad = Dune::QuadratureRules::rule(isGeometry.type(), intOrder);
+ for (auto&& qp : quad)
+ {
+ const auto weight = qp.weight();
+ const auto ie = isGeometry.integrationElement(qp.position());
+ const auto globalPos = isGeometry.global(qp.position());
+
+ std::vector< Dune::FieldVector > targetShapeVals;
+ targetLocalBasis.evaluateFunction(targetElementGeometry.local(globalPos), targetShapeVals);
+
+ // mass matrix entries target domain
+ for (unsigned int i = 0; i < targetLocalBasis.size(); ++i)
+ {
+ const auto dofIdx = targetLocalView.index(i);
+ forwardM[dofIdx][dofIdx] += ie*weight*targetShapeVals[i];
+ }
+
+ // If targetDim < domainDim, there can be several "neighbors" if
+ // targetElement is aligned with a facet of domainElement. In
+ // this case make sure the basis functions are not added
+ // multiple times! (division by numNeighbors)
+ const auto numNeighbors = is.neighbor(/*targetSide*/1);
+ for (unsigned int nIdx = 0; nIdx < numNeighbors; ++nIdx)
+ {
+ const auto& domainElement = is.inside(nIdx);
+ domainLocalView.bind( domainElement );
+ const auto& domainLocalBasis = domainLocalView.tree().finiteElement().localBasis();
+
+ std::vector< Dune::FieldVector > domainShapeVals;
+ domainLocalBasis.evaluateFunction(domainElement.geometry().local(globalPos), domainShapeVals);
+
+ // add entries in matrices
+ for (unsigned int i = 0; i < domainLocalBasis.size(); ++i)
+ {
+ const auto dofIdxDomain = domainLocalView.index(i);
+ const auto domainShapeVal = domainShapeVals[i];
+ backwardM[dofIdxDomain][dofIdxDomain] += ie*weight*domainShapeVal;
+
+ for (unsigned int j = 0; j < targetLocalBasis.size(); ++j)
+ {
+ const auto dofIdxTarget = targetLocalView.index(j);
+ const auto entry = ie*weight*domainShapeVal*targetShapeVals[j];
+
+ forwardP[dofIdxTarget][dofIdxDomain] += entry/numNeighbors;
+ if (doBidirectional)
+ backwardP[dofIdxDomain][dofIdxTarget] += entry;
+ }
+ }
+ }
+ }
+ }
+
+ // On those dofs that to not take part in any intersection we will have zeroes
+ // in the mass matrices. The right hand side will be zero because the projection
+ // matrix has no entries for those dofs as there was no intersection. Thus, we set
+ // 1.0 on the diagonal for those dofs, such that after projection, the projected
+ // solution is 0 on them.
+ for (std::size_t dofIdx = 0; dofIdx < forwardM.N(); ++dofIdx)
+ if (forwardM[dofIdx][dofIdx] == 0.0)
+ forwardM[dofIdx][dofIdx] = 1.0;
+
+ if (doBidirectional)
+ {
+ for (std::size_t dofIdx = 0; dofIdx < backwardM.N(); ++dofIdx)
+ if (backwardM[dofIdx][dofIdx] == 0.0)
+ backwardM[dofIdx][dofIdx] = 1.0;
+ }
+
+ ForwardProjector fw(std::move(forwardM), std::move(forwardP));
+ BackwardProjector bw(std::move(backwardM), std::move(backwardP));
+
+ return std::make_pair(std::move(fw), std::move(bw));
+}
+
+} // end namespace Implementation
+
+
+/*!
+ * \ingroup Discretization
+ * \brief Creates a pair of projectors between the space with
+ * basis feBasisDomain to the space with basis feBasisTarget.
+ * \param feBasisDomain The domain finite element space basis
+ * \param feBasisTarget The target finite element space basis
+ * \param glue The glue object containing the intersections between the grids.
+ * \return A pair of projectors where the first is the forward
+ * projector from the space with basis feBasisDomain to
+ * the space with basis feBasisTarget and the second
+ * does the backward projection.
+ */
+template< class FEBasisDomain, class FEBasisTarget, class GlueType >
+std::pair< typename ProjectorTraits::Projector,
+ typename ProjectorTraits::Projector >
+makeProjectorPair(const FEBasisDomain& feBasisDomain,
+ const FEBasisTarget& feBasisTarget,
+ GlueType glue)
+{
+ // we assume that target dim <= domain dimension
+ static constexpr int domainDim = FEBasisDomain::GridView::dimension;
+ static constexpr int targetDim = FEBasisTarget::GridView::dimension;
+ static_assert(targetDim <= domainDim, "makeProjectorPair() expects targetDim < domainDim, please swap arguments");
+
+ return Impl::makeProjectorPair(feBasisDomain, feBasisTarget, glue);
+}
+
+/*!
+ * \ingroup Discretization
+ * \brief Creates a forward projector from the space feBasisDomain
+ * to the space with basis feBasisTarget.
+ * \param feBasisDomain The domain finite element space basis
+ * \param feBasisTarget The target finite element space basis
+ * \param glue The glue object containing the intersections between the grids.
+ * \return The forward projector from the space with basis feBasisDomain
+ * to the space with basis feBasisTarget.
+ */
+template< class FEBasisDomain, class FEBasisTarget, class GlueType >
+typename ProjectorTraits::Projector
+makeProjector(const FEBasisDomain& feBasisDomain,
+ const FEBasisTarget& feBasisTarget,
+ GlueType glue)
+{
+ // we assume that target dim <= domain dimension
+ static constexpr int domainDim = FEBasisDomain::GridView::dimension;
+ static constexpr int targetDim = FEBasisTarget::GridView::dimension;
+ static_assert(targetDim <= domainDim, "makeProjectorPair() expects targetDim < domainDim, please swap arguments");
+
+ return Impl::makeProjectorPair(feBasisDomain, feBasisTarget, glue).first;
+}
+
+} // end namespace Dumux
+
+#endif // DUMUX_PROJECTOR_HH
diff --git a/dune.module b/dune.module
index 5635a5a85c9ab861b7ca5a300009fb4caedccb71..261fdfcf42a8a0c4089c4a027021c75c520ebe50 100644
--- a/dune.module
+++ b/dune.module
@@ -2,5 +2,5 @@ Module: dumux
Version: 3.1-git
Maintainer: dumux@listserv.uni-stuttgart.de
Depends: dune-common (>= 2.6) dune-grid (>= 2.6) dune-localfunctions (>= 2.6) dune-istl (>= 2.6)
-Suggests: dune-alugrid (>=2.6) dune-foamgrid (>=2.6) dune-uggrid (>=2.6) opm-common opm-grid dune-subgrid dune-spgrid (>= 2.6)
+Suggests: dune-alugrid (>=2.6) dune-foamgrid (>=2.6) dune-uggrid (>=2.6) dune-functions (>=2.6) opm-common opm-grid dune-subgrid dune-spgrid (>= 2.6)
Whitespace-Hook: Yes
diff --git a/test/discretization/CMakeLists.txt b/test/discretization/CMakeLists.txt
index b42b74522ab08268f20e2097f3e1509b9984e348..f127b603f79c94575badc24a7eb39c9328c88892 100644
--- a/test/discretization/CMakeLists.txt
+++ b/test/discretization/CMakeLists.txt
@@ -1,3 +1,4 @@
add_subdirectory(cellcentered)
add_subdirectory(staggered)
add_subdirectory(box)
+add_subdirectory(projection)
diff --git a/test/discretization/projection/CMakeLists.txt b/test/discretization/projection/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..cec331a494c782e4a9771ebb3e54fccb4021c1ab
--- /dev/null
+++ b/test/discretization/projection/CMakeLists.txt
@@ -0,0 +1,41 @@
+dune_symlink_to_source_files(FILES "params.input")
+
+dune_add_test(NAME test_projection_2d1d
+ SOURCES test_projection_2d1d.cc
+ CMAKE_GUARD "( dune-foamgrid_FOUND AND dune-functions_FOUND )"
+ LABELS unit discretization
+ COMMAND ${CMAKE_SOURCE_DIR}/bin/testing/runtest.py
+ CMD_ARGS --script fuzzyData
+ --files ${CMAKE_SOURCE_DIR}/test/references/test_projection_2d1d_tpfa_tpfa_1d.csv
+ ${CMAKE_CURRENT_BINARY_DIR}/1d_tpfa_tpfa.csv
+ ${CMAKE_SOURCE_DIR}/test/references/test_projection_2d1d_tpfa_tpfa_2d.csv
+ ${CMAKE_CURRENT_BINARY_DIR}/2d_tpfa_tpfa.csv
+ ${CMAKE_SOURCE_DIR}/test/references/test_projection_2d1d_box_box_1d.csv
+ ${CMAKE_CURRENT_BINARY_DIR}/1d_box_box.csv
+ ${CMAKE_SOURCE_DIR}/test/references/test_projection_2d1d_box_box_2d.csv
+ ${CMAKE_CURRENT_BINARY_DIR}/2d_box_box.csv
+ ${CMAKE_SOURCE_DIR}/test/references/test_projection_2d1d_fem_fem_1d.csv
+ ${CMAKE_CURRENT_BINARY_DIR}/1d_fem_fem.csv
+ ${CMAKE_SOURCE_DIR}/test/references/test_projection_2d1d_fem_fem_2d.csv
+ ${CMAKE_CURRENT_BINARY_DIR}/2d_fem_fem.csv
+ ${CMAKE_SOURCE_DIR}/test/references/test_projection_2d1d_tpfa_box_1d.csv
+ ${CMAKE_CURRENT_BINARY_DIR}/1d_tpfa_box.csv
+ ${CMAKE_SOURCE_DIR}/test/references/test_projection_2d1d_tpfa_box_2d.csv
+ ${CMAKE_CURRENT_BINARY_DIR}/2d_tpfa_box.csv
+ ${CMAKE_SOURCE_DIR}/test/references/test_projection_2d1d_box_tpfa_1d.csv
+ ${CMAKE_CURRENT_BINARY_DIR}/1d_box_tpfa.csv
+ ${CMAKE_SOURCE_DIR}/test/references/test_projection_2d1d_box_tpfa_2d.csv
+ ${CMAKE_CURRENT_BINARY_DIR}/2d_box_tpfa.csv
+ ${CMAKE_SOURCE_DIR}/test/references/test_projection_2d1d_fem_box_2d.csv
+ ${CMAKE_CURRENT_BINARY_DIR}/2d_fem_box.csv
+ ${CMAKE_SOURCE_DIR}/test/references/test_projection_2d1d_fem_box_1d.csv
+ ${CMAKE_CURRENT_BINARY_DIR}/1d_fem_box.csv
+ ${CMAKE_SOURCE_DIR}/test/references/test_projection_2d1d_fem_tpfa_2d.csv
+ ${CMAKE_CURRENT_BINARY_DIR}/2d_fem_tpfa.csv
+ ${CMAKE_SOURCE_DIR}/test/references/test_projection_2d1d_fem_tpfa_1d.csv
+ ${CMAKE_CURRENT_BINARY_DIR}/1d_fem_tpfa.csv
+ ${CMAKE_SOURCE_DIR}/test/references/test_projection_2d1d_tpfa_fem_2d.csv
+ ${CMAKE_CURRENT_BINARY_DIR}/2d_tpfa_fem.csv
+ ${CMAKE_SOURCE_DIR}/test/references/test_projection_2d1d_tpfa_fem_1d.csv
+ ${CMAKE_CURRENT_BINARY_DIR}/1d_tpfa_fem.csv
+ --command "${CMAKE_CURRENT_BINARY_DIR}/test_projection_2d1d params.input")
diff --git a/test/discretization/projection/params.input b/test/discretization/projection/params.input
new file mode 100644
index 0000000000000000000000000000000000000000..5058750258c5ce67e703e5ff8c0e9427aeb52004
--- /dev/null
+++ b/test/discretization/projection/params.input
@@ -0,0 +1,2 @@
+[Vtk]
+EnableOutput = false
diff --git a/test/discretization/projection/test_projection_2d1d.cc b/test/discretization/projection/test_projection_2d1d.cc
new file mode 100644
index 0000000000000000000000000000000000000000..16dd7b00387710a8d04b6d047fc5f54406f6463e
--- /dev/null
+++ b/test/discretization/projection/test_projection_2d1d.cc
@@ -0,0 +1,215 @@
+// -*- 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 . *
+ *****************************************************************************/
+/*!
+ * \file
+ * \brief Test for the projection of discrete solutions from
+ * a two-dimensional discrete solution to a one-dimensional.
+ */
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+template< class Scalar, class SolutionVector, class GridGeometry1, class GridGeometry2 >
+void writeProjection(const GridGeometry1& gg1, const GridGeometry2& gg2,
+ const SolutionVector& sol1, const SolutionVector& sol2,
+ const std::string& acro1, const std::string& acro2)
+{
+ const auto projectors = Dumux::makeProjectorPair( getFunctionSpaceBasis(gg1),
+ getFunctionSpaceBasis(gg2),
+ makeGlue(gg1, gg2) );
+ const auto& forwardProjector = projectors.first;
+ const auto& backwardProjector = projectors.second;
+
+ SolutionVector p2_1, p1_2;
+ forwardProjector.project(sol1, p2_1);
+ backwardProjector.project(sol2, p1_2);
+
+ const std::string filename1 = "2d_" + acro1 + "_" + acro2;
+ const std::string filename2 = "1d_" + acro1 + "_" + acro2;
+
+ Dumux::writeContainerToFile(p1_2, filename1 + ".csv");
+ Dumux::writeContainerToFile(p2_1, filename2 + ".csv");
+
+ static const bool writeVtk = Dumux::getParam("Vtk.EnableOutput", false);
+ if (writeVtk)
+ {
+ using namespace Dune;
+ VTKWriter writer1(gg1.gridView());
+ VTKWriter writer2(gg2.gridView());
+
+ const auto& basis1 = Dumux::getFunctionSpaceBasis(gg1);
+ const auto& basis2 = Dumux::getFunctionSpaceBasis(gg2);
+
+ const auto uInfo = VTK::FieldInfo("u", VTK::FieldInfo::Type::scalar, 1);
+ const auto upInfo = VTK::FieldInfo("u_p", VTK::FieldInfo::Type::scalar, 1);
+
+ const auto uFunction1 = Functions::makeDiscreteGlobalBasisFunction(basis1, sol1);
+ const auto uFunction2 = Functions::makeDiscreteGlobalBasisFunction(basis2, sol2);
+ const auto upFunction1 = Functions::makeDiscreteGlobalBasisFunction(basis1, p1_2);
+ const auto upFunction2 = Functions::makeDiscreteGlobalBasisFunction(basis2, p2_1);
+
+ if (GridGeometry1::discMethod == Dumux::DiscretizationMethod::cctpfa)
+ {
+ writer1.addCellData(uFunction1, uInfo);
+ writer1.addCellData(upFunction1, upInfo);
+ }
+ else
+ {
+ writer1.addVertexData(uFunction1, uInfo);
+ writer1.addVertexData(upFunction1, upInfo);
+ }
+
+ if (GridGeometry2::discMethod == Dumux::DiscretizationMethod::cctpfa)
+ {
+ writer2.addCellData(uFunction2, uInfo);
+ writer2.addCellData(upFunction2, upInfo);
+ }
+ else
+ {
+ writer2.addVertexData(uFunction2, uInfo);
+ writer2.addVertexData(upFunction2, upInfo);
+ }
+
+ writer1.write(filename1);
+ writer2.write(filename2);
+ }
+}
+
+int main (int argc, char *argv[]) try
+{
+ // maybe initialize mpi
+ Dune::MPIHelper::instance(argc, argv);
+
+ // initialize parameter tree
+ Dumux::Parameters::init(argc, argv);
+
+ using Grid1 = Dune::YaspGrid<2>;
+ using Grid2 = Dune::FoamGrid<1, 2>;
+
+ using GridView1 = typename Grid1::LeafGridView;
+ using GridView2 = typename Grid2::LeafGridView;
+ using FEBasis1 = Dune::Functions::LagrangeBasis;
+ using FEBasis2 = Dune::Functions::LagrangeBasis;
+
+ using Scalar = double;
+ using TpfaGridGeometry1 = Dumux::CCTpfaFVGridGeometry;
+ using BoxGridGeometry1 = Dumux::BoxFVGridGeometry;
+ using FEGridGeometry1 = Dumux::FEGridGeometry;
+
+ using TpfaGridGeometry2 = Dumux::CCTpfaFVGridGeometry;
+ using BoxGridGeometry2 = Dumux::BoxFVGridGeometry;
+ using FEGridGeometry2 = Dumux::FEGridGeometry;
+
+ // make the 2d grid
+ using GlobalPosition = Dune::FieldVector;
+ GlobalPosition lower1(0.0);
+ GlobalPosition upper1(1.0);
+ std::array els1{{10, 10}};
+ std::shared_ptr grid1 = Dune::StructuredGridFactory::createCubeGrid(lower1, upper1, els1);
+
+ // make the 1d grid
+ GlobalPosition lower2({0., 0.5});
+ GlobalPosition upper2({1. ,0.5});
+ std::array els2{{13}};
+ std::shared_ptr grid2 = Dune::StructuredGridFactory::createSimplexGrid(lower2, upper2, els2);
+
+ Dune::Functions::LagrangeBasis q0Basis1(grid1->leafGridView());
+ Dune::Functions::LagrangeBasis q0Basis2(grid2->leafGridView());
+ Dune::Functions::LagrangeBasis q1Basis1(grid1->leafGridView());
+ Dune::Functions::LagrangeBasis q1Basis2(grid2->leafGridView());
+
+ // create all grid geometries
+ TpfaGridGeometry1 tpfaGridGeometry1(grid1->leafGridView());
+ BoxGridGeometry1 boxGridGeometry1(grid1->leafGridView());
+ FEGridGeometry1 feGridGeometry1(std::make_shared(grid1->leafGridView()));
+
+ TpfaGridGeometry2 tpfaGridGeometry2(grid2->leafGridView());
+ BoxGridGeometry2 boxGridGeometry2(grid2->leafGridView());
+ FEGridGeometry2 feGridGeometry2(std::make_shared(grid2->leafGridView()));
+
+ // function to distribute on grids
+ auto evalFunction1 = [] (const auto& pos)
+ { return std::sin(2*M_PI*pos[0])*std::cos(4*M_PI*pos[1]); };
+ auto evalFunction2 = [] (const auto& pos)
+ { return std::cos(4*M_PI*(pos[0] - M_PI))*std::sin(2*M_PI*(pos[1]-M_PI/2.0)); };
+
+ // create solution vectors
+ using SolutionVector = Dune::BlockVector< Dune::FieldVector >;
+ SolutionVector sol2d_tpfa, sol2d_box, sol2d_fem;
+ SolutionVector sol1d_tpfa, sol1d_box, sol1d_fem;
+
+ Dune::Functions::interpolate(q0Basis1, sol2d_tpfa, evalFunction1);
+ Dune::Functions::interpolate(q0Basis2, sol1d_tpfa, evalFunction2);
+
+ Dune::Functions::interpolate(q1Basis1, sol2d_box, evalFunction1);
+ Dune::Functions::interpolate(q1Basis2, sol1d_box, evalFunction2);
+
+ Dune::Functions::interpolate(feGridGeometry1.feBasis(), sol2d_fem, evalFunction1);
+ Dune::Functions::interpolate(feGridGeometry2.feBasis(), sol1d_fem, evalFunction2);
+
+ // tpfa - tpfa
+ writeProjection(tpfaGridGeometry1, tpfaGridGeometry2, sol2d_tpfa, sol1d_tpfa, "tpfa", "tpfa");
+ // box - box
+ writeProjection(boxGridGeometry1, boxGridGeometry2, sol2d_box, sol1d_box, "box", "box");
+ // fem - fem
+ writeProjection(feGridGeometry1, feGridGeometry2, sol2d_fem, sol1d_fem, "fem", "fem");
+ // box - tpfa
+ writeProjection(boxGridGeometry1, tpfaGridGeometry2, sol2d_box, sol1d_tpfa, "box", "tpfa");
+ // tpfa - box
+ writeProjection(tpfaGridGeometry1, boxGridGeometry2, sol2d_tpfa, sol1d_box, "tpfa", "box");
+ // fem - tpfa
+ writeProjection(feGridGeometry1, tpfaGridGeometry2, sol2d_fem, sol1d_tpfa, "fem", "tpfa");
+ // fem - box
+ writeProjection(feGridGeometry1, boxGridGeometry2, sol2d_fem, sol1d_box, "fem", "box");
+ // tpfa - fem
+ writeProjection(tpfaGridGeometry1, feGridGeometry2, sol2d_tpfa, sol1d_fem, "tpfa", "fem");
+
+ return 0;
+}
+// //////////////////////////////////
+// Error handler
+// /////////////////////////////////
+catch (Dune::Exception &e) {
+
+ std::cout << e << std::endl;
+ return 1;
+}
diff --git a/test/references/test_projection_2d1d_box_box_1d.csv b/test/references/test_projection_2d1d_box_box_1d.csv
new file mode 100644
index 0000000000000000000000000000000000000000..6f79a83904152582c7af5578159090cf47bd6ec4
--- /dev/null
+++ b/test/references/test_projection_2d1d_box_box_1d.csv
@@ -0,0 +1,14 @@
+1.507142e-01
+4.422696e-01
+7.796350e-01
+9.409501e-01
+8.891607e-01
+6.289358e-01
+2.258410e-01
+-2.258410e-01
+-6.289358e-01
+-8.891607e-01
+-9.409501e-01
+-7.796350e-01
+-4.422696e-01
+-1.507142e-01
diff --git a/test/references/test_projection_2d1d_box_box_2d.csv b/test/references/test_projection_2d1d_box_box_2d.csv
new file mode 100644
index 0000000000000000000000000000000000000000..6197ca93b3d710bba2772da7de4daa191386c202
--- /dev/null
+++ b/test/references/test_projection_2d1d_box_box_2d.csv
@@ -0,0 +1,121 @@
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+-7.637070e-02
+-3.016355e-01
+-2.583696e-01
+1.419646e-01
+3.462732e-01
+7.198979e-02
+-3.016499e-01
+-2.587126e-01
+1.419077e-01
+3.460640e-01
+2.207074e-01
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
diff --git a/test/references/test_projection_2d1d_box_tpfa_1d.csv b/test/references/test_projection_2d1d_box_tpfa_1d.csv
new file mode 100644
index 0000000000000000000000000000000000000000..5035330ff9436ed316ac9bef525d190b7d8f45b0
--- /dev/null
+++ b/test/references/test_projection_2d1d_box_tpfa_1d.csv
@@ -0,0 +1,13 @@
+2.260713e-01
+6.359015e-01
+9.007574e-01
+9.496593e-01
+7.833929e-01
+4.486884e-01
+1.608432e-16
+-4.486884e-01
+-7.833929e-01
+-9.496593e-01
+-9.007574e-01
+-6.359015e-01
+-2.260713e-01
diff --git a/test/references/test_projection_2d1d_box_tpfa_2d.csv b/test/references/test_projection_2d1d_box_tpfa_2d.csv
new file mode 100644
index 0000000000000000000000000000000000000000..c68fe4ee3ec3d32ddb0a96978c898429be32ce48
--- /dev/null
+++ b/test/references/test_projection_2d1d_box_tpfa_2d.csv
@@ -0,0 +1,121 @@
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+-1.322347e-01
+-3.147229e-01
+-2.684315e-01
+1.457959e-01
+3.607137e-01
+7.450318e-02
+-3.129911e-01
+-2.681667e-01
+1.480351e-01
+3.600225e-01
+2.827186e-01
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
diff --git a/test/references/test_projection_2d1d_fem_box_1d.csv b/test/references/test_projection_2d1d_fem_box_1d.csv
new file mode 100644
index 0000000000000000000000000000000000000000..6f79a83904152582c7af5578159090cf47bd6ec4
--- /dev/null
+++ b/test/references/test_projection_2d1d_fem_box_1d.csv
@@ -0,0 +1,14 @@
+1.507142e-01
+4.422696e-01
+7.796350e-01
+9.409501e-01
+8.891607e-01
+6.289358e-01
+2.258410e-01
+-2.258410e-01
+-6.289358e-01
+-8.891607e-01
+-9.409501e-01
+-7.796350e-01
+-4.422696e-01
+-1.507142e-01
diff --git a/test/references/test_projection_2d1d_fem_box_2d.csv b/test/references/test_projection_2d1d_fem_box_2d.csv
new file mode 100644
index 0000000000000000000000000000000000000000..6197ca93b3d710bba2772da7de4daa191386c202
--- /dev/null
+++ b/test/references/test_projection_2d1d_fem_box_2d.csv
@@ -0,0 +1,121 @@
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+-7.637070e-02
+-3.016355e-01
+-2.583696e-01
+1.419646e-01
+3.462732e-01
+7.198979e-02
+-3.016499e-01
+-2.587126e-01
+1.419077e-01
+3.460640e-01
+2.207074e-01
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
diff --git a/test/references/test_projection_2d1d_fem_fem_1d.csv b/test/references/test_projection_2d1d_fem_fem_1d.csv
new file mode 100644
index 0000000000000000000000000000000000000000..6f79a83904152582c7af5578159090cf47bd6ec4
--- /dev/null
+++ b/test/references/test_projection_2d1d_fem_fem_1d.csv
@@ -0,0 +1,14 @@
+1.507142e-01
+4.422696e-01
+7.796350e-01
+9.409501e-01
+8.891607e-01
+6.289358e-01
+2.258410e-01
+-2.258410e-01
+-6.289358e-01
+-8.891607e-01
+-9.409501e-01
+-7.796350e-01
+-4.422696e-01
+-1.507142e-01
diff --git a/test/references/test_projection_2d1d_fem_fem_2d.csv b/test/references/test_projection_2d1d_fem_fem_2d.csv
new file mode 100644
index 0000000000000000000000000000000000000000..6197ca93b3d710bba2772da7de4daa191386c202
--- /dev/null
+++ b/test/references/test_projection_2d1d_fem_fem_2d.csv
@@ -0,0 +1,121 @@
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+-7.637070e-02
+-3.016355e-01
+-2.583696e-01
+1.419646e-01
+3.462732e-01
+7.198979e-02
+-3.016499e-01
+-2.587126e-01
+1.419077e-01
+3.460640e-01
+2.207074e-01
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
diff --git a/test/references/test_projection_2d1d_fem_tpfa_1d.csv b/test/references/test_projection_2d1d_fem_tpfa_1d.csv
new file mode 100644
index 0000000000000000000000000000000000000000..5035330ff9436ed316ac9bef525d190b7d8f45b0
--- /dev/null
+++ b/test/references/test_projection_2d1d_fem_tpfa_1d.csv
@@ -0,0 +1,13 @@
+2.260713e-01
+6.359015e-01
+9.007574e-01
+9.496593e-01
+7.833929e-01
+4.486884e-01
+1.608432e-16
+-4.486884e-01
+-7.833929e-01
+-9.496593e-01
+-9.007574e-01
+-6.359015e-01
+-2.260713e-01
diff --git a/test/references/test_projection_2d1d_fem_tpfa_2d.csv b/test/references/test_projection_2d1d_fem_tpfa_2d.csv
new file mode 100644
index 0000000000000000000000000000000000000000..c68fe4ee3ec3d32ddb0a96978c898429be32ce48
--- /dev/null
+++ b/test/references/test_projection_2d1d_fem_tpfa_2d.csv
@@ -0,0 +1,121 @@
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+-1.322347e-01
+-3.147229e-01
+-2.684315e-01
+1.457959e-01
+3.607137e-01
+7.450318e-02
+-3.129911e-01
+-2.681667e-01
+1.480351e-01
+3.600225e-01
+2.827186e-01
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
diff --git a/test/references/test_projection_2d1d_tpfa_box_1d.csv b/test/references/test_projection_2d1d_tpfa_box_1d.csv
new file mode 100644
index 0000000000000000000000000000000000000000..d5785cd2cb8508db75fd8205dfc55fe35bce1fce
--- /dev/null
+++ b/test/references/test_projection_2d1d_tpfa_box_1d.csv
@@ -0,0 +1,14 @@
+2.500000e-01
+3.491046e-01
+6.486663e-01
+7.804329e-01
+7.170844e-01
+5.250658e-01
+1.955902e-01
+-1.955902e-01
+-5.250658e-01
+-7.170844e-01
+-7.804329e-01
+-6.486663e-01
+-3.491046e-01
+-2.500000e-01
diff --git a/test/references/test_projection_2d1d_tpfa_box_2d.csv b/test/references/test_projection_2d1d_tpfa_box_2d.csv
new file mode 100644
index 0000000000000000000000000000000000000000..8164eac7cd35d50996d60bf5ff96a41757613ec0
--- /dev/null
+++ b/test/references/test_projection_2d1d_tpfa_box_2d.csv
@@ -0,0 +1,100 @@
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+-1.522871e-01
+-3.678820e-01
+-7.734819e-02
+3.249741e-01
+2.748287e-01
+-1.506308e-01
+-3.730112e-01
+-7.651990e-02
+3.204851e-01
+2.773912e-01
+-1.522871e-01
+-3.678820e-01
+-7.734819e-02
+3.249741e-01
+2.748287e-01
+-1.506308e-01
+-3.730112e-01
+-7.651990e-02
+3.204851e-01
+2.773912e-01
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
diff --git a/test/references/test_projection_2d1d_tpfa_fem_1d.csv b/test/references/test_projection_2d1d_tpfa_fem_1d.csv
new file mode 100644
index 0000000000000000000000000000000000000000..d5785cd2cb8508db75fd8205dfc55fe35bce1fce
--- /dev/null
+++ b/test/references/test_projection_2d1d_tpfa_fem_1d.csv
@@ -0,0 +1,14 @@
+2.500000e-01
+3.491046e-01
+6.486663e-01
+7.804329e-01
+7.170844e-01
+5.250658e-01
+1.955902e-01
+-1.955902e-01
+-5.250658e-01
+-7.170844e-01
+-7.804329e-01
+-6.486663e-01
+-3.491046e-01
+-2.500000e-01
diff --git a/test/references/test_projection_2d1d_tpfa_fem_2d.csv b/test/references/test_projection_2d1d_tpfa_fem_2d.csv
new file mode 100644
index 0000000000000000000000000000000000000000..8164eac7cd35d50996d60bf5ff96a41757613ec0
--- /dev/null
+++ b/test/references/test_projection_2d1d_tpfa_fem_2d.csv
@@ -0,0 +1,100 @@
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+-1.522871e-01
+-3.678820e-01
+-7.734819e-02
+3.249741e-01
+2.748287e-01
+-1.506308e-01
+-3.730112e-01
+-7.651990e-02
+3.204851e-01
+2.773912e-01
+-1.522871e-01
+-3.678820e-01
+-7.734819e-02
+3.249741e-01
+2.748287e-01
+-1.506308e-01
+-3.730112e-01
+-7.651990e-02
+3.204851e-01
+2.773912e-01
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
diff --git a/test/references/test_projection_2d1d_tpfa_tpfa_1d.csv b/test/references/test_projection_2d1d_tpfa_tpfa_1d.csv
new file mode 100644
index 0000000000000000000000000000000000000000..f5b02f90593c584386af839e4869ed8c148fee39
--- /dev/null
+++ b/test/references/test_projection_2d1d_tpfa_tpfa_1d.csv
@@ -0,0 +1,13 @@
+2.500000e-01
+5.331559e-01
+7.163119e-01
+7.935661e-01
+6.545085e-01
+3.309017e-01
+-8.413409e-17
+-3.309017e-01
+-6.545085e-01
+-7.935661e-01
+-7.163119e-01
+-5.331559e-01
+-2.500000e-01
diff --git a/test/references/test_projection_2d1d_tpfa_tpfa_2d.csv b/test/references/test_projection_2d1d_tpfa_tpfa_2d.csv
new file mode 100644
index 0000000000000000000000000000000000000000..dd3b22182425a4c34a5a46e0c41ba8496b927f49
--- /dev/null
+++ b/test/references/test_projection_2d1d_tpfa_tpfa_2d.csv
@@ -0,0 +1,100 @@
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+-1.837861e-01
+-3.788640e-01
+-9.653650e-02
+3.406744e-01
+2.786035e-01
+-1.478096e-01
+-3.870042e-01
+-6.424080e-02
+3.288829e-01
+3.100804e-01
+-1.837861e-01
+-3.788640e-01
+-9.653650e-02
+3.406744e-01
+2.786035e-01
+-1.478096e-01
+-3.870042e-01
+-6.424080e-02
+3.288829e-01
+3.100804e-01
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00
+0.000000e+00