diff --git a/CHANGELOG.md b/CHANGELOG.md index 16ed7dbf9b1dbb7eedd2e2c11dae6989ea743e03..693fd71034413418929ced54b807b40e8b6993f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,13 +7,26 @@ Differences Between DuMuX 3.1 and DuMuX 3.0 - Added new porous medium model for the energy balance of a porous solid (general heat equation) - __Multidomain__: It is now possible to use the facet coupling module together with the mpfa-o scheme in the bulk domain. +- Added a `StaggeredNewtonConvergenceWriter` for the staggered grid discretization scheme ### Immediate interface changes not allowing/requiring a deprecation period +- `NewtonConvergenceWriter`'s first template argument has changed from `GridView`, the `FVGridGeometry`. This allows to call the `resize()` method after a grid change without any arguments. + Here is an example of how to instatiate the convergence writer: + ``` + using NewtonConvergenceWriter = Dumux::NewtonConvergenceWriter<FVGridGeometry, SolutionVector>; + auto convergenceWriter = std::make_shared<NewtonConvergenceWriter>(*fvGridGeometry); + ``` + + ### Deprecated classes/files, to be removed after 3.1: ### Deprecated member functions, to be removed after 3.1: +- The convergence writer is no longer passed to `NewtonSolver`'s `solve()` method. + For outputting convergence data, please use `newtonSolver.attachConvergenceWriter(convWriter)` in `main.cc` (directly after instantiating the writer). + To stop the output, the writer can also be detached again using `newtonSolver.detachConvergenceWriter()`. + ### Deleted classes/files, property names, constants/enums diff --git a/dumux/nonlinear/newtonconvergencewriter.hh b/dumux/nonlinear/newtonconvergencewriter.hh index 0ebfcb6577246a00e018c3e2781105cf0e769c58..6d06350ed547ac3f5c3b8c1e11fe6aa3f37dfb4e 100644 --- a/dumux/nonlinear/newtonconvergencewriter.hh +++ b/dumux/nonlinear/newtonconvergencewriter.hh @@ -25,9 +25,10 @@ #ifndef DUMUX_NEWTON_CONVERGENCE_WRITER_HH #define DUMUX_NEWTON_CONVERGENCE_WRITER_HH -#include <dune/common/exceptions.hh> +#include <string> + #include <dune/grid/io/file/vtk/vtksequencewriter.hh> -#include <dumux/common/parameters.hh> +#include <dumux/discretization/method.hh> namespace Dumux { @@ -49,12 +50,15 @@ struct ConvergenceWriterInterface * to write out multiple Newton solves with a unique id, if you don't call use all * Newton iterations just come after each other in the pvd file. */ -// template <class Scalar, class GridView, int numEq> -template <class GridView, class SolutionVector> +template <class FVGridGeometry, class SolutionVector> class NewtonConvergenceWriter : public ConvergenceWriterInterface<SolutionVector> { + using GridView = typename FVGridGeometry::GridView; static constexpr auto numEq = SolutionVector::block_type::dimension; using Scalar = typename SolutionVector::block_type::value_type; + + static_assert(FVGridGeometry::discMethod != DiscretizationMethod::staggered, + "This convergence writer does not work for the staggered method, use the StaggeredNewtonConvergenceWriter instead"); public: /*! * \brief Constructor @@ -62,14 +66,14 @@ public: * \param size the size of the solution data * \param name base name of the vtk output */ - NewtonConvergenceWriter(const GridView& gridView, - std::size_t size, + NewtonConvergenceWriter(const FVGridGeometry& fvGridGeometry, const std::string& name = "newton_convergence") - : writer_(gridView, name, "", "") + : fvGridGeometry_(fvGridGeometry) + , writer_(fvGridGeometry.gridView(), name, "", "") { - resize(size); + resize(); - if (size == gridView.size(GridView::dimension)) + if (FVGridGeometry::discMethod == DiscretizationMethod::box) { for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { @@ -78,7 +82,7 @@ public: writer_.addVertexData(def_[eqIdx], "defect_" + std::to_string(eqIdx)); } } - else if (size == gridView.size(0)) + else { for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { @@ -87,21 +91,19 @@ public: writer_.addCellData(def_[eqIdx], "defect_" + std::to_string(eqIdx)); } } - else - { - DUNE_THROW(Dune::InvalidStateException, "Wrong size of output fields in the Newton convergence writer!"); - } } //! Resizes the output fields. This has to be called whenever the grid changes - void resize(std::size_t size) + void resize() { + const auto numDofs = fvGridGeometry_.numDofs(); + // resize the output fields for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { - def_[eqIdx].resize(size); - delta_[eqIdx].resize(size); - x_[eqIdx].resize(size); + def_[eqIdx].resize(numDofs); + delta_[eqIdx].resize(numDofs); + x_[eqIdx].resize(numDofs); } } @@ -110,9 +112,9 @@ public: void reset(std::size_t newId = 0UL) { id_ = newId; iteration_ = 0UL; } - void write(const SolutionVector &uLastIter, - const SolutionVector &deltaU, - const SolutionVector &residual) override + void write(const SolutionVector& uLastIter, + const SolutionVector& deltaU, + const SolutionVector& residual) override { assert(uLastIter.size() == deltaU.size() && uLastIter.size() == residual.size()); @@ -134,6 +136,8 @@ private: std::size_t id_ = 0UL; std::size_t iteration_ = 0UL; + const FVGridGeometry& fvGridGeometry_; + Dune::VTKSequenceWriter<GridView> writer_; std::array<std::vector<Scalar>, numEq> def_; diff --git a/dumux/nonlinear/newtonsolver.hh b/dumux/nonlinear/newtonsolver.hh index 9739eebd94e8f03d89937934916ffc7ba6ec2b81..4eeb779abffd974f15ae0a6a86cfd34d44125c49 100644 --- a/dumux/nonlinear/newtonsolver.hh +++ b/dumux/nonlinear/newtonsolver.hh @@ -49,6 +49,8 @@ #include "newtonconvergencewriter.hh" +#include <dune/common/deprecated.hh> + namespace Dumux { namespace Detail { @@ -193,8 +195,21 @@ public: * Does time step control when the Newton fails to converge */ template<class TimeLoop> + DUNE_DEPRECATED_MSG("Use attachConvergenceWriter(convWriter) and solve(x, *timeLoop) instead") void solve(SolutionVector& uCurrentIter, TimeLoop& timeLoop, - std::shared_ptr<ConvergenceWriter> convWriter = nullptr) + std::shared_ptr<ConvergenceWriter> convWriter) + { + if (!convergenceWriter_) + attachConvergenceWriter(convWriter); + + solve(uCurrentIter, timeLoop); + } + /*! + * \brief Run the Newton method to solve a non-linear system. + * Does time step control when the Newton fails to converge + */ + template<class TimeLoop> + void solve(SolutionVector& uCurrentIter, TimeLoop& timeLoop) { if (assembler_->isStationaryProblem()) DUNE_THROW(Dune::InvalidStateException, "Using time step control with stationary problem makes no sense!"); @@ -203,7 +218,7 @@ public: for (std::size_t i = 0; i <= maxTimeStepDivisions_; ++i) { // linearize & solve - const bool converged = solve_(uCurrentIter, convWriter); + const bool converged = solve_(uCurrentIter); if (converged) return; @@ -238,9 +253,9 @@ public: * \brief Run the Newton method to solve a non-linear system. * The solver is responsible for all the strategic decisions. */ - void solve(SolutionVector& uCurrentIter, std::shared_ptr<ConvergenceWriter> convWriter = nullptr) + void solve(SolutionVector& uCurrentIter) { - const bool converged = solve_(uCurrentIter, convWriter); + const bool converged = solve_(uCurrentIter); if (!converged) DUNE_THROW(NumericalProblem, "Newton solver didn't converge after " << numSteps_ << " iterations.\n"); @@ -256,6 +271,15 @@ public: { numSteps_ = 0; initPriVarSwitch_(u, HasPriVarsSwitch{}); + + // write the initial residual if a convergence writer was set + if (convergenceWriter_) + { + assembler_->assembleResidual(u); + SolutionVector delta(u); + delta = 0; // dummy vector, there is no delta before solving the linear system + convergenceWriter_->write(u, delta, assembler_->residual()); + } } /*! @@ -621,6 +645,18 @@ public: const std::string& paramGroup() const { return paramGroup_; } + /*! + * \brief Attach a convergence writer to write out intermediate results after each iteration + */ + void attachConvergenceWriter(std::shared_ptr<ConvergenceWriter> convWriter) + { convergenceWriter_ = convWriter; } + + /*! + * \brief Detach the convergence writer to stop the output + */ + void detachConvergenceWriter() + { convergenceWriter_ = nullptr; } + protected: void computeResidualReduction_(const SolutionVector &uCurrentIter) @@ -724,7 +760,7 @@ private: * \brief Run the Newton method to solve a non-linear system. * The solver is responsible for all the strategic decisions. */ - bool solve_(SolutionVector& uCurrentIter, std::shared_ptr<ConvergenceWriter> convWriter = nullptr) + bool solve_(SolutionVector& uCurrentIter) { // the given solution is the initial guess SolutionVector uLastIter(uCurrentIter); @@ -814,10 +850,10 @@ private: newtonEndStep(uCurrentIter, uLastIter); // if a convergence writer was specified compute residual and write output - if (convWriter) + if (convergenceWriter_) { assembler_->assembleResidual(uCurrentIter); - convWriter->write(uLastIter, deltaU, assembler_->residual()); + convergenceWriter_->write(uCurrentIter, deltaU, assembler_->residual()); } // detect if the method has converged @@ -1240,6 +1276,9 @@ private: std::unique_ptr<PrimaryVariableSwitch> priVarSwitch_; //! if we switched primary variables in the last iteration bool priVarsSwitchedInLastIteration_ = false; + + //! convergence writer + std::shared_ptr<ConvergenceWriter> convergenceWriter_ = nullptr; }; } // end namespace Dumux diff --git a/dumux/nonlinear/staggerednewtonconvergencewriter.hh b/dumux/nonlinear/staggerednewtonconvergencewriter.hh new file mode 100644 index 0000000000000000000000000000000000000000..1cb85da200eb9be4485731a8dbe9d380b29e785c --- /dev/null +++ b/dumux/nonlinear/staggerednewtonconvergencewriter.hh @@ -0,0 +1,185 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \ingroup Nonlinear + * \brief This class provides the infrastructure to write the + * convergence behaviour of the newton method for the + * staggered discretization scheme into a VTK file. + */ +#ifndef DUMUX_STAGGERED_NEWTON_CONVERGENCE_WRITER_HH +#define DUMUX_STAGGERED_NEWTON_CONVERGENCE_WRITER_HH + +#include <string> + +#include <dune/grid/io/file/vtk/vtksequencewriter.hh> +#include <dumux/io/vtksequencewriter.hh> +#include <dumux/io/pointcloudvtkwriter.hh> +#include "newtonconvergencewriter.hh" + +namespace Dumux { + +/*! + * \ingroup Nonlinear + * \brief Writes the intermediate solutions for every Newton iteration (for staggered grid scheme) + * \note To use this create a shared_ptr to an instance of this class in the main file + * and pass it to newton.solve(x, convergencewriter). You can use the reset method + * to write out multiple Newton solves with a unique id, if you don't call use all + * Newton iterations just come after each other in the pvd file. + */ +template <class FVGridGeometry, class SolutionVector> +class StaggeredNewtonConvergenceWriter : public ConvergenceWriterInterface<SolutionVector> +{ + using GridView = typename FVGridGeometry::GridView; + + using CellCenterSolutionVector = typename std::decay_t<decltype(std::declval<SolutionVector>()[FVGridGeometry::cellCenterIdx()])>; + using FaceSolutionVector = typename std::decay_t<decltype(std::declval<SolutionVector>()[FVGridGeometry::faceIdx()])>; + + using Scalar = typename CellCenterSolutionVector::block_type::value_type; + + static constexpr auto numEqCellCenter = CellCenterSolutionVector::block_type::dimension; + static constexpr auto numEqFace = FaceSolutionVector::block_type::dimension; + + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + + static_assert(FVGridGeometry::discMethod == DiscretizationMethod::staggered, + "This convergence writer does only work for the staggered method, use the NewtonConvergenceWriter instead"); +public: + /*! + * \brief Constructor + * \param fvGridGeometry The finite volume geometry on the grid view + * \param name Base name of the vtk output + */ + StaggeredNewtonConvergenceWriter(const FVGridGeometry& fvGridGeometry, + const std::string& name = "newton_convergence") + : fvGridGeometry_(fvGridGeometry) + , ccWriter_(fvGridGeometry.gridView(), name, "", "") + , faceWriter_(std::make_shared<PointCloudVtkWriter<Scalar, GlobalPosition>>(coordinates_)) + , faceSequenceWriter_(faceWriter_, name + "-face", "","", + fvGridGeometry.gridView().comm().rank(), + fvGridGeometry.gridView().comm().size()) + { + resize(); + + for (int eqIdx = 0; eqIdx < numEqCellCenter; ++eqIdx) + { + ccWriter_.addCellData(xCellCenter_[eqIdx], "x_" + std::to_string(eqIdx)); + ccWriter_.addCellData(deltaCellCenter_[eqIdx], "delta_" + std::to_string(eqIdx)); + ccWriter_.addCellData(defCellCenter_[eqIdx], "defect_" + std::to_string(eqIdx)); + } + } + + //! Resizes the output fields. This has to be called whenever the grid changes + void resize() + { + const auto numCellCenterDofs = fvGridGeometry_.numCellCenterDofs(); + const auto numFaceDofs = fvGridGeometry_.numFaceDofs(); + + // resize the cell center output fields + for (int eqIdx = 0; eqIdx < numEqCellCenter; ++eqIdx) + { + defCellCenter_[eqIdx].resize(numCellCenterDofs); + deltaCellCenter_[eqIdx].resize(numCellCenterDofs); + xCellCenter_[eqIdx].resize(numCellCenterDofs); + } + + // resize the face output fields + for (int eqIdx = 0; eqIdx < numEqFace; ++eqIdx) + { + defFace_[eqIdx].resize(numFaceDofs); + deltaFace_[eqIdx].resize(numFaceDofs); + xFace_[eqIdx].resize(numFaceDofs); + } + + coordinates_.resize(numFaceDofs); + for (auto&& facet : facets(fvGridGeometry_.gridView())) + { + const auto dofIdxGlobal = fvGridGeometry_.gridView().indexSet().index(facet); + coordinates_[dofIdxGlobal] = facet.geometry().center(); + } + } + + //! Reset the convergence writer for a possible next Newton step + //! You may set a different id in case you don't want the output to be overwritten by the next step + void reset(std::size_t newId = 0UL) + { id_ = newId; iteration_ = 0UL; } + + void write(const SolutionVector& uLastIter, + const SolutionVector& deltaU, + const SolutionVector& residual) override + { + assert(uLastIter.size() == deltaU.size() && uLastIter.size() == residual.size()); + + for (std::size_t dofIdxGlobal = 0; dofIdxGlobal < deltaU[FVGridGeometry::cellCenterIdx()].size(); ++dofIdxGlobal) + { + for (int eqIdx = 0; eqIdx < numEqCellCenter; ++eqIdx) + { + xCellCenter_[eqIdx][dofIdxGlobal] = uLastIter[FVGridGeometry::cellCenterIdx()][dofIdxGlobal][eqIdx]; + deltaCellCenter_[eqIdx][dofIdxGlobal] = - deltaU[FVGridGeometry::cellCenterIdx()][dofIdxGlobal][eqIdx]; + defCellCenter_[eqIdx][dofIdxGlobal] = residual[FVGridGeometry::cellCenterIdx()][dofIdxGlobal][eqIdx]; + } + } + + for (int eqIdx = 0; eqIdx < numEqFace; ++eqIdx) + { + faceWriter_->addPointData(xFace_[eqIdx], "x_" + std::to_string(eqIdx)); + faceWriter_->addPointData(deltaFace_[eqIdx], "delta_" + std::to_string(eqIdx)); + faceWriter_->addPointData(defFace_[eqIdx], "defect_" + std::to_string(eqIdx)); + } + + for (std::size_t dofIdxGlobal = 0; dofIdxGlobal < deltaU[FVGridGeometry::faceIdx()].size(); ++dofIdxGlobal) + { + for (int eqIdx = 0; eqIdx < numEqFace; ++eqIdx) + { + xFace_[eqIdx][dofIdxGlobal] = uLastIter[FVGridGeometry::faceIdx()][dofIdxGlobal][eqIdx]; + deltaFace_[eqIdx][dofIdxGlobal] = - deltaU[FVGridGeometry::faceIdx()][dofIdxGlobal][eqIdx]; + defFace_[eqIdx][dofIdxGlobal] = residual[FVGridGeometry::faceIdx()][dofIdxGlobal][eqIdx]; + } + } + + ccWriter_.write(static_cast<double>(id_) + static_cast<double>(iteration_)/1000); + faceSequenceWriter_.write(static_cast<double>(id_) + static_cast<double>(iteration_)/1000); + ++iteration_; + } + +private: + std::size_t id_ = 0UL; + std::size_t iteration_ = 0UL; + + const FVGridGeometry& fvGridGeometry_; + + Dune::VTKSequenceWriter<GridView> ccWriter_; + + std::vector<GlobalPosition> coordinates_; + std::shared_ptr<PointCloudVtkWriter<Scalar, GlobalPosition>> faceWriter_; + VTKSequenceWriter<PointCloudVtkWriter<Scalar, GlobalPosition>> faceSequenceWriter_; + + std::array<std::vector<Scalar>, numEqCellCenter> defCellCenter_; + std::array<std::vector<Scalar>, numEqCellCenter> deltaCellCenter_; + std::array<std::vector<Scalar>, numEqCellCenter> xCellCenter_; + + std::array<std::vector<Scalar>, numEqFace> defFace_; + std::array<std::vector<Scalar>, numEqFace> deltaFace_; + std::array<std::vector<Scalar>, numEqFace> xFace_; +}; + +} // end namespace Dumux + +#endif diff --git a/test/freeflow/navierstokes/donea/main.cc b/test/freeflow/navierstokes/donea/main.cc index 79d9334fb7791d01f6731bb18bc5226db1314d60..80e92746d2ca71a9ee2f3f4ff28f37c0a3d55564 100644 --- a/test/freeflow/navierstokes/donea/main.cc +++ b/test/freeflow/navierstokes/donea/main.cc @@ -43,6 +43,7 @@ #include <dumux/io/staggeredvtkoutputmodule.hh> #include <dumux/linear/seqsolverbackend.hh> #include <dumux/nonlinear/newtonsolver.hh> +#include <dumux/nonlinear/staggerednewtonconvergencewriter.hh> #include "problem.hh" @@ -116,6 +117,10 @@ int main(int argc, char** argv) try using NewtonSolver = Dumux::NewtonSolver<Assembler, LinearSolver>; NewtonSolver nonLinearSolver(assembler, linearSolver); + using NewtonConvergenceWriter = StaggeredNewtonConvergenceWriter<FVGridGeometry, SolutionVector>; + auto convergenceWriter = std::make_shared<NewtonConvergenceWriter>(*fvGridGeometry); + nonLinearSolver.attachConvergenceWriter(convergenceWriter); + // linearize & solve Dune::Timer timer; nonLinearSolver.solve(x); diff --git a/test/porousmediumflow/2p/implicit/nonisothermal/main.cc b/test/porousmediumflow/2p/implicit/nonisothermal/main.cc index 17dffc5ede1e99e79324b148bada652ca8cf4dfc..3c5aa3face8e33f9969c09fe9548039e215aa34d 100644 --- a/test/porousmediumflow/2p/implicit/nonisothermal/main.cc +++ b/test/porousmediumflow/2p/implicit/nonisothermal/main.cc @@ -155,9 +155,9 @@ int main(int argc, char** argv) try NewtonSolver nonLinearSolver(assembler, linearSolver); //the convergence writer - using GridView = GetPropType<TypeTag, Properties::GridView>; - using NewtonConvergenceWriter = Dumux::NewtonConvergenceWriter<GridView, SolutionVector>; - auto convergenceWriter = std::make_shared<NewtonConvergenceWriter>(leafGridView, fvGridGeometry->numDofs()); + using NewtonConvergenceWriter = Dumux::NewtonConvergenceWriter<FVGridGeometry, SolutionVector>; + auto convergenceWriter = std::make_shared<NewtonConvergenceWriter>(*fvGridGeometry); + nonLinearSolver.attachConvergenceWriter(convergenceWriter); // time loop timeLoop->start(); do @@ -165,8 +165,11 @@ int main(int argc, char** argv) try // set previous solution for storage evaluations assembler->setPreviousSolution(xOld); + // reset the convergence writer so that each time can be easily identified in the pvd file + convergenceWriter->reset(timeLoop->timeStepIndex()); + // solve the non-linear system with time step control - nonLinearSolver.solve(x, *timeLoop, convergenceWriter); + nonLinearSolver.solve(x, *timeLoop); // make the new solution the old solution xOld = x;