diff --git a/test/porousmediumflow/2pnc/implicit/fuelcellproblem.hh b/test/porousmediumflow/2pnc/implicit/fuelcellproblem.hh index 18d6aaacee9b0b623ed71ef501fc373b46fd3a08..eb3ed5df471c3db7548685f64c5052ad4ac31711 100644 --- a/test/porousmediumflow/2pnc/implicit/fuelcellproblem.hh +++ b/test/porousmediumflow/2pnc/implicit/fuelcellproblem.hh @@ -24,9 +24,10 @@ #ifndef DUMUX_FUELCELL_PROBLEM_HH #define DUMUX_FUELCELL_PROBLEM_HH -#include <dumux/implicit/cellcentered/tpfa/properties.hh> +#include <dumux/discretization/cellcentered/tpfa/properties.hh> +#include <dumux/discretization/box/properties.hh> #include <dumux/porousmediumflow/2pnc/implicit/model.hh> -#include <dumux/porousmediumflow/implicit/problem.hh> +#include <dumux/porousmediumflow/problem.hh> #include <dumux/material/fluidsystems/h2on2o2.hh> #include <dumux/material/chemistry/electrochemistry/electrochemistry.hh> @@ -75,14 +76,13 @@ SET_INT_PROP(FuelCellProblem, ReplaceCompEqIdx, 3); * <tt>./test_box2pnc</tt> */ template <class TypeTag> -class FuelCellProblem : public ImplicitPorousMediaProblem<TypeTag> +class FuelCellProblem : public PorousMediumFlowProblem<TypeTag> { - using ParentType = ImplicitPorousMediaProblem<TypeTag>; + using ParentType = PorousMediumFlowProblem<TypeTag>; using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); using Indices = typename GET_PROP_TYPE(TypeTag, Indices); using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); - using TimeManager = typename GET_PROP_TYPE(TypeTag, TimeManager); using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); using Sources = typename GET_PROP_TYPE(TypeTag, NumEqVector); @@ -90,9 +90,15 @@ class FuelCellProblem : public ImplicitPorousMediaProblem<TypeTag> using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry); using SubControlVolume = typename GET_PROP_TYPE(TypeTag, SubControlVolume); using SubControlVolumeFace = typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace); - using VtkOutputModule = typename GET_PROP_TYPE(TypeTag, VtkOutputModule); using GridView = typename GET_PROP_TYPE(TypeTag, GridView); using Element = typename GridView::template Codim<0>::Entity; + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + using VolumeVariables = typename GET_PROP_TYPE(TypeTag, VolumeVariables); + using ElementSolutionVector = typename GET_PROP_TYPE(TypeTag, ElementSolutionVector); + + + // Select the electrochemistry method using ElectroChemistry = typename Dumux::ElectroChemistry<TypeTag, ElectroChemistryModel::Ochs>; @@ -124,7 +130,7 @@ class FuelCellProblem : public ImplicitPorousMediaProblem<TypeTag> static constexpr int dim = GridView::dimension; static constexpr int dimWorld = GridView::dimensionworld; - static constexpr bool isBox = GET_PROP_VALUE(TypeTag, ImplicitIsBox); + static constexpr bool isBox = GET_PROP_VALUE(TypeTag, DiscretizationMethod) == DiscretizationMethods::Box; using GlobalPosition = Dune::FieldVector<Scalar, dimWorld>; enum { dofCodim = isBox ? dim : 0 }; @@ -135,20 +141,20 @@ public: * \param timeManager The time manager * \param gridView The grid view */ - FuelCellProblem(TimeManager &timeManager, const GridView &gridView) - : ParentType(timeManager, gridView) + FuelCellProblem(std::shared_ptr<const FVGridGeometry> fvGridGeometry) + : ParentType(fvGridGeometry) { - nTemperature_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, FluidSystem, NTemperature); - nPressure_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, FluidSystem, NPressure); - pressureLow_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, FluidSystem, PressureLow); - pressureHigh_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, FluidSystem, PressureHigh); - temperatureLow_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, FluidSystem, TemperatureLow); - temperatureHigh_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, FluidSystem, TemperatureHigh); - temperature_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, FluidSystem, InitialTemperature); + nTemperature_ = getParam<int>("Problem.NTemperature"); + nPressure_ = getParam<int>("Problem.NPressure"); + pressureLow_ = getParam<Scalar>("Problem.PressureLow"); + pressureHigh_ = getParam<Scalar>("Problem.PressureHigh"); + temperatureLow_ = getParam<Scalar>("Problem.TemperatureLow"); + temperatureHigh_ = getParam<Scalar>("Problem.TemperatureHigh"); + temperature_ = getParam<Scalar>("Problem.InitialTemperature"); - name_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, std::string, Problem, Name); + name_ = getParam<std::string>("Problem.Name"); - pO2Inlet_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, ElectroChemistry, pO2Inlet); + pO2Inlet_ = getParam<Scalar>("ElectroChemistry.pO2Inlet"); FluidSystem::init(/*Tmin=*/temperatureLow_, /*Tmax=*/temperatureHigh_, @@ -156,6 +162,10 @@ public: /*pmin=*/pressureLow_, /*pmax=*/pressureHigh_, /*np=*/nPressure_); + + currentDensity_.resize(fvGridGeometry->gridView().size(dofCodim)); + reactionSourceH2O_.resize(fvGridGeometry->gridView().size(dofCodim)); + reactionSourceO2_.resize(fvGridGeometry->gridView().size(dofCodim)); } /*! @@ -259,50 +269,63 @@ public: /*! * \brief Adds additional VTK output data to the VTKWriter. Function is called by the output module on every write. */ - void addVtkOutputFields(VtkOutputModule& outputModule) const + const std::vector<Scalar>& getCurrentDensity() { - // create the required scalar fields - auto& currentDensity = outputModule.createScalarField("currentDensity [A/cm^2]", dofCodim); - auto& reactionSourceH2O = outputModule.createScalarField("reactionSourceH2O [mol/(sm^2)]", dofCodim); - auto& reactionSourceO2 = outputModule.createScalarField("reactionSourceO2 [mol/(sm^2)]", dofCodim); + return currentDensity_; + } - for (const auto& element : elements(this->gridView())) + const std::vector<Scalar>& getReactionSourceH2O() + { + return reactionSourceH2O_; + } + + const std::vector<Scalar>& getReactionSourceO2() + { + return reactionSourceO2_; + } + + + void updateVtkOutput(const SolutionVector& curSol) + { + for (const auto& element : elements(this->fvGridGeometry().gridView())) { - auto fvGeometry = localView(this->model().fvGridGeometry()); - fvGeometry.bindElement(element); + ElementSolutionVector elemSol(element, curSol, this->fvGridGeometry()); - auto elemVolVars = localView(this->model().curGlobalVolVars()); - elemVolVars.bindElement(element, fvGeometry, this->model().curSol()); + auto fvGeometry = localView(this->fvGridGeometry()); + fvGeometry.bindElement(element); for (auto&& scv : scvs(fvGeometry)) { + VolumeVariables volVars; + volVars.update(elemSol, *this, element, scv); const auto& globalPos = scv.dofPosition(); const auto dofIdxGlobal = scv.dofIndex(); - //reaction sources from electro chemistry if(inReactionLayer_(globalPos)) { //reactionSource Output PrimaryVariables source; - auto i = ElectroChemistry::calculateCurrentDensity(elemVolVars[scv]); + auto i = ElectroChemistry::calculateCurrentDensity(volVars); ElectroChemistry::reactionSource(source, i); - reactionSourceH2O[dofIdxGlobal] = source[wPhaseIdx]; - reactionSourceO2[dofIdxGlobal] = source[numComponents-1]; + reactionSourceH2O_[dofIdxGlobal] = source[wPhaseIdx]; + reactionSourceO2_[dofIdxGlobal] = source[numComponents-1]; //Current Output in A/cm^2 - currentDensity[dofIdxGlobal] = i/10000; + currentDensity_[dofIdxGlobal] = i/10000; } else { - reactionSourceH2O[dofIdxGlobal] = 0.0; - reactionSourceO2[dofIdxGlobal] = 0.0; - currentDensity[dofIdxGlobal] = 0.0; + reactionSourceH2O_[dofIdxGlobal] = 0.0; + reactionSourceO2_[dofIdxGlobal] = 0.0; + currentDensity_[dofIdxGlobal] = 0.0; } } } } + + private: PrimaryVariables initial_(const GlobalPosition &globalPos) const @@ -319,19 +342,19 @@ private: } bool onLeftBoundary_(const GlobalPosition &globalPos) const - { return globalPos[0] < this->bBoxMin()[0] + eps_; } + { return globalPos[0] < this->fvGridGeometry().bBoxMin()[0] + eps_; } bool onRightBoundary_(const GlobalPosition &globalPos) const - { return globalPos[0] > this->bBoxMax()[0] - eps_; } + { return globalPos[0] > this->fvGridGeometry().bBoxMax()[0] - eps_; } bool onLowerBoundary_(const GlobalPosition &globalPos) const - { return globalPos[1] < this->bBoxMin()[1] + eps_; } + { return globalPos[1] < this->fvGridGeometry().bBoxMin()[1] + eps_; } bool onUpperBoundary_(const GlobalPosition &globalPos) const - { return globalPos[1] > this->bBoxMax()[1] - eps_; } + { return globalPos[1] > this->fvGridGeometry().bBoxMax()[1] - eps_; } bool inReactionLayer_(const GlobalPosition& globalPos) const - { return globalPos[1] < 0.1*(this->bBoxMax()[1] - this->bBoxMin()[1]) + eps_; } + { return globalPos[1] < 0.1*(this->fvGridGeometry().bBoxMax()[1] - this->fvGridGeometry().bBoxMin()[1]) + eps_; } Scalar temperature_; static constexpr Scalar eps_ = 1e-6; @@ -341,6 +364,9 @@ private: Scalar pressureLow_, pressureHigh_; Scalar temperatureLow_, temperatureHigh_; Scalar pO2Inlet_; + std::vector<double> currentDensity_; + std::vector<double> reactionSourceH2O_; + std::vector<double> reactionSourceO2_; }; } //end namespace Dumux diff --git a/test/porousmediumflow/2pnc/implicit/fuelcellspatialparams.hh b/test/porousmediumflow/2pnc/implicit/fuelcellspatialparams.hh index 6d591fb9eeaaf1d6bf4ad08a689c83ef9f185e80..255da0f8e9f99d4b2690f5eeb418e77789cc1a3c 100644 --- a/test/porousmediumflow/2pnc/implicit/fuelcellspatialparams.hh +++ b/test/porousmediumflow/2pnc/implicit/fuelcellspatialparams.hh @@ -100,8 +100,8 @@ public: * * \param gridView The grid view */ - FuelCellSpatialParams(const Problem& problem, const GridView &gridView) - : ParentType(problem, gridView), K_(0) + FuelCellSpatialParams(const Problem& problem) + : ParentType(problem), K_(0) { // intrinsic permeabilities K_[0][0] = 5e-11; diff --git a/test/porousmediumflow/2pnc/implicit/test_2pnc.input b/test/porousmediumflow/2pnc/implicit/test_2pnc.input index 387623cb1d62bba07a7e304ccc3990fea777b79d..00a7b1890c9fda8fde2c5589d4b36eacb3520c56 100644 --- a/test/porousmediumflow/2pnc/implicit/test_2pnc.input +++ b/test/porousmediumflow/2pnc/implicit/test_2pnc.input @@ -2,7 +2,7 @@ # Everything behind a '#' is a comment. # Type "./test_2pnc --help" for more information. -[TimeManager] +[TimeLoop] DtInitial = 5e-1 # [s] initial time step size MaxTimeStepSize = 1000 # [s] maximum time step size TEnd= 1e3 # [s] duration of the simulation @@ -14,8 +14,6 @@ Cells = 21 6 # [-] number of cells in x,y-direction [Problem] Name = fuelcell EnableGravity = 0 - -[FluidSystem] NTemperature = 3 # [-] number of temperature table entries NPressure = 200 # [-] number of pressure table entries PressureLow = 1e5 # [Pa] lower pressure limit for tabularization diff --git a/test/porousmediumflow/2pnc/implicit/test_box2pnc.cc b/test/porousmediumflow/2pnc/implicit/test_box2pnc.cc index 826b60d1217f7d77df8b232f592eea3ae4c2cc77..2a77047cfd1e4b2bf7e278425b3ae0d734176a9e 100644 --- a/test/porousmediumflow/2pnc/implicit/test_box2pnc.cc +++ b/test/porousmediumflow/2pnc/implicit/test_box2pnc.cc @@ -22,8 +22,34 @@ * \brief Test for the 2pnc box model used for water management in PEM fuel cells. */ #include <config.h> + +#include <ctime> +#include <iostream> + +#include <dune/common/parallel/mpihelper.hh> +#include <dune/common/timer.hh> +#include <dune/grid/io/file/dgfparser/dgfexception.hh> +#include <dune/grid/io/file/vtk.hh> +#include <dune/istl/io.hh> + #include "fuelcellproblem.hh" -#include <dumux/common/start.hh> + +#include <dumux/common/propertysystem.hh> +#include <dumux/common/parameters.hh> +#include <dumux/common/valgrind.hh> +#include <dumux/common/dumuxmessage.hh> +#include <dumux/common/defaultusagemessage.hh> + +#include <dumux/linear/amgbackend.hh> +#include <dumux/nonlinear/newtonmethod.hh> +#include <dumux/porousmediumflow/compositional/privarswitchnewtoncontroller.hh> + +#include <dumux/assembly/fvassembler.hh> +#include <dumux/assembly/diffmethod.hh> + +#include <dumux/discretization/methods.hh> + +#include <dumux/io/vtkoutputmodule.hh> /*! * \brief Provides an interface for customizing error messages associated with @@ -48,8 +74,177 @@ void usage(const char *progName, const std::string &errorMsg) } } -int main(int argc, char** argv) +int main(int argc, char** argv) try +{ + using namespace Dumux; + + // define the type tag for this problem + using TypeTag = TTAG(FuelCellBoxProblem); + + // initialize MPI, finalize is done automatically on exit + const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); + + // print dumux start message + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/true); + + // parse command line arguments and input file + Parameters::init(argc, argv, usage); + + // try to create a grid (from the given grid file or the input file) + using GridCreator = typename GET_PROP_TYPE(TypeTag, GridCreator); + GridCreator::makeGrid(); + GridCreator::loadBalance(); + + //////////////////////////////////////////////////////////// + // run instationary non-linear problem on this grid + //////////////////////////////////////////////////////////// + + // we compute on the leaf grid view + const auto& leafGridView = GridCreator::grid().leafGridView(); + + // create the finite volume grid geometry + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + auto fvGridGeometry = std::make_shared<FVGridGeometry>(leafGridView); + fvGridGeometry->update(); + + // the problem (initial and boundary conditions) + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + auto problem = std::make_shared<Problem>(fvGridGeometry); + + // the solution vector + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + SolutionVector x(leafGridView.size(GridView::dimension)); + problem->applyInitialSolution(x); + auto xOld = x; + + // the grid variables + using GridVariables = typename GET_PROP_TYPE(TypeTag, GridVariables); + auto gridVariables = std::make_shared<GridVariables>(problem, fvGridGeometry); + gridVariables->init(x, xOld); + + // get some time loop parameters + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + const auto tEnd = getParam<Scalar>("TimeLoop.TEnd"); + const auto maxDivisions = getParam<int>("TimeLoop.MaxTimeStepDivisions"); + const auto maxDt = getParam<Scalar>("TimeLoop.MaxTimeStepSize"); + auto dt = getParam<Scalar>("TimeLoop.DtInitial"); + + // check if we are about to restart a previously interrupted simulation + Scalar restartTime = 0; + if (Parameters::getTree().hasKey("Restart") || Parameters::getTree().hasKey("TimeLoop.Restart")) + restartTime = getParam<Scalar>("TimeLoop.Restart"); + + // intialize the vtk output module + using VtkOutputFields = typename GET_PROP_TYPE(TypeTag, VtkOutputFields); + VtkOutputModule<TypeTag> vtkWriter(*problem, *fvGridGeometry, *gridVariables, x, problem->name()); + VtkOutputFields::init(vtkWriter); //! Add model specific output fields + //add specific output + vtkWriter.addField(problem->getCurrentDensity(), "currentDensity [A/cm^2]"); + vtkWriter.addField(problem->getReactionSourceH2O(), "reactionSourceH2O [mol/(sm^2)]"); + vtkWriter.addField(problem->getReactionSourceO2(), "reactionSourceO2 [mol/(sm^2)]"); + vtkWriter.write(0.0); + + // instantiate time loop + auto timeLoop = std::make_shared<TimeLoop<Scalar>>(restartTime, dt, tEnd); + timeLoop->setMaxTimeStepSize(maxDt); + + // the assembler with time loop for instationary problem + using Assembler = FVAssembler<TypeTag, DiffMethod::numeric>; + auto assembler = std::make_shared<Assembler>(problem, fvGridGeometry, gridVariables, timeLoop); + + // the linear solver + using LinearSolver = AMGBackend<TypeTag>; + auto linearSolver = std::make_shared<LinearSolver>(leafGridView, fvGridGeometry->elementMapper()); + + // the non-linear solver + using NewtonController = PriVarSwitchNewtonController<TypeTag>; + using NewtonMethod = 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(); + + problem->updateVtkOutput(xOld); + + // 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; +} // end main +catch (Dumux::ParameterException &e) +{ + std::cerr << std::endl << e << " ---> Abort!" << std::endl; + return 1; +} +catch (Dune::DGFException & e) +{ + std::cerr << "DGF exception thrown (" << e << + "). Most likely, the DGF file name is wrong " + "or the DGF file is corrupted, " + "e.g. missing hash at end of file or wrong number (dimensions) of entries." + << " ---> Abort!" << std::endl; + return 2; +} +catch (Dune::Exception &e) +{ + std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; + return 3; +} +catch (...) { - typedef TTAG(FuelCellBoxProblem) ProblemTypeTag; - return Dumux::start<ProblemTypeTag>(argc, argv, usage); + std::cerr << "Unknown exception thrown! ---> Abort!" << std::endl; + return 4; } diff --git a/test/porousmediumflow/2pnc/implicit/test_cc2pnc.cc b/test/porousmediumflow/2pnc/implicit/test_cc2pnc.cc index 7786be91f6af42353d346dab84014bf752633bfa..8b0406201e4c9a6f6c85e6d6f7de3e408c192044 100644 --- a/test/porousmediumflow/2pnc/implicit/test_cc2pnc.cc +++ b/test/porousmediumflow/2pnc/implicit/test_cc2pnc.cc @@ -22,8 +22,34 @@ * \brief Test for the 2pnc cc model used for water management in PEM fuel cells. */ #include <config.h> + +#include <ctime> +#include <iostream> + +#include <dune/common/parallel/mpihelper.hh> +#include <dune/common/timer.hh> +#include <dune/grid/io/file/dgfparser/dgfexception.hh> +#include <dune/grid/io/file/vtk.hh> +#include <dune/istl/io.hh> + #include "fuelcellproblem.hh" -#include <dumux/common/start.hh> + +#include <dumux/common/propertysystem.hh> +#include <dumux/common/parameters.hh> +#include <dumux/common/valgrind.hh> +#include <dumux/common/dumuxmessage.hh> +#include <dumux/common/defaultusagemessage.hh> + +#include <dumux/linear/amgbackend.hh> +#include <dumux/nonlinear/newtonmethod.hh> +#include <dumux/porousmediumflow/compositional/privarswitchnewtoncontroller.hh> + +#include <dumux/assembly/fvassembler.hh> +#include <dumux/assembly/diffmethod.hh> + +#include <dumux/discretization/methods.hh> + +#include <dumux/io/vtkoutputmodule.hh> /*! * \brief Provides an interface for customizing error messages associated with @@ -48,8 +74,176 @@ void usage(const char *progName, const std::string &errorMsg) } } -int main(int argc, char** argv) +int main(int argc, char** argv) try +{ + using namespace Dumux; + + // define the type tag for this problem + using TypeTag = TTAG(FuelCellCCProblem); + + // initialize MPI, finalize is done automatically on exit + const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); + + // print dumux start message + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/true); + + // parse command line arguments and input file + Parameters::init(argc, argv, usage); + + // try to create a grid (from the given grid file or the input file) + using GridCreator = typename GET_PROP_TYPE(TypeTag, GridCreator); + GridCreator::makeGrid(); + GridCreator::loadBalance(); + + //////////////////////////////////////////////////////////// + // run instationary non-linear problem on this grid + //////////////////////////////////////////////////////////// + + // we compute on the leaf grid view + const auto& leafGridView = GridCreator::grid().leafGridView(); + + // create the finite volume grid geometry + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + auto fvGridGeometry = std::make_shared<FVGridGeometry>(leafGridView); + fvGridGeometry->update(); + + // the problem (initial and boundary conditions) + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + auto problem = std::make_shared<Problem>(fvGridGeometry); + + // the solution vector + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + SolutionVector x(leafGridView.size(0)); + problem->applyInitialSolution(x); + auto xOld = x; + + // the grid variables + using GridVariables = typename GET_PROP_TYPE(TypeTag, GridVariables); + auto gridVariables = std::make_shared<GridVariables>(problem, fvGridGeometry); + gridVariables->init(x, xOld); + + // get some time loop parameters + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + const auto tEnd = getParam<Scalar>("TimeLoop.TEnd"); + const auto maxDivisions = getParam<int>("TimeLoop.MaxTimeStepDivisions"); + const auto maxDt = getParam<Scalar>("TimeLoop.MaxTimeStepSize"); + auto dt = getParam<Scalar>("TimeLoop.DtInitial"); + + // check if we are about to restart a previously interrupted simulation + Scalar restartTime = 0; + if (Parameters::getTree().hasKey("Restart") || Parameters::getTree().hasKey("TimeLoop.Restart")) + restartTime = getParam<Scalar>("TimeLoop.Restart"); + + // intialize the vtk output module + using VtkOutputFields = typename GET_PROP_TYPE(TypeTag, VtkOutputFields); + VtkOutputModule<TypeTag> vtkWriter(*problem, *fvGridGeometry, *gridVariables, x, problem->name()); + VtkOutputFields::init(vtkWriter); //! Add model specific output fields + //add specific output + vtkWriter.addField(problem->getCurrentDensity(), "currentDensity [A/cm^2]"); + vtkWriter.addField(problem->getReactionSourceH2O(), "reactionSourceH2O [mol/(sm^2)]"); + vtkWriter.addField(problem->getReactionSourceO2(), "reactionSourceO2 [mol/(sm^2)]"); + vtkWriter.write(0.0); + + // instantiate time loop + auto timeLoop = std::make_shared<TimeLoop<Scalar>>(restartTime, dt, tEnd); + timeLoop->setMaxTimeStepSize(maxDt); + + // the assembler with time loop for instationary problem + using Assembler = FVAssembler<TypeTag, DiffMethod::numeric>; + auto assembler = std::make_shared<Assembler>(problem, fvGridGeometry, gridVariables, timeLoop); + + // the linear solver + using LinearSolver = AMGBackend<TypeTag>; + auto linearSolver = std::make_shared<LinearSolver>(leafGridView, fvGridGeometry->elementMapper()); + + // the non-linear solver + using NewtonController = PriVarSwitchNewtonController<TypeTag>; + using NewtonMethod = 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(); + + problem->updateVtkOutput(xOld); + + // 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; +} // end main +catch (Dumux::ParameterException &e) +{ + std::cerr << std::endl << e << " ---> Abort!" << std::endl; + return 1; +} +catch (Dune::DGFException & e) +{ + std::cerr << "DGF exception thrown (" << e << + "). Most likely, the DGF file name is wrong " + "or the DGF file is corrupted, " + "e.g. missing hash at end of file or wrong number (dimensions) of entries." + << " ---> Abort!" << std::endl; + return 2; +} +catch (Dune::Exception &e) +{ + std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; + return 3; +} +catch (...) { - typedef TTAG(FuelCellCCProblem) ProblemTypeTag; - return Dumux::start<ProblemTypeTag>(argc, argv, usage); + std::cerr << "Unknown exception thrown! ---> Abort!" << std::endl; + return 4; }