diff --git a/dumux/CMakeLists.txt b/dumux/CMakeLists.txt
index 8b27a751738d8a46aa6be7c793cfafad433a5d6d..291ce498c1b32697a4fa06a7556b489f84246582 100644
--- a/dumux/CMakeLists.txt
+++ b/dumux/CMakeLists.txt
@@ -13,6 +13,7 @@ add_subdirectory(multidomain)
add_subdirectory(nonlinear)
add_subdirectory(parallel)
add_subdirectory(porousmediumflow)
+add_subdirectory(timestepping)
# if Python bindings are enabled, include necessary sub directories.
if(DUNE_ENABLE_PYTHONBINDINGS)
diff --git a/dumux/assembly/CMakeLists.txt b/dumux/assembly/CMakeLists.txt
index 2543a6631f3a61445e1bbb4aeeb8461b93115bd7..63a13d1e43643adf778ad09a48d437e10987d427 100644
--- a/dumux/assembly/CMakeLists.txt
+++ b/dumux/assembly/CMakeLists.txt
@@ -1,4 +1,5 @@
install(FILES
+assembler.hh
boxlocalassembler.hh
boxlocalresidual.hh
cclocalassembler.hh
@@ -10,6 +11,7 @@ fvlocalassemblerbase.hh
fvlocalresidual.hh
initialsolution.hh
jacobianpattern.hh
+localassembler.hh
numericepsilon.hh
partialreassembler.hh
staggeredfvassembler.hh
diff --git a/dumux/assembly/assembler.hh b/dumux/assembly/assembler.hh
new file mode 100644
index 0000000000000000000000000000000000000000..40913644c388834cd6b6e8a37bc187acb9479b79
--- /dev/null
+++ b/dumux/assembly/assembler.hh
@@ -0,0 +1,498 @@
+// -*- 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 Assembly
+ * \brief Assembler class for residuals and jacobian matrices
+ * for grid-based numerical schemes.
+ */
+#ifndef DUMUX_ASSEMBLER_HH
+#define DUMUX_ASSEMBLER_HH
+
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "localassembler.hh"
+#include "jacobianpattern.hh"
+
+namespace Dumux {
+
+//! Default types used for the linear system
+template struct DefaultLinearSystemTraits;
+
+//! Default linear system types for Dune::BlockVector
+template
+struct DefaultLinearSystemTraits>
+{
+private:
+ static constexpr int numEq = PrimaryVariables::size();
+ using Scalar = typename PrimaryVariables::value_type;
+ using MatrixBlockType = Dune::FieldMatrix;
+
+public:
+ using ResidualVector = Dune::BlockVector;
+ using JacobianMatrix = Dune::BCRSMatrix;
+};
+
+/*!
+ * \ingroup Assembly
+ * \brief A linear system assembler (residual and Jacobian) for grid-based numerical schemes
+ * \tparam LO The local operator (evaluation of the terms of the equation)
+ * \tparam diffMethod The differentiation method to compute derivatives
+ * \tparam LST The linear system traits (types used for jacobians and residuals)
+ */
+template< class LO, DiffMethod diffMethod,
+ class LST = DefaultLinearSystemTraits >
+class Assembler
+{
+ using ThisType = Assembler;
+
+ using GG = typename LO::GridVariables::GridGeometry;
+ using GGLocalView = typename GG::LocalView;
+ using GridView = typename GG::GridView;
+ using Element = typename GridView::template Codim<0>::Entity;
+ using ElementVariables = typename LO::GridVariables::LocalView;
+
+public:
+ //! export the types used for the linear system
+ using Scalar = typename LO::GridVariables::Scalar;
+ using JacobianMatrix = typename LST::JacobianMatrix;
+ using ResidualVector = typename LST::ResidualVector;
+ using ResidualType = ResidualVector;
+
+ //! export the local operator type
+ using LocalOperator = LO;
+
+ //! the local operator states the type of variables needed for evaluation
+ using GridVariables = typename LO::GridVariables;
+
+ //! export the underlying grid geometry type
+ using GridGeometry = GG;
+
+ //! export a grid-independent alias of the variables
+ using Variables = GridVariables;
+
+ //! export type used for solution vectors
+ using SolutionVector = typename GridVariables::SolutionVector;
+
+ //! export the type for parameters of a stage in time integration
+ using StageParams = MultiStageParams;
+
+ /*!
+ * \brief The Constructor from a grid geometry.
+ * \param gridGeometry A grid geometry instance
+ * \note This assembler class is, after construction, defined for a specific equation
+ * (given by the template argument of the LocalOperator) and a specific grid
+ * geometry - which defines the connectivity of the degrees of freedom of the
+ * underlying discretization scheme on a particular grid. The evaluation point,
+ * consisting of a particular solution/variables/parameters may vary, and therefore,
+ * an instance of the grid variables is passed to the assembly functions.
+ * \note This constructor (without time integration method) assumes hhat a stationary problem
+ * is assembled, and thus, an implicit jacobian pattern is used.
+ */
+ Assembler(std::shared_ptr gridGeometry)
+ : gridGeometry_(gridGeometry)
+ , isImplicit_(true)
+ {}
+
+ /*!
+ * \brief The Constructor from a grid geometry.
+ * \param gridGeometry A grid geometry instance
+ * \param method the time integration method that will be used.
+ */
+ template
+ Assembler(std::shared_ptr gridGeometry,
+ const TimeIntegrationMethod& method)
+ : gridGeometry_(gridGeometry)
+ , isImplicit_(method.implicit())
+ {}
+
+ /*!
+ * \brief Assembles the Jacobian matrix and the residual around the given evaluation point
+ * which is determined by the grid variables, containing all quantities required
+ * to evaluate the equations to be assembled.
+ * \param gridVariables The variables corresponding to the given solution state
+ * \note We assume the grid geometry on which the grid variables are defined
+ * to be the same as the one used to instantiate this class
+ */
+ template
+ void assembleJacobianAndResidual(const GridVariables& gridVariables,
+ const PartialReassembler* partialReassembler = nullptr)
+ {
+ resetJacobian_(partialReassembler);
+ resetResidual_();
+
+ // TODO: Remove this assert?
+ assert(gridVariables.gridGeometry().numDofs() == gridGeometry().numDofs());
+
+ assemble_([&](const Element& element)
+ {
+ auto ggLocalView = localView(gridGeometry());
+ ggLocalView.bind(element);
+ auto elemVars = this->prepareElemVariables_(gridVariables, element, ggLocalView);
+
+ using LocalAssembler = Dumux::LocalAssembler;
+ LocalAssembler localAssembler(element, ggLocalView, elemVars, stageParams_);
+ localAssembler.assembleJacobianAndResidual(*jacobian_, *residual_, partialReassembler);
+ });
+
+ // TODO: Put these into discretization-specific helpers?
+ enforceDirichletConstraints_(gridVariables, *jacobian_, *residual_);
+ enforceInternalConstraints_(gridVariables, *jacobian_, *residual_);
+ enforcePeriodicConstraints_(gridVariables, *jacobian_, *residual_);
+ }
+
+ /*!
+ * \brief Assembles the Jacobian matrix of the discrete system of equations
+ * around a given state represented by the grid variables object.
+ */
+ void assembleJacobian(const GridVariables& gridVariables)
+ {
+ resetJacobian_();
+
+ // TODO: Remove this assert?
+ assert(gridVariables.gridGeometry().numDofs() == gridGeometry().numDofs());
+
+ assemble_([&](const Element& element)
+ {
+ auto ggLocalView = localView(gridGeometry());
+ ggLocalView.bind(element);
+ auto elemVars = this->prepareElemVariables_(gridVariables, element, ggLocalView);
+
+ using LocalAssembler = Dumux::LocalAssembler;
+ LocalAssembler localAssembler(element, ggLocalView, elemVars, stageParams_);
+ localAssembler.assembleJacobianAndResidual(*jacobian_);
+ });
+
+ // TODO: Put these into discretization-specific helpers?
+ enforceDirichletConstraints_(gridVariables, *jacobian_, *residual_);
+ enforceInternalConstraints_(gridVariables, *jacobian_, *residual_);
+ enforcePeriodicConstraints_(gridVariables, *jacobian_, *residual_);
+ }
+
+ /*!
+ * \brief Assembles the residual for a given state represented by the provided
+ * grid variables object, using the internal residual vector to store the result.
+ */
+ void assembleResidual(const GridVariables& gridVariables)
+ {
+ resetResidual_();
+ assembleResidual(*residual_, gridVariables);
+ }
+
+ /*!
+ * \brief Assembles the residual for a given state represented by the provided
+ * grid variables object, using the provided residual vector to store the result.
+ */
+ void assembleResidual(ResidualVector& r, const GridVariables& gridVariables) const
+ {
+ assemble_([&](const Element& element)
+ {
+ auto ggLocalView = localView(gridGeometry());
+ ggLocalView.bind(element);
+ auto elemVars = this->prepareElemVariables_(gridVariables, element, ggLocalView);
+
+ using LocalAssembler = Dumux::LocalAssembler;
+ LocalAssembler localAssembler(element, ggLocalView, elemVars, stageParams_);
+ localAssembler.assembleResidual(r);
+ });
+ }
+
+ //! Will become obsolete once the new linear solvers are available
+ Scalar residualNorm(const GridVariables& gridVars) const
+ {
+ ResidualType residual(numDofs());
+ assembleResidual(residual, gridVars);
+
+ // for box communicate the residual with the neighboring processes
+ if (GridGeometry::discMethod == DiscretizationMethod::box && gridView().comm().size() > 1)
+ {
+ using VertexMapper = typename GridGeometry::VertexMapper;
+ VectorCommDataHandleSum
+ sumResidualHandle(gridGeometry_->vertexMapper(), residual);
+ gridView().communicate(sumResidualHandle,
+ Dune::InteriorBorder_InteriorBorder_Interface,
+ Dune::ForwardCommunication);
+ }
+
+ // calculate the square norm of the residual
+ Scalar result2 = residual.two_norm2();
+ if (gridView().comm().size() > 1)
+ result2 = gridView().comm().sum(result2);
+
+ using std::sqrt;
+ return sqrt(result2);
+ }
+
+
+ /*!
+ * \brief Tells the assembler which jacobian and residual to use.
+ * This also resizes the containers to the required sizes and sets the
+ * sparsity pattern of the jacobian matrix.
+ */
+ void setLinearSystem(std::shared_ptr A,
+ std::shared_ptr r)
+ {
+ jacobian_ = A;
+ residual_ = r;
+
+ // check and/or set the BCRS matrix's build mode
+ if (jacobian_->buildMode() == JacobianMatrix::BuildMode::unknown)
+ jacobian_->setBuildMode(JacobianMatrix::random);
+ else if (jacobian_->buildMode() != JacobianMatrix::BuildMode::random)
+ DUNE_THROW(Dune::NotImplemented, "Only BCRS matrices with random build mode are supported at the moment");
+
+ setJacobianPattern();
+ setResidualSize();
+ }
+
+ /*!
+ * \brief The version without arguments uses the default constructor to create
+ * the jacobian and residual objects in this assembler if you don't need them outside this class
+ */
+ void setLinearSystem()
+ {
+ jacobian_ = std::make_shared();
+ jacobian_->setBuildMode(JacobianMatrix::random);
+ residual_ = std::make_shared();
+
+ setJacobianPattern();
+ setResidualSize();
+ }
+
+ /*!
+ * \brief Resizes the jacobian and sets the jacobian' sparsity pattern.
+ */
+ void setJacobianPattern()
+ {
+ // resize the jacobian and the residual
+ const auto numDofs = this->numDofs();
+ jacobian_->setSize(numDofs, numDofs);
+
+ // create occupation pattern of the jacobian
+ // TODO: Does the bool need to be at compile time in getJacobianPattern?
+ const auto occupationPattern = isImplicit_ ? getJacobianPattern(gridGeometry())
+ : getJacobianPattern(gridGeometry());
+
+ // export pattern to jacobian
+ occupationPattern.exportIdx(*jacobian_);
+ }
+
+ /*!
+ * \brief Prepare for a new stage within a time integration step.
+ * This caches the given grid variables, which are then used as a
+ * representation of the previous stage. Moreover, the given grid
+ * variables are then updated to the time level of the upcoming stage.
+ * \param gridVars the grid variables representing the previous stage
+ * \param params the parameters with the weights to be used in the upcoming stage
+ * \todo TODO: This function does two things, namely caching and then updating.
+ * Should we split/delegate this, or is the current name descriptive enough?
+ * When used from outside, one would expect the gridvars to be prepared maybe,
+ * and that is what's done. Caching might not be expected from the outside but
+ * it is also not important that that is known from there?
+ */
+ void prepareStage(GridVariables& gridVars,
+ std::shared_ptr params)
+ {
+ stageParams_ = params;
+ const auto curStage = params->size() - 1;
+
+ // we keep track of previous stages, they are needed for residual assembly
+ prevStageVariables_.push_back(gridVars);
+
+ // Now we update the time level of the given grid variables
+ const auto t = params->timeAtStage(curStage);
+ const auto prevT = params->timeAtStage(0);
+ const auto dtFraction = params->timeStepFraction(curStage);
+ TimeLevel timeLevel(t, prevT, dtFraction);
+
+ gridVars.updateTime(timeLevel);
+ }
+
+ /*!
+ * \brief Remove traces from stages within a time integration step.
+ */
+ void clearStages()
+ {
+ prevStageVariables_.clear();
+ stageParams_ = nullptr;
+ }
+
+ //! Resizes the residual
+ void setResidualSize()
+ { residual_->resize(numDofs()); }
+
+ //! Returns the number of degrees of freedom
+ std::size_t numDofs() const
+ { return gridGeometry().numDofs(); }
+
+ //! The global finite volume geometry
+ const GridGeometry& gridGeometry() const
+ { return *gridGeometry_; }
+
+ //! The gridview
+ const GridView& gridView() const
+ { return gridGeometry().gridView(); }
+
+ //! The jacobian matrix
+ JacobianMatrix& jacobian()
+ { return *jacobian_; }
+
+ //! The residual vector (rhs)
+ ResidualVector& residual()
+ { return *residual_; }
+
+protected:
+ // reset the residual vector to 0.0
+ void resetResidual_()
+ {
+ if (!residual_)
+ {
+ residual_ = std::make_shared();
+ setResidualSize();
+ }
+
+ (*residual_) = 0.0;
+ }
+
+ // reset the Jacobian matrix to 0.0
+ template
+ void resetJacobian_(const PartialReassembler *partialReassembler = nullptr)
+ {
+ if (!jacobian_)
+ {
+ jacobian_ = std::make_shared();
+ jacobian_->setBuildMode(JacobianMatrix::random);
+ setJacobianPattern();
+ }
+
+ if (partialReassembler)
+ partialReassembler->resetJacobian(*this);
+ else
+ *jacobian_ = 0.0;
+ }
+
+ /*!
+ * \brief A method assembling something per element
+ * \note Handles exceptions for parallel runs
+ * \throws NumericalProblem on all processes if something throwed during assembly
+ */
+ template
+ void assemble_(AssembleElementFunc&& assembleElement) const
+ {
+ // a state that will be checked on all processes
+ bool succeeded = false;
+
+ // try assembling using the local assembly function
+ try
+ {
+ // let the local assembler add the element contributions
+ for (const auto& element : elements(gridView()))
+ assembleElement(element);
+
+ // if we get here, everything worked well on this process
+ succeeded = true;
+ }
+ // throw exception if a problem ocurred
+ catch (NumericalProblem &e)
+ {
+ std::cout << "rank " << gridView().comm().rank()
+ << " caught an exception while assembling:" << e.what()
+ << "\n";
+ succeeded = false;
+ }
+
+ // make sure everything worked well on all processes
+ if (gridView().comm().size() > 1)
+ succeeded = gridView().comm().min(succeeded);
+
+ // if not succeeded rethrow the error on all processes
+ if (!succeeded)
+ DUNE_THROW(NumericalProblem, "A process did not succeed in linearizing the system");
+ }
+
+ void enforceDirichletConstraints_(const GridVariables& gridVars, JacobianMatrix& jac, SolutionVector& res)
+ { /*TODO: Implement / where to put this? Currently, local assemblers do this*/ }
+
+ void enforceInternalConstraints_(const GridVariables& gridVars, JacobianMatrix& jac, SolutionVector& res)
+ { /*TODO: Implement / where to put this? Currently, local assemblers do this*/ }
+
+ void enforcePeriodicConstraints_(const GridVariables& gridVars, JacobianMatrix& jac, SolutionVector& res)
+ { /*TODO: Implement / where to put this? Currently, local assemblers do this*/ }
+
+ //! prepares the local views on the grid variables for the given element
+ //! \todo: TODO: when stageparams.skipSpatial() == true, we don't need to bind flux vars caches!
+ std::vector prepareElemVariables_(const GridVariables& gridVars,
+ const Element& element,
+ const GGLocalView& ggLocalView) const
+ {
+ if (!stageParams_)
+ {
+ auto elemVars = localView(gridVars);
+ elemVars.bind(element, ggLocalView);
+ return {elemVars};
+ }
+ else
+ {
+ std::vector elemVars;
+ elemVars.reserve(stageParams_->size());
+
+ for (int i = 0; i < stageParams_->size()-1; ++i)
+ elemVars.emplace_back(prevStageVariables_[i]);
+ elemVars.emplace_back(gridVars);
+
+ for (auto& evv : elemVars)
+ evv.bind(element, ggLocalView);
+
+ return elemVars;
+ }
+ }
+
+private:
+ //! the grid geometry on which it is assembled
+ std::shared_ptr gridGeometry_;
+
+ //! states if an implicit of explicit scheme is used (affects jacobian pattern)
+ bool isImplicit_;
+
+ //! shared pointers to the jacobian matrix and residual
+ std::shared_ptr jacobian_;
+ std::shared_ptr residual_;
+
+ //! parameters containing information on the current stage of time integration
+ std::shared_ptr stageParams_ = nullptr;
+
+ //! keep track of the states of previous stages within a time integration step
+ std::vector prevStageVariables_;
+};
+
+} // namespace Dumux
+
+#endif
diff --git a/dumux/assembly/fv/CMakeLists.txt b/dumux/assembly/fv/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..40d6910f65323ddab8e818e6c6739d5fdf9c9ace
--- /dev/null
+++ b/dumux/assembly/fv/CMakeLists.txt
@@ -0,0 +1,5 @@
+install(FILES
+boxlocalassembler.hh
+localoperator.hh
+operators.hh
+DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dumux/assembly/fv)
diff --git a/dumux/assembly/fv/boxlocalassembler.hh b/dumux/assembly/fv/boxlocalassembler.hh
new file mode 100644
index 0000000000000000000000000000000000000000..80fd526a1cf362e66d957e8b659005f841681003
--- /dev/null
+++ b/dumux/assembly/fv/boxlocalassembler.hh
@@ -0,0 +1,421 @@
+// -*- 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 Assembly
+ * \brief An assembler for Jacobian and residual contribution
+ * per element for the box scheme.
+ */
+#ifndef DUMUX_BOX_LOCAL_ASSEMBLER_HH
+#define DUMUX_BOX_LOCAL_ASSEMBLER_HH
+
+#include
+#include
+
+#include
+
+#include
+#include
+
+#include
+#include
+
+
+namespace Dumux {
+
+/*!
+ * \ingroup Assembly
+ * \brief An assembler for Jacobian and residual contribution
+ * per element for the box scheme.
+ * \tparam Assembler The grid-wide assembler type
+ */
+template
+class BoxLocalAssembler
+{
+ using GridVariables = typename Assembler::GridVariables;
+ using GridGeometry = typename GridVariables::GridGeometry;
+
+ using FVElementGeometry = typename GridGeometry::LocalView;
+ using ElementVariables = typename GridVariables::LocalView;
+ using PrimaryVariables = typename GridVariables::PrimaryVariables;
+ using Scalar = typename GridVariables::Scalar;
+
+ using GridView = typename GridGeometry::GridView;
+ using Element = typename GridView::template Codim<0>::Entity;
+
+ using LocalOperator = typename Assembler::LocalOperator;
+ using JacobianMatrix = typename Assembler::JacobianMatrix;
+ using ResidualVector = typename Assembler::ResidualVector;
+
+ static constexpr int numEq = PrimaryVariables::size();
+ static_assert(diffMethod == DiffMethod::numeric, "Analytical assembly not implemented");
+ static_assert(numEq == JacobianMatrix::block_type::cols, "Matrix block size doesn't match privars size");
+
+ //! the parameters of a stage in time integration
+ using StageParams = MultiStageParams;
+
+public:
+ using ElementResidualVector = typename LocalOperator::ElementResidualVector;
+
+ /*!
+ * \brief Constructor for stationary problems.
+ */
+ explicit BoxLocalAssembler(const Element& element,
+ const FVElementGeometry& fvGeometry,
+ std::vector& elemVars)
+ : element_(element)
+ , fvGeometry_(fvGeometry)
+ , elemVariables_(elemVars)
+ , elementIsGhost_(element.partitionType() == Dune::GhostEntity)
+ , stageParams_(nullptr)
+ {
+ assert(elemVars.size() == 1);
+ }
+
+ /*!
+ * \brief Constructor for instationary problems.
+ * \note Using this constructor, we assemble one stage within
+ * a time integration step using multi-stage methods.
+ */
+ explicit BoxLocalAssembler(const Element& element,
+ const FVElementGeometry& fvGeometry,
+ std::vector& elemVars,
+ std::shared_ptr stageParams)
+ : element_(element)
+ , fvGeometry_(fvGeometry)
+ , elemVariables_(elemVars)
+ , elementIsGhost_(element.partitionType() == Dune::GhostEntity)
+ , stageParams_(stageParams)
+ {}
+
+ /*!
+ * \brief Computes the derivatives with respect to the given element and adds
+ * them to the global matrix. The element residual is written into the
+ * right hand side.
+ */
+ template
+ void assembleJacobianAndResidual(JacobianMatrix& jac,
+ ResidualVector& res,
+ const PartialReassembler* partialReassembler = nullptr)
+ {
+ const auto eIdxGlobal = fvGeometry().gridGeometry().elementMapper().index(element());
+
+ if (partialReassembler && partialReassembler->elementColor(eIdxGlobal) == EntityColor::green)
+ {
+ const auto residual = evalLocalResidual();
+ for (const auto& scv : scvs(fvGeometry()))
+ res[scv.dofIndex()] += residual[scv.localDofIndex()];
+ }
+ else if (!elementIsGhost_)
+ {
+ const auto residual = assembleJacobianAndResidual_(jac, partialReassembler);
+ for (const auto& scv : scvs(fvGeometry()))
+ res[scv.dofIndex()] += residual[scv.localDofIndex()];
+ }
+ else
+ {
+ static constexpr int dim = GridView::dimension;
+
+ for (const auto& scv : scvs(fvGeometry()))
+ {
+ const auto vIdxLocal = scv.indexInElement();
+ const auto& v = element().template subEntity(vIdxLocal);
+
+ // do not change the non-ghost vertices
+ if (v.partitionType() == Dune::InteriorEntity ||
+ v.partitionType() == Dune::BorderEntity)
+ continue;
+
+ const auto dofIdx = scv.dofIndex();
+ res[dofIdx] = 0;
+ for (int pvIdx = 0; pvIdx < numEq; ++pvIdx)
+ jac[dofIdx][dofIdx][pvIdx][pvIdx] = 1.0;
+ }
+ }
+
+ auto applyDirichlet = [&] (const auto& scvI,
+ const auto& dirichletValues,
+ const auto eqIdx,
+ const auto pvIdx)
+ {
+ res[scvI.dofIndex()][eqIdx] = elemVariables().elemVolVars()[scvI].priVars()[pvIdx] - dirichletValues[pvIdx];
+
+ auto& row = jac[scvI.dofIndex()];
+ for (auto col = row.begin(); col != row.end(); ++col)
+ row[col.index()][eqIdx] = 0.0;
+
+ jac[scvI.dofIndex()][scvI.dofIndex()][eqIdx][pvIdx] = 1.0;
+
+ // if a periodic dof has Dirichlet values also apply the same Dirichlet values to the other dof
+ if (fvGeometry().gridGeometry().dofOnPeriodicBoundary(scvI.dofIndex()))
+ {
+ const auto periodicDof = fvGeometry().gridGeometry().periodicallyMappedDof(scvI.dofIndex());
+ res[periodicDof][eqIdx] = elemVariables().elemVolVars()[scvI].priVars()[pvIdx] - dirichletValues[pvIdx];
+ const auto end = jac[periodicDof].end();
+ for (auto it = jac[periodicDof].begin(); it != end; ++it)
+ (*it) = periodicDof != it.index() ? 0.0 : 1.0;
+ }
+ };
+
+ enforceDirichletConstraints(applyDirichlet);
+ }
+
+ /*!
+ * \brief Computes the derivatives with respect to the given element and adds them
+ * to the global matrix.
+ */
+ void assembleJacobian(JacobianMatrix& jac)
+ {
+ assembleJacobianAndResidual_(jac);
+
+ auto applyDirichlet = [&] (const auto& scvI,
+ const auto& dirichletValues,
+ const auto eqIdx,
+ const auto pvIdx)
+ {
+ auto& row = jac[scvI.dofIndex()];
+ for (auto col = row.begin(); col != row.end(); ++col)
+ row[col.index()][eqIdx] = 0.0;
+
+ jac[scvI.dofIndex()][scvI.dofIndex()][eqIdx][pvIdx] = 1.0;
+ };
+
+ enforceDirichletConstraints(applyDirichlet);
+ }
+
+ /*!
+ * \brief Assemble the residual only
+ */
+ void assembleResidual(ResidualVector& res)
+ {
+ const auto residual = evalLocalResidual();
+ for (const auto& scv : scvs(fvGeometry()))
+ res[scv.dofIndex()] += residual[scv.localDofIndex()];
+
+ auto applyDirichlet = [&] (const auto& scvI,
+ const auto& dirichletValues,
+ const auto eqIdx,
+ const auto pvIdx)
+ {
+ res[scvI.dofIndex()][eqIdx] = elemVariables().elemVolVars()[scvI].priVars()[pvIdx] - dirichletValues[pvIdx];
+ };
+
+ enforceDirichletConstraints(applyDirichlet);
+ }
+
+ //! Enforce Dirichlet constraints
+ template
+ void enforceDirichletConstraints(const ApplyFunction& applyDirichlet)
+ {
+ // enforce Dirichlet boundary conditions
+ evalDirichletBoundaries(applyDirichlet);
+ // TODO: take care of internal Dirichlet constraints (if enabled)
+ // enforceInternalDirichletConstraints(applyDirichlet);
+ }
+
+ /*!
+ * \brief Evaluates Dirichlet boundaries
+ */
+ template< typename ApplyDirichletFunctionType >
+ void evalDirichletBoundaries(ApplyDirichletFunctionType applyDirichlet)
+ {
+ for (const auto& scvI : scvs(fvGeometry()))
+ {
+ const auto bcTypes = problem().boundaryTypes(element(), scvI);
+ if (bcTypes.hasDirichlet())
+ {
+ const auto dirichletValues = problem().dirichlet(element(), scvI);
+
+ // set the Dirichlet conditions in residual and jacobian
+ for (int eqIdx = 0; eqIdx < numEq; ++eqIdx)
+ {
+ if (bcTypes.isDirichlet(eqIdx))
+ {
+ const auto pvIdx = bcTypes.eqToDirichletIndex(eqIdx);
+ assert(0 <= pvIdx && pvIdx < numEq);
+ applyDirichlet(scvI, dirichletValues, eqIdx, pvIdx);
+ }
+ }
+ }
+ }
+ }
+
+protected:
+
+ /*!
+ * \brief Evaluate the complete local residual for the current element.
+ */
+ ElementResidualVector evalLocalResidual() const
+ {
+ if (isStationary())
+ {
+ LocalOperator localOperator(element(), fvGeometry(), elemVariables());
+ return elementIsGhost_ ? localOperator.getEmptyResidual()
+ : localOperator.evalFluxesAndSources();
+ }
+ else
+ {
+ ElementResidualVector residual(fvGeometry().numScv());
+ residual = 0.0;
+
+ for (std::size_t k = 0; k < stageParams_->size(); ++k)
+ {
+ LocalOperator localOperator(element(), fvGeometry(), elemVariables_[k]);
+
+ if (!stageParams_->skipTemporal(k))
+ residual.axpy(stageParams_->temporalWeight(k), localOperator.evalStorage());
+ if (!stageParams_->skipSpatial(k))
+ residual.axpy(stageParams_->spatialWeight(k), localOperator.evalFluxesAndSources());
+ }
+
+ return residual;
+ }
+ }
+
+ /*!
+ * \brief Computes the derivatives with respect to the dofs of the given
+ * element and adds them to the global matrix.
+ * \return The element residual at the current solution.
+ */
+ template< class PartialReassembler = DefaultPartialReassembler >
+ ElementResidualVector assembleJacobianAndResidual_(JacobianMatrix& A,
+ const PartialReassembler* partialReassembler = nullptr)
+ {
+ if constexpr (diffMethod == DiffMethod::numeric)
+ return assembleJacobianAndResidualNumeric_(A, partialReassembler);
+ else
+ DUNE_THROW(Dune::NotImplemented, "Analytic assembler for box");
+ }
+
+ /*!
+ * \brief Computes the derivatives by means of numeric differentiation
+ * and adds them to the global matrix.
+ * \return The element residual at the current solution.
+ * \note This specialization is for the box scheme with numeric differentiation
+ */
+ template< class PartialReassembler = DefaultPartialReassembler >
+ ElementResidualVector assembleJacobianAndResidualNumeric_(JacobianMatrix& A,
+ const PartialReassembler* partialReassembler = nullptr)
+ {
+ // get the variables of the current stage
+ auto& curVariables = elemVariables();
+ auto& curElemVolVars = curVariables.elemVolVars();
+ const auto& x = curVariables.gridVariables().dofs();
+
+ const auto origResiduals = evalLocalResidual();
+ const auto origElemSol = elementSolution(element(), x, fvGeometry().gridGeometry());
+ auto elemSol = origElemSol;
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ // Calculate derivatives of the residual of all dofs in element with respect to themselves. //
+ //////////////////////////////////////////////////////////////////////////////////////////////
+
+ ElementResidualVector partialDerivs(fvGeometry().numScv());
+ for (const auto& scvI : scvs(fvGeometry()))
+ {
+ // dof index and corresponding actual pri vars
+ const auto globalI = scvI.dofIndex();
+ const auto localI = scvI.localDofIndex();
+
+ const auto origCurVolVars = curElemVolVars[scvI];
+ auto& curVolVars = curElemVolVars[scvI];
+
+ // calculate derivatives w.r.t to the privars at the dof at hand
+ for (int pvIdx = 0; pvIdx < numEq; pvIdx++)
+ {
+ partialDerivs = 0.0;
+ auto evalResiduals = [&](Scalar priVar)
+ {
+ // update the volume variables and compute element residual
+ elemSol[scvI.localDofIndex()][pvIdx] = priVar;
+ curVolVars.update(elemSol, problem(), element(), scvI);
+ return evalLocalResidual();
+ };
+
+ // derive the residuals numerically
+ static const NumericEpsilon eps_{problem().paramGroup()};
+ static const int numDiffMethod = getParamFromGroup(problem().paramGroup(), "Assembly.NumericDifferenceMethod");
+ NumericDifferentiation::partialDerivative(evalResiduals, elemSol[localI][pvIdx], partialDerivs,
+ origResiduals, eps_(elemSol[localI][pvIdx], pvIdx),
+ numDiffMethod);
+
+ // TODO: Distinguish between implicit/explicit here. For explicit schemes,
+ // no entries between different scvs of an element are reserved. Thus,
+ // we currently get an error when using explicit schemes.
+ // TODO: Doesn't this mean we only have to evaluate the residual for a single
+ // scv instead of calling evalLocalResidual()? That computes the residuals
+ // and derivs for all other scvs of the element, too, which are never used.
+ // Note: this is the same in the current implementation of master.
+ // Should we try to optimize this for explicit schemes? Or adjust the Jacobian pattern?
+ // update the global stiffness matrix with the current partial derivatives
+ for (const auto& scvJ : scvs(fvGeometry()))
+ {
+ const auto globalJ = scvJ.dofIndex();
+ const auto localJ = scvJ.localDofIndex();
+
+ // don't add derivatives for green entities
+ if (!partialReassembler || partialReassembler->dofColor(globalJ) != EntityColor::green)
+ {
+ for (int eqIdx = 0; eqIdx < numEq; eqIdx++)
+ {
+ // A[i][col][eqIdx][pvIdx] is the rate of change of the
+ // the residual of equation 'eqIdx' at dof 'i'
+ // depending on the primary variable 'pvIdx' at dof 'col'
+ A[globalJ][globalI][eqIdx][pvIdx] += partialDerivs[localJ][eqIdx];
+ }
+ }
+ }
+
+ // restore the original element solution & volume variables
+ elemSol[localI][pvIdx] = origElemSol[localI][pvIdx];
+ curVolVars = origCurVolVars;
+
+ // TODO additional dof dependencies
+ }
+ }
+
+ return origResiduals;
+ }
+
+ //! Return references to the local views
+ const Element& element() const { return element_; }
+ const FVElementGeometry& fvGeometry() const { return fvGeometry_; }
+ const ElementVariables& elemVariables() const { return elemVariables_.back(); }
+ ElementVariables& elemVariables() { return elemVariables_.back(); }
+
+ //! Returns if a stationary problem is assembled
+ bool isStationary() const { return !stageParams_; }
+
+ //! Return a reference to the underlying problem
+ //! TODO: Should grid vars return problem directly!?
+ const auto& problem() const
+ { return elemVariables().gridVariables().gridVolVars().problem(); }
+
+private:
+ const Element& element_;
+ const FVElementGeometry& fvGeometry_;
+ std::vector& elemVariables_;
+
+ bool elementIsGhost_;
+ std::shared_ptr stageParams_;
+};
+
+} // end namespace Dumux
+
+#endif
diff --git a/dumux/assembly/fv/cclocalassembler.hh b/dumux/assembly/fv/cclocalassembler.hh
new file mode 100644
index 0000000000000000000000000000000000000000..a1f92a3334c7dab1ccb2f609af897a23c19ce2ce
--- /dev/null
+++ b/dumux/assembly/fv/cclocalassembler.hh
@@ -0,0 +1,412 @@
+// -*- 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 Assembly
+ * \brief An assembler for Jacobian and residual contribution
+ * per element for cell-centered schemes.
+ */
+#ifndef DUMUX_CC_LOCAL_ASSEMBLER_HH
+#define DUMUX_CC_LOCAL_ASSEMBLER_HH
+
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+
+namespace Dumux {
+
+/*!
+ * \ingroup Assembly
+ * \brief An assembler for Jacobian and residual contribution
+ * per element for cell-centered schemes.
+ * \tparam Assembler The grid-wide assembler type
+ */
+template
+class CCLocalAssembler
+{
+ using GridVariables = typename Assembler::GridVariables;
+ using GridGeometry = typename GridVariables::GridGeometry;
+
+ using FVElementGeometry = typename GridGeometry::LocalView;
+ using ElementVariables = typename GridVariables::LocalView;
+ using PrimaryVariables = typename GridVariables::PrimaryVariables;
+ using Scalar = typename GridVariables::Scalar;
+
+ using GridView = typename GridGeometry::GridView;
+ using Element = typename GridView::template Codim<0>::Entity;
+
+ using JacobianMatrix = typename Assembler::JacobianMatrix;
+ using ResidualVector = typename Assembler::ResidualVector;
+ using LocalOperator = typename Assembler::LocalOperator;
+ using Operators = typename LocalOperator::Operators;
+ using NumEqVector = typename Operators::NumEqVector;
+
+ static constexpr int numEq = PrimaryVariables::size();
+ static constexpr int maxElementStencilSize = GridGeometry::maxElementStencilSize;
+ static_assert(diffMethod == DiffMethod::numeric, "Analytical assembly not implemented");
+ static_assert(numEq == JacobianMatrix::block_type::cols, "Matrix block size doesn't match privars size");
+
+ //! the parameters of a stage in time integration
+ using StageParams = MultiStageParams;
+
+public:
+ using ElementResidualVector = typename LocalOperator::ElementResidualVector;
+
+ /*!
+ * \brief Constructor for stationary problems.
+ */
+ explicit CCLocalAssembler(const Element& element,
+ const FVElementGeometry& fvGeometry,
+ std::vector& elemVars)
+ : element_(element)
+ , fvGeometry_(fvGeometry)
+ , elementVariables_(elemVars)
+ , elementIsGhost_(element.partitionType() == Dune::GhostEntity)
+ , stageParams_(nullptr)
+ {
+ assert(elemVars.size() == 1);
+ assert(fvGeometry_.numScv() == 1);
+ }
+
+ /*!
+ * \brief Constructor for instationary problems.
+ * \note Using this constructor, we assemble one stage within
+ * a time integration step using multi-stage methods.
+ */
+ explicit CCLocalAssembler(const Element& element,
+ const FVElementGeometry& fvGeometry,
+ std::vector& elemVars,
+ std::shared_ptr stageParams)
+ : element_(element)
+ , fvGeometry_(fvGeometry)
+ , elementVariables_(elemVars)
+ , elementIsGhost_(element.partitionType() == Dune::GhostEntity)
+ , stageParams_(stageParams)
+ { assert(fvGeometry_.numScv() == 1); }
+
+ /*!
+ * \brief Computes the derivatives with respect to the given element and adds
+ * them to the global matrix. The element residual is written into the
+ * right hand side.
+ */
+ template
+ void assembleJacobianAndResidual(JacobianMatrix& jac,
+ ResidualVector& res,
+ const PartialReassembler* partialReassembler = nullptr)
+ {
+ const auto globalI = fvGeometry().gridGeometry().elementMapper().index(element());
+ if (partialReassembler
+ && partialReassembler->elementColor(globalI) == EntityColor::green)
+ {
+ res[globalI] = evalLocalResidual()[0]; // forward to the internal implementation
+ }
+ else
+ {
+ res[globalI] = assembleJacobianAndResidual_(jac); // forward to the internal implementation
+ }
+ }
+
+ /*!
+ * \brief Computes the derivatives with respect to the given element and adds them
+ * to the global matrix.
+ */
+ void assembleJacobian(JacobianMatrix& jac)
+ {
+ assembleJacobianAndResidual_(jac);
+ }
+
+ /*!
+ * \brief Assemble the residual only
+ */
+ void assembleResidual(ResidualVector& res)
+ {
+ const auto residual = evalLocalResidual();
+ for (const auto& scv : scvs(fvGeometry()))
+ res[scv.dofIndex()] += residual[scv.localDofIndex()];
+ }
+
+protected:
+
+ /*!
+ * \brief Evaluate the complete local residual for the current element.
+ */
+ ElementResidualVector evalLocalResidual() const
+ {
+ if (isStationary())
+ {
+ LocalOperator localOperator(element(), fvGeometry(), elemVariables());
+ return elementIsGhost_ ? localOperator.getEmptyResidual()
+ : localOperator.evalFluxesAndSources();
+ }
+ else
+ {
+ ElementResidualVector residual(fvGeometry().numScv());
+ residual = 0.0;
+
+ for (std::size_t k = 0; k < stageParams_->size(); ++k)
+ {
+ LocalOperator localOperator(element(), fvGeometry(), elementVariables_[k]);
+
+ if (!stageParams_->skipTemporal(k))
+ residual.axpy(stageParams_->temporalWeight(k), localOperator.evalStorage());
+ if (!stageParams_->skipSpatial(k))
+ residual.axpy(stageParams_->spatialWeight(k), localOperator.evalFluxesAndSources());
+ }
+
+ return residual;
+ }
+ }
+
+ /*!
+ * \brief Computes the derivatives with respect to the dofs of the given
+ * element and adds them to the global matrix.
+ * \return The element residual at the current solution.
+ */
+ NumEqVector assembleJacobianAndResidual_(JacobianMatrix& A)
+ {
+ if constexpr (diffMethod == DiffMethod::numeric)
+ return assembleJacobianAndResidualNumeric_(A);
+ else
+ DUNE_THROW(Dune::NotImplemented, "Analytic assembler for cc schemes");
+ }
+
+ /*!
+ * \brief Computes the derivatives by means of numeric differentiation
+ * and adds them to the global matrix.
+ * \return The element residual at the current solution.
+ * \note This specialization is for numeric differentiation
+ */
+ NumEqVector assembleJacobianAndResidualNumeric_(JacobianMatrix& A)
+ {
+ using Problem = std::decay_t;
+
+ // get the variables of the current stage
+ auto& curVariables = elemVariables_();
+ auto& curElemVolVars = curVariables.elemVolVars();
+ auto& curElemFluxVarsCache = curVariables.elemFluxVarsCache();
+ const auto& x = curVariables.gridVariables().dofs();
+
+ // get stencil informations
+ const auto& gridGeometry = fvGeometry().gridGeometry();
+ const auto& connectivityMap = gridGeometry.connectivityMap();
+ const auto globalI = gridGeometry.elementMapper().index(element_);
+ const auto numNeighbors = connectivityMap[globalI].size();
+
+ // container to store the neighboring elements
+ Dune::ReservedVector neighborElements;
+ neighborElements.resize(numNeighbors);
+
+ // assemble the undeflected residual
+ using Residuals = ReservedBlockVector;
+ Residuals origResiduals(numNeighbors + 1); origResiduals = 0.0;
+ origResiduals[0] = evalLocalResidual()[0];
+
+ // lambda for convenient evaluation of the fluxes across scvfs in the neighbors
+ // if the neighbor is a ghost we don't want to add anything to their residual
+ // so we return 0 and omit computing the flux
+ auto evalNeighborFlux = [&] (const auto& neighbor, const auto& scvf)
+ {
+ if (neighbor.partitionType() == Dune::GhostEntity)
+ return NumEqVector(0.0);
+ else
+ return Operators::flux(problem(), neighbor, fvGeometry(),
+ curElemVolVars, curElemFluxVarsCache, scvf);
+ };
+
+ // get the elements in which we need to evaluate the fluxes
+ // and calculate these in the undeflected state
+ unsigned int j = 1;
+ for (const auto& dataJ : connectivityMap[globalI])
+ {
+ neighborElements[j-1] = gridGeometry.element(dataJ.globalJ);
+ for (const auto scvfIdx : dataJ.scvfsJ)
+ origResiduals[j] += evalNeighborFlux(neighborElements[j-1], fvGeometry().scvf(scvfIdx));
+ ++j;
+ }
+
+ // reference to the element's scv (needed later) and corresponding vol vars
+ // TODO: Support the case of caching?!
+ const auto& scv = fvGeometry().scv(globalI);
+ auto& curVolVars = curElemVolVars[scv];
+
+ // save a copy of the original privars and vol vars in order
+ // to restore the original solution after deflection
+ const auto origPriVars = x[globalI];
+ const auto origVolVars = curVolVars;
+
+ // element solution container to be deflected
+ auto elemSol = elementSolution(element_, x, gridGeometry);
+
+ // derivatives in the neighbors with repect to the current elements
+ // in index 0 we save the derivative of the element residual with respect to it's own dofs
+ Residuals partialDerivs(numNeighbors + 1);
+ for (int pvIdx = 0; pvIdx < numEq; ++pvIdx)
+ {
+ partialDerivs = 0.0;
+
+ auto evalResiduals = [&](Scalar priVar)
+ {
+ Residuals partialDerivsTmp(numNeighbors + 1);
+ partialDerivsTmp = 0.0;
+
+ // update the volume variables and the flux var cache
+ elemSol[0][pvIdx] = priVar;
+ curVolVars.update(elemSol, problem(), element_, scv);
+ curElemFluxVarsCache.update(element_, fvGeometry(), curElemVolVars);
+ // TODO: UPDATE GLOBAL FLUX VARS CACHE HERE?
+
+ // calculate the residual with the deflected primary variables
+ partialDerivsTmp[0] = evalLocalResidual()[0];
+
+ // calculate the fluxes in the neighbors with the deflected primary variables
+ for (std::size_t k = 0; k < numNeighbors; ++k)
+ for (auto scvfIdx : connectivityMap[globalI][k].scvfsJ)
+ partialDerivsTmp[k+1] += evalNeighborFlux(neighborElements[k], fvGeometry().scvf(scvfIdx));
+
+ return partialDerivsTmp;
+ };
+
+ // derive the residuals numerically
+ static const NumericEpsilon eps_{problem().paramGroup()};
+ static const int numDiffMethod = getParamFromGroup(problem().paramGroup(), "Assembly.NumericDifferenceMethod");
+ NumericDifferentiation::partialDerivative(evalResiduals, elemSol[0][pvIdx], partialDerivs, origResiduals,
+ eps_(elemSol[0][pvIdx], pvIdx), numDiffMethod);
+
+ // Correct derivative for ghost elements, i.e. set a 1 for the derivative w.r.t. the
+ // current primary variable and a 0 elsewhere. As we always solve for a delta of the
+ // solution with repect to the initial one, this results in a delta of 0 for ghosts.
+ if (elementIsGhost_)
+ {
+ partialDerivs[0] = 0.0;
+ partialDerivs[0][pvIdx] = 1.0;
+ }
+
+ // For instationary simulations, scale the coupling
+ // fluxes of the current stage correctly
+ if (stageParams_)
+ {
+ for (std::size_t k = 0; k < numNeighbors; ++k)
+ partialDerivs[k+1] *= stageParams_->spatialWeight(stageParams_->size()-1);
+ }
+
+ // add the current partial derivatives to the global jacobian matrix
+ // no special treatment is needed if globalJ is a ghost because then derivatives have been assembled to 0 above
+ if constexpr (Problem::enableInternalDirichletConstraints())
+ {
+ // check if own element has internal Dirichlet constraint
+ const auto internalDirichletConstraintsOwnElement = this->problem().hasInternalDirichletConstraint(element_, scv);
+ const auto dirichletValues = this->problem().internalDirichlet(element_, scv);
+
+ for (int eqIdx = 0; eqIdx < numEq; ++eqIdx)
+ {
+ if (internalDirichletConstraintsOwnElement[eqIdx])
+ {
+ origResiduals[0][eqIdx] = origVolVars.priVars()[eqIdx] - dirichletValues[eqIdx];
+ A[globalI][globalI][eqIdx][pvIdx] = (eqIdx == pvIdx) ? 1.0 : 0.0;
+ }
+ else
+ A[globalI][globalI][eqIdx][pvIdx] += partialDerivs[0][eqIdx];
+ }
+
+ // off-diagonal entries
+ j = 1;
+ for (const auto& dataJ : connectivityMap[globalI])
+ {
+ const auto& neighborElement = neighborElements[j-1];
+ const auto& neighborScv = fvGeometry().scv(dataJ.globalJ);
+ const auto internalDirichletConstraintsNeighbor = problem().hasInternalDirichletConstraint(neighborElement, neighborScv);
+
+ for (int eqIdx = 0; eqIdx < numEq; ++eqIdx)
+ {
+ if (internalDirichletConstraintsNeighbor[eqIdx])
+ A[dataJ.globalJ][globalI][eqIdx][pvIdx] = 0.0;
+ else
+ A[dataJ.globalJ][globalI][eqIdx][pvIdx] += partialDerivs[j][eqIdx];
+ }
+
+ ++j;
+ }
+ }
+ else // no internal Dirichlet constraints specified
+ {
+ for (int eqIdx = 0; eqIdx < numEq; eqIdx++)
+ {
+ // the diagonal entries
+ A[globalI][globalI][eqIdx][pvIdx] += partialDerivs[0][eqIdx];
+
+ // off-diagonal entries
+ j = 1;
+ for (const auto& dataJ : connectivityMap[globalI])
+ A[dataJ.globalJ][globalI][eqIdx][pvIdx] += partialDerivs[j++][eqIdx];
+ }
+ }
+
+ // restore the original state of the scv's volume variables
+ curVolVars = origVolVars;
+
+ // restore the current element solution
+ elemSol[0][pvIdx] = origPriVars[pvIdx];
+ }
+
+ // restore original state of the flux vars cache in case of global caching.
+ // This has to be done in order to guarantee that everything is in an undeflected
+ // state before the assembly of another element is called. In the case of local caching
+ // this is obsolete because the elemFluxVarsCache used here goes out of scope after this.
+ // We only have to do this for the last primary variable, for all others the flux var cache
+ // is updated with the correct element volume variables before residual evaluations
+ // TODO: RESET GLOBAL FLUX VARS CACHE HERE
+
+ // return the original residual
+ return origResiduals[0];
+ }
+
+ //! Return references to the local views
+ const Element& element() const { return element_; }
+ const FVElementGeometry& fvGeometry() const { return fvGeometry_; }
+ const ElementVariables& elemVariables() const { return elementVariables_.back(); }
+ ElementVariables& elemVariables_() { return elementVariables_.back(); }
+
+ //! Returns if a stationary problem is assembled
+ bool isStationary() const { return !stageParams_; }
+
+ //! Return a reference to the underlying problem
+ //! TODO: Should grid vars return problem directly!?
+ const auto& problem() const
+ { return elemVariables().gridVariables().gridVolVars().problem(); }
+
+private:
+ const Element& element_;
+ const FVElementGeometry& fvGeometry_;
+ std::vector& elementVariables_;
+
+ bool elementIsGhost_;
+ std::shared_ptr stageParams_;
+};
+
+} // end namespace Dumux
+
+#endif
diff --git a/dumux/assembly/fv/localoperator.hh b/dumux/assembly/fv/localoperator.hh
new file mode 100644
index 0000000000000000000000000000000000000000..d4374b8df10903a07836aa652caf9a70921a7fcd
--- /dev/null
+++ b/dumux/assembly/fv/localoperator.hh
@@ -0,0 +1,251 @@
+// -*- 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 Assembly
+ * \brief An element-wise local operator for finite-volume schemes.
+ */
+#ifndef DUMUX_FV_LOCAL_OPERATOR_HH
+#define DUMUX_FV_LOCAL_OPERATOR_HH
+
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+
+namespace Dumux {
+
+/*!
+ * \ingroup Assembly
+ * \brief The element-wise local operator for finite volume schemes.
+ * This allows for element-wise evaluation of individual terms
+ * of the equations to be solved.
+ * \tparam ElementVariables local view on the grid variables
+ * \tparam Op The model-specific operators
+ */
+template
+class FVLocalOperator
+{
+ // The variables required for the evaluation of the equation
+ using GridVars = typename ElementVariables::GridVariables;
+ using PrimaryVariables = typename GridVars::PrimaryVariables;
+
+ // The grid geometry on which the scheme operates
+ using GridGeometry = typename GridVars::GridGeometry;
+ using FVElementGeometry = typename GridGeometry::LocalView;
+ using SubControlVolumeFace = typename GridGeometry::SubControlVolumeFace;
+ using Extrusion = Extrusion_t;
+
+ using GridView = typename GridGeometry::GridView;
+ using Element = typename GridView::template Codim<0>::Entity;
+
+ using NumEqVector = typename Op::NumEqVector;
+ static constexpr int dim = GridView::dimension;
+ static constexpr int numEq = NumEqVector::size();
+ static constexpr bool isBox = GridGeometry::discMethod == DiscretizationMethod::box;
+
+public:
+ //! export operators
+ using Operators = Op;
+
+ //! export the grid variables type this operator requires a local view of
+ using GridVariables = GridVars;
+
+ //! export a grid-independent alias for compatibility with non grid-based schemes
+ //! TODO: Necessary?
+ using Variables = GridVars;
+
+ //! the container storing the operator values on all dofs of an element
+ using ElementResidualVector = Dune::BlockVector;
+
+ //! export a grid-independent alias for compatibility with non grid-based schemes
+ //! TODO: Necessary?
+ using Residual = ElementResidualVector;
+
+ /*!
+ * \brief The constructor
+ * \note The grid geometry/grid variables local views are expected to
+ * be bound to the same (the given) element
+ */
+ explicit FVLocalOperator(const Element& element,
+ const FVElementGeometry& fvGeometry,
+ const ElementVariables& elemVars)
+ : element_(element)
+ , fvGeometry_(fvGeometry)
+ , elemVariables_(elemVars)
+ {}
+
+ /*!
+ * \name Main interface
+ * \note Methods used by the assembler to compute derivatives and residual
+ */
+ // \{
+
+ /*!
+ * \brief Compute the terms of the local residual that do not appear in
+ * time derivatives. These are the sources and the fluxes.
+ */
+ ElementResidualVector evalFluxesAndSources() const
+ {
+ const auto& problem = elemVariables_.gridVariables().gridVolVars().problem();
+ const auto& evv = elemVariables_.elemVolVars();
+
+ // source term
+ auto result = getEmptyResidual();
+ for (const auto& scv : scvs(fvGeometry_))
+ result[scv.localDofIndex()] -= Operators::source(problem, element_, fvGeometry_, evv, scv);
+
+ // flux term
+ for (const auto& scvf : scvfs(fvGeometry_))
+ addFlux_(result, scvf);
+
+ return result;
+ }
+
+ /*!
+ * \brief Compute the storage term, i.e. the term appearing in the time derivative.
+ */
+ ElementResidualVector evalStorage() const
+ {
+ const auto& problem = elemVariables_.gridVariables().gridVolVars().problem();
+ const auto& evv = elemVariables_.elemVolVars();
+
+ // TODO: Until now, FVLocalResidual defined storage as the entire
+ // time derivative. Now it means the term above the time derivative.
+ // We should think about the correct naming here...
+ // TODO: Should storage() NOT multiply with volume?? That was different until
+ // now but flux() also returns the flux multiplied with area so this should
+ // be more consistent
+ auto result = getEmptyResidual();
+ for (const auto& scv : scvs(fvGeometry_))
+ result[scv.localDofIndex()] += operators_.storage(problem, scv, evv[scv]);
+
+ return result;
+ }
+
+ ElementResidualVector getEmptyResidual() const
+ {
+ ElementResidualVector res(fvGeometry_.numScv());
+ res = 0.0;
+ return res;
+ }
+
+ // \}
+
+ /*!
+ * \name Interfaces for analytic Jacobian computation
+ */
+ // \{
+
+ //! \todo TODO: Add interfaces. Or, should this be here at all!?
+
+ //\}
+
+ // \}
+
+protected:
+
+ //! compute and add the flux across the given face to the container (cc schemes)
+ template = 0>
+ void addFlux_(ElementResidualVector& r, const SubControlVolumeFace& scvf) const
+ {
+ const auto& insideScv = fvGeometry_.scv(scvf.insideScvIdx());
+ const auto localDofIdx = insideScv.localDofIndex();
+
+ const auto& problem = elemVariables_.gridVariables().gridVolVars().problem();
+ const auto& evv = elemVariables_.elemVolVars();
+ const auto& efvc = elemVariables_.elemFluxVarsCache();
+
+ if (!scvf.boundary())
+ r[localDofIdx] += Operators::flux(problem, element_, fvGeometry_, evv, efvc, scvf);
+ else
+ {
+ const auto& bcTypes = problem.boundaryTypes(element_, scvf);
+
+ // Dirichlet boundaries
+ if (bcTypes.hasDirichlet() && !bcTypes.hasNeumann())
+ r[localDofIdx] += Operators::flux(problem, element_, fvGeometry_, evv, efvc, scvf);
+
+ // Neumann and Robin ("solution dependent Neumann") boundary conditions
+ else if (bcTypes.hasNeumann() && !bcTypes.hasDirichlet())
+ {
+ auto neumannFluxes = problem.neumann(element_, fvGeometry_, evv, efvc, scvf);
+
+ // multiply neumann fluxes with the area and the extrusion factor
+ neumannFluxes *= Extrusion::area(scvf)*evv[insideScv].extrusionFactor();
+ r[localDofIdx] += neumannFluxes;
+ }
+
+ else
+ DUNE_THROW(Dune::NotImplemented, "Mixed boundary conditions for cell-centered schemes. " <<
+ "Use pure boundary conditions by converting Dirichlet BCs to Robin BCs");
+ }
+ }
+
+ //! compute and add the flux across the given face to the container (box scheme)
+ template = 0>
+ void addFlux_(ElementResidualVector& r, const SubControlVolumeFace& scvf) const
+ {
+ const auto& problem = elemVariables_.gridVariables().gridVolVars().problem();
+ const auto& evv = elemVariables_.elemVolVars();
+ const auto& efvc = elemVariables_.elemFluxVarsCache();
+
+ // inner faces
+ if (!scvf.boundary())
+ {
+ const auto flux = Operators::flux(problem, element_, fvGeometry_, evv, efvc, scvf);
+ r[fvGeometry_.scv(scvf.insideScvIdx()).localDofIndex()] += flux;
+ r[fvGeometry_.scv(scvf.outsideScvIdx()).localDofIndex()] -= flux;
+ }
+
+ // boundary faces
+ else
+ {
+ const auto& scv = fvGeometry_.scv(scvf.insideScvIdx());
+ const auto& bcTypes = problem.boundaryTypes(element_, scv);
+
+ // Treat Neumann and Robin ("solution dependent Neumann") boundary conditions.
+ if (bcTypes.hasNeumann())
+ {
+ const auto neumannFluxes = problem.neumann(element_, fvGeometry_, evv, efvc, scvf);
+ const auto area = Extrusion::area(scvf)*evv[scv].extrusionFactor();
+
+ // only add fluxes to equations for which Neumann is set
+ for (int eqIdx = 0; eqIdx < NumEqVector::size(); ++eqIdx)
+ if (bcTypes.isNeumann(eqIdx))
+ r[scv.localDofIndex()][eqIdx] += neumannFluxes[eqIdx]*area;
+ }
+ }
+ }
+
+private:
+
+ const Element& element_; //!< pointer to the element for which the residual is computed
+ const FVElementGeometry& fvGeometry_; //!< the local view on the finite element grid geometry
+ const ElementVariables& elemVariables_; //!< the local view on the grid variables
+ Operators operators_; //!< evaluates storage/flux operators of the actual equation
+};
+
+} // end namespace Dumux
+
+#endif
diff --git a/dumux/assembly/fv/operators.hh b/dumux/assembly/fv/operators.hh
new file mode 100644
index 0000000000000000000000000000000000000000..2c8352e7c9e76174eb82d3f47403085c24eb5ab1
--- /dev/null
+++ b/dumux/assembly/fv/operators.hh
@@ -0,0 +1,148 @@
+// -*- 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 Assembly
+ * \brief The base class for the sub-control entity-local evaluation of
+ * the terms of equations in the context of finite-volume schemes
+ */
+#ifndef DUMUX_FV_OPERATORS_HH
+#define DUMUX_FV_OPERATORS_HH
+
+#include
+#include
+
+#include
+#include
+
+namespace Dumux {
+
+/*!
+ * \ingroup Assembly
+ * \brief The base class for the sub-control entity-local evaluation of
+ * the terms of equations in the context of finite-volume schemes
+ * \todo TODO: Should operators have a state? That is, be constructed and have non-static functions?
+ */
+template
+class FVOperators
+{
+ // The variables required for the evaluation of the equation
+ using GridVariables = typename ElementVariables::GridVariables;
+ using VolumeVariables = typename GridVariables::VolumeVariables;
+ using ElementVolumeVariables = typename ElementVariables::ElementVolumeVariables;
+ using ElementFluxVariablesCache = typename ElementVariables::ElementFluxVariablesCache;
+ using Scalar = typename GridVariables::Scalar;
+
+ using PrimaryVariables = typename GridVariables::PrimaryVariables;
+ static constexpr int numEq = PrimaryVariables::size();
+
+ // The grid geometry on which the scheme operates
+ using GridGeometry = typename GridVariables::GridGeometry;
+ using FVElementGeometry = typename GridGeometry::LocalView;
+ using SubControlVolume = typename GridGeometry::SubControlVolume;
+ using SubControlVolumeFace = typename GridGeometry::SubControlVolumeFace;
+ using Extrusion = Extrusion_t;
+
+ using GridView = typename GridGeometry::GridView;
+ using Element = typename GridView::template Codim<0>::Entity;
+
+ // user-input defined in the problem
+ using Problem = std::decay_t().gridVolVars().problem())>;
+
+public:
+ //! export the type used to store scalar values for all equations
+ //! TODO: How to obtain user-defined NumEqVector (ProblemTraits?)
+ using NumEqVector = Dune::FieldVector;
+
+ // export the types of the flux/source/storage terms
+ using FluxTerm = NumEqVector;
+ using SourceTerm = NumEqVector;
+ using StorageTerm = NumEqVector;
+
+ /*!
+ * \name Model specific interfaces
+ * \note The following method are the model specific implementations of the
+ * operators appearing in the equation and should be overloaded by the
+ * implementations.
+ */
+ // \{
+
+ /*!
+ * \brief Compute the storage term of the equations for the given sub-control volume
+ * \param problem The problem to be solved (could store additionally required quantities)
+ * \param scv The sub-control volume
+ * \param volVars The primary & secondary variables evaluated for the scv
+ * \note This must be overloaded by the implementation
+ */
+ static StorageTerm storage(const Problem& problem,
+ const SubControlVolume& scv,
+ const VolumeVariables& volVars)
+ { DUNE_THROW(Dune::NotImplemented, "Storage operator not implemented!"); }
+
+ /*!
+ * \brief Compute the flux term of the equations for the given sub-control volume face
+ * \param problem The problem to be solved (could store additionally required quantities)
+ * \param element The grid element
+ * \param fvGeometry The element-local view on the finite volume grid geometry
+ * \param elemVolVars The element-local view on the grid volume variables
+ * \param elemFluxVarsCache The element-local view on the grid flux variables cache
+ * \param scvf The sub-control volume face for which the flux term is to be computed
+ * \note This must be overloaded by the implementation
+ */
+ static FluxTerm flux(const Problem& problem,
+ const Element& element,
+ const FVElementGeometry& fvGeometry,
+ const ElementVolumeVariables& elemVolVars,
+ const ElementFluxVariablesCache& elemFluxVarsCache,
+ const SubControlVolumeFace& scvf)
+ { DUNE_THROW(Dune::NotImplemented, "This model does not implement a flux method!"); }
+
+ /*!
+ * \brief Compute the source term of the equations for the given sub-control volume
+ * \param problem The problem to be solved (could store additionally required quantities)
+ * \param element The grid element
+ * \param fvGeometry The element-local view on the finite volume grid geometry
+ * \param elemVolVars The element-local view on the grid volume variables
+ * \param scv The sub-control volume for which the source term is to be computed
+ * \note This is a default implementation forwarding to interfaces in the problem
+ */
+ static SourceTerm source(const Problem& problem,
+ const Element& element,
+ const FVElementGeometry& fvGeometry,
+ const ElementVolumeVariables& elemVolVars,
+ const SubControlVolume& scv)
+ {
+ SourceTerm source(0.0);
+
+ // add contributions from volume flux sources
+ source += problem.source(element, fvGeometry, elemVolVars, scv);
+
+ // add contribution from possible point sources
+ source += problem.scvPointSources(element, fvGeometry, elemVolVars, scv);
+
+ // multiply with scv volume
+ source *= Extrusion::volume(scv)*elemVolVars[scv].extrusionFactor();
+
+ return source;
+ }
+};
+
+} // end namespace Dumux
+
+#endif
diff --git a/dumux/assembly/localassembler.hh b/dumux/assembly/localassembler.hh
new file mode 100644
index 0000000000000000000000000000000000000000..8fa6e006d7ff426be021da241975393f8e81b7b2
--- /dev/null
+++ b/dumux/assembly/localassembler.hh
@@ -0,0 +1,65 @@
+// -*- 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 Assembly
+ * \brief Helper alias to select a local assembler based on the discretization scheme.
+ */
+#ifndef DUMUX_LOCAL_ASSEMBLER_HH
+#define DUMUX_LOCAL_ASSEMBLER_HH
+
+#include
+#include "fv/boxlocalassembler.hh"
+#include "fv/cclocalassembler.hh"
+
+namespace Dumux {
+namespace Impl {
+
+ template
+ struct LocalAssemblerChooser;
+
+ template
+ struct LocalAssemblerChooser
+ { using type = BoxLocalAssembler; };
+
+ template
+ struct LocalAssemblerChooser
+ { using type = CCLocalAssembler; };
+
+ template
+ struct LocalAssemblerChooser
+ { using type = CCLocalAssembler; };
+
+ template
+ using LocalAssemblerType = typename LocalAssemblerChooser::type;
+
+} // end namespace Immpl
+
+/*!
+ * \ingroup Assembly
+ * \brief Helper alias to select the local assembler type from an assembler.
+ */
+template
+using LocalAssembler = Impl::LocalAssemblerType;
+
+} // namespace Dumux
+
+#endif
diff --git a/dumux/common/CMakeLists.txt b/dumux/common/CMakeLists.txt
index e26b4dfb2abc466eb260114c3435dfd9b36a0942..5bd0405ba61caccd299efa2042f3b044956fb825 100644
--- a/dumux/common/CMakeLists.txt
+++ b/dumux/common/CMakeLists.txt
@@ -48,4 +48,6 @@ timeloop.hh
timemanager.hh
valgrind.hh
variablelengthspline_.hh
+variables.hh
+variablesbackend.hh
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dumux/common)
diff --git a/dumux/common/pdesolver.hh b/dumux/common/pdesolver.hh
index 99f61bcaa65deee6bba5fb40996c9f93f472744c..49ce5123e0975803ca970da9a509c38ceb7a9ac2 100644
--- a/dumux/common/pdesolver.hh
+++ b/dumux/common/pdesolver.hh
@@ -28,6 +28,7 @@
#include
#include
+#include
#include
@@ -35,9 +36,25 @@
namespace Dune {
template
class MultiTypeBlockMatrix;
-}
+} // end namespace Dune
namespace Dumux {
+namespace Impl {
+
+ template
+ using AssemblerVariablesType = typename Assembler::Variables;
+
+ template
+ inline constexpr bool exportsVariables = Dune::Std::is_detected_v;
+
+ template> struct VariablesChooser;
+ template struct VariablesChooser { using Type = AssemblerVariablesType; };
+ template struct VariablesChooser { using Type = typename A::ResidualType; };
+
+ template
+ using AssemblerVariables = typename VariablesChooser::Type;
+
+} // end namespace Impl
/*!
* \ingroup Common
@@ -47,17 +64,28 @@ namespace Dumux {
* and has a method solve that linearizes (if not already linear), assembles, solves and updates
* given an initial solution producing a new solution.
*
- * \tparam Assembler A PDE linearized system assembler
- * \tparam LinearSolver A linear system solver
+ * \tparam A Assembler for the PDE linearized system
+ * \tparam LS Linear system solver
*/
-template
+template
class PDESolver
{
- using SolutionVector = typename Assembler::ResidualType;
- using Scalar = typename Assembler::Scalar;
+ using Scalar = typename A::Scalar;
using TimeLoop = TimeLoopBase;
public:
+ //! export the assembler and linear solver types
+ using Assembler = A;
+ using LinearSolver = LS;
+
+ //! export the type of variables that represent a numerical solution
+ using Variables = Impl::AssemblerVariables;
+
+ /*!
+ * \brief Constructor
+ * \param assembler pointer to the assembler of the linear system
+ * \param linearSolver pointer to the solver of the resulting linear system
+ */
PDESolver(std::shared_ptr assembler,
std::shared_ptr linearSolver)
: assembler_(assembler)
@@ -68,24 +96,25 @@ public:
/*!
* \brief Solve the given PDE system (usually assemble + solve linear system + update)
- * \param sol a solution vector possbilty containing an initial solution
+ * \param vars instance of the `Variables` class representing a numerical
+ * solution, defining primary and possibly secondary variables
+ * and information on the time level.
*/
- virtual void solve(SolutionVector& sol) = 0;
+ virtual void solve(Variables& vars) = 0;
/*!
* \brief Solve the given PDE system with time step control
* \note This is used for solvers that are allowed to e.g. automatically reduce the
* time step if the solve was not successful
- * \param sol a solution vector possbilty containing an initial solution
+ * \param vars instance of the `Variables` class representing a numerical solution
* \param timeLoop a reference to the current time loop
*/
- virtual void solve(SolutionVector& sol, TimeLoop& timeLoop)
+ virtual void solve(Variables& vars, TimeLoop& timeLoop)
{
// per default we just forward to the method without time step control
- solve(sol);
+ solve(vars);
}
-protected:
/*!
* \brief Access the assembler
*/
@@ -110,6 +139,8 @@ protected:
LinearSolver& linearSolver()
{ return *linearSolver_; }
+protected:
+
/*!
* \brief Helper function to assure the MultiTypeBlockMatrix's sub-blocks have the correct sizes.
*/
diff --git a/dumux/common/variables.hh b/dumux/common/variables.hh
new file mode 100644
index 0000000000000000000000000000000000000000..f75731626b1b1540d1060d72826bdeab6d2e053e
--- /dev/null
+++ b/dumux/common/variables.hh
@@ -0,0 +1,133 @@
+// -*- 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 Common
+ * \copydoc Dumux::Variables
+ */
+#ifndef DUMUX_VARIABLES_HH
+#define DUMUX_VARIABLES_HH
+
+#include
+
+#include
+#include
+
+namespace Dumux {
+
+/*!
+ * \ingroup Discretization
+ * \brief Class that represents the variables of a model.
+ * We assume that models are formulated on the basis of primary and
+ * possibly secondary variables, where the latter may non-linearly
+ * depend on the former. Variables in Dumux represent the state of
+ * a numerical solution of a model, consisting of all primary/secondary
+ * variables and, if the a transient problem is modeled, of time information.
+ *
+ * This class defines the interface that is expected of variable classes,
+ * and it provides the implementation for models that do not require storing
+ * any additional information besides the primary variables and (optionally)
+ * time.
+ * \tparam X The type used for solution vectors, i.e. all primary variables.
+ */
+template
+class Variables
+{
+ template
+ struct ScalarExtractorHelper;
+
+ template
+ struct ScalarExtractorHelper
+ { using Type = T; };
+
+ template
+ struct ScalarExtractorHelper
+ {
+ private:
+ using ValueType = std::decay_t()[0])>;
+ static constexpr bool indexable = IsIndexable::value;
+ public:
+ using Type = typename ScalarExtractorHelper::Type;
+ };
+
+public:
+ //! export the type of solution vector
+ using SolutionVector = X;
+
+ //! export the underlying scalar type
+ using Scalar = typename ScalarExtractorHelper::value>::Type;
+
+ //! export the time representation
+ using TimeLevel = Dumux::TimeLevel;
+
+ //! Default constructor
+ explicit Variables() : x_(), t_(0.0) {}
+
+ //! Construction from a solution
+ explicit Variables(const SolutionVector& x,
+ const TimeLevel& t = TimeLevel{0.0})
+ : x_(x), t_(t)
+ {}
+
+ //! Construction from a solution
+ explicit Variables(SolutionVector&& x,
+ const TimeLevel& t = TimeLevel{0.0})
+ : x_(std::move(x)), t_(t)
+ {}
+
+ //! Construction from initializer lambda
+ template), int> = 0>
+ explicit Variables(const Initializer& initializeSolution,
+ const TimeLevel& timeLevel = TimeLevel{0.0})
+ : t_(timeLevel)
+ {
+ initializeSolution(x_);
+ }
+
+ //! Return reference to the solution
+ const SolutionVector& dofs() const { return x_; }
+
+ //! Non-const access still required for privar switch (TODO: Remove dependency)
+ SolutionVector& dofs() { return x_; }
+
+ //! Update the state to a new solution
+ void update(const SolutionVector& x)
+ { x_ = x; }
+
+ //! Update the time level only
+ void updateTime(const TimeLevel& t)
+ { t_ = t; }
+
+ //! Update the state to a new solution & time level
+ void update(const SolutionVector& x,
+ const TimeLevel& t)
+ {
+ x_ = x;
+ t_ = t;
+ }
+
+private:
+ SolutionVector x_;
+ TimeLevel t_;
+};
+
+} // end namespace Dumux
+
+#endif
diff --git a/dumux/common/variablesbackend.hh b/dumux/common/variablesbackend.hh
new file mode 100644
index 0000000000000000000000000000000000000000..52e506e4aeb9a7a76bfbd90d7424206330e9bcb5
--- /dev/null
+++ b/dumux/common/variablesbackend.hh
@@ -0,0 +1,204 @@
+// -*- 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 Nonlinear
+ * \brief Backends for operations on different solution vector types
+ * or more generic variable classes to be used in places where
+ * several different types/layouts should be supported.
+ */
+#ifndef DUMUX_VARIABLES_BACKEND_HH
+#define DUMUX_VARIABLES_BACKEND_HH
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+// forward declaration
+namespace Dune {
+
+template
+class MultiTypeBlockVector;
+
+} // end namespace Dune
+
+namespace Dumux {
+
+/*!
+ * \ingroup Nonlinear
+ * \brief Class providing operations with primary variable vectors
+ */
+template
+class DofBackend;
+
+/*!
+ * \ingroup Nonlinear
+ * \brief Specialization providing operations for scalar/number types
+ */
+template
+class DofBackend::value, Scalar>>
+{
+public:
+ using DofVector = Scalar; //!< the type of the dofs parametrizing the variables object
+
+ static std::size_t size(const DofVector& d)
+ { return 1; }
+
+ static DofVector makeZeroDofVector(std::size_t size)
+ { return 0.0; }
+};
+
+/*!
+ * \ingroup Nonlinear
+ * \brief Specialization providing operations for block vectors
+ */
+template
+class DofBackend>
+{
+
+public:
+ using DofVector = Dune::BlockVector; //!< the type of the dofs parametrizing the variables object
+
+ static std::size_t size(const DofVector& d)
+ { return d.size(); }
+
+ static DofVector makeZeroDofVector(std::size_t size)
+ { DofVector d; d.resize(size); return d; }
+};
+
+/*!
+ * \ingroup Nonlinear
+ * \brief Specialization providing operations for multitype block vectors
+ */
+template
+class DofBackend>
+{
+ using DV = Dune::MultiTypeBlockVector;
+ static constexpr auto numBlocks = DV::size();
+
+ using VectorSizeInfo = std::array;
+
+public:
+ using DofVector = DV; //!< the type of the dofs parametrizing the variables object
+
+ static VectorSizeInfo size(const DofVector& d)
+ {
+ VectorSizeInfo result;
+ using namespace Dune::Hybrid;
+ forEach(std::make_index_sequence{}, [&](auto i) {
+ result[i] = d[Dune::index_constant{}].size();
+ });
+ return result;
+ }
+
+ static DofVector makeZeroDofVector(const VectorSizeInfo& size)
+ {
+ DofVector result;
+ using namespace Dune::Hybrid;
+ forEach(std::make_index_sequence{}, [&](auto i) {
+ result[Dune::index_constant{}].resize(size[i]);
+ });
+ return result;
+ }
+};
+
+namespace Impl {
+
+template
+using SolutionVectorType = typename Vars::SolutionVector;
+
+template
+class VariablesBackend;
+
+/*!
+ * \ingroup Nonlinear
+ * \brief Class providing operations for primary variable vector/scalar types
+ * \note We assume the variables being simply a dof vector if we
+ * do not find the variables class to export `SolutionVector`.
+ */
+template
+class VariablesBackend
+: public DofBackend
+{
+ using ParentType = DofBackend;
+
+public:
+ using Variables = Vars;
+ using typename ParentType::DofVector;
+
+ //! update to new solution vector
+ static void update(Variables& v, const DofVector& dofs)
+ { v = dofs; }
+
+ //! return const reference to dof vector
+ static const DofVector& getDofVector(const Variables& v)
+ { return v; }
+
+ //! return reference to dof vector
+ static DofVector& getDofVector(Variables& v)
+ { return v; }
+};
+
+/*!
+ * \file
+ * \ingroup Nonlinear
+ * \brief Class providing operations for generic variable classes,
+ * containing primary and possibly also secondary variables.
+ */
+template
+class VariablesBackend
+: public DofBackend
+{
+public:
+ using DofVector = typename Vars::SolutionVector;
+ using Variables = Vars; //!< the type of the variables object
+
+ //! update to new solution vector
+ static void update(Variables& v, const DofVector& dofs)
+ { v.update(dofs); }
+
+ //! return const reference to dof vector
+ static const DofVector& getDofVector(const Variables& v)
+ { return v.dofs(); }
+
+ //! return reference to dof vector
+ static DofVector& getDofVector(Variables& v)
+ { return v.dofs(); }
+};
+} // end namespace Impl
+
+/*!
+ * \ingroup Nonlinear
+ * \brief Class providing operations for generic variable classes
+ * that represent the state of a numerical solution, possibly
+ * consisting of primary/secondary variables and information on
+ * the time level.
+ */
+template
+using VariablesBackend = Impl::VariablesBackend>;
+
+} // end namespace Dumux
+
+#endif
diff --git a/dumux/discretization/CMakeLists.txt b/dumux/discretization/CMakeLists.txt
index a43e90fb378a589d1ad6eb942091d1764ad5af9a..05fa5cb88a09f2a76a8a4dfd9d34c9a109aed8e3 100644
--- a/dumux/discretization/CMakeLists.txt
+++ b/dumux/discretization/CMakeLists.txt
@@ -18,6 +18,7 @@ fluxstencil.hh
functionspacebasis.hh
fvgridvariables.hh
fvproperties.hh
+gridvariables.hh
localview.hh
method.hh
rotationpolicy.hh
diff --git a/dumux/discretization/fvgridvariables.hh b/dumux/discretization/fvgridvariables.hh
index ce693bd28360c4e92c0f8276d6485fde9fc2b2b0..21c518847f18e87a13ef99d07a6f8b78e3f7ffc2 100644
--- a/dumux/discretization/fvgridvariables.hh
+++ b/dumux/discretization/fvgridvariables.hh
@@ -19,32 +19,123 @@
/*!
* \file
* \ingroup Discretization
- * \brief The grid variable class for finite volume schemes storing variables on scv and scvf (volume and flux variables)
+ * \brief The grid variable class for finite volume schemes,
+ * storing variables on scv and scvf (volume and flux variables)
*/
#ifndef DUMUX_FV_GRID_VARIABLES_HH
#define DUMUX_FV_GRID_VARIABLES_HH
#include
#include
-#include
+
+// TODO: Remove once default template argument is omitted,
+// which is there solely to ensure backwards compatibility.
+#include
+
+#include
+#include "gridvariables.hh"
namespace Dumux {
/*!
* \ingroup Discretization
- * \brief The grid variable class for finite volume schemes storing variables on scv and scvf (volume and flux variables)
- * \tparam the type of the grid geometry
- * \tparam the type of the grid volume variables
- * \tparam the type of the grid flux variables cache
+ * \brief Finite volume-specific local view on grid variables.
+ * \tparam GV The grid variables class
*/
-template
+template
+class FVGridVariablesLocalView
+{
+ using GridGeometry = typename GV::GridGeometry;
+ using FVElementGeometry = typename GridGeometry::LocalView;
+
+ using GridView = typename GridGeometry::GridView;
+ using Element = typename GridView::template Codim<0>::Entity;
+
+public:
+ //! export corresponding grid-wide class
+ using GridVariables = GV;
+
+ //! export underlying local views
+ using ElementVolumeVariables = typename GV::GridVolumeVariables::LocalView;
+ using ElementFluxVariablesCache = typename GV::GridFluxVariablesCache::LocalView;
+
+ //! Constructor
+ FVGridVariablesLocalView(const GridVariables& gridVariables)
+ : gridVariables_(&gridVariables)
+ , elemVolVars_(gridVariables.gridVolVars())
+ , elemFluxVarsCache_(gridVariables.gridFluxVarsCache())
+ {}
+
+ /*!
+ * \brief Bind this local view to a grid element.
+ * \param element The grid element
+ * \param fvGeometry Local view on the grid geometry
+ */
+ void bind(const Element& element,
+ const FVElementGeometry& fvGeometry)
+ {
+ const auto& x = gridVariables().dofs();
+ elemVolVars_.bind(element, fvGeometry, x);
+ elemFluxVarsCache_.bind(element, fvGeometry, elemVolVars_);
+ }
+
+ /*!
+ * \brief Bind only the volume variables local view to a grid element.
+ * \param element The grid element
+ * \param fvGeometry Local view on the grid geometry
+ */
+ void bindElemVolVars(const Element& element,
+ const FVElementGeometry& fvGeometry)
+ {
+ elemVolVars_.bind(element, fvGeometry, gridVariables().dofs());
+
+ // unbind flux variables cache
+ elemFluxVarsCache_ = localView(gridVariables().gridFluxVarsCache());
+ }
+
+ //! return reference to the elem vol vars
+ const ElementVolumeVariables& elemVolVars() const { return elemVolVars_; }
+ ElementVolumeVariables& elemVolVars() { return elemVolVars_; }
+
+ //! return reference to the flux variables cache
+ const ElementFluxVariablesCache& elemFluxVarsCache() const { return elemFluxVarsCache_; }
+ ElementFluxVariablesCache& elemFluxVarsCache() { return elemFluxVarsCache_; }
+
+ //! Return reference to the grid variables
+ const GridVariables& gridVariables() const
+ { return *gridVariables_; }
+
+private:
+ const GridVariables* gridVariables_;
+ ElementVolumeVariables elemVolVars_;
+ ElementFluxVariablesCache elemFluxVarsCache_;
+};
+
+/*!
+ * \ingroup Discretization
+ * \brief The grid variable class for finite volume schemes, storing
+ * variables on scv and scvf (volume and flux variables).
+ * \tparam GG the type of the grid geometry
+ * \tparam GVV the type of the grid volume variables
+ * \tparam GFVC the type of the grid flux variables cache
+ * \tparam X the type used for solution vectors
+ * \todo TODO: GG is an obsolote template argument?
+ */
+template>
class FVGridVariables
+: public GridVariables
{
+ using ParentType = GridVariables;
+ using ThisType = FVGridVariables;
+
public:
+ using typename ParentType::SolutionVector;
+
//! export type of the finite volume grid geometry
using GridGeometry = GG;
- //! export type of the finite volume grid geometry
+ //! export type of the grid volume variables
using GridVolumeVariables = GVV;
//! export type of the volume variables
@@ -53,59 +144,109 @@ public:
//! export primary variable type
using PrimaryVariables = typename VolumeVariables::PrimaryVariables;
- //! export scalar type (TODO get it directly from the volvars)
- using Scalar = std::decay_t()[0])>;
-
- //! export type of the finite volume grid geometry
+ //! export cache type for flux variables
using GridFluxVariablesCache = GFVC;
+ //! export the local view on this class
+ using LocalView = FVGridVariablesLocalView;
+
+ /*!
+ * \brief Constructor
+ * \param problem The problem to be solved
+ * \param gridGeometry The geometry of the computational grid
+ * \todo TODO: Here we could forward to the base class with
+ * [problem] (auto& x) { problem->applyInitialSolution(x); },
+ * but we cannot be sure that user problems implement
+ * the initial() or initialAtPos() functions?
+ */
template
FVGridVariables(std::shared_ptr problem,
std::shared_ptr gridGeometry)
- : gridGeometry_(gridGeometry)
- , curGridVolVars_(*problem)
+ : ParentType(gridGeometry)
+ , gridVolVars_(*problem)
, prevGridVolVars_(*problem)
, gridFluxVarsCache_(*problem)
{}
+ /*!
+ * \brief Constructor with custom initialization of the solution.
+ * \param problem The problem to be solved
+ * \param gridGeometry The geometry of the computational grid
+ * \param solOrInitializer This can be either a reference to a
+ * solution vector, or an initializer
+ * lambda. See Dumux::Variables.
+ */
+ template
+ FVGridVariables(std::shared_ptr problem,
+ std::shared_ptr gridGeometry,
+ SolOrInitializer&& solOrInitializer)
+ : ParentType(gridGeometry, std::forward(solOrInitializer))
+ , gridVolVars_(*problem)
+ , prevGridVolVars_(*problem)
+ , gridFluxVarsCache_(*problem)
+ {
+ gridVolVars_.update(this->gridGeometry(), this->dofs());
+ gridFluxVarsCache_.update(this->gridGeometry(), gridVolVars_, this->dofs(), true);
+ prevGridVolVars_ = gridVolVars_;
+ }
+
//! initialize all variables (stationary case)
- template
+ [[deprecated("Initialize grid variables upon construction instead.")]]
void init(const SolutionVector& curSol)
{
+ ParentType::update(curSol);
+
// resize and update the volVars with the initial solution
- curGridVolVars_.update(*gridGeometry_, curSol);
+ gridVolVars_.update(this->gridGeometry(), curSol);
// update the flux variables caches (always force flux cache update on initialization)
- gridFluxVarsCache_.update(*gridGeometry_, curGridVolVars_, curSol, true);
+ gridFluxVarsCache_.update(this->gridGeometry(), gridVolVars_, curSol, true);
// set the volvars of the previous time step in case we have an instationary problem
// note that this means some memory overhead in the case of enabled caching, however
// this it outweighted by the advantage of having a single grid variables object for
// stationary and instationary problems
- prevGridVolVars_ = curGridVolVars_;
+ // TODO: Remove this in the context of new time integration schemes
+ prevGridVolVars_ = gridVolVars_;
}
- //! update all variables
- template
- void update(const SolutionVector& curSol, bool forceFluxCacheUpdate = false)
+ //! Deprecated update interface.
+ [[deprecated("Use update(curSol) or forceUpdateAll(curSol) instead.")]]
+ void update(const SolutionVector& curSol, bool forceFluxCacheUpdate)
{
+ if (forceFluxCacheUpdate)
+ forceUpdateAll(curSol);
+ else
+ update(curSol);
+ }
+
+ //! update all variables after grid adaption
+ [[deprecated("use forceUpdateAll() instead.")]]
+ void updateAfterGridAdaption(const SolutionVector& curSol)
+ { forceUpdateAll(curSol); }
+
+ //! Update all variables that may be affected by a change in solution
+ void update(const SolutionVector& curSol)
+ {
+ ParentType::update(curSol);
+
// resize and update the volVars with the initial solution
- curGridVolVars_.update(*gridGeometry_, curSol);
+ gridVolVars_.update(this->gridGeometry(), curSol);
// update the flux variables caches
- gridFluxVarsCache_.update(*gridGeometry_, curGridVolVars_, curSol, forceFluxCacheUpdate);
+ gridFluxVarsCache_.update(this->gridGeometry(), gridVolVars_, curSol);
}
- //! update all variables after grid adaption
- template
- void updateAfterGridAdaption(const SolutionVector& curSol)
+ //! Force the update of all variables
+ void forceUpdateAll(const SolutionVector& curSol)
{
- // update (always force flux cache update as the grid changed)
- update(curSol, true);
+ ParentType::update(curSol);
- // for instationary problems also update the variables
- // for the previous time step to the new grid
- prevGridVolVars_ = curGridVolVars_;
+ // resize and update the volVars with the initial solution
+ gridVolVars_.update(this->gridGeometry(), curSol);
+
+ // update the flux variables caches
+ gridFluxVarsCache_.update(this->gridGeometry(), gridVolVars_, curSol, true);
}
/*!
@@ -114,18 +255,19 @@ public:
*/
void advanceTimeStep()
{
- prevGridVolVars_ = curGridVolVars_;
+ prevGridVolVars_ = gridVolVars_;
}
//! resets state to the one before time integration
- template
void resetTimeStep(const SolutionVector& solution)
{
+ ParentType::update(solution);
+
// set the new time step vol vars to old vol vars
- curGridVolVars_ = prevGridVolVars_;
+ gridVolVars_ = prevGridVolVars_;
// update the flux variables caches
- gridFluxVarsCache_.update(*gridGeometry_, curGridVolVars_, solution);
+ gridFluxVarsCache_.update(this->gridGeometry(), gridVolVars_, solution);
}
//! return the flux variables cache
@@ -137,12 +279,22 @@ public:
{ return gridFluxVarsCache_; }
//! return the current volume variables
+ const GridVolumeVariables& gridVolVars() const
+ { return gridVolVars_; }
+
+ //! return the current volume variables
+ GridVolumeVariables& gridVolVars()
+ { return gridVolVars_; }
+
+ //! return the current volume variables
+ [[deprecated("Use gridVolVars() instead")]]
const GridVolumeVariables& curGridVolVars() const
- { return curGridVolVars_; }
+ { return gridVolVars_; }
//! return the current volume variables
+ [[deprecated("Use gridVolVars() instead")]]
GridVolumeVariables& curGridVolVars()
- { return curGridVolVars_; }
+ { return gridVolVars_; }
//! return the volume variables of the previous time step (for instationary problems)
const GridVolumeVariables& prevGridVolVars() const
@@ -152,18 +304,9 @@ public:
GridVolumeVariables& prevGridVolVars()
{ return prevGridVolVars_; }
- //! return the finite volume grid geometry
- const GridGeometry& gridGeometry() const
- { return *gridGeometry_; }
-
-protected:
-
- std::shared_ptr gridGeometry_; //!< pointer to the constant grid geometry
-
private:
- GridVolumeVariables curGridVolVars_; //!< the current volume variables (primary and secondary variables)
- GridVolumeVariables prevGridVolVars_; //!< the previous time step's volume variables (primary and secondary variables)
-
+ GridVolumeVariables gridVolVars_; //!< the current volume variables (primary and secondary variables)
+ GridVolumeVariables prevGridVolVars_; //!< the previous time step's volume variables (primary and secondary variables)
GridFluxVariablesCache gridFluxVarsCache_; //!< the flux variables cache
};
diff --git a/dumux/discretization/gridvariables.hh b/dumux/discretization/gridvariables.hh
new file mode 100644
index 0000000000000000000000000000000000000000..35e239c8342e2907c01383dac35ed550020cf593
--- /dev/null
+++ b/dumux/discretization/gridvariables.hh
@@ -0,0 +1,70 @@
+// -*- 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 Base class for grid variables
+ */
+#ifndef DUMUX_GRID_VARIABLES_HH
+#define DUMUX_GRID_VARIABLES_HH
+
+#include
+
+#include
+
+namespace Dumux {
+
+/*!
+ * \ingroup Discretization
+ * \brief Base class for grid variables.
+ * \tparam GG The grid geometry type
+ * \tparam X The type used for solution vectors
+ */
+template
+class GridVariables
+: public Variables
+{
+ using ParentType = Variables;
+
+public:
+ //! export the grid geometry type
+ using GridGeometry = GG;
+
+ /*!
+ * \brief Constructor from a grid geometry. The remaining arguments must
+ * be valid arguments for the construction of the Variables class.
+ */
+ template
+ explicit GridVariables(std::shared_ptr gridGeometry,
+ Args&&... args)
+ : ParentType(std::forward(args)...)
+ , gridGeometry_(gridGeometry)
+ {}
+
+ //! Return a reference to the grid geometry
+ const GridGeometry& gridGeometry() const
+ { return *gridGeometry_; }
+
+private:
+ std::shared_ptr gridGeometry_;
+};
+
+} // end namespace Dumux
+
+#endif
diff --git a/dumux/discretization/staggered.hh b/dumux/discretization/staggered.hh
index f8c5bec7e4b9447a16d0d5df59d859c818114f77..82255542803a088b818908fd48e1b8734bdfb46f 100644
--- a/dumux/discretization/staggered.hh
+++ b/dumux/discretization/staggered.hh
@@ -111,8 +111,9 @@ private:
using GVV = GetPropType;
using GFVC = GetPropType;
using GFV = GetPropType;
+ using X = GetPropType;
public:
- using type = StaggeredGridVariables;
+ using type = StaggeredGridVariables;
};
//! Use the cell center element boundary types per default
diff --git a/dumux/discretization/staggered/gridvariables.hh b/dumux/discretization/staggered/gridvariables.hh
index be35f83ac582c677e4e144c0f35849e74dc48bb3..26e7b74f6967bb45aca111cc437e0527f829c8b2 100644
--- a/dumux/discretization/staggered/gridvariables.hh
+++ b/dumux/discretization/staggered/gridvariables.hh
@@ -24,6 +24,8 @@
#ifndef DUMUX_STAGGERED_GRID_VARIABLES_HH
#define DUMUX_STAGGERED_GRID_VARIABLES_HH
+#include
+
#include
namespace Dumux {
@@ -93,7 +95,7 @@ public:
//! return the fv grid geometry
const GridGeometry& gridGeometry() const
- { return (*gridVariables_->gridGeometry_); }
+ { return gridVariables_->gridGeometry(); }
// return the actual grid variables
const ActualGridVariables& gridVariables() const
@@ -189,12 +191,15 @@ public:
* \tparam GVV the type of the grid volume variables
* \tparam GFVC the type of the grid flux variables cache
* \tparam GFV the type of the grid face variables
+ * \tparam X the solution vector type
*/
-template
-class StaggeredGridVariables : public FVGridVariables
+template
+class StaggeredGridVariables
+: public FVGridVariables< GG, GVV, GFVC, std::decay_t()[GG::cellCenterIdx()])> >
{
- using ParentType = FVGridVariables;
- using ThisType = StaggeredGridVariables;
+ using CellSolutionVector = std::decay_t()[GG::cellCenterIdx()])>;
+ using ParentType = FVGridVariables;
+ using ThisType = StaggeredGridVariables;
friend class StaggeredGridVariablesView;
static constexpr auto cellCenterIdx = GG::cellCenterIdx();
@@ -227,7 +232,7 @@ public:
void update(const SolutionVector& curSol)
{
ParentType::update(curSol[cellCenterIdx]);
- curGridFaceVariables_.update(*this->gridGeometry_, curSol[faceIdx]);
+ curGridFaceVariables_.update(this->gridGeometry(), curSol[faceIdx]);
}
//! initialize all variables (stationary case)
@@ -235,8 +240,8 @@ public:
void init(const SolutionVector& curSol)
{
ParentType::init(curSol[cellCenterIdx]);
- curGridFaceVariables_.update(*this->gridGeometry_, curSol[faceIdx]);
- prevGridFaceVariables_.update(*this->gridGeometry_, curSol[faceIdx]);
+ curGridFaceVariables_.update(this->gridGeometry(), curSol[faceIdx]);
+ prevGridFaceVariables_.update(this->gridGeometry(), curSol[faceIdx]);
}
//! Sets the current state as the previous for next time step
diff --git a/dumux/linear/pdesolver.hh b/dumux/linear/pdesolver.hh
index ce204474e949b65155ccd7906161384a6c048fc5..4bb0d5536436b75152232c8453ad7a13856139f5 100644
--- a/dumux/linear/pdesolver.hh
+++ b/dumux/linear/pdesolver.hh
@@ -42,6 +42,8 @@
#include
#include
#include
+#include
+
#include
#include
@@ -67,6 +69,8 @@ class LinearPDESolver : public PDESolver
using TimeLoop = TimeLoopBase;
public:
+ using typename ParentType::Variables;
+
/*!
* \brief The Constructor
*/
@@ -85,7 +89,7 @@ public:
/*!
* \brief Solve a linear PDE system
*/
- void solve(SolutionVector& uCurrentIter) override
+ void solve(Variables& vars) override
{
Dune::Timer assembleTimer(false);
Dune::Timer solveTimer(false);
@@ -102,7 +106,7 @@ public:
// linearize the problem at the current solution
assembleTimer.start();
- this->assembler().assembleJacobianAndResidual(uCurrentIter);
+ this->assembler().assembleJacobianAndResidual(vars);
assembleTimer.stop();
///////////////
@@ -124,7 +128,8 @@ public:
solveTimer.start();
// set the delta vector to zero before solving the linear system!
- SolutionVector deltaU(uCurrentIter);
+ using Backend = VariablesBackend;
+ auto deltaU = Backend::getDofVector(vars);
deltaU = 0;
// solve by calling the appropriate implementation depending on whether the linear solver
@@ -146,8 +151,12 @@ public:
// update the current solution and secondary variables
updateTimer.start();
+
+ // TODO: This currently does one additional copy in case assembly from solution is used
+ auto uCurrentIter = Backend::getDofVector(vars);
uCurrentIter -= deltaU;
- this->assembler().updateGridVariables(uCurrentIter);
+ Backend::update(vars, uCurrentIter);
+
updateTimer.stop();
if (verbose_) {
diff --git a/dumux/multidomain/newtonsolver.hh b/dumux/multidomain/newtonsolver.hh
index dba8c58a41b89b18c546275b1fb0fe29dda0235e..898b263e89ad5f7b3ddfca440340418ae6bfec47 100644
--- a/dumux/multidomain/newtonsolver.hh
+++ b/dumux/multidomain/newtonsolver.hh
@@ -50,7 +50,10 @@ template
{
using ParentType = NewtonSolver;
- using SolutionVector = typename Assembler::ResidualType;
+ using typename ParentType::Backend;
+ using typename ParentType::SolutionVector;
+
+ static constexpr bool assemblerExportsVariables = Impl::exportsVariables;
template