diff --git a/dumux/discretization/staggered/globalvolumevariables.hh b/dumux/discretization/staggered/globalvolumevariables.hh index cea72bcffc194021be12fe54072cf643eccc8ea2..fd5d2492ce1eae6fc0a3fc0588feb16b77d93e9f 100644 --- a/dumux/discretization/staggered/globalvolumevariables.hh +++ b/dumux/discretization/staggered/globalvolumevariables.hh @@ -57,6 +57,10 @@ class StaggeredGlobalVolumeVariables<TypeTag, /*enableGlobalVolVarsCache*/true> using SubControlVolume = typename GET_PROP_TYPE(TypeTag, SubControlVolume); using IndexType = typename GridView::IndexSet::IndexType; + using DofTypeIndices = typename GET_PROP(TypeTag, DofTypeIndices); + typename DofTypeIndices::CellCenterIdx cellCenterIdx; + typename DofTypeIndices::FaceIdx faceIdx; + static const int dim = GridView::dimension; using Element = typename GridView::template Codim<0>::Entity; @@ -75,7 +79,7 @@ public: fvGeometry.bindElement(element); for (auto&& scv : scvs(fvGeometry)) - volumeVariables_[scv.index()].update(sol[scv.dofIndex()], problem, element, scv); + volumeVariables_[scv.index()].update(sol[cellCenterIdx][scv.dofIndex()], problem, element, scv); // handle the boundary volume variables for (auto&& scvf : scvfs(fvGeometry)) diff --git a/dumux/freeflow/staggered/model.hh b/dumux/freeflow/staggered/model.hh index bae02ce4593f2cb83a42950724429d1270353dd3..1df03e8fb914fd25aab32bafcce1620b83c0be28 100644 --- a/dumux/freeflow/staggered/model.hh +++ b/dumux/freeflow/staggered/model.hh @@ -57,8 +57,11 @@ template<class TypeTag > class NavierStokesModel : public GET_PROP_TYPE(TypeTag, BaseModel) { typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, GlobalFVGeometry) GlobalFVGeometry; // typedef typename GET_PROP_TYPE(TypeTag, SpatialParams) SpatialParams; typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, JacobianAssembler) JacobianAssembler; typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; enum { dim = GridView::dimension }; @@ -69,7 +72,14 @@ class NavierStokesModel : public GET_PROP_TYPE(TypeTag, BaseModel) using StencilsVector = typename GET_PROP_TYPE(TypeTag, StencilsVector); using Element = typename GridView::template Codim<0>::Entity; + using DofTypeIndices = typename GET_PROP(TypeTag, DofTypeIndices); + typename DofTypeIndices::CellCenterIdx cellCenterIdx; + typename DofTypeIndices::FaceIdx faceIdx; + public: + + + /*! * \brief \copybrief Dumux::ImplicitModel::addOutputVtkFields * @@ -132,61 +142,6 @@ public: writer.attachCellData(*rank, "process rank"); } - /*! - * \brief Returns the number of global degrees of freedoms (DOFs) - */ - size_t numDofs() const - { - return this->gridView_().size(0) + this->gridView_().size(1); - } - - /*! - * \brief Returns the number of cell center degrees of freedoms (DOFs) - */ - size_t numCellCenterDofs() const - { - return this->gridView_().size(0); - } - - /*! - * \brief Returns the number of cell center degrees of freedoms (DOFs) - */ - size_t numFaceDofs() const - { - return this->gridView_().size(1); - } - - /*! - * \brief Returns the size of a complete face dof stencil - */ - size_t fullFaceToCellCenterStencilSize(const int idx) const - { - return this->stencilsVector_.fullFaceToCellCenterStencilSize(idx); - } - - /*! - * \brief Returns the size of a complete face dof stencil - */ - size_t fullfaceToFaceStencilSize(const int idx) const - { - return this->stencilsVector_.fullfaceToFaceStencilSize(idx); - } - - /*! - * \brief Returns a unique pointer to the complete face dof stencils which is used once for setting up the global matrix and deleted afterwards - */ - auto getFullFaceToCellCenterStencilsPtr() - { - return this->stencilsVector_.getFullFaceToCellCenterStencilsPtr(); - } - - /*! - * \brief Returns a unique pointer to the complete face dof stencils which is used once for setting up the global matrix and deleted afterwards - */ - auto getFullfaceToFaceStencilsPtr() - { - return this->stencilsVector_.getFullfaceToFaceStencilsPtr(); - } }; } diff --git a/dumux/implicit/staggered/assembler.hh b/dumux/implicit/staggered/assembler.hh index e1f528ed885eac6cb7e0c48a1447be76cf2011da..e7a334f6e95bca9e3be940425e77dd105f268bbf 100644 --- a/dumux/implicit/staggered/assembler.hh +++ b/dumux/implicit/staggered/assembler.hh @@ -55,6 +55,37 @@ class StaggeredAssembler : public ImplicitAssembler<TypeTag> typename DofTypeIndices::CellCenterIdx cellCenterIdx; typename DofTypeIndices::FaceIdx faceIdx; +public: + + /*! + * \brief Initialize the jacobian assembler. + * + * At this point we can assume that all objects in the problem and + * the model have been allocated. We can not assume that they are + * fully initialized, though. + * + * \param problem The problem object + */ + void init(Problem& problem) + { + std::cout << "init(Problem& problem)" << std::endl; + this->problemPtr_ = &problem; + + // initialize the BCRS matrix + createMatrix_(); + + // initialize the jacobian matrix with zeros + *this->matrix_ = 0; + + // allocate the residual vector + this->residual_[cellCenterIdx].resize(problem.model().numCellCenterDofs()); + this->residual_[faceIdx].resize(problem.model().numFaceDofs()); +// printmatrix(std::cout, matrix(), "", ""); + } + + +private: + // Construct the multitype matrix for the global jacobian void createMatrix_() { diff --git a/dumux/implicit/staggered/localjacobian.hh b/dumux/implicit/staggered/localjacobian.hh index 0059b955c2fa60c7694d319bf26c58cb806334fe..939de60938f69bce58fbdee27d152b3342c05965 100644 --- a/dumux/implicit/staggered/localjacobian.hh +++ b/dumux/implicit/staggered/localjacobian.hh @@ -90,6 +90,10 @@ class StaggeredLocalJacobian : public ImplicitLocalJacobian<TypeTag> enum { numEq = GET_PROP_VALUE(TypeTag, NumEq) }; + using DofTypeIndices = typename GET_PROP(TypeTag, DofTypeIndices); + typename DofTypeIndices::CellCenterIdx cellCenterIdx; + typename DofTypeIndices::FaceIdx faceIdx; + using AssemblyMap = std::vector<std::vector<std::vector<IndexType>>>; public: @@ -145,14 +149,14 @@ public: if (isGhost) { this->residual_ = 0.0; - residual[globalI_] = 0.0; + residual[cellCenterIdx][globalI_] = 0.0; } else { this->localResidual().eval(element, fvGeometry, prevElemVolVars, curElemVolVars, elemBcTypes, elemFluxVarsCache); this->residual_ = this->localResidual().residual(); // store residual in global container as well - residual[globalI_] = this->localResidual().residual(0); + residual[cellCenterIdx][globalI_] = this->localResidual().residual(0); } this->model_().updatePVWeights(fvGeometry); diff --git a/dumux/implicit/staggered/model.hh b/dumux/implicit/staggered/model.hh new file mode 100644 index 0000000000000000000000000000000000000000..afdc5d04667efaae1c62d41df85d6e8dd8ccf6ff --- /dev/null +++ b/dumux/implicit/staggered/model.hh @@ -0,0 +1,496 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Base class for all models which use the one-phase, + * fully implicit model. + * Adaption of the fully implicit scheme to the one-phase flow model. + */ + +#ifndef DUMUX_STAGGERED_BASEMODEL_HH +#define DUMUX_STAGGERED_BASEMODEL_HH + +// #include <dumux/porousmediumflow/implicit/velocityoutput.hh> +#include <dumux/implicit/model.hh> +#include "properties.hh" + +namespace Dumux +{ +/*! + * \ingroup NavierStokesModel + * \brief A single-phase, isothermal flow model using the fully implicit scheme. + * + * Single-phase, isothermal flow model, which uses a standard Darcy approach as the + * equation for the conservation of momentum: + * \f[ + v = - \frac{\textbf K}{\mu} + \left(\textbf{grad}\, p - \varrho {\textbf g} \right) + * \f] + * + * and solves the mass continuity equation: + * \f[ + \phi \frac{\partial \varrho}{\partial t} + \text{div} \left\lbrace + - \varrho \frac{\textbf K}{\mu} \left( \textbf{grad}\, p -\varrho {\textbf g} \right) \right\rbrace = q, + * \f] + * All equations are discretized using a vertex-centered finite volume (box) + * or cell-centered finite volume scheme as spatial + * and the implicit Euler method as time discretization. + * The model supports compressible as well as incompressible fluids. + */ +template<class TypeTag > +class StaggeredBaseModel : public ImplicitModel<TypeTag> +{ + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, GlobalFVGeometry) GlobalFVGeometry; +// typedef typename GET_PROP_TYPE(TypeTag, SpatialParams) SpatialParams; + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, JacobianAssembler) JacobianAssembler; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + enum { dim = GridView::dimension }; + enum { dimWorld = GridView::dimensionworld }; + + enum { isBox = GET_PROP_VALUE(TypeTag, ImplicitIsBox) }; + enum { dofCodim = isBox ? dim : 0 }; + using StencilsVector = typename GET_PROP_TYPE(TypeTag, StencilsVector); + using Element = typename GridView::template Codim<0>::Entity; + using Implementation = typename GET_PROP_TYPE(TypeTag, Model); + using NewtonMethod = typename GET_PROP_TYPE(TypeTag, NewtonMethod); + using NewtonController = typename GET_PROP_TYPE(TypeTag, NewtonController); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + + using DofTypeIndices = typename GET_PROP(TypeTag, DofTypeIndices); + typename DofTypeIndices::CellCenterIdx cellCenterIdx; + typename DofTypeIndices::FaceIdx faceIdx; + + static_assert(!isBox, "must not be box!"); + +public: + + /*! + * \brief Apply the initial conditions to the model. + * + * \param problem The object representing the problem which needs to + * be simulated. + */ + void init(Problem &problem) + { + this->problemPtr_ = &problem; + + this->updateBoundaryIndices_(); + + this->globalFvGeometryPtr_ = std::make_shared<GlobalFVGeometry>(problem.gridView()); + this->globalFvGeometryPtr_->update(problem); + + this->uCur_[cellCenterIdx].resize(asImp_().numCellCenterDofs()); + this->uCur_[faceIdx].resize(asImp_().numFaceDofs()); + + // apply initial solution + // for compositional models initial the phase presence herein + asImp_().applyInitialSolution_(); + + // resize and update the volVars with the initial solution + this->curGlobalVolVars_.update(problem, this->curSol()); + + // update stencils + this->stencilsVector_.update(problem); + + // update the flux variables caches + this->globalfluxVarsCache_.update(problem); + + // initialize assembler and create matrix + this->localJacobian_.init(problem); + this->jacAsm_ = std::make_shared<JacobianAssembler>(); + this->jacAsm_->init(problem); + + // also set the solution of the "previous" time step to the + // initial solution. + this->uPrev_ = this->uCur_; + this->prevGlobalVolVars_ = this->curGlobalVolVars_; + } + + /*! + * \brief Try to progress the model to the next timestep. + * + * \param solver The non-linear solver + * \param controller The controller which specifies the behaviour + * of the non-linear solver + */ + bool update(NewtonMethod &solver, + NewtonController &controller) + { +#if HAVE_VALGRIND + for (size_t i = 0; i < this->curSol()[cellCenterIdx].size(); ++i) + Valgrind::CheckDefined(this->curSol()[cellCenterIdx][i]); + for (size_t i = 0; i < this->curSol()[faceIdx].size(); ++i) + Valgrind::CheckDefined(this->curSol()[faceIdx][i]); +#endif // HAVE_VALGRIND + + asImp_().updateBegin(); + + int converged = solver.execute(controller); + + if (this->gridView_().comm().size() > 1) + { + converged = this->gridView_().comm().min(converged); + } + if (converged) { + asImp_().updateSuccessful(); + } + else + asImp_().updateFailed(); + +#if HAVE_VALGRIND + for (size_t i = 0; i < this->curSol()[cellCenterIdx].size(); ++i) + Valgrind::CheckDefined(this->curSol()[cellCenterIdx][i]); + for (size_t i = 0; i < this->curSol()[faceIdx].size(); ++i) + Valgrind::CheckDefined(this->curSol()[faceIdx][i]); +#endif // HAVE_VALGRIND + + return converged; + } + + /*! + * \brief Applies the initial solution for all vertices of the grid. + * + * \todo the initial condition needs to be unique for + * each vertex. we should think about the API... + */ + void applyInitialSolution_() + { + // first set the whole domain to zero + this->uCur_ = Scalar(0.0); + + // iterate through leaf grid and evaluate initial + // condition at the center of each sub control volume + for (const auto& element : elements(this->gridView_())) + { + // deal with the current element, bind FVGeometry to it + auto fvGeometry = localView(this->globalFvGeometry()); + fvGeometry.bindElement(element); + + // loop over sub control volumes + for (auto&& scv : scvs(fvGeometry)) + { + // let the problem do the dirty work of nailing down + // the initial solution. + auto initPriVars = this->problem_().initial(scv); + + auto dofIdxGlobal = scv.dofIndex(); + this->uCur_[cellCenterIdx][dofIdxGlobal] += initPriVars; + } + } + } + + /*! + * \brief Write the current solution for a vertex to a restart + * file. + * + * \param outstream The stream into which the vertex data should + * be serialized to + * \param entity The entity which's data should be + * serialized, i.e. a vertex for the box method + * and an element for the cell-centered method + */ + template <class Entity> + void serializeEntity(std::ostream &outstream, + const Entity &entity) + { + DUNE_THROW(Dune::NotImplemented, "deserializeEntity() not implemented yet"); +// int dofIdxGlobal = dofMapper().index(entity); +// int dofIdxGlobal = dofMapper().index(entity); +// +// // write phase state +// if (!outstream.good()) { +// DUNE_THROW(Dune::IOError, +// "Could not serialize vertex " +// << dofIdxGlobal); +// } +// +// for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { +// outstream << curSol()[dofIdxGlobal][eqIdx] << " "; +// } + } + + /*! + * \brief Reads the current solution variables for a vertex from a + * restart file. + * + * \param instream The stream from which the vertex data should + * be deserialized from + * \param entity The entity which's data should be + * serialized, i.e. a vertex for the box method + * and an element for the cell-centered method + */ + template <class Entity> + void deserializeEntity(std::istream &instream, + const Entity &entity) + { + DUNE_THROW(Dune::NotImplemented, "deserializeEntity() not implemented yet"); +// int dofIdxGlobal = dofMapper().index(entity); +// +// for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { +// if (!instream.good()) +// DUNE_THROW(Dune::IOError, +// "Could not deserialize vertex " +// << dofIdxGlobal); +// instream >> curSol()[dofIdxGlobal][eqIdx]; +// } + } + + /*! + * \brief \copybrief Dumux::ImplicitModel::addOutputVtkFields + * + * Specialization for the NavierStokesModel, adding the pressure and + * the process rank to the VTK writer. + */ + template<class MultiWriter> + void addOutputVtkFields(const SolutionVector &sol, + MultiWriter &writer) + { + // typedef Dune::BlockVector<Dune::FieldVector<double, dimWorld> > VectorField; + + // create the required scalar fields + unsigned numDofs = this->numDofs(); + auto *p = writer.allocateManagedBuffer(numDofs); + // VectorField *velocity = writer.template allocateManagedBuffer<double, dimWorld>(numDofs); + // ImplicitVelocityOutput<TypeTag> velocityOutput(this->problem_()); + + // if (velocityOutput.enableOutput()) + // { + // // initialize velocity field + // for (unsigned int i = 0; i < numDofs; ++i) + // { + // (*velocity)[i] = double(0); + // } + // } + + unsigned numElements = this->gridView_().size(0); + auto *rank = writer.allocateManagedBuffer(numElements); + + for (const auto& element : elements(this->gridView_(), Dune::Partitions::interior)) + { + auto eIdx = this->elementMapper().index(element); + (*rank)[eIdx] = this->gridView_().comm().rank(); + + // get the local fv geometry + auto fvGeometry = localView(this->globalFvGeometry()); + fvGeometry.bindElement(element); + + auto elemVolVars = localView(this->curGlobalVolVars()); + elemVolVars.bindElement(element, fvGeometry, this->curSol()); + + for (auto&& scv : scvs(fvGeometry)) + { + const auto& volVars = elemVolVars[scv]; + auto dofIdxGlobal = scv.dofIndex(); + + (*p)[dofIdxGlobal] = volVars.pressure(); + } + + // velocity output + //velocityOutput.calculateVelocity(*velocity, elemVolVars, fvGeometry, element, /*phaseIdx=*/0); + } + + writer.attachDofData(*p, "p", isBox); + // if (velocityOutput.enableOutput()) + // { + // writer.attachDofData(*velocity, "velocity", isBox, dim); + // } + writer.attachCellData(*rank, "process rank"); + } + + /*! + * \brief Add the vector fields for analysing the convergence of + * the newton method to the a VTK multi writer. + * + * \tparam MultiWriter The type of the VTK multi writer + * + * \param writer The VTK multi writer object on which the fields should be added. + * \param u The solution function + * \param deltaU The delta of the solution function before and after the Newton update + */ + template <class MultiWriter> + void addConvergenceVtkFields(MultiWriter &writer, + const SolutionVector &u, + const SolutionVector &deltaU) + { +// typedef Dune::BlockVector<Dune::FieldVector<double, 1> > ScalarField; +// +// SolutionVector residual(u); +// asImp_().globalResidual(residual, u); +// +// // create the required scalar fields +// unsigned numDofs = asImp_().numDofs(); +// +// // global defect of the two auxiliary equations +// ScalarField* def[numEq]; +// ScalarField* delta[numEq]; +// ScalarField* x[numEq]; +// for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { +// x[eqIdx] = writer.allocateManagedBuffer(numDofs); +// delta[eqIdx] = writer.allocateManagedBuffer(numDofs); +// def[eqIdx] = writer.allocateManagedBuffer(numDofs); +// } +// +// for (unsigned int dofIdxGlobal = 0; dofIdxGlobal < u.size(); dofIdxGlobal++) +// { +// for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) +// { +// (*x[eqIdx])[dofIdxGlobal] = u[dofIdxGlobal][eqIdx]; +// (*delta[eqIdx])[dofIdxGlobal] = - deltaU[dofIdxGlobal][eqIdx]; +// (*def[eqIdx])[dofIdxGlobal] = residual[dofIdxGlobal][eqIdx]; +// } +// } +// +// for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { +// std::ostringstream oss; +// oss.str(""); oss << "x_" << eqIdx; +// if (isBox) +// writer.attachVertexData(*x[eqIdx], oss.str()); +// else +// writer.attachCellData(*x[eqIdx], oss.str()); +// oss.str(""); oss << "delta_" << eqIdx; +// if (isBox) +// writer.attachVertexData(*delta[eqIdx], oss.str()); +// else +// writer.attachCellData(*delta[eqIdx], oss.str()); +// oss.str(""); oss << "defect_" << eqIdx; +// if (isBox) +// writer.attachVertexData(*def[eqIdx], oss.str()); +// else +// writer.attachCellData(*def[eqIdx], oss.str()); +// } +// +// asImp_().addOutputVtkFields(u, writer); + } + + /*! + * \brief Compute the global residual for an arbitrary solution + * vector. + * + * \param residual Stores the result + * \param u The solution for which the residual ought to be calculated + */ + Scalar globalResidual(SolutionVector &residual, + const SolutionVector &u) + { + SolutionVector tmp(this->curSol()); + this->curSol() = u; + Scalar res = globalResidual(residual); + this->curSol() = tmp; + return res; + } + + /*! + * \brief Compute the global residual for the current solution + * vector. + * + * \param residual Stores the result + */ + Scalar globalResidual(SolutionVector &residual) + { + residual = 0; + + for (const auto& element : elements(this->gridView_())) { + this->localResidual().eval(element); + + + int globalI = this->elementMapper().index(element); + residual[cellCenterIdx][globalI] = this->localResidual().residual(0); + } + + // calculate the square norm of the residual + Scalar result2 = residual.two_norm2(); + if (this->gridView_().comm().size() > 1) + result2 = this->gridView_().comm().sum(result2); + + return std::sqrt(result2); + } + + /*! + * \brief Returns the number of global degrees of freedoms (DOFs) + */ + size_t numDofs() const + { + return this->gridView_().size(0) + this->gridView_().size(1); + } + + /*! + * \brief Returns the number of cell center degrees of freedoms (DOFs) + */ + size_t numCellCenterDofs() const + { + return this->gridView_().size(0); + } + + /*! + * \brief Returns the number of cell center degrees of freedoms (DOFs) + */ + size_t numFaceDofs() const + { + return this->gridView_().size(1); + } + + /*! + * \brief Returns the size of a complete face dof stencil + */ + size_t fullFaceToCellCenterStencilSize(const int idx) const + { + return this->stencilsVector_.fullFaceToCellCenterStencilSize(idx); + } + + /*! + * \brief Returns the size of a complete face dof stencil + */ + size_t fullfaceToFaceStencilSize(const int idx) const + { + return this->stencilsVector_.fullfaceToFaceStencilSize(idx); + } + + /*! + * \brief Returns a unique pointer to the complete face dof stencils which is used once for setting up the global matrix and deleted afterwards + */ + auto getFullFaceToCellCenterStencilsPtr() + { + return this->stencilsVector_.getFullFaceToCellCenterStencilsPtr(); + } + + /*! + * \brief Returns a unique pointer to the complete face dof stencils which is used once for setting up the global matrix and deleted afterwards + */ + auto getFullfaceToFaceStencilsPtr() + { + return this->stencilsVector_.getFullfaceToFaceStencilsPtr(); + } +private: + + Implementation &asImp_() + { return *static_cast<Implementation*>(this); } + const Implementation &asImp_() const + { return *static_cast<const Implementation*>(this); } + +}; +} + +#include "propertydefaults.hh" + +#endif diff --git a/dumux/implicit/staggered/newtoncontroller.hh b/dumux/implicit/staggered/newtoncontroller.hh index 75d750399c7a41e27059cb089431ae37399582f2..791d8779e59428998480b40c0557ff892ee95b17 100644 --- a/dumux/implicit/staggered/newtoncontroller.hh +++ b/dumux/implicit/staggered/newtoncontroller.hh @@ -49,6 +49,10 @@ class StaggeredNewtonController : public NewtonController<TypeTag> typedef typename GET_PROP_TYPE(TypeTag, JacobianMatrix) JacobianMatrix; typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + using DofTypeIndices = typename GET_PROP(TypeTag, DofTypeIndices); + typename DofTypeIndices::CellCenterIdx cellCenterIdx; + typename DofTypeIndices::FaceIdx faceIdx; + public: StaggeredNewtonController(const Problem &problem) : ParentType(problem) @@ -119,6 +123,88 @@ public: } } + /*! + * \brief Update the current solution with a delta vector. + * + * The error estimates required for the newtonConverged() and + * newtonProceed() methods should be updated inside this method. + * + * Different update strategies, such as line search and chopped + * updates can be implemented. The default behavior is just to + * subtract deltaU from uLastIter, i.e. + * \f[ u^{k+1} = u^k - \Delta u^k \f] + * + * \param uCurrentIter The solution vector after the current iteration + * \param uLastIter The solution vector after the last iteration + * \param deltaU The delta as calculated from solving the linear + * system of equations. This parameter also stores + * the updated solution. + */ + void newtonUpdate(SolutionVector &uCurrentIter, + const SolutionVector &uLastIter, + const SolutionVector &deltaU) + { + if (this->enableShiftCriterion_) + this->newtonUpdateShift(uLastIter, deltaU); + + this->writeConvergence_(uLastIter, deltaU); + + if (this->useLineSearch_) + { + this->lineSearchUpdate_(uCurrentIter, uLastIter, deltaU); + } + else { + for (unsigned int i = 0; i < uLastIter[cellCenterIdx].size(); ++i) { + uCurrentIter[cellCenterIdx][i] = uLastIter[cellCenterIdx][i]; + uCurrentIter[cellCenterIdx][i] -= deltaU[cellCenterIdx][i]; + } + for (unsigned int i = 0; i < uLastIter[faceIdx].size(); ++i) { + uCurrentIter[faceIdx][i] = uLastIter[faceIdx][i]; + uCurrentIter[faceIdx][i] -= deltaU[faceIdx][i]; + } + + if (this->enableResidualCriterion_) + { + SolutionVector tmp(uLastIter); + this->reduction_ = this->method().model().globalResidual(tmp, uCurrentIter); + this->reduction_ /= this->initialResidual_; + } + } + } + + /*! + * \brief Update the maximum relative shift of the solution compared to + * the previous iteration. + * + * \param uLastIter The current iterative solution + * \param deltaU The difference between the current and the next solution + */ + void newtonUpdateShift(const SolutionVector &uLastIter, + const SolutionVector &deltaU) + { + this->shift_ = 0; + + for (int i = 0; i < int(uLastIter[cellCenterIdx].size()); ++i) { + auto uNewI = uLastIter[cellCenterIdx][i]; + uNewI -= deltaU[cellCenterIdx][i]; + + Scalar shiftAtDof = this->model_().relativeShiftAtDof(uLastIter[cellCenterIdx][i], + uNewI); + this->shift_ = std::max(this->shift_, shiftAtDof); + } + for (int i = 0; i < int(uLastIter[faceIdx].size()); ++i) { + auto uNewI = uLastIter[faceIdx][i]; + uNewI -= deltaU[faceIdx][i]; + + Scalar shiftAtDof = this->model_().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_); + } + }; } diff --git a/dumux/implicit/staggered/properties.hh b/dumux/implicit/staggered/properties.hh index bcf6c90cbd0352beebb197b14ae87243db05f3ca..7710767b869fda0653a36fe0eebc48b25a198a26 100644 --- a/dumux/implicit/staggered/properties.hh +++ b/dumux/implicit/staggered/properties.hh @@ -45,6 +45,9 @@ NEW_TYPE_TAG(StaggeredModel, INHERITS_FROM(ImplicitBase)); NEW_PROP_TAG(CellCenterPrimaryVariables); //!< A vector of primary variables for cell center dofs NEW_PROP_TAG(FacePrimaryVariables); //!< A vector of primary variables for face dofs +NEW_PROP_TAG(CellCenterSolutionVector); //!< Vector containing all cell centered primary variables +NEW_PROP_TAG(FaceSolutionVector); //!< Vector containing all face primary variables + NEW_PROP_TAG(NumEqCellCenter); //!< Number of equations per cell center dof NEW_PROP_TAG(NumEqFace); //!< Number of equations per face dof diff --git a/dumux/implicit/staggered/propertydefaults.hh b/dumux/implicit/staggered/propertydefaults.hh index 9e10b99ec2e99ac201795de15774053a73a8eafb..315d3a305c48478a184965b5b5f4b01e99d62424 100644 --- a/dumux/implicit/staggered/propertydefaults.hh +++ b/dumux/implicit/staggered/propertydefaults.hh @@ -61,6 +61,7 @@ #include "localjacobian.hh" #include "properties.hh" #include "newtoncontroller.hh" +#include "model.hh" namespace Dumux { @@ -74,6 +75,10 @@ SET_PROP(StaggeredModel, DiscretizationMethod) static const DiscretizationMethods value = DiscretizationMethods::Staggered; }; + +SET_TYPE_PROP(StaggeredModel, BaseModel, StaggeredBaseModel<TypeTag>); + + //! Set the default for the global finite volume geometry SET_TYPE_PROP(StaggeredModel, GlobalFVGeometry, StaggeredGlobalFVGeometry<TypeTag, GET_PROP_VALUE(TypeTag, EnableGlobalFVGeometryCache)>); @@ -146,8 +151,39 @@ SET_TYPE_PROP(StaggeredModel, NewtonController, StaggeredNewtonController<TypeTa SET_INT_PROP(StaggeredModel, NumEqCellCenter, 1); SET_INT_PROP(StaggeredModel, NumEqFace, 1); +//! A vector of primary variables +SET_TYPE_PROP(StaggeredModel, + CellCenterPrimaryVariables, + Dune::FieldVector<typename GET_PROP_TYPE(TypeTag, Scalar), + GET_PROP_VALUE(TypeTag, NumEqCellCenter)>); +//! A vector of primary variables +SET_TYPE_PROP(StaggeredModel, + FacePrimaryVariables, + Dune::FieldVector<typename GET_PROP_TYPE(TypeTag, Scalar), + GET_PROP_VALUE(TypeTag, NumEqFace)>); + +//! The type of a solution for the whole grid at a fixed time +SET_TYPE_PROP(StaggeredModel, + CellCenterSolutionVector, + Dune::BlockVector<typename GET_PROP_TYPE(TypeTag, CellCenterPrimaryVariables)>); + +//! The type of a solution for the whole grid at a fixed time +SET_TYPE_PROP(StaggeredModel, + FaceSolutionVector, + Dune::BlockVector<typename GET_PROP_TYPE(TypeTag, FacePrimaryVariables)>); + +//! default property value for the solution vector only used for monolithic solver +SET_PROP(StaggeredModel, SolutionVector) +{ +private: + using CellCenterSolutionVector = typename GET_PROP_TYPE(TypeTag, CellCenterSolutionVector); + using FaceSolutionVector = typename GET_PROP_TYPE(TypeTag, FaceSolutionVector); +public: + typedef typename Dune::MultiTypeBlockVector<CellCenterSolutionVector, FaceSolutionVector> type; +}; + //! Set the type of a global jacobian matrix from the solution types -SET_PROP(StaggeredModel, JacobianMatrix/*New*/) +SET_PROP(StaggeredModel, JacobianMatrix) { private: using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar);