diff --git a/dumux/assembly/staggeredfvassembler.hh b/dumux/assembly/staggeredfvassembler.hh index 28020a6beddcf309029e1ac025a05fb4902538ca..d3dc3203c8e16ea360b8d7a99a2a19090d1853cf 100644 --- a/dumux/assembly/staggeredfvassembler.hh +++ b/dumux/assembly/staggeredfvassembler.hh @@ -109,9 +109,7 @@ public: if(!jacobian_) { - jacobian_ = std::make_shared<JacobianMatrix>(); - jacobian_->setBuildMode(JacobianMatrix::random); - setJacobianPattern(); + setLinearSystem(); } if(!residual_) @@ -129,12 +127,17 @@ public: { // let the local assembler add the element contributions for (const auto element : elements(gridView())) - LocalAssembler::assemble(*this, *jacobian_, *residual_, element, curSol); + LocalAssembler::assembleJacobianAndResidual(*this, *jacobian_, *residual_, element, curSol); // if we get here, everything worked well succeeded = true; if (gridView().comm().size() > 1) succeeded = gridView().comm().min(succeeded); + + // printmatrix(std::cout, (*jacobian_)[cellCenterIdx][cellCenterIdx], "A11", ""); + // printmatrix(std::cout, (*jacobian_)[cellCenterIdx][faceIdx], "A12", ""); + // printmatrix(std::cout, (*jacobian_)[faceIdx][cellCenterIdx], "A21", ""); + // printmatrix(std::cout, (*jacobian_)[faceIdx][faceIdx], "A22", ""); } // throw exception if a problem ocurred catch (NumericalProblem &e) @@ -220,7 +223,9 @@ public: //! computes the residual norm Scalar residualNorm(const SolutionVector& curSol) const { - ResidualType residual(numDofs()); + ResidualType residual; + residual[cellCenterIdx].resize(numCellCenterDofs()); + residual[faceIdx].resize(numFaceDofs()); assembleResidual(residual, curSol); // calculate the square norm of the residual @@ -240,11 +245,23 @@ public: void setLinearSystem(std::shared_ptr<JacobianMatrix> A, std::shared_ptr<SolutionVector> r) { - DUNE_THROW(Dune::NotImplemented, "Setting linear system with existing matrix not implemented yet."); - // jacobian_ = A; - // residual_ = r; + jacobian_ = A; + residual_ = r; // - // // check and/or set the BCRS matrix's build mode + // check and/or set the BCRS matrix's build mode + // convenience references + CCToCCMatrixBlock& A11 = (*jacobian_)[cellCenterIdx][cellCenterIdx]; + CCToFaceMatrixBlock& A12 = (*jacobian_)[cellCenterIdx][faceIdx]; + FaceToFaceMatrixBlock& A21 = (*jacobian_)[faceIdx][cellCenterIdx]; + FaceToCCMatrixBlock& A22 = (*jacobian_)[faceIdx][faceIdx]; + + A11.setBuildMode(CCToCCMatrixBlock::random); + A12.setBuildMode(CCToFaceMatrixBlock::random); + A21.setBuildMode(FaceToFaceMatrixBlock::random); + A22.setBuildMode(FaceToCCMatrixBlock::random); + + setJacobianPattern(); + setResidualSize(); // if (jacobian_->buildMode() == JacobianMatrix::BuildMode::unknown) // jacobian_->setBuildMode(JacobianMatrix::random); // else if (jacobian_->buildMode() != JacobianMatrix::BuildMode::random) diff --git a/dumux/assembly/staggeredlocalassembler.hh b/dumux/assembly/staggeredlocalassembler.hh index 272d4f2d7669d6e7c6f139e4a62ca37d73538e40..42aef23ca2606a796f06479fa3e54603b201318a 100644 --- a/dumux/assembly/staggeredlocalassembler.hh +++ b/dumux/assembly/staggeredlocalassembler.hh @@ -30,6 +30,8 @@ #include <dumux/implicit/properties.hh> #include <dumux/assembly/diffmethod.hh> +#include <dumux/implicit/staggered/primaryvariables.hh> + namespace Dumux { /*! @@ -51,6 +53,9 @@ class StaggeredLocalAssembler<TypeTag, using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); using ElementBoundaryTypes = typename GET_PROP_TYPE(TypeTag, ElementBoundaryTypes); + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry); + using GlobalFaceVars = typename GET_PROP_TYPE(TypeTag, GlobalFaceVars); + using ElementFluxVariablesCache = typename GET_PROP_TYPE(TypeTag, ElementFluxVariablesCache); using Element = typename GET_PROP_TYPE(TypeTag, GridView)::template Codim<0>::Entity; using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); using ElementSolutionVector = typename GET_PROP_TYPE(TypeTag, ElementSolutionVector); @@ -60,8 +65,22 @@ class StaggeredLocalAssembler<TypeTag, using SubControlVolume = typename GET_PROP_TYPE(TypeTag, SubControlVolume); using JacobianMatrix = typename GET_PROP_TYPE(TypeTag, JacobianMatrix); + using NumCellCenterEqVector = typename GET_PROP_TYPE(TypeTag, CellCenterPrimaryVariables); + using NumFaceEqVector = typename GET_PROP_TYPE(TypeTag, FacePrimaryVariables); + + using FaceSolutionVector = typename GET_PROP_TYPE(TypeTag, FaceSolutionVector); + enum { numEq = GET_PROP_VALUE(TypeTag, NumEq) }; + using Indices = typename GET_PROP_TYPE(TypeTag, Indices); + using PriVarIndices = typename Dumux::PriVarIndices<TypeTag>; + using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); + using FacePrimaryVariables = typename GET_PROP_TYPE(TypeTag, FacePrimaryVariables); + using CellCenterPrimaryVariables = typename GET_PROP_TYPE(TypeTag, CellCenterPrimaryVariables); + // using DofTypeIndices = typename GET_PROP(TypeTag, DofTypeIndices); + // typename DofTypeIndices::CellCenterIdx cellCenterIdx; + // typename DofTypeIndices::FaceIdx faceIdx; + static constexpr bool enableGlobalFluxVarsCache = GET_PROP_VALUE(TypeTag, EnableGlobalFluxVariablesCache); public: @@ -71,71 +90,13 @@ public: * to the global matrix. The element residual is written into the right hand side. */ template<class Assembler> - static void assemble(Assembler& assembler, JacobianMatrix& jac, SolutionVector& res, + static void assembleJacobianAndResidual(Assembler& assembler, JacobianMatrix& jac, SolutionVector& res, const Element& element, const SolutionVector& curSol) { - const auto globalI = assembler.fvGridGeometry().elementMapper().index(element); - res[globalI] = assemble_(assembler, jac, element, curSol); - } + using DofTypeIndices = typename GET_PROP(TypeTag, DofTypeIndices); + typename DofTypeIndices::CellCenterIdx cellCenterIdx; + typename DofTypeIndices::FaceIdx faceIdx; - /*! - * \brief Computes the derivatives with respect to the given element and adds them - * to the global matrix. - */ - template<class Assembler> - static void assemble(Assembler& assembler, JacobianMatrix& jac, - const Element& element, const SolutionVector& curSol) - { - assemble_(assembler, jac, element, curSol); - } - - /*! - * \brief Assemble the residual only - */ - template<class Assembler> - static void assemble(Assembler& assembler, SolutionVector& res, - const Element& element, const SolutionVector& curSol) - { - const auto globalI = assembler.fvGridGeometry().elementMapper().index(element); - res[globalI] = assemble_(assembler, element, curSol); - } - - /*! - * \brief Computes the epsilon used for numeric differentiation - * for a given value of a primary variable. - * - * \param priVar The value of the primary variable - */ - static Scalar numericEpsilon(const Scalar priVar) - { - // define the base epsilon as the geometric mean of 1 and the - // resolution of the scalar type. E.g. for standard 64 bit - // floating point values, the resolution is about 10^-16 and - // the base epsilon is thus approximately 10^-8. - /* - static const Scalar baseEps - = Dumux::geometricMean<Scalar>(std::numeric_limits<Scalar>::epsilon(), 1.0); - */ - static const Scalar baseEps = 1e-10; - assert(std::numeric_limits<Scalar>::epsilon()*1e4 < baseEps); - // the epsilon value used for the numeric differentiation is - // now scaled by the absolute value of the primary variable... - return baseEps*(std::abs(priVar) + 1.0); - } - -private: - /*! - * \brief Computes the residual - * - * \return The element residual at the current solution. - */ - template<class Assembler> - static NumEqVector assemble_(Assembler& assembler, - const Element& element, const SolutionVector& curSol) - { - // is the actual element a ghost element? - const bool isGhost = (element.partitionType() == Dune::GhostEntity); - if (isGhost) return NumEqVector(0.0); // get some references for convenience const auto& problem = assembler.problem(); @@ -152,473 +113,73 @@ private: auto elemFluxVarsCache = localView(gridVariables.gridFluxVarsCache()); elemFluxVarsCache.bind(element, fvGeometry, curElemVolVars); - const bool isStationary = localResidual.isStationary(); - auto prevElemVolVars = localView(gridVariables.prevGridVolVars()); - if (!isStationary) - prevElemVolVars.bindElement(element, fvGeometry, localResidual.prevSol()); - - // for compatibility with box models - ElementBoundaryTypes elemBcTypes; - - // the actual element's current residual - NumEqVector residual(0.0); - if (isStationary) - { - residual = localResidual.eval(problem, - element, - fvGeometry, - curElemVolVars, - elemBcTypes, - elemFluxVarsCache)[0]; - } - else - { - residual = localResidual.eval(problem, - element, - fvGeometry, - prevElemVolVars, - curElemVolVars, - elemBcTypes, - elemFluxVarsCache)[0]; - } - - return residual; - } - - /*! - * \brief Computes the derivatives with respect to the given element and adds them - * to the global matrix. - * - * \return The element residual at the current solution. - */ - template<class Assembler> - static NumEqVector assemble_(Assembler& assembler, JacobianMatrix& A, - const Element& element, const SolutionVector& curSol) - { - // get some references for convenience - const auto& problem = assembler.problem(); - const auto& fvGridGeometry = assembler.fvGridGeometry(); - const auto& connectivityMap = fvGridGeometry.connectivityMap(); - auto& localResidual = assembler.localResidual(); - auto& gridVariables = assembler.gridVariables(); - - // prepare the local views - auto fvGeometry = localView(assembler.fvGridGeometry()); - fvGeometry.bind(element); - - auto curElemVolVars = localView(gridVariables.curGridVolVars()); - curElemVolVars.bind(element, fvGeometry, curSol); - - auto elemFluxVarsCache = localView(gridVariables.gridFluxVarsCache()); - elemFluxVarsCache.bind(element, fvGeometry, curElemVolVars); + auto& curGlobalFaceVars = gridVariables.curGridFaceVars(); + auto& prevGlobalFaceVars = gridVariables.prevGridFaceVars(); const bool isStationary = localResidual.isStationary(); auto prevElemVolVars = localView(gridVariables.prevGridVolVars()); if (!isStationary) prevElemVolVars.bindElement(element, fvGeometry, localResidual.prevSol()); - // the global dof of the actual element - const auto globalI = fvGridGeometry.elementMapper().index(element); - - // check for boundaries on the element - // TODO Do we need them for cell-centered models? + // for compatibility with box models ElementBoundaryTypes elemBcTypes; - elemBcTypes.update(problem, element, fvGeometry); - - // is the actual element a ghost element? - const bool isGhost = (element.partitionType() == Dune::GhostEntity); - // the actual element's current residual - NumEqVector residual(0.0); - if (!isGhost) - { - if (isStationary) - { - residual = localResidual.eval(problem, - element, - fvGeometry, - curElemVolVars, - elemBcTypes, - elemFluxVarsCache)[0]; - } - else - { - residual = localResidual.eval(problem, - element, - fvGeometry, - prevElemVolVars, - curElemVolVars, - elemBcTypes, - elemFluxVarsCache)[0]; - } - } - - - // TODO Do we really need this?????????? - // this->model_().updatePVWeights(fvGeometry); + const auto cellCenterGlobalI = assembler.fvGridGeometry().elementMapper().index(element); + res[cellCenterIdx][cellCenterGlobalI] = localResidual.evalCellCenter(problem, + element, + fvGeometry, + prevElemVolVars, + curElemVolVars, + curGlobalFaceVars, + prevGlobalFaceVars, + elemBcTypes, + elemFluxVarsCache)[0]; - ////////////////////////////////////////////////////////////////////////////////////////////////// - // // - // Calculate derivatives of all dofs in stencil with respect to the dofs in the element. In the // - // neighboring elements we do so by computing the derivatives of the fluxes which depend on the // - // actual element. In the actual element we evaluate the derivative of the entire residual. // - // // - ////////////////////////////////////////////////////////////////////////////////////////////////// - static const std::string group = GET_PROP_VALUE(TypeTag, ModelParameterGroup); - static const int numericDifferenceMethod = getParamFromGroup<int>(group, "Implicit.NumericDifferenceMethod"); + // std::cout << "at elem: " << cellCenterGlobalI << std::endl; + // std::cout << "orig res: " << res[cellCenterIdx][cellCenterGlobalI] << std::endl; - // get stencil informations - const auto numNeighbors = connectivityMap[globalI].size(); + // treat the local residua of the face dofs: + // create a cache to reuse some results for the calculation of the derivatives - // container to store the neighboring elements - std::vector<Element> neighborElements; - neighborElements.reserve(numNeighbors); + FaceSolutionVector faceResidualCache; + faceResidualCache.resize(fvGeometry.numScvf()); + faceResidualCache = 0.0; - // get the elements in which we need to evaluate the fluxes - // and calculate these in the undeflected state - Dune::BlockVector<NumEqVector> origFlux(numNeighbors); - origFlux = 0.0; - unsigned int j = 0; - for (const auto& dataJ : connectivityMap[globalI]) + for(auto&& scvf : scvfs(fvGeometry)) { - neighborElements.emplace_back(fvGridGeometry.element(dataJ.globalJ)); - for (const auto scvfIdx : dataJ.scvfsJ) - { - origFlux[j] += localResidual.evalFlux(problem, - neighborElements.back(), - fvGeometry, - curElemVolVars, - elemFluxVarsCache, - fvGeometry.scvf(scvfIdx)); - } - // increment neighbor counter - ++j; + // res[faceIdx][scvf.dofIndex()] = 0.0; + faceResidualCache[scvf.localFaceIdx()] = localResidual.evalFace(problem, + element, + fvGeometry, + scvf, + prevElemVolVars, + curElemVolVars, + curGlobalFaceVars, + prevGlobalFaceVars, + elemBcTypes, + elemFluxVarsCache)[0]; + + res[faceIdx][scvf.dofIndex()] += faceResidualCache[scvf.localFaceIdx()] ; } - // reference to the element's scv (needed later) and corresponding vol vars - const auto& scv = fvGeometry.scv(globalI); - auto& curVolVars = getVolVarAccess(gridVariables.curGridVolVars(), curElemVolVars, scv); - - // save a copy of the original privars and vol vars in order - // to restore the original solution after deflection - const auto origPriVars = curSol[globalI]; - const auto origVolVars = curVolVars; - - // element solution container to be deflected - ElementSolutionVector elemSol({origPriVars}); - - // derivatives in the neighbors with repect to the current elements - Dune::BlockVector<NumEqVector> neighborDeriv(numNeighbors); - for (int pvIdx = 0; pvIdx < numEq; pvIdx++) - { - // reset derivatives of element dof with respect to itself - // as well as neighbor derivatives - NumEqVector partialDeriv(0.0); - neighborDeriv = 0.0; - - if (isGhost) - partialDeriv[pvIdx] = 1.0; - - Scalar eps = numericEpsilon(curVolVars.priVar(pvIdx)); - Scalar delta = 0; - - if (numericDifferenceMethod >= 0) - { - // we are not using backward differences, i.e. we need to - // calculate f(x + \epsilon) - - // deflect primary variables - elemSol[0][pvIdx] += eps; - delta += eps; - - // update the volume variables and the flux var cache - curVolVars.update(elemSol, problem, element, scv); - if (enableGlobalFluxVarsCache) - gridVariables.gridFluxVarsCache().updateElement(element, fvGeometry, curElemVolVars); - else - elemFluxVarsCache.update(element, fvGeometry, curElemVolVars); - - // calculate the residual with the deflected primary variables - if (!isGhost) - { - if (isStationary) - { - partialDeriv = localResidual.eval(problem, - element, - fvGeometry, - curElemVolVars, - elemBcTypes, - elemFluxVarsCache)[0]; - } - else - { - partialDeriv = localResidual.eval(problem, - element, - fvGeometry, - prevElemVolVars, - curElemVolVars, - elemBcTypes, - elemFluxVarsCache)[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) - { - neighborDeriv[k] += localResidual.evalFlux(problem, - neighborElements[k], - fvGeometry, - curElemVolVars, - elemFluxVarsCache, - fvGeometry.scvf(scvfIdx)); - } - } - else - { - // we are using backward differences, i.e. we don't need - // to calculate f(x + \epsilon) and we can recycle the - // (already calculated) residual f(x) - if (!isGhost) - partialDeriv = residual; - neighborDeriv = origFlux; - } - - if (numericDifferenceMethod <= 0) - { - // we are not using forward differences, i.e. we - // need to calculate f(x - \epsilon) - - // deflect the primary variables - elemSol[0][pvIdx] -= delta + eps; - delta += eps; - - // update the volume variables and the flux var cache - curVolVars.update(elemSol, problem, element, scv); - if (enableGlobalFluxVarsCache) - gridVariables.gridFluxVarsCache().updateElement(element, fvGeometry, curElemVolVars); - else - elemFluxVarsCache.update(element, fvGeometry, curElemVolVars); - - // calculate the residual with the deflected primary variables and subtract it - if (!isGhost) - { - if (isStationary) - { - partialDeriv -= localResidual.eval(problem, - element, - fvGeometry, - curElemVolVars, - elemBcTypes, - elemFluxVarsCache)[0]; - } - else - { - partialDeriv -= localResidual.eval(problem, - element, - fvGeometry, - prevElemVolVars, - curElemVolVars, - elemBcTypes, - elemFluxVarsCache)[0]; - } - } - - // calculate the fluxes into element with the deflected primary variables - for (std::size_t k = 0; k < numNeighbors; ++k) - for (auto scvfIdx : connectivityMap[globalI][k].scvfsJ) - { - neighborDeriv[k] += localResidual.evalFlux(problem, - neighborElements[k], - fvGeometry, - curElemVolVars, - elemFluxVarsCache, - fvGeometry.scvf(scvfIdx)); - } - } - else - { - // we are using forward differences, i.e. we don't need to - // calculate f(x - \epsilon) and we can recycle the - // (already calculated) residual f(x) - if (!isGhost) - partialDeriv -= residual; - neighborDeriv -= origFlux; - } - - // divide difference in residuals by the magnitude of the - // deflections between the two function evaluation - if (!isGhost) - partialDeriv /= delta; - neighborDeriv /= delta; - - // restore the original state of the scv's volume variables - curVolVars = origVolVars; - - // restore the current element solution - elemSol[0][pvIdx] = origPriVars[pvIdx]; - - // add the current partial derivatives to the global jacobian matrix - for (int eqIdx = 0; eqIdx < numEq; eqIdx++) - { - // the diagonal entries - A[globalI][globalI][eqIdx][pvIdx] += partialDeriv[eqIdx]; - - // off-diagonal entries - j = 0; - for (const auto& dataJ : connectivityMap[globalI]) - A[dataJ.globalJ][globalI][eqIdx][pvIdx] += neighborDeriv[j++][eqIdx]; - } - } - - ////////////////////////////////////////////////////////////////////////////////////////////// - // // - // Calculate derivatives of the dofs in the element with respect to user-defined additional // - // dof dependencies. We do so by evaluating the change in the source term of the current // - // element with respect to the primary variables at the given additional dofs. // - // // - ////////////////////////////////////////////////////////////////////////////////////////////// - - // const auto& additionalDofDepedencies = problem.getAdditionalDofDependencies(globalI); - // if (!additionalDofDepedencies.empty() && !isGhost) - // { - // // compute the source in the undeflected state - // auto source = localResidual.computeSource(element, fvGeometry, curElemVolVars, scv); - // source *= -scv.volume()*curVolVarsI.extrusionFactor(); - - // // deflect solution at given dofs and recalculate the source - // for (auto globalJ : additionalDofDependencies) - // { - // const auto& scvJ = fvGeometry.scv(globalJ); - // auto& curVolVarsJ = curElemVolVars[scv]; - // const auto& elementJ = fvGridGeometry.element(globalJ); - - // // save a copy of the original privars and volvars - // // to restore original solution after deflection - // const auto origPriVars = curSol[globalJ]; - // const auto origVolVarsJ = curVolVarsJ; - - // // derivatives with repect to the additional DOF we depend on - // for (int pvIdx = 0; pvIdx < numEq; pvIdx++) - // { - // // derivatives of element dof with respect to itself - // NumEqVector partialDeriv(0.0); - // const auto eps = numericEpsilon(curVolVarsJ.priVar(pvIdx)); - // Scalar delta = 0; - - // if (numericDifferenceMethod >= 0) - // { - // // we are not using backward differences, i.e. we need to - // // calculate f(x + \epsilon) - - // // deflect primary variables - // curSol[globalJ][pvIdx] += eps; - // delta += eps; - - // // update the volume variables and the flux var cache - // curVolVarsJ.update(gridVariables.elementSolution(elementJ, curSol), problem, elementJ, scvJ); - - // // calculate the source with the deflected primary variables - // auto deflSource = localResidual.computeSource(element, fvGeometry, curElemVolVars, scv); - // deflSource *= -scv.volume()*curVolVarsI.extrusionFactor(); - // partialDeriv = std::move(deflSource); - // } - // else - // { - // // we are using backward differences, i.e. we don't need - // // to calculate f(x + \epsilon) and we can recycle the - // // (already calculated) source f(x) - // partialDeriv = source; - // } - - // if (numericDifferenceMethod <= 0) - // { - // // we are not using forward differences, i.e. we - // // need to calculate f(x - \epsilon) - - // // deflect the primary variables - // curSol[globalJ][pvIdx] -= delta + eps; - // delta += eps; - - // // update the volume variables and the flux var cache - // curVolVarsJ.update(gridVariables.elementSolution(elementJ, curSol), problem, elementJ, scvJ); - - // // calculate the source with the deflected primary variables and subtract - // auto deflSource = localResidual.computeSource(element, fvGeometry, curElemVolVars, scv); - // deflSource *= -scv.volume()*curVolVarsI.extrusionFactor(); - // partialDeriv -= std::move(deflSource); - // } - // else - // { - // // we are using forward differences, i.e. we don't need to - // // calculate f(x - \epsilon) and we can recycle the - // // (already calculated) source f(x) - // partialDeriv -= source; - // } - - // // divide difference in residuals by the magnitude of the - // // deflections between the two function evaluation - // partialDeriv /= delta; - - // // restore the original state of the dofs privars and the volume variables - // curSol[globalJ] = origPriVars; - // curVolVarsJ = origVolVarsJ; - - // // add the current partial derivatives to the global jacobian matrix - // for (int eqIdx = 0; eqIdx < numEq; eqIdx++) - // A[globalI][globalJ][eqIdx][pvIdx] += partialDeriv[eqIdx]; - // } - // } - // } - - // return the original residual - return residual; - } -private: - template<class T = TypeTag> - static typename std::enable_if<!GET_PROP_VALUE(T, EnableGlobalVolumeVariablesCache), VolumeVariables&>::type - getVolVarAccess(GridVolumeVariables& gridVolVars, ElementVolumeVariables& elemVolVars, const SubControlVolume& scv) - { return elemVolVars[scv]; } + // calculate derivatives of all dofs in stencil with respect to the dofs in the element + evalPartialDerivatives_(assembler, + element, + fvGeometry, + prevElemVolVars, + curElemVolVars, + prevGlobalFaceVars, + curGlobalFaceVars, + elemFluxVarsCache, + elemBcTypes, + jac, + res[cellCenterIdx][cellCenterGlobalI], + faceResidualCache); - template<class T = TypeTag> - static typename std::enable_if<GET_PROP_VALUE(T, EnableGlobalVolumeVariablesCache), VolumeVariables&>::type - getVolVarAccess(GridVolumeVariables& gridVolVars, ElementVolumeVariables& elemVolVars, const SubControlVolume& scv) - { return gridVolVars.volVars(scv); } -}; -//! Explicit assembler with numeric differentiation -template<class TypeTag> -class StaggeredLocalAssembler<TypeTag, - DiffMethod::numeric, - /*implicit=*/false> -{ - using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); - using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); - using ElementBoundaryTypes = typename GET_PROP_TYPE(TypeTag, ElementBoundaryTypes); - using Element = typename GET_PROP_TYPE(TypeTag, GridView)::template Codim<0>::Entity; - using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); - using ElementSolutionVector = typename GET_PROP_TYPE(TypeTag, ElementSolutionVector); - using ElementVolumeVariables = typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables); - using GridVolumeVariables = typename GET_PROP_TYPE(TypeTag, GlobalVolumeVariables); - using VolumeVariables = typename GET_PROP_TYPE(TypeTag, VolumeVariables); - using SubControlVolume = typename GET_PROP_TYPE(TypeTag, SubControlVolume); - using JacobianMatrix = typename GET_PROP_TYPE(TypeTag, JacobianMatrix); - enum { numEq = GET_PROP_VALUE(TypeTag, NumEq) }; - -public: - /*! - * \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<class Assembler> - static void assemble(Assembler& assembler, JacobianMatrix& jac, SolutionVector& res, - const Element& element, const SolutionVector& curSol) - { - const auto globalI = assembler.fvGridGeometry().elementMapper().index(element); - res[globalI] = assemble_(assembler, jac, element, curSol); } /*! @@ -629,6 +190,7 @@ public: static void assemble(Assembler& assembler, JacobianMatrix& jac, const Element& element, const SolutionVector& curSol) { + std::cout << "calling wrong \n"; assemble_(assembler, jac, element, curSol); } @@ -639,8 +201,39 @@ public: static void assemble(Assembler& assembler, SolutionVector& res, const Element& element, const SolutionVector& curSol) { - const auto globalI = assembler.fvGridGeometry().elementMapper().index(element); - res[globalI] = assemble_(assembler, element, curSol); + std::cout << "calling wrong \n"; + // using DofTypeIndices = typename GET_PROP(TypeTag, DofTypeIndices); + // typename DofTypeIndices::CellCenterIdx cellCenterIdx; + // typename DofTypeIndices::FaceIdx faceIdx; + + // const auto cellCenterGlobalI = assembler.fvGridGeometry().elementMapper().index(element); + // res[cellCenterIdx][cellCenterGlobalI] = localResidual.evalCellCenter(problem, + // element, + // fvGeometry, + // prevElemVolVars, + // curElemVolVars, + // curGlobalFaceVars, + // prevGlobalFaceVars, + // elemBcTypes, + // elemFluxVarsCache)[0]; + + + + // treat the local residua of the face dofs: + // create a cache to reuse some results for the calculation of the derivatives + // FaceSolutionVector faceResidualCache; + // faceResidualCache.resize(assembler.fvGridGeometry().numScvf()); + // faceResidualCache = 0.0; + // + // auto fvGeometry = localView(assembler.fvGridGeometry()); + // fvGeometry.bind(element); + // auto faceResiduals = assembleFace_(assembler, element, curSol); + // + // for(auto&& scvf : scvfs(fvGeometry)) + // { + // res[faceIdx][scvf.dofIndex()] += faceResiduals[scvf.localFaceIdx()]; + // faceResidualCache[scvf.localFaceIdx()] = faceResiduals[scvf.localFaceIdx()]; + // } } /*! @@ -666,730 +259,362 @@ public: return baseEps*(std::abs(priVar) + 1.0); } -private: - - /*! - * \brief Computes the residual - * - * \return The element residual at the current solution. - */ - template<class Assembler> - static NumEqVector assemble_(Assembler& assembler, - const Element& element, const SolutionVector& curSol) - { - // is the actual element a ghost element? - const bool isGhost = (element.partitionType() == Dune::GhostEntity); - if (isGhost) return NumEqVector(0.0); - - // get some references for convenience - const auto& problem = assembler.problem(); - auto& localResidual = assembler.localResidual(); - auto& gridVariables = assembler.gridVariables(); - - // using an explicit assembler doesn't make sense for stationary problems - if (localResidual.isStationary()) - DUNE_THROW(Dune::InvalidStateException, "Using explicit jacobian assembler with stationary local residual"); - - // prepare the local views - auto fvGeometry = localView(assembler.fvGridGeometry()); - fvGeometry.bind(element); - - auto curElemVolVars = localView(gridVariables.curGridVolVars()); - curElemVolVars.bindElement(element, fvGeometry, curSol); - - auto prevElemVolVars = localView(gridVariables.prevGridVolVars()); - prevElemVolVars.bind(element, fvGeometry, localResidual.prevSol()); - - auto elemFluxVarsCache = localView(gridVariables.gridFluxVarsCache()); - elemFluxVarsCache.bind(element, fvGeometry, prevElemVolVars); - - // compatibility with box method - ElementBoundaryTypes elemBcTypes; - - // the actual element's previous time step residual - auto residual = localResidual.eval(problem, - element, - fvGeometry, - prevElemVolVars, - elemBcTypes, - elemFluxVarsCache)[0]; - - auto storageResidual = localResidual.evalStorage(problem, - element, - fvGeometry, - prevElemVolVars, - curElemVolVars, - elemBcTypes, - elemFluxVarsCache)[0]; - - residual += storageResidual; - - return residual; - } - - /*! - * \brief Computes the derivatives with respect to the given element and adds them - * to the global matrix. - * - * \return The element residual at the current solution. - */ +protected: template<class Assembler> - static NumEqVector assemble_(Assembler& assembler, JacobianMatrix& A, - const Element& element, const SolutionVector& curSol) - { - // get some references for convenience - const auto& problem = assembler.problem(); - const auto& fvGridGeometry = assembler.fvGridGeometry(); - auto& localResidual = assembler.localResidual(); - auto& gridVariables = assembler.gridVariables(); - - // using an explicit assembler doesn't make sense for stationary problems - if (localResidual.isStationary()) - DUNE_THROW(Dune::InvalidStateException, "Using explicit jacobian assembler with stationary local residual"); - - // prepare the local views - auto fvGeometry = localView(assembler.fvGridGeometry()); - fvGeometry.bind(element); - - auto curElemVolVars = localView(gridVariables.curGridVolVars()); - curElemVolVars.bindElement(element, fvGeometry, curSol); + static void evalPartialDerivatives_(Assembler& assembler, + const Element& element, + const FVElementGeometry& fvGeometry, + const ElementVolumeVariables& prevElemVolVars, + ElementVolumeVariables& curElemVolVars, + const GlobalFaceVars& prevGlobalFaceVars, + GlobalFaceVars& curGlobalFaceVars, + ElementFluxVariablesCache& elemFluxVarsCache, + const ElementBoundaryTypes& elemBcTypes, + JacobianMatrix& matrix, + const NumCellCenterEqVector& ccResidual, + const FaceSolutionVector& faceResidualCache) +{ + // compute the derivatives of the cell center residuals with respect to cell center dofs + dCCdCC_(assembler, element, fvGeometry, prevElemVolVars, curElemVolVars, prevGlobalFaceVars, curGlobalFaceVars, elemFluxVarsCache, elemBcTypes, matrix, ccResidual); - auto prevElemVolVars = localView(gridVariables.prevGridVolVars()); - prevElemVolVars.bind(element, fvGeometry, localResidual.prevSol()); + // compute the derivatives of the cell center residuals with respect to face dofs + dCCdFace_(assembler, element, fvGeometry, prevElemVolVars, curElemVolVars, prevGlobalFaceVars, curGlobalFaceVars, elemFluxVarsCache, elemBcTypes, matrix, ccResidual); - auto elemFluxVarsCache = localView(gridVariables.gridFluxVarsCache()); - elemFluxVarsCache.bind(element, fvGeometry, prevElemVolVars); + // compute the derivatives of the face residuals with respect to cell center dofs + dFacedCC_(assembler, element, fvGeometry, prevElemVolVars, curElemVolVars, prevGlobalFaceVars, curGlobalFaceVars, elemFluxVarsCache, elemBcTypes, matrix, faceResidualCache); - // the global dof of the actual element - const auto globalI = fvGridGeometry.elementMapper().index(element); + // compute the derivatives of the face residuals with respect to face dofs + dFacedFace_(assembler, element, fvGeometry, prevElemVolVars, curElemVolVars, prevGlobalFaceVars, curGlobalFaceVars, elemFluxVarsCache, elemBcTypes, matrix, faceResidualCache); +} - // check for boundaries on the element - // TODO Do we need them for cell-centered models? - ElementBoundaryTypes elemBcTypes; - elemBcTypes.update(problem, element, fvGeometry); +/*! +* \brief Computes the derivatives of the cell center residuals with respect to cell center dofs +*/ +template<class Assembler> +static void dCCdCC_(Assembler& assembler, + const Element& element, + const FVElementGeometry& fvGeometry, + const ElementVolumeVariables& prevElemVolVars, + ElementVolumeVariables& curElemVolVars, + const GlobalFaceVars& prevGlobalFaceVars, + const GlobalFaceVars& curGlobalFaceVars, + ElementFluxVariablesCache& elemFluxVarsCache, + const ElementBoundaryTypes& elemBcTypes, + JacobianMatrix& matrix, + const NumCellCenterEqVector& ccResidual) +{ + using DofTypeIndices = typename GET_PROP(TypeTag, DofTypeIndices); + typename DofTypeIndices::CellCenterIdx cellCenterIdx; - // is the actual element a ghost element? - const bool isGhost = (element.partitionType() == Dune::GhostEntity); + const auto& problem = assembler.problem(); + auto& localResidual = assembler.localResidual(); + auto& gridVariables = assembler.gridVariables(); - // the actual element's previous time step residual - NumEqVector residual(0.0), storageResidual(0.0); - if (!isGhost) - { - residual = localResidual.eval(problem, - element, - fvGeometry, - prevElemVolVars, - elemBcTypes, - elemFluxVarsCache)[0]; - - storageResidual = localResidual.evalStorage(problem, - element, - fvGeometry, - prevElemVolVars, - curElemVolVars, - elemBcTypes, - elemFluxVarsCache)[0]; - - residual += storageResidual; - } + // build derivatives with for cell center dofs w.r.t. cell center dofs + const auto cellCenterGlobalI = assembler.fvGridGeometry().elementMapper().index(element); - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Calculate derivatives of all dofs in stencil with respect to the dofs in the element. In the // - // neighboring elements all derivatives are zero. For the assembled element only the storage // - // derivatives are non-zero. // - ////////////////////////////////////////////////////////////////////////////////////////////////// + const auto& connectivityMap = assembler.fvGridGeometry().connectivityMap(); - static const std::string group = GET_PROP_VALUE(TypeTag, ModelParameterGroup); - static const int numericDifferenceMethod = getParamFromGroup<int>(group, "Implicit.NumericDifferenceMethod"); + for(const auto& globalJ : connectivityMap(cellCenterIdx, cellCenterIdx, cellCenterGlobalI)) + { + // get the volVars of the element with respect to which we are going to build the derivative + auto&& scvJ = fvGeometry.scv(globalJ); + const auto elementJ = fvGeometry.fvGridGeometry().element(globalJ); + auto& curVolVars = getVolVarAccess(gridVariables.curGridVolVars(), curElemVolVars, scvJ); + // auto& curVolVars = getCurVolVars(curElemVolVars, scvJ); + VolumeVariables origVolVars(curVolVars); - // reference to the element's scv (needed later) and corresponding vol vars - const auto& scv = fvGeometry.scv(globalI); - auto& curVolVars = getVolVarAccess(gridVariables.curGridVolVars(), curElemVolVars, scv); + for(auto pvIdx : PriVarIndices(cellCenterIdx)) + { + PrimaryVariables priVars(CellCenterPrimaryVariables(localResidual.prevSol()[cellCenterIdx][globalJ]), + FacePrimaryVariables(0.0)); - // save a copy of the original privars and vol vars in order - // to restore the original solution after deflection - const auto origPriVars = curSol[globalI]; - const auto origVolVars = curVolVars; + const Scalar eps = numericEpsilon(priVars[pvIdx], cellCenterIdx, cellCenterIdx); + priVars[pvIdx] += eps; + ElementSolutionVector elemSol{std::move(priVars)}; + curVolVars.update(elemSol, problem, elementJ, scvJ); - // element solution container to be deflected - ElementSolutionVector elemSol({origPriVars}); + auto deflectedResidual = localResidual.evalCellCenter(problem, element, fvGeometry, prevElemVolVars, curElemVolVars, + prevGlobalFaceVars, curGlobalFaceVars, + elemBcTypes, elemFluxVarsCache); - // derivatives in the neighbors with repect to the current elements - for (int pvIdx = 0; pvIdx < numEq; pvIdx++) - { - // reset derivatives of element dof with respect to itself - // as well as neighbor derivatives - NumEqVector partialDeriv(0.0); - - if (isGhost) - partialDeriv[pvIdx] = 1.0; - - Scalar eps = numericEpsilon(curVolVars.priVar(pvIdx)); - Scalar delta = 0; - - if (numericDifferenceMethod >= 0) - { - // we are not using backward differences, i.e. we need to - // calculate f(x + \epsilon) - - // deflect primary variables - elemSol[0][pvIdx] += eps; - delta += eps; - - // update the volume variables and the flux var cache - curVolVars.update(elemSol, problem, element, scv); - - // calculate the residual with the deflected primary variables - if (!isGhost) - { - partialDeriv = localResidual.evalStorage(problem, - element, - fvGeometry, - prevElemVolVars, - curElemVolVars, - elemBcTypes, - elemFluxVarsCache)[0]; - } - } - else - { - // we are using backward differences, i.e. we don't need - // to calculate f(x + \epsilon) and we can recycle the - // (already calculated) residual f(x) - if (!isGhost) - partialDeriv = storageResidual; - } - - if (numericDifferenceMethod <= 0) - { - // we are not using forward differences, i.e. we - // need to calculate f(x - \epsilon) - - // deflect the primary variables - elemSol[0][pvIdx] -= delta + eps; - delta += eps; - - // update the volume variables and the flux var cache - curVolVars.update(elemSol, problem, element, scv); - - // calculate the residual with the deflected primary variables and subtract it - if (!isGhost) - { - partialDeriv -= localResidual.evalStorage(problem, - element, - fvGeometry, - prevElemVolVars, - curElemVolVars, - elemBcTypes, - elemFluxVarsCache)[0]; - } - } - else - { - // we are using forward differences, i.e. we don't need to - // calculate f(x - \epsilon) and we can recycle the - // (already calculated) residual f(x) - if (!isGhost) - partialDeriv -= storageResidual; - } - - // divide difference in residuals by the magnitude of the - // deflections between the two function evaluation - if (!isGhost) - partialDeriv /= delta; - - // restore the original state of the scv's volume variables - curVolVars = origVolVars; - - // restore the current element solution - elemSol[0][pvIdx] = origPriVars[pvIdx]; - - // add the current partial derivatives to the global jacobian matrix - for (int eqIdx = 0; eqIdx < numEq; eqIdx++) - { - // the diagonal entries - A[globalI][globalI][eqIdx][pvIdx] += partialDeriv[eqIdx]; - } - } + auto partialDeriv = (deflectedResidual - ccResidual); + partialDeriv /= eps; - // return the original residual - return residual; - } -private: - template<class T = TypeTag> - static typename std::enable_if<!GET_PROP_VALUE(T, EnableGlobalVolumeVariablesCache), VolumeVariables&>::type - getVolVarAccess(GridVolumeVariables& gridVolVars, ElementVolumeVariables& elemVolVars, const SubControlVolume& scv) - { return elemVolVars[scv]; } + // update the global jacobian matrix with the current partial derivatives + updateGlobalJacobian_(matrix[cellCenterIdx][cellCenterIdx], cellCenterGlobalI, globalJ, pvIdx, partialDeriv); - template<class T = TypeTag> - static typename std::enable_if<GET_PROP_VALUE(T, EnableGlobalVolumeVariablesCache), VolumeVariables&>::type - getVolVarAccess(GridVolumeVariables& gridVolVars, ElementVolumeVariables& elemVolVars, const SubControlVolume& scv) - { return gridVolVars.volVars(scv); } -}; + // restore the original volVars + curVolVars = origVolVars; + } + } +} -//! implicit assembler using analytic differentiation -template<class TypeTag> -class StaggeredLocalAssembler<TypeTag, - DiffMethod::analytic, - /*implicit=*/true> +/*! +* \brief Computes the derivatives of the cell center residuals with respect to face dofs +*/ +template<class Assembler> +static void dCCdFace_(Assembler& assembler, + const Element& element, + const FVElementGeometry& fvGeometry, + const ElementVolumeVariables& prevElemVolVars, + const ElementVolumeVariables& curElemVolVars, + const GlobalFaceVars& prevGlobalFaceVars, + GlobalFaceVars& curGlobalFaceVars, + ElementFluxVariablesCache& elemFluxVarsCache, + const ElementBoundaryTypes& elemBcTypes, + JacobianMatrix& matrix, + const NumCellCenterEqVector& ccResidual) { - using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); - using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); - using ElementBoundaryTypes = typename GET_PROP_TYPE(TypeTag, ElementBoundaryTypes); - using Element = typename GET_PROP_TYPE(TypeTag, GridView)::template Codim<0>::Entity; - using IndexType = typename GET_PROP_TYPE(TypeTag, GridView)::IndexSet::IndexType; - using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); - using JacobianMatrix = typename GET_PROP_TYPE(TypeTag, JacobianMatrix); - using ElementSolutionVector = typename GET_PROP_TYPE(TypeTag, ElementSolutionVector); - using ElementVolumeVariables = typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables); - using GridVolumeVariables = typename GET_PROP_TYPE(TypeTag, GlobalVolumeVariables); - using VolumeVariables = typename GET_PROP_TYPE(TypeTag, VolumeVariables); - using SubControlVolume = typename GET_PROP_TYPE(TypeTag, SubControlVolume); + // build derivatives with for cell center dofs w.r.t. face dofs + using DofTypeIndices = typename GET_PROP(TypeTag, DofTypeIndices); + typename DofTypeIndices::CellCenterIdx cellCenterIdx; + typename DofTypeIndices::FaceIdx faceIdx; - enum { numEq = GET_PROP_VALUE(TypeTag, NumEq) }; + const auto& problem = assembler.problem(); + auto& localResidual = assembler.localResidual(); + // auto& gridVariables = assembler.gridVariables(); -public: + // build derivatives with for cell center dofs w.r.t. cell center dofs + const auto cellCenterGlobalI = assembler.fvGridGeometry().elementMapper().index(element); - /*! - * \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<class Assembler> - static void assemble(Assembler& assembler, JacobianMatrix& jac, SolutionVector& res, - const Element& element, const SolutionVector& curSol) - { - const auto globalI = assembler.fvGridGeometry().elementMapper().index(element); - res[globalI] = assemble_(assembler, jac, element, curSol); - } + const auto& connectivityMap = assembler.fvGridGeometry().connectivityMap(); - /*! - * \brief Computes the derivatives with respect to the given element and adds them - * to the global matrix. - */ - template<class Assembler> - static void assemble(Assembler& assembler, JacobianMatrix& jac, - const Element& element, const SolutionVector& curSol) - { - assemble_(assembler, jac, element, curSol); - } - - /*! - * \brief Assemble the residual only - */ - template<class Assembler> - static void assemble(Assembler& assembler, SolutionVector& res, - const Element& element, const SolutionVector& curSol) - { - const auto globalI = assembler.fvGridGeometry().elementMapper().index(element); - res[globalI] = assemble_(assembler, element, curSol); - } + for(const auto& globalJ : connectivityMap(cellCenterIdx, faceIdx, cellCenterGlobalI)) + { + // get the faceVars of the face with respect to which we are going to build the derivative + auto origFaceVars = curGlobalFaceVars.faceVars(globalJ); + auto& curFaceVars = curGlobalFaceVars.faceVars(globalJ); -private: + for(auto pvIdx : PriVarIndices(faceIdx)) + { + PrimaryVariables priVars(CellCenterPrimaryVariables(0.0), FacePrimaryVariables(localResidual.prevSol()[faceIdx][globalJ])); - /*! - * \brief Computes the residual - * - * \return The element residual at the current solution. - */ - template<class Assembler> - static NumEqVector assemble_(Assembler& assembler, - const Element& element, const SolutionVector& curSol) - { - // is the actual element a ghost element? - const bool isGhost = (element.partitionType() == Dune::GhostEntity); - if (isGhost) return NumEqVector(0.0); - - // get some references for convenience - const auto& problem = assembler.problem(); - auto& localResidual = assembler.localResidual(); - auto& gridVariables = assembler.gridVariables(); - // prepare the local views - auto fvGeometry = localView(assembler.fvGridGeometry()); - fvGeometry.bind(element); + // std::cout << "orig velo : " << curGlobalFaceVars.faceVars(globalJ).velocity() << std::endl; - auto curElemVolVars = localView(gridVariables.curGridVolVars()); - curElemVolVars.bind(element, fvGeometry, curSol); + const Scalar eps = numericEpsilon(priVars[pvIdx], cellCenterIdx, faceIdx); + priVars[pvIdx] += eps; - auto elemFluxVarsCache = localView(gridVariables.gridFluxVarsCache()); - elemFluxVarsCache.bind(element, fvGeometry, curElemVolVars); + // std::cout << "deflecting " << globalJ << std::endl; + // std::cout << "eps. " << eps << std::endl; + curFaceVars.update(priVars[faceIdx]); - auto prevElemVolVars = localView(gridVariables.prevGridVolVars()); + // std::cout << "deflected velo: " << curGlobalFaceVars.faceVars(globalJ).velocity() << std::endl; - // check for boundaries on the element - // TODO Do we need them for cell-centered models? - ElementBoundaryTypes elemBcTypes; - elemBcTypes.update(problem, element, fvGeometry); + auto deflectedResidual = localResidual.evalCellCenter(problem, element, fvGeometry, + prevElemVolVars, curElemVolVars, + prevGlobalFaceVars, curGlobalFaceVars, + elemBcTypes, elemFluxVarsCache); - NumEqVector residual(0.0); - if (!localResidual.isStationary()) - { - prevElemVolVars.bindElement(element, fvGeometry, localResidual.prevSol()); + auto partialDeriv = (deflectedResidual - ccResidual); + partialDeriv /= eps; - residual = localResidual.eval(problem, - element, - fvGeometry, - curElemVolVars, - elemBcTypes, - elemFluxVarsCache)[0]; - } - else - { - residual = localResidual.eval(problem, - element, - fvGeometry, - prevElemVolVars, - curElemVolVars, - elemBcTypes, - elemFluxVarsCache)[0]; - } + // std::cout << "ccResidual dccdface " << ccResidual << std::endl; + // std::cout << "deflectedResidual dccdface " << deflectedResidual << std::endl; + // std::cout << "partialDeriv dccdface " << partialDeriv << std::endl; - return residual; - } + // update the global jacobian matrix with the current partial derivatives + updateGlobalJacobian_(matrix[cellCenterIdx][faceIdx], cellCenterGlobalI, globalJ, pvIdx - Indices::faceOffset, partialDeriv); - /*! - * \brief Computes the derivatives with respect to the given element and adds them - * to the global matrix. - * - * \return The element residual at the current solution. - */ - template<class Assembler> - static NumEqVector assemble_(Assembler& assembler, JacobianMatrix& A, - const Element& element, const SolutionVector& curSol) - { - // get some references for convenience - const auto& problem = assembler.problem(); - const auto& fvGridGeometry = assembler.fvGridGeometry(); - auto& localResidual = assembler.localResidual(); - auto& gridVariables = assembler.gridVariables(); + // restore the original faceVars + curFaceVars = origFaceVars; + } + } +} - // prepare the local views - auto fvGeometry = localView(assembler.fvGridGeometry()); - fvGeometry.bind(element); +/*! +* \brief Computes the derivatives of the face residuals with respect to cell center dofs +*/ +template<class Assembler> +static void dFacedCC_(Assembler& assembler, + const Element& element, + const FVElementGeometry& fvGeometry, + const ElementVolumeVariables& prevElemVolVars, + ElementVolumeVariables& curElemVolVars, + const GlobalFaceVars& prevGlobalFaceVars, + const GlobalFaceVars& curGlobalFaceVars, + ElementFluxVariablesCache& elemFluxVarsCache, + const ElementBoundaryTypes& elemBcTypes, + JacobianMatrix& matrix, + const FaceSolutionVector& cachedResidual) +{ + for(auto&& scvf : scvfs(fvGeometry)) + { + using DofTypeIndices = typename GET_PROP(TypeTag, DofTypeIndices); + typename DofTypeIndices::CellCenterIdx cellCenterIdx; + typename DofTypeIndices::FaceIdx faceIdx; + + const auto& problem = assembler.problem(); + auto& localResidual = assembler.localResidual(); + auto& gridVariables = assembler.gridVariables(); + const auto& connectivityMap = assembler.fvGridGeometry().connectivityMap(); + + // set the actual dof index + const auto faceGlobalI = scvf.dofIndex(); + + // build derivatives with for face dofs w.r.t. cell center dofs + for(const auto& globalJ : connectivityMap(faceIdx, cellCenterIdx, scvf.index())) + { + // get the volVars of the element with respect to which we are going to build the derivative + auto&& scvJ = fvGeometry.scv(globalJ); + const auto elementJ = fvGeometry.fvGridGeometry().element(globalJ); + auto& curVolVars = getVolVarAccess(gridVariables.curGridVolVars(), curElemVolVars, scvJ); + VolumeVariables origVolVars(curVolVars); + + for(auto pvIdx : PriVarIndices(cellCenterIdx)) + { + PrimaryVariables priVars(CellCenterPrimaryVariables(localResidual.prevSol()[cellCenterIdx][globalJ]), + FacePrimaryVariables(0.0)); + + const Scalar eps = numericEpsilon(priVars[pvIdx], faceIdx, cellCenterIdx); + priVars[pvIdx] += eps; + ElementSolutionVector elemSol{std::move(priVars)}; + curVolVars.update(elemSol, problem, elementJ, scvJ); + + auto deflectedResidual = localResidual.evalFace(problem, element, fvGeometry, scvf, + prevElemVolVars, curElemVolVars, + prevGlobalFaceVars, curGlobalFaceVars, + elemBcTypes, elemFluxVarsCache); + + auto partialDeriv = (deflectedResidual - cachedResidual[scvf.localFaceIdx()]); + partialDeriv /= eps; + // update the global jacobian matrix with the current partial derivatives + updateGlobalJacobian_(matrix[faceIdx][cellCenterIdx], faceGlobalI, globalJ, pvIdx, partialDeriv); + + // restore the original volVars + curVolVars = origVolVars; + } + } + } +} - auto curElemVolVars = localView(gridVariables.curGridVolVars()); - curElemVolVars.bind(element, fvGeometry, curSol); +/*! +* \brief Computes the derivatives of the face residuals with respect to cell center dofs +*/ +template<class Assembler> +static void dFacedFace_(Assembler& assembler, + const Element& element, + const FVElementGeometry& fvGeometry, + const ElementVolumeVariables& prevElemVolVars, + const ElementVolumeVariables& curElemVolVars, + const GlobalFaceVars& prevGlobalFaceVars, + GlobalFaceVars& curGlobalFaceVars, + ElementFluxVariablesCache& elemFluxVarsCache, + const ElementBoundaryTypes& elemBcTypes, + JacobianMatrix& matrix, + const FaceSolutionVector& cachedResidual) +{ + using DofTypeIndices = typename GET_PROP(TypeTag, DofTypeIndices); + typename DofTypeIndices::FaceIdx faceIdx; - auto elemFluxVarsCache = localView(gridVariables.gridFluxVarsCache()); - elemFluxVarsCache.bind(element, fvGeometry, curElemVolVars); + const auto& problem = assembler.problem(); + auto& localResidual = assembler.localResidual(); + const auto& connectivityMap = assembler.fvGridGeometry().connectivityMap(); - const bool isStationary = localResidual.isStationary(); - auto prevElemVolVars = localView(gridVariables.prevGridVolVars()); - if (!isStationary) - prevElemVolVars.bindElement(element, fvGeometry, localResidual.prevSol()); + for(auto&& scvf : scvfs(fvGeometry)) + { + // set the actual dof index + const auto faceGlobalI = scvf.dofIndex(); - // the global dof of the actual element - const auto globalI = fvGridGeometry.elementMapper().index(element); + // build derivatives with for face dofs w.r.t. cell center dofs + for(const auto& globalJ : connectivityMap(faceIdx, faceIdx, scvf.index())) + { + // get the faceVars of the face with respect to which we are going to build the derivative + auto origFaceVars = curGlobalFaceVars.faceVars(globalJ); + auto& curFaceVars = curGlobalFaceVars.faceVars(globalJ); - // check for boundaries on the element - // TODO Do we need them for cell-centered models? - ElementBoundaryTypes elemBcTypes; - elemBcTypes.update(problem, element, fvGeometry); + for(auto pvIdx : PriVarIndices(faceIdx)) + { + PrimaryVariables priVars(CellCenterPrimaryVariables(0.0), FacePrimaryVariables(localResidual.prevSol()[faceIdx][globalJ])); - // is the actual element a ghost element? - const bool isGhost = (element.partitionType() == Dune::GhostEntity); + const Scalar eps = numericEpsilon(priVars[pvIdx], faceIdx, faceIdx); + priVars[pvIdx] += eps; + curFaceVars.update(priVars[faceIdx]); - // the actual element's current residual (will be returned by this function) - NumEqVector residual(0.0); - if (!isGhost) - { - if (isStationary) - { - residual = localResidual.eval(problem, - element, - fvGeometry, - curElemVolVars, - elemBcTypes, - elemFluxVarsCache)[0]; - } - else - { - residual = localResidual.eval(problem, - element, - fvGeometry, - prevElemVolVars, - curElemVolVars, - elemBcTypes, - elemFluxVarsCache)[0]; - } - } + auto deflectedResidual = localResidual.evalFace(problem, element, fvGeometry, scvf, + prevElemVolVars, curElemVolVars, + prevGlobalFaceVars, curGlobalFaceVars, + elemBcTypes, elemFluxVarsCache); - // get reference to the element's current vol vars - const auto& scv = fvGeometry.scv(globalI); - const auto& volVars = curElemVolVars[scv]; + auto partialDeriv = (deflectedResidual - cachedResidual[scvf.localFaceIdx()]); + partialDeriv /= eps; - // if the problem is instationary, add derivative of storage term - if (!isStationary) - localResidual.addStorageDerivatives(A[globalI][globalI], - problem, - element, - fvGeometry, - volVars, - scv); - - // add source term derivatives - localResidual.addSourceDerivatives(A[globalI][globalI], - problem, - element, - fvGeometry, - volVars, - scv); - - // add flux derivatives for each scvf - for (const auto& scvf : scvfs(fvGeometry)) - { - if (!scvf.boundary()) - { - localResidual.addFluxDerivatives(A[globalI], - problem, - element, - fvGeometry, - curElemVolVars, - elemFluxVarsCache, - scvf); - } - else - { - const auto& bcTypes = problem.boundaryTypes(element, scvf); - - // add Dirichlet boundary flux derivatives - if (bcTypes.hasDirichlet() && !bcTypes.hasNeumann()) - { - localResidual.addCCDirichletFluxDerivatives(A[globalI], - problem, - element, - fvGeometry, - curElemVolVars, - elemFluxVarsCache, - scvf); - } - // add Robin ("solution dependent Neumann") boundary flux derivatives - else if (bcTypes.hasNeumann() && !bcTypes.hasDirichlet()) - { - localResidual.addRobinFluxDerivatives(A[globalI], - problem, - element, - fvGeometry, - curElemVolVars, - elemFluxVarsCache, - scvf); - } - else - DUNE_THROW(Dune::NotImplemented, "Mixed boundary conditions. Use pure boundary conditions by converting Dirichlet BCs to Robin BCs"); - } - } + // update the global jacobian matrix with the current partial derivatives + updateGlobalJacobian_(matrix[faceIdx][faceIdx], faceGlobalI, globalJ, pvIdx - Indices::faceOffset, partialDeriv); - // TODO Do we really need this?????????? - // this->model_().updatePVWeights(fvGeometry); + // restore the original faceVars + curFaceVars = origFaceVars; + } + } + } +} - // TODO: Additional dof dependencies??? - // return element residual - return residual; - } -}; -//! explicit assembler using analytic differentiation -template<class TypeTag> -class StaggeredLocalAssembler<TypeTag, - DiffMethod::analytic, - /*implicit=*/false> +static Scalar numericEpsilon(const Scalar priVar, const int idx1, const int idx2) { - using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); - using NumEqVector = typename GET_PROP_TYPE(TypeTag, NumEqVector); - using ElementBoundaryTypes = typename GET_PROP_TYPE(TypeTag, ElementBoundaryTypes); - using Element = typename GET_PROP_TYPE(TypeTag, GridView)::template Codim<0>::Entity; - using IndexType = typename GET_PROP_TYPE(TypeTag, GridView)::IndexSet::IndexType; - using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); - using JacobianMatrix = typename GET_PROP_TYPE(TypeTag, JacobianMatrix); - using ElementSolutionVector = typename GET_PROP_TYPE(TypeTag, ElementSolutionVector); - using ElementVolumeVariables = typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables); - using GridVolumeVariables = typename GET_PROP_TYPE(TypeTag, GlobalVolumeVariables); - using VolumeVariables = typename GET_PROP_TYPE(TypeTag, VolumeVariables); - using SubControlVolume = typename GET_PROP_TYPE(TypeTag, SubControlVolume); + // define the base epsilon as the geometric mean of 1 and the + // resolution of the scalar type. E.g. for standard 64 bit + // floating point values, the resolution is about 10^-16 and + // the base epsilon is thus approximately 10^-8. + /* + static const Scalar baseEps + = Dumux::geometricMean<Scalar>(std::numeric_limits<Scalar>::epsilon(), 1.0); + */ + using BaseEpsilon = typename GET_PROP(TypeTag, BaseEpsilon); + const std::array<std::array<Scalar, 2>, 2> baseEps_ = BaseEpsilon::getEps(); + + + static const Scalar baseEps = baseEps_[idx1][idx2]; + assert(std::numeric_limits<Scalar>::epsilon()*1e4 < baseEps); + // the epsilon value used for the numeric differentiation is + // now scaled by the absolute value of the primary variable... + return baseEps*(std::abs(priVar) + 1.0); +} - enum { numEq = GET_PROP_VALUE(TypeTag, NumEq) }; -public: - - /*! - * \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<class Assembler> - static void assemble(Assembler& assembler, JacobianMatrix& jac, SolutionVector& res, - const Element& element, const SolutionVector& curSol) - { - const auto globalI = assembler.fvGridGeometry().elementMapper().index(element); - res[globalI] = assemble_(assembler, jac, element, curSol); - } - - /*! - * \brief Computes the derivatives with respect to the given element and adds them - * to the global matrix. - */ - template<class Assembler> - static void assemble(Assembler& assembler, JacobianMatrix& jac, - const Element& element, const SolutionVector& curSol) - { - assemble_(assembler, jac, element, curSol); - } - - /*! - * \brief Assemble the residual only - */ - template<class Assembler> - static void assemble(Assembler& assembler, SolutionVector& res, - const Element& element, const SolutionVector& curSol) - { - const auto globalI = assembler.fvGridGeometry().elementMapper().index(element); - res[globalI] = assemble_(assembler, element, curSol); - } - -private: - - /*! - * \brief Computes the residual - * - * \return The element residual at the current solution. - */ - template<class Assembler> - static NumEqVector assemble_(Assembler& assembler, - const Element& element, const SolutionVector& curSol) +/*! + * \brief Updates the current global Jacobian matrix with the + * partial derivatives of all equations in regard to the + * primary variable 'pvIdx' at dof 'col'. Specialization for cc methods. + */ +template<class SubMatrix, class CCOrFacePrimaryVariables> +static void updateGlobalJacobian_(SubMatrix& matrix, + const int globalI, + const int globalJ, + const int pvIdx, + const CCOrFacePrimaryVariables &partialDeriv) +{ + for (int eqIdx = 0; eqIdx < partialDeriv.size(); eqIdx++) { - // is the actual element a ghost element? - const bool isGhost = (element.partitionType() == Dune::GhostEntity); - if (isGhost) return NumEqVector(0.0); - - // get some references for convenience - const auto& problem = assembler.problem(); - auto& localResidual = assembler.localResidual(); - auto& gridVariables = assembler.gridVariables(); - - // using an explicit assembler doesn't make sense for stationary problems - if (localResidual.isStationary()) - DUNE_THROW(Dune::InvalidStateException, "Using explicit jacobian assembler with stationary local residual"); - - // prepare the local views - auto fvGeometry = localView(assembler.fvGridGeometry()); - fvGeometry.bind(element); - - auto curElemVolVars = localView(gridVariables.curGridVolVars()); - curElemVolVars.bindElement(element, fvGeometry, curSol); - - auto prevElemVolVars = localView(gridVariables.prevGridVolVars()); - prevElemVolVars.bind(element, fvGeometry, localResidual.prevSol()); - - auto elemFluxVarsCache = localView(gridVariables.gridFluxVarsCache()); - elemFluxVarsCache.bind(element, fvGeometry, prevElemVolVars); - - // check for boundaries on the element - // TODO Do we need them for cell-centered models? - ElementBoundaryTypes elemBcTypes; - elemBcTypes.update(problem, element, fvGeometry); - - // the actual element's previous time step residual - auto residual = localResidual.eval(problem, - element, - fvGeometry, - prevElemVolVars, - elemBcTypes, - elemFluxVarsCache)[0]; - - auto storageResidual = localResidual.evalStorage(problem, - element, - fvGeometry, - prevElemVolVars, - curElemVolVars, - elemBcTypes, - elemFluxVarsCache)[0]; - - residual += storageResidual; - - return residual; + // A[i][col][eqIdx][pvIdx] is the rate of change of + // the residual of equation 'eqIdx' at dof 'i' + // depending on the primary variable 'pvIdx' at dof + // 'col'. + + assert(pvIdx >= 0); + assert(eqIdx < matrix[globalI][globalJ].size()); + assert(pvIdx < matrix[globalI][globalJ][eqIdx].size()); + matrix[globalI][globalJ][eqIdx][pvIdx] += partialDeriv[eqIdx]; + Valgrind::CheckDefined(matrix[globalI][globalJ][eqIdx][pvIdx]); } +} - /*! - * \brief Computes the derivatives with respect to the given element and adds them - * to the global matrix. - * - * \return The element residual at the current solution. - */ - template<class Assembler> - static NumEqVector assemble_(Assembler& assembler, JacobianMatrix& A, - const Element& element, const SolutionVector& curSol) - { - // get some references for convenience - const auto& problem = assembler.problem(); - const auto& fvGridGeometry = assembler.fvGridGeometry(); - auto& localResidual = assembler.localResidual(); - auto& gridVariables = assembler.gridVariables(); - // using an explicit assembler doesn't make sense for stationary problems - if (localResidual.isStationary()) - DUNE_THROW(Dune::InvalidStateException, "Using explicit jacobian assembler with stationary local residual"); - // prepare the local views - auto fvGeometry = localView(assembler.fvGridGeometry()); - fvGeometry.bind(element); - - auto curElemVolVars = localView(gridVariables.curGridVolVars()); - curElemVolVars.bindElement(element, fvGeometry, curSol); - - auto prevElemVolVars = localView(gridVariables.prevGridVolVars()); - prevElemVolVars.bind(element, fvGeometry, localResidual.prevSol()); - - auto elemFluxVarsCache = localView(gridVariables.gridFluxVarsCache()); - elemFluxVarsCache.bind(element, fvGeometry, prevElemVolVars); - - // the global dof of the actual element - const auto globalI = fvGridGeometry.elementMapper().index(element); - - // check for boundaries on the element - // TODO Do we need them for cell-centered models? - ElementBoundaryTypes elemBcTypes; - elemBcTypes.update(problem, element, fvGeometry); - - // is the actual element a ghost element? - const bool isGhost = (element.partitionType() == Dune::GhostEntity); - // the actual element's previous time step residual - NumEqVector residual(0.0), storageResidual(0.0); - if (!isGhost) - { - residual = localResidual.eval(problem, - element, - fvGeometry, - prevElemVolVars, - elemBcTypes, - elemFluxVarsCache)[0]; - - storageResidual = localResidual.evalStorage(problem, - element, - fvGeometry, - prevElemVolVars, - curElemVolVars, - elemBcTypes, - elemFluxVarsCache)[0]; - - residual += storageResidual; - } - // get reference to the element's current vol vars - const auto& scv = fvGeometry.scv(globalI); - const auto& volVars = curElemVolVars[scv]; - - // add derivative of storage term - localResidual.addStorageDerivatives(A[globalI][globalI], - problem, - element, - fvGeometry, - volVars, - scv); +private: + template<class T = TypeTag> + static typename std::enable_if<!GET_PROP_VALUE(T, EnableGlobalVolumeVariablesCache), VolumeVariables&>::type + getVolVarAccess(GridVolumeVariables& gridVolVars, ElementVolumeVariables& elemVolVars, const SubControlVolume& scv) + { return elemVolVars[scv]; } - // return the original residual - return residual; - } + template<class T = TypeTag> + static typename std::enable_if<GET_PROP_VALUE(T, EnableGlobalVolumeVariablesCache), VolumeVariables&>::type + getVolVarAccess(GridVolumeVariables& gridVolVars, ElementVolumeVariables& elemVolVars, const SubControlVolume& scv) + { return gridVolVars.volVars(scv); } }; } // end namespace Dumux diff --git a/dumux/freeflow/staggered/fluxvariables.hh b/dumux/freeflow/staggered/fluxvariables.hh index 4635bb46e161dbf51ca74f36755801e45dbd0228..86a2e12b126d17e51a17b5bf7f26fc4ce602aa7c 100644 --- a/dumux/freeflow/staggered/fluxvariables.hh +++ b/dumux/freeflow/staggered/fluxvariables.hh @@ -117,7 +117,7 @@ public: const auto& upstreamVolVars = insideIsUpstream ? insideVolVars : outsideVolVars; const auto& downstreamVolVars = insideIsUpstream ? insideVolVars : outsideVolVars; - const Scalar upWindWeight = GET_PROP_VALUE(TypeTag, ImplicitUpwindWeight); + const Scalar upWindWeight = getParamFromGroup<Scalar>(GET_PROP_VALUE(TypeTag, ModelParameterGroup), "Implicit.UpwindWeight"); flux = (upWindWeight * upstreamVolVars.density() + (1.0 - upWindWeight) * downstreamVolVars.density()) * velocity; diff --git a/dumux/freeflow/staggered/localresidual.hh b/dumux/freeflow/staggered/localresidual.hh index 1a4a112db5d87b3e5cea03d275711c5fc3105b58..61caa336cbad9d1224a4142b52618a3cc001ab73 100644 --- a/dumux/freeflow/staggered/localresidual.hh +++ b/dumux/freeflow/staggered/localresidual.hh @@ -96,6 +96,10 @@ class StaggeredNavierStokesResidualImpl<TypeTag, false> : public Dumux::Staggere typename DofTypeIndices::CellCenterIdx cellCenterIdx; typename DofTypeIndices::FaceIdx faceIdx; + using CellCenterResidual = typename GET_PROP_TYPE(TypeTag, CellCenterPrimaryVariables); + using FaceResidual = typename GET_PROP_TYPE(TypeTag, FacePrimaryVariables); + using FaceResidualVector = typename GET_PROP_TYPE(TypeTag, FaceSolutionVector); + enum { // grid and world dimension dim = GridView::dimension, @@ -119,29 +123,32 @@ public: using ParentType::ParentType; - CellCenterPrimaryVariables computeFluxForCellCenter(const Element &element, + CellCenterPrimaryVariables computeFluxForCellCenter(const Problem& problem, + const Element &element, const FVElementGeometry& fvGeometry, const ElementVolumeVariables& elemVolVars, const GlobalFaceVars& globalFaceVars, const SubControlVolumeFace &scvf, - const ElementFluxVariablesCache& elemFluxVarsCache) + const ElementFluxVariablesCache& elemFluxVarsCache) const { FluxVariables fluxVars; - CellCenterPrimaryVariables flux = fluxVars.computeFluxForCellCenter(this->problem(), element, fvGeometry, elemVolVars, + CellCenterPrimaryVariables flux = fluxVars.computeFluxForCellCenter(problem, element, fvGeometry, elemVolVars, globalFaceVars, scvf, elemFluxVarsCache[scvf]); - EnergyFluxVariables::energyFlux(flux, this->problem(), element, fvGeometry, elemVolVars, globalFaceVars, scvf, elemFluxVarsCache[scvf]); + EnergyFluxVariables::energyFlux(flux, problem, element, fvGeometry, elemVolVars, globalFaceVars, scvf, elemFluxVarsCache[scvf]); return flux; } - CellCenterPrimaryVariables computeSourceForCellCenter(const Element &element, + CellCenterPrimaryVariables computeSourceForCellCenter(const Problem& problem, + const Element &element, const FVElementGeometry& fvGeometry, const ElementVolumeVariables& elemVolVars, const GlobalFaceVars& globalFaceVars, - const SubControlVolume &scv) + const SubControlVolume &scv) const { return CellCenterPrimaryVariables(0.0); + // TODO sources } @@ -184,16 +191,17 @@ public: return storage; } - FacePrimaryVariables computeSourceForFace(const SubControlVolumeFace& scvf, + FacePrimaryVariables computeSourceForFace(const Problem& problem, + const SubControlVolumeFace& scvf, const ElementVolumeVariables& elemVolVars, - const GlobalFaceVars& globalFaceVars) + const GlobalFaceVars& globalFaceVars) const { FacePrimaryVariables source(0.0); const auto insideScvIdx = scvf.insideScvIdx(); const auto& insideVolVars = elemVolVars[insideScvIdx]; - source += this->problem().gravity()[scvf.directionIndex()] * insideVolVars.density(); + source += problem.gravity()[scvf.directionIndex()] * insideVolVars.density(); - source += this->problem().sourceAtPos(scvf.center())[faceIdx][scvf.directionIndex()]; + source += problem.sourceAtPos(scvf.center())[faceIdx][scvf.directionIndex()]; return source; } @@ -205,18 +213,19 @@ public: * \param elemVolVars All volume variables for the element * \param globalFaceVars The face variables */ - FacePrimaryVariables computeFluxForFace(const Element& element, + FacePrimaryVariables computeFluxForFace(const Problem& problem, + const Element& element, const SubControlVolumeFace& scvf, const FVElementGeometry& fvGeometry, const ElementVolumeVariables& elemVolVars, const GlobalFaceVars& globalFaceVars, - const ElementFluxVariablesCache& elemFluxVarsCache) + const ElementFluxVariablesCache& elemFluxVarsCache) const { FacePrimaryVariables flux(0.0); FluxVariables fluxVars; - flux += fluxVars.computeNormalMomentumFlux(this->problem(), element, scvf, fvGeometry, elemVolVars, globalFaceVars); - flux += fluxVars.computeTangetialMomentumFlux(this->problem(), element, scvf, fvGeometry, elemVolVars, globalFaceVars); - flux += computePressureTerm_(element, scvf, fvGeometry, elemVolVars, globalFaceVars); + flux += fluxVars.computeNormalMomentumFlux(problem, element, scvf, fvGeometry, elemVolVars, globalFaceVars); + flux += fluxVars.computeTangetialMomentumFlux(problem, element, scvf, fvGeometry, elemVolVars, globalFaceVars); + flux += computePressureTerm_(problem, element, scvf, fvGeometry, elemVolVars, globalFaceVars); return flux; } @@ -242,21 +251,23 @@ protected: /*! * \brief Evaluate boundary conditions for a cell center dof */ - void evalBoundaryForCellCenter_(const Element& element, + void evalBoundaryForCellCenter_(CellCenterResidual& residual, + const Problem& problem, + const Element& element, const FVElementGeometry& fvGeometry, const ElementVolumeVariables& elemVolVars, const GlobalFaceVars& faceVars, const ElementBoundaryTypes& elemBcTypes, - const ElementFluxVariablesCache& elemFluxVarsCache) + const ElementFluxVariablesCache& elemFluxVarsCache) const { for (auto&& scvf : scvfs(fvGeometry)) { if (scvf.boundary()) { - auto boundaryFlux = computeFluxForCellCenter(element, fvGeometry, elemVolVars, faceVars, scvf, elemFluxVarsCache); + auto boundaryFlux = computeFluxForCellCenter(problem, element, fvGeometry, elemVolVars, faceVars, scvf, elemFluxVarsCache); // handle the actual boundary conditions: - const auto bcTypes = this->problem().boundaryTypes(element, scvf); + const auto bcTypes = problem.boundaryTypes(element, scvf); if(bcTypes.hasNeumann()) { @@ -265,14 +276,14 @@ protected: if(bcTypes.isNeumann(eqIdx)) { const auto extrusionFactor = 1.0; //TODO: get correct extrusion factor - boundaryFlux[eqIdx] = this->problem().neumannAtPos(scvf.center())[cellCenterIdx][eqIdx] + boundaryFlux[eqIdx] = problem.neumannAtPos(scvf.center())[cellCenterIdx][eqIdx] * extrusionFactor * scvf.area(); } } - this->ccResidual_ += boundaryFlux; + residual += boundaryFlux; - asImp_().setFixedCell_(fvGeometry.scv(scvf.insideScvIdx()), elemVolVars, bcTypes); + asImp_().setFixedCell_(residual, problem, fvGeometry.scv(scvf.insideScvIdx()), elemVolVars, bcTypes); } } } @@ -285,40 +296,44 @@ protected: * \param elemVolVars The current or previous element volVars * \param bcTypes The boundary types */ - void setFixedCell_(const SubControlVolume& insideScv, + void setFixedCell_(CellCenterResidual& residual, + const Problem& problem, + const SubControlVolume& insideScv, const ElementVolumeVariables& elemVolVars, - const BoundaryTypes& bcTypes) + const BoundaryTypes& bcTypes) const { // set a fixed pressure for cells adjacent to a wall if(bcTypes.isDirichletCell(massBalanceIdx)) { const auto& insideVolVars = elemVolVars[insideScv]; - this->ccResidual_[pressureIdx] = insideVolVars.pressure() - this->problem().dirichletAtPos(insideScv.center())[cellCenterIdx][pressureIdx]; + residual[pressureIdx] = insideVolVars.pressure() - problem.dirichletAtPos(insideScv.center())[cellCenterIdx][pressureIdx]; } } /*! * \brief Evaluate boundary conditions for a face dof */ - void evalBoundaryForFace_(const Element& element, + void evalBoundaryForFace_(FaceResidual& residual, + const Problem& problem, + const Element& element, const FVElementGeometry& fvGeometry, const SubControlVolumeFace& scvf, const ElementVolumeVariables& elemVolVars, const GlobalFaceVars& faceVars, const ElementBoundaryTypes& elemBcTypes, - const ElementFluxVariablesCache& elemFluxVarsCache) + const ElementFluxVariablesCache& elemFluxVarsCache) const { if (scvf.boundary()) { // handle the actual boundary conditions: - const auto bcTypes = this->problem().boundaryTypes(element, scvf); + const auto bcTypes = problem.boundaryTypes(element, scvf); // set a fixed value for the velocity for Dirichlet boundary conditions if(bcTypes.isDirichlet(momentumBalanceIdx)) { const Scalar velocity = faceVars.faceVars(scvf.dofIndex()).velocity(); - const Scalar dirichletValue = this->problem().dirichlet(element, scvf)[faceIdx][scvf.directionIndex()]; - this->faceResiduals_[scvf.localFaceIdx()] = velocity - dirichletValue; + const Scalar dirichletValue = problem.dirichlet(element, scvf)[faceIdx][scvf.directionIndex()]; + residual = velocity - dirichletValue; } // For symmetry boundary conditions, there is no flow accross the boundary and @@ -327,14 +342,14 @@ protected: { const Scalar velocity = faceVars.faceVars(scvf.dofIndex()).velocity(); const Scalar fixedValue = 0.0; - this->faceResiduals_[scvf.localFaceIdx()] = velocity - fixedValue; + residual = velocity - fixedValue; } // outflow condition for the momentum balance equation if(bcTypes.isOutflow(momentumBalanceIdx)) { if(bcTypes.isDirichlet(massBalanceIdx)) - this->faceResiduals_[scvf.localFaceIdx()] += computeFluxForFace(element, scvf, fvGeometry, elemVolVars, faceVars, elemFluxVarsCache); + residual += computeFluxForFace(problem, element, scvf, fvGeometry, elemVolVars, faceVars, elemFluxVarsCache); else DUNE_THROW(Dune::InvalidStateException, "Face at " << scvf.center() << " has an outflow BC for the momentum balance but no Dirichlet BC for the pressure!"); } @@ -353,23 +368,24 @@ private: * \param elemVolVars All volume variables for the element * \param globalFaceVars The face variables */ - FacePrimaryVariables computePressureTerm_(const Element& element, + FacePrimaryVariables computePressureTerm_(const Problem& problem, + const Element& element, const SubControlVolumeFace& scvf, const FVElementGeometry& fvGeometry, const ElementVolumeVariables& elemVolVars, - const GlobalFaceVars& globalFaceVars) + const GlobalFaceVars& globalFaceVars) const { const auto insideScvIdx = scvf.insideScvIdx(); const auto& insideVolVars = elemVolVars[insideScvIdx]; - const Scalar deltaP = normalizePressure ? this->problem().initialAtPos(scvf.center())[cellCenterIdx][pressureIdx] : 0.0; + const Scalar deltaP = normalizePressure ? problem.initialAtPos(scvf.center())[cellCenterIdx][pressureIdx] : 0.0; Scalar result = (insideVolVars.pressure() - deltaP) * scvf.area() * -1.0 * sign(scvf.outerNormalScalar()); // treat outflow BCs if(scvf.boundary()) { - const Scalar pressure = this->problem().dirichlet(element, scvf)[cellCenterIdx][pressureIdx] - deltaP; + const Scalar pressure = problem.dirichlet(element, scvf)[cellCenterIdx][pressureIdx] - deltaP; result += pressure * scvf.area() * sign(scvf.outerNormalScalar()); } return result; diff --git a/dumux/freeflow/staggered/vtkoutputfields.hh b/dumux/freeflow/staggered/vtkoutputfields.hh index 6708e6a8bdd08eb26bf8c6bea193b3ef3c734cf5..5d142ea1acca34be679a944090837b12d25b0432 100644 --- a/dumux/freeflow/staggered/vtkoutputfields.hh +++ b/dumux/freeflow/staggered/vtkoutputfields.hh @@ -45,7 +45,8 @@ public: // vtk.addSecondaryVariable("Sn", [](const VolumeVariables& v){ return v.saturation(Indices::nPhaseIdx); }); // vtk.addSecondaryVariable("pw", [](const VolumeVariables& v){ return v.pressure(Indices::wPhaseIdx); }); // vtk.addSecondaryVariable("pn", [](const VolumeVariables& v){ return v.pressure(Indices::nPhaseIdx); }); - vtk.addSecondaryVariable("p", [](const VolumeVariables& v){ return v.pressure(); }); + // vtk.addSecondaryVariable("p", [](const VolumeVariables& v){ return v.pressure(); }); + vtk.addVolumeVariable([](const VolumeVariables& v){ return v.pressure(); }, "p"); // vtk.addSecondaryVariable("pc", [](const VolumeVariables& v){ return v.capillaryPressure(); }); // vtk.addSecondaryVariable("rhoW", [](const VolumeVariables& v){ return v.density(Indices::wPhaseIdx); }); // vtk.addSecondaryVariable("rhoN", [](const VolumeVariables& v){ return v.density(Indices::nPhaseIdx); }); diff --git a/dumux/implicit/staggered/localresidual.hh b/dumux/implicit/staggered/localresidual.hh index 4b8d1be3e9b9edf1f41dfd84eb4787ccf9cf4376..e2f3caf56bb3a04505e51c143fa75bf96ed513af 100644 --- a/dumux/implicit/staggered/localresidual.hh +++ b/dumux/implicit/staggered/localresidual.hh @@ -68,6 +68,10 @@ class StaggeredLocalResidual using FacePrimaryVariables = typename GET_PROP_TYPE(TypeTag, FacePrimaryVariables); using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + using CellCenterResidual = typename GET_PROP_TYPE(TypeTag, CellCenterPrimaryVariables); + using FaceResidual = typename GET_PROP_TYPE(TypeTag, FacePrimaryVariables); + using FaceResidualVector = typename GET_PROP_TYPE(TypeTag, FaceSolutionVector); + using DofTypeIndices = typename GET_PROP(TypeTag, DofTypeIndices); typename DofTypeIndices::CellCenterIdx cellCenterIdx; @@ -98,89 +102,6 @@ public: */ // \{ - /*! - * \brief Compute the local residual, i.e. the deviation of the - * equations from zero. - * - * \param element The DUNE Codim<0> entity for which the residual - * ought to be calculated - */ - // void eval(const Element &element) - // { - // // make sure FVElementGeometry and volume variables are bound to the element - // auto fvGeometry = localView(this->problem().model().fvGridGeometry()); - // fvGeometry.bind(element); - // - // auto curElemVolVars = localView(problem().model().curGlobalVolVars()); - // curElemVolVars.bind(element, fvGeometry, problem().model().curSol()); - // - // auto prevElemVolVars = localView(problem().model().prevGlobalVolVars()); - // prevElemVolVars.bindElement(element, fvGeometry, problem().model().prevSol()); - // - // auto elemFluxVarsCache = localView(problem().model().globalFluxVarsCache()); - // elemFluxVarsCache.bindElement(element, fvGeometry, curElemVolVars); - // - // ElementBoundaryTypes bcTypes; - // bcTypes.update(problem(), element, fvGeometry); - // - // auto& curGlobalFaceVars = problem().model().curGlobalFaceVars(); - // auto& prevGlobalFaceVars = problem().model().prevGlobalFaceVars(); - // - // - // asImp_().eval(element, fvGeometry, - // prevElemVolVars, curElemVolVars, - // prevGlobalFaceVars, curGlobalFaceVars, - // bcTypes, elemFluxVarsCache); - // } - - /*! - * \brief Compute the local residual, i.e. the deviation of the - * equations from zero. - * - * \param element The DUNE Codim<0> entity for which the residual - * ought to be calculated - * \param fvGeometry The finite-volume geometry of the element - * \param prevVolVars The volume averaged variables for all - * sub-control volumes of the element at the previous - * time level - * \param curVolVars The volume averaged variables for all - * sub-control volumes of the element at the current - * time level - * \param bcTypes The types of the boundary conditions for all - * vertices of the element - */ - void eval(const Problem& problem, - const Element &element, - const FVElementGeometry& fvGeometry, - const ElementVolumeVariables& prevElemVolVars, - const ElementVolumeVariables& curElemVolVars, - const GlobalFaceVars& prevFaceVars, - const GlobalFaceVars& curFaceVars, - const ElementBoundaryTypes &bcTypes, - const ElementFluxVariablesCache& elemFluxVarsCache) - { - // resize and reset all face terms - const auto numScvf = fvGeometry.numScvf(); - - faceResiduals_.resize(numScvf, false /*copyOldValues*/); - faceStorageTerms_.resize(numScvf, false /*copyOldValues*/); - faceResiduals_ = 0.0; - faceStorageTerms_ = 0.0; - - // evaluate the volume terms (storage + source terms) - for (auto&& scv : scvs(fvGeometry)) - { - // treat the cell center dof - evalCellCenter(problem, element, fvGeometry, scv, prevElemVolVars, curElemVolVars, prevFaceVars, curFaceVars, bcTypes, elemFluxVarsCache); - - // now, treat the dofs on the facets: - for(auto&& scvf : scvfs(fvGeometry)) - { - evalFace(problem, element, fvGeometry, scvf, prevElemVolVars, curElemVolVars, prevFaceVars, curFaceVars, bcTypes, elemFluxVarsCache); - } - } - } - /*! * \brief Compute the local residual, i.e. the deviation of the * equations from zero. @@ -197,24 +118,26 @@ public: * \param bcTypes The types of the boundary conditions for all * vertices of the element */ - void evalCellCenter(const Problem& problem, + auto evalCellCenter(const Problem& problem, const Element &element, const FVElementGeometry& fvGeometry, - const SubControlVolume& scv, const ElementVolumeVariables& prevElemVolVars, const ElementVolumeVariables& curElemVolVars, const GlobalFaceVars& prevFaceVars, const GlobalFaceVars& curFaceVars, const ElementBoundaryTypes &bcTypes, - const ElementFluxVariablesCache& elemFluxVarsCache) + const ElementFluxVariablesCache& elemFluxVarsCache) const { // reset all terms - ccResidual_ = 0.0; - ccStorageTerm_ = 0.0; + CellCenterResidual residual; + residual = 0.0; + // ccStorageTerm_ = 0.0; + + asImp_().evalVolumeTermForCellCenter_(residual, problem, element, fvGeometry, prevElemVolVars, curElemVolVars, prevFaceVars, curFaceVars, bcTypes); + asImp_().evalFluxesForCellCenter_(residual, problem, element, fvGeometry, curElemVolVars, curFaceVars, bcTypes, elemFluxVarsCache); + asImp_().evalBoundaryForCellCenter_(residual, problem, element, fvGeometry, curElemVolVars, curFaceVars, bcTypes, elemFluxVarsCache); - asImp_().evalVolumeTermForCellCenter_(problem, element, fvGeometry, scv, prevElemVolVars, curElemVolVars, prevFaceVars, curFaceVars, bcTypes); - asImp_().evalFluxesForCellCenter_(problem, element, fvGeometry, curElemVolVars, curFaceVars, bcTypes, elemFluxVarsCache); - asImp_().evalBoundaryForCellCenter_(problem, element, fvGeometry, curElemVolVars, curFaceVars, bcTypes, elemFluxVarsCache); + return residual; } /*! @@ -233,7 +156,7 @@ public: * \param bcTypes The types of the boundary conditions for all * vertices of the element */ - void evalFace(const Problem& problem, + auto evalFace(const Problem& problem, const Element &element, const FVElementGeometry& fvGeometry, const SubControlVolumeFace& scvf, @@ -243,32 +166,38 @@ public: const GlobalFaceVars& curFaceVars, const ElementBoundaryTypes &bcTypes, const ElementFluxVariablesCache& elemFluxVarsCache, - const bool resizeResidual = false) + const bool resizeResidual = false) const { - if(resizeResidual) - { - const auto numScvf = fvGeometry.numScvf(); - faceResiduals_.resize(numScvf); - faceStorageTerms_.resize(numScvf); - } + FaceResidual residual; - faceResiduals_[scvf.localFaceIdx()] = 0.0; - faceStorageTerms_[scvf.localFaceIdx()] = 0.0; + asImp_().evalVolumeTermForFace_(residual, problem, element, fvGeometry, scvf, prevElemVolVars, curElemVolVars, prevFaceVars, curFaceVars, bcTypes); + asImp_().evalFluxesForFace_(residual, problem, element, fvGeometry, scvf, curElemVolVars, curFaceVars, bcTypes, elemFluxVarsCache); + asImp_().evalBoundaryForFace_(residual, problem, element, fvGeometry, scvf, curElemVolVars, curFaceVars, bcTypes, elemFluxVarsCache); - asImp_().evalVolumeTermForFace_(problem, element, fvGeometry, scvf, prevElemVolVars, curElemVolVars, prevFaceVars, curFaceVars, bcTypes); - asImp_().evalFluxesForFace_(problem, element, fvGeometry, scvf, curElemVolVars, curFaceVars, bcTypes, elemFluxVarsCache); - asImp_().evalBoundaryForFace_(problem, element, fvGeometry, scvf, curElemVolVars, curFaceVars, bcTypes, elemFluxVarsCache); + return residual; } + /*! + * \brief Sets the solution from which to start the time integration. Has to be + * called prior to assembly for time-dependent problems. + */ + void setPreviousSolution(const SolutionVector& u) + { prevSol_ = &u; } - const auto& ccResidual() const - { return ccResidual_; } - - const auto& faceResiduals() const - { return faceResiduals_; } + /*! + * \brief Return the solution that has been set as the previous one. + */ + const SolutionVector& prevSol() const + { + assert(prevSol_ && "no solution set for storage term evaluation"); + return *prevSol_; + } - const auto& faceResidual(const int fIdx) const - { return faceResiduals_[fIdx]; } + /*! + * \brief If no solution has been set, we treat the problem as stationary. + */ + bool isStationary() const + { return !prevSol_; } protected: @@ -276,35 +205,37 @@ protected: /*! * \brief Evaluate the flux terms for cell center dofs */ - void evalFluxesForCellCenter_(const Problem& problem, + void evalFluxesForCellCenter_(CellCenterResidual& residual, + const Problem& problem, const Element& element, const FVElementGeometry& fvGeometry, const ElementVolumeVariables& elemVolVars, const GlobalFaceVars& faceVars, const ElementBoundaryTypes& bcTypes, - const ElementFluxVariablesCache& elemFluxVarsCache) + const ElementFluxVariablesCache& elemFluxVarsCache) const { for (auto&& scvf : scvfs(fvGeometry)) { if(!scvf.boundary()) - ccResidual_ += asImp_().computeFluxForCellCenter(problem, element, fvGeometry, elemVolVars, faceVars, scvf, elemFluxVarsCache); + residual += asImp_().computeFluxForCellCenter(problem, element, fvGeometry, elemVolVars, faceVars, scvf, elemFluxVarsCache); } } /*! * \brief Evaluate the flux terms for face dofs */ - void evalFluxesForFace_(const Problem& problem, + void evalFluxesForFace_(FaceResidual& residual, + const Problem& problem, const Element& element, const FVElementGeometry& fvGeometry, const SubControlVolumeFace& scvf, const ElementVolumeVariables& elemVolVars, const GlobalFaceVars& globalFaceVars, const ElementBoundaryTypes& bcTypes, - const ElementFluxVariablesCache& elemFluxVarsCache) + const ElementFluxVariablesCache& elemFluxVarsCache) const { if(!scvf.boundary()) - faceResiduals_[scvf.localFaceIdx()] += asImp_().computeFluxForFace(problem, element, scvf, fvGeometry, elemVolVars, globalFaceVars, elemFluxVarsCache); + residual += asImp_().computeFluxForFace(problem, element, scvf, fvGeometry, elemVolVars, globalFaceVars, elemFluxVarsCache); } /*! @@ -328,23 +259,25 @@ protected: */ template<class P = Problem> typename std::enable_if<Dumux::Capabilities::isStationary<P>::value, void>::type - evalVolumeTermForCellCenter_(const Problem& problem, + evalVolumeTermForCellCenter_(CellCenterResidual& residual, + const Problem& problem, const Element &element, const FVElementGeometry& fvGeometry, - const SubControlVolume& scv, const ElementVolumeVariables& prevElemVolVars, const ElementVolumeVariables& curElemVolVars, const GlobalFaceVars& prevFaceVars, const GlobalFaceVars& curFaceVars, - const ElementBoundaryTypes &bcTypes) + const ElementBoundaryTypes &bcTypes) const { - const auto curExtrusionFactor = curElemVolVars[scv].extrusionFactor(); - - // subtract the source term from the local rate - CellCenterPrimaryVariables source = asImp_().computeSourceForCellCenter(problem, element, fvGeometry, curElemVolVars, curFaceVars, scv); - source *= scv.volume()*curExtrusionFactor; + for(auto&& scv : scvs(fvGeometry)) + { + const auto curExtrusionFactor = curElemVolVars[scv].extrusionFactor(); - ccResidual_ -= source; + // subtract the source term from the local rate + CellCenterPrimaryVariables source = asImp_().computeSourceForCellCenter(problem, element, fvGeometry, curElemVolVars, curFaceVars, scv); + source *= scv.volume()*curExtrusionFactor; + residual -= source; + } } /*! @@ -352,7 +285,8 @@ protected: */ template<class P = Problem> typename std::enable_if<Dumux::Capabilities::isStationary<P>::value, void>::type - evalVolumeTermForFace_(const Problem& problem, + evalVolumeTermForFace_(FaceResidual& residual, + const Problem& problem, const Element &element, const FVElementGeometry& fvGeometry, const SubControlVolumeFace& scvf, @@ -360,14 +294,14 @@ protected: const ElementVolumeVariables& curElemVolVars, const GlobalFaceVars& prevFaceVars, const GlobalFaceVars& curFaceVars, - const ElementBoundaryTypes &bcTypes) + const ElementBoundaryTypes &bcTypes) const { // the source term: auto faceSource = asImp_().computeSourceForFace(problem, scvf, curElemVolVars, curFaceVars); const auto& scv = fvGeometry.scv(scvf.insideScvIdx()); const auto curExtrusionFactor = curElemVolVars[scv].extrusionFactor(); faceSource *= 0.5*scv.volume()*curExtrusionFactor; - faceResiduals_[scvf.localFaceIdx()] -= faceSource; + residual -= faceSource; } /*! @@ -375,44 +309,49 @@ protected: */ template<class P = Problem> typename std::enable_if<!Dumux::Capabilities::isStationary<P>::value, void>::type - evalVolumeTermForCellCenter_(const Problem& problem, - const Element &element, - const FVElementGeometry& fvGeometry, - const SubControlVolume& scv, - const ElementVolumeVariables& prevElemVolVars, - const ElementVolumeVariables& curElemVolVars, - const GlobalFaceVars& prevFaceVars, - const GlobalFaceVars& curFaceVars, - const ElementBoundaryTypes &bcTypes) + evalVolumeTermForCellCenter_(CellCenterResidual& residual, + const Problem& problem, + const Element &element, + const FVElementGeometry& fvGeometry, + const ElementVolumeVariables& prevElemVolVars, + const ElementVolumeVariables& curElemVolVars, + const GlobalFaceVars& prevFaceVars, + const GlobalFaceVars& curFaceVars, + const ElementBoundaryTypes &bcTypes) const { - const auto& curVolVars = curElemVolVars[scv]; - const auto& prevVolVars = prevElemVolVars[scv]; + for(auto&& scv : scvs(fvGeometry)) + { + const auto& curVolVars = curElemVolVars[scv]; + const auto& prevVolVars = prevElemVolVars[scv]; - // mass balance within the element. this is the - // \f$\frac{m}{\partial t}\f$ term if using implicit - // euler as time discretization. - // - // We might need a more explicit way for - // doing the time discretization... - auto prevCCStorage = asImp_().computeStorageForCellCenter(problem, scv, prevVolVars); - auto curCCStorage = asImp_().computeStorageForCellCenter(problem, scv, curVolVars); + // mass balance within the element. this is the + // \f$\frac{m}{\partial t}\f$ term if using implicit + // euler as time discretization. + // + // We might need a more explicit way for + // doing the time discretization... + auto prevCCStorage = asImp_().computeStorageForCellCenter(problem, scv, prevVolVars); + auto curCCStorage = asImp_().computeStorageForCellCenter(problem, scv, curVolVars); - prevCCStorage *= prevVolVars.extrusionFactor(); - curCCStorage *= curVolVars.extrusionFactor(); + prevCCStorage *= prevVolVars.extrusionFactor(); + curCCStorage *= curVolVars.extrusionFactor(); - ccStorageTerm_ = std::move(curCCStorage); - ccStorageTerm_ -= std::move(prevCCStorage); - ccStorageTerm_ *= scv.volume(); - ccStorageTerm_ /= timeLoop_->timeStepSize(); + CellCenterResidual storageTerm = 0.0; - // add the storage term to the residual - ccResidual_ += ccStorageTerm_; + storageTerm = std::move(curCCStorage); + storageTerm -= std::move(prevCCStorage); + storageTerm *= scv.volume(); + storageTerm /= timeLoop_->timeStepSize(); - // subtract the source term from the local rate - CellCenterPrimaryVariables source = asImp_().computeSourceForCellCenter(problem, element, fvGeometry, curElemVolVars, curFaceVars, scv); - source *= scv.volume()*curVolVars.extrusionFactor(); + // add the storage term to the residual + residual += storageTerm; - ccResidual_ -= source; + // subtract the source term from the local rate + CellCenterPrimaryVariables source = asImp_().computeSourceForCellCenter(problem, element, fvGeometry, curElemVolVars, curFaceVars, scv); + source *= scv.volume()*curVolVars.extrusionFactor(); + + residual -= source; + } } /*! @@ -420,15 +359,16 @@ protected: */ template<class P = Problem> typename std::enable_if<!Dumux::Capabilities::isStationary<P>::value, void>::type - evalVolumeTermForFace_(const Problem& problem, - const Element &element, - const FVElementGeometry& fvGeometry, - const SubControlVolumeFace& scvf, - const ElementVolumeVariables& prevElemVolVars, - const ElementVolumeVariables& curElemVolVars, - const GlobalFaceVars& prevFaceVars, - const GlobalFaceVars& curFaceVars, - const ElementBoundaryTypes &bcTypes) + evalVolumeTermForFace_(FaceResidual& residual, + const Problem& problem, + const Element &element, + const FVElementGeometry& fvGeometry, + const SubControlVolumeFace& scvf, + const ElementVolumeVariables& prevElemVolVars, + const ElementVolumeVariables& curElemVolVars, + const GlobalFaceVars& prevFaceVars, + const GlobalFaceVars& curFaceVars, + const ElementBoundaryTypes &bcTypes) const { const auto& scv = fvGeometry.scv(scvf.insideScvIdx()); const auto& curVolVars = curElemVolVars[scv]; @@ -437,16 +377,16 @@ protected: auto curFaceStorage = asImp_().computeStorageForFace(problem, scvf, curVolVars, curFaceVars); // the storage term - faceStorageTerms_[scvf.localFaceIdx()] = std::move(curFaceStorage); - faceStorageTerms_[scvf.localFaceIdx()] -= std::move(prevFaceStorage); - faceStorageTerms_[scvf.localFaceIdx()] *= (scv.volume()/2.0); - faceStorageTerms_[scvf.localFaceIdx()] /= timeLoop_->timeStepSize(); - faceResiduals_[scvf.localFaceIdx()] += faceStorageTerms_[scvf.localFaceIdx()]; + residual = std::move(curFaceStorage); + residual -= std::move(prevFaceStorage); + residual *= (scv.volume()/2.0); + residual /= timeLoop_->timeStepSize(); + // residuals[scvf.localFaceIdx()] += faceStorageTerms_[scvf.localFaceIdx()]; // the source term: auto faceSource = asImp_().computeSourceForFace(problem, scvf, curElemVolVars, curFaceVars); faceSource *= 0.5*scv.volume()*curVolVars.extrusionFactor(); - faceResiduals_[scvf.localFaceIdx()] -= faceSource; + residual -= faceSource; } Implementation &asImp_() @@ -455,36 +395,7 @@ protected: const Implementation &asImp_() const { return *static_cast<const Implementation*>(this); } - CellCenterPrimaryVariables ccResidual_; - CellCenterPrimaryVariables ccStorageTerm_; - FaceSolutionVector faceResiduals_; - FaceSolutionVector faceStorageTerms_; - - - - /*! - * \brief Sets the solution from which to start the time integration. Has to be - * called prior to assembly for time-dependent problems. - */ - void setPreviousSolution(const SolutionVector& u) - { prevSol_ = &u; } - - /*! - * \brief Return the solution that has been set as the previous one. - */ - const SolutionVector& prevSol() const - { - assert(prevSol_ && "no solution set for storage term evaluation"); - return *prevSol_; - } - - /*! - * \brief If no solution has been set, we treat the problem as stationary. - */ - bool isStationary() const - { return !prevSol_; } -protected: TimeLoop& timeLoop() { return *timeLoop_; } diff --git a/dumux/implicit/staggered/newtoncontroller.hh b/dumux/implicit/staggered/newtoncontroller.hh index d142371dce7c2e81bb6f75dd318a8d635adeb6a3..c10185d12e2594e4980f65b219794acceefcfcb3 100644 --- a/dumux/implicit/staggered/newtoncontroller.hh +++ b/dumux/implicit/staggered/newtoncontroller.hh @@ -65,6 +65,9 @@ class StaggeredNewtonController : public NewtonController<TypeTag> typedef typename GET_PROP_TYPE(TypeTag, JacobianMatrix) JacobianMatrix; typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using Communicator = typename GridView::CollectiveCommunication; + using DofTypeIndices = typename GET_PROP(TypeTag, DofTypeIndices); typename DofTypeIndices::CellCenterIdx cellCenterIdx; typename DofTypeIndices::FaceIdx faceIdx; @@ -75,8 +78,12 @@ class StaggeredNewtonController : public NewtonController<TypeTag> }; public: - StaggeredNewtonController(const Problem &problem) - : ParentType(problem) + StaggeredNewtonController(const Communicator& comm) + : ParentType(comm) + {} + + StaggeredNewtonController(const Communicator& comm, std::shared_ptr<TimeLoop<Scalar>> timeLoop) + : ParentType(comm, timeLoop) {} /*! @@ -92,11 +99,16 @@ public: * \param x The vector which solves the linear system * \param b The right hand side of the linear system */ - template<typename T = TypeTag> - typename std::enable_if<!LinearSolverAcceptsMultiTypeMatrix<T>::value, void>::type - newtonSolveLinear(JacobianMatrix &A, - SolutionVector &x, - SolutionVector &b) + // template<typename T = TypeTag> + // typename std::enable_if<!LinearSolverAcceptsMultiTypeMatrix<T>::value, void>::type + // newtonSolveLinear(JacobianMatrix &A, + // SolutionVector &x, + // SolutionVector &b) + template<class LinearSolver, class JacobianMatrix, class SolutionVector> + void solveLinearSystem(LinearSolver& ls, + JacobianMatrix& A, + SolutionVector& x, + SolutionVector& b) { try { @@ -127,7 +139,7 @@ public: // printmatrix(std::cout, M, "", ""); // solve - const bool converged = this->linearSolver_.solve(M, y, bTmp); + const bool converged = ls.solve(M, y, bTmp); // copy back the result y into x VectorConverter<SolutionVector>::retrieveValues(x, y); @@ -153,29 +165,29 @@ public: * \param x The vector which solves the linear system * \param b The right hand side of the linear system */ - template<typename T = TypeTag> - typename std::enable_if<LinearSolverAcceptsMultiTypeMatrix<T>::value, void>::type - newtonSolveLinear(JacobianMatrix &A, - SolutionVector &x, - SolutionVector &b) - { - try - { - if (this->numSteps_ == 0) - this->initialResidual_ = b.two_norm(); - - bool converged = this->linearSolver_.solve(A, x, b); - - if (!converged) - DUNE_THROW(NumericalProblem, "Linear solver did not converge"); - } - catch (const Dune::Exception &e) - { - Dumux::NumericalProblem p; - p.message(e.what()); - throw p; - } - } + // template<typename T = TypeTag> + // typename std::enable_if<LinearSolverAcceptsMultiTypeMatrix<T>::value, void>::type + // newtonSolveLinear(JacobianMatrix &A, + // SolutionVector &x, + // SolutionVector &b) + // { + // try + // { + // if (this->numSteps_ == 0) + // this->initialResidual_ = b.two_norm(); + // + // bool converged = this->linearSolver_.solve(A, x, b); + // + // if (!converged) + // DUNE_THROW(NumericalProblem, "Linear solver did not converge"); + // } + // catch (const Dune::Exception &e) + // { + // Dumux::NumericalProblem p; + // p.message(e.what()); + // throw p; + // } + // } /*! * \brief Update the current solution with a delta vector. @@ -194,18 +206,20 @@ public: * system of equations. This parameter also stores * the updated solution. */ - void newtonUpdate(SolutionVector &uCurrentIter, - const SolutionVector &uLastIter, - const SolutionVector &deltaU) + template<class JacobianAssembler, class SolutionVector> + void newtonUpdate(JacobianAssembler& assembler, + SolutionVector &uCurrentIter, + const SolutionVector &uLastIter, + const SolutionVector &deltaU) { if (this->enableShiftCriterion_) this->newtonUpdateShift(uLastIter, deltaU); - this->writeConvergence_(uLastIter, deltaU); + // this->writeConvergence_(uLastIter, deltaU); if (this->useLineSearch_) { - this->lineSearchUpdate_(uCurrentIter, uLastIter, deltaU); + this->lineSearchUpdate_(assembler, uCurrentIter, uLastIter, deltaU); } else { for (unsigned int i = 0; i < uLastIter[cellCenterIdx].size(); ++i) { @@ -219,8 +233,8 @@ public: if (this->enableResidualCriterion_) { - SolutionVector tmp(uLastIter); - this->reduction_ = this->method().model().globalResidual(tmp, uCurrentIter); + this->residualNorm_ = assembler.residualNorm(uCurrentIter); + this->reduction_ = this->residualNorm_; this->reduction_ /= this->initialResidual_; } } @@ -242,7 +256,7 @@ public: auto uNewI = uLastIter[cellCenterIdx][i]; uNewI -= deltaU[cellCenterIdx][i]; - Scalar shiftAtDof = this->model_().relativeShiftAtDof(uLastIter[cellCenterIdx][i], + Scalar shiftAtDof = this->relativeShiftAtDof_(uLastIter[cellCenterIdx][i], uNewI); this->shift_ = std::max(this->shift_, shiftAtDof); } @@ -250,13 +264,13 @@ public: auto uNewI = uLastIter[faceIdx][i]; uNewI -= deltaU[faceIdx][i]; - Scalar shiftAtDof = this->model_().relativeShiftAtDof(uLastIter[faceIdx][i], + Scalar shiftAtDof = this->relativeShiftAtDof_(uLastIter[faceIdx][i], uNewI); this->shift_ = std::max(this->shift_, shiftAtDof); } - if (this->gridView_().comm().size() > 1) - this->shift_ = this->gridView_().comm().max(this->shift_); + if (this->communicator().size() > 1) + this->shift_ = this->communicator().max(this->shift_); } }; diff --git a/test/freeflow/staggered/doneatestproblem.hh b/test/freeflow/staggered/doneatestproblem.hh index 5aaff6433a23ab73c5947fcaddbfcde3c16a72c6..019360ee5b13e16644c11345acc0122ea1980b2b 100644 --- a/test/freeflow/staggered/doneatestproblem.hh +++ b/test/freeflow/staggered/doneatestproblem.hh @@ -274,8 +274,8 @@ public: InitialValues initialAtPos(const GlobalPosition& globalPos) const { InitialValues values; - values[pressureIdx] = 123.0; - values[velocityXIdx] = 44.0; + values[pressureIdx] = 0.0; + values[velocityXIdx] = 0.0; values[velocityYIdx] = 0.0; return values; diff --git a/test/freeflow/staggered/test_donea.cc b/test/freeflow/staggered/test_donea.cc index a0a1d7044925dc149010bf00077f883fa06c4704..14e4edcf567a277c0a2645d18d29eddc3fd3f931 100644 --- a/test/freeflow/staggered/test_donea.cc +++ b/test/freeflow/staggered/test_donea.cc @@ -124,7 +124,7 @@ int main(int argc, char** argv) auto problem = std::make_shared<Problem>(fvGridGeometry); // the solution vector - using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + // using GridView = typename GET_PROP_TYPE(TypeTag, GridView); using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); using DofTypeIndices = typename GET_PROP(TypeTag, DofTypeIndices); typename DofTypeIndices::CellCenterIdx cellCenterIdx; @@ -167,5 +167,73 @@ int main(int argc, char** argv) // the assembler with time loop for instationary problem using Assembler = StaggeredFVAssembler<TypeTag, DiffMethod::numeric>; auto assembler = std::make_shared<Assembler>(problem, fvGridGeometry, gridVariables, timeLoop); - assembler->setLinearSystem(); + // assembler->setLinearSystem(); + + // the linear solver + using LinearSolver = Dumux::UMFPackBackend<TypeTag>; + auto linearSolver = std::make_shared<LinearSolver>(); + + // the non-linear solver + using NewtonController = typename GET_PROP_TYPE(TypeTag, NewtonController); + using NewtonMethod = Dumux::NewtonMethod<TypeTag, NewtonController, Assembler, LinearSolver>; + auto newtonController = std::make_shared<NewtonController>(leafGridView.comm(), timeLoop); + NewtonMethod nonLinearSolver(newtonController, assembler, linearSolver); + + // time loop + timeLoop->start(); do + { + // set previous solution for storage evaluations + assembler->setPreviousSolution(xOld); + + // try solving the non-linear system + for (int i = 0; i < maxDivisions; ++i) + { + // linearize & solve + auto converged = nonLinearSolver.solve(x); + + if (converged) + break; + + if (!converged && i == maxDivisions-1) + DUNE_THROW(Dune::MathError, + "Newton solver didn't converge after " + << maxDivisions + << " time-step divisions. dt=" + << timeLoop->timeStepSize() + << ".\nThe solutions of the current and the previous time steps " + << "have been saved to restart files."); + } + + // make the new solution the old solution + xOld = x; + gridVariables->advanceTimeStep(); + + // advance to the time loop to the next step + timeLoop->advanceTimeStep(); + + // write vtk output + vtkWriter.write(timeLoop->time()); + + // report statistics of this time step + timeLoop->reportTimeStep(); + + // set new dt as suggested by newton controller + timeLoop->setTimeStepSize(newtonController->suggestTimeStepSize(timeLoop->timeStepSize())); + + } while (!timeLoop->finished()); + + timeLoop->finalize(leafGridView.comm()); + + //////////////////////////////////////////////////////////// + // finalize, print dumux message to say goodbye + //////////////////////////////////////////////////////////// + + // print dumux end message + if (mpiHelper.rank() == 0) + { + Parameters::print(); + DumuxMessage::print(/*firstCall=*/false); + } + + return 0; }