diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index fbf20b2458099cde5fd04af2561380804f40aedb..aacf28405d7293db3555bf49083ee4b425b9947c 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(2pinfiltration) add_subdirectory(1ptracer) add_subdirectory(biomineralization) +add_subdirectory(cahn_hilliard) add_subdirectory(diffusion) add_subdirectory(embedded_network_1d3d) add_subdirectory(shallowwaterfriction) diff --git a/examples/cahn_hilliard/CMakeLists.txt b/examples/cahn_hilliard/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..580fe4111efe2719777fc6270fd383d3d37a22ee --- /dev/null +++ b/examples/cahn_hilliard/CMakeLists.txt @@ -0,0 +1,2 @@ +dune_symlink_to_source_files(FILES "params.input") +dumux_add_test(NAME example_cahn_hilliard SOURCES main.cc) diff --git a/examples/cahn_hilliard/main.cc b/examples/cahn_hilliard/main.cc new file mode 100644 index 0000000000000000000000000000000000000000..fa1bc08904d39562adfd5e244988c5ce95a3916c --- /dev/null +++ b/examples/cahn_hilliard/main.cc @@ -0,0 +1,284 @@ +// -*- 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/>. * + *****************************************************************************/ + +#include <config.h> + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// Problem //////// (often in a separate file problem.hh) //////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +#include <dumux/common/properties.hh> +#include <dumux/common/parameters.hh> +#include <dumux/common/numeqvector.hh> + +#include <dumux/common/boundarytypes.hh> +#include <dumux/common/fvproblem.hh> + +namespace Dumux { + +template<class TypeTag> +class CahnHilliardTestProblem : public FVProblem<TypeTag> +{ + using ParentType = FVProblem<TypeTag>; + using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>; + using FVElementGeometry = typename GridGeometry::LocalView; + using SubControlVolume = typename GridGeometry::SubControlVolume; + using GridView = typename GetPropType<TypeTag, Properties::GridGeometry>::GridView; + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + + using Scalar = GetPropType<TypeTag, Properties::Scalar>; + using PrimaryVariables = GetPropType<TypeTag, Properties::PrimaryVariables>; + using NumEqVector = Dumux::NumEqVector<PrimaryVariables>; + using BoundaryTypes = Dumux::BoundaryTypes<GetPropType<TypeTag, Properties::ModelTraits>::numEq()>; + using Indices = typename GetPropType<TypeTag, Properties::ModelTraits>::Indices; +public: + CahnHilliardTestProblem(std::shared_ptr<const GridGeometry> gridGeometry) + : ParentType(gridGeometry) + { + mobility_ = getParam<Scalar>("Problem.Mobility"); + surfaceTension_ = getParam<Scalar>("Problem.SurfaceTension"); + energyScale_ = getParam<Scalar>("Problem.EnergyScale"); + } + + // here we implement the derivative of the free energy + template<class ElementVolumeVariables> + NumEqVector source(const Element &element, + const FVElementGeometry& fvGeometry, + const ElementVolumeVariables& elemVolVars, + const SubControlVolume &scv) const + { + NumEqVector values(0.0); + const auto& c = elemVolVars[scv].concentration(); + values[Indices::chemicalPotentialEqIdx] = -energyScale_*2.0*c*(2.0*c*c - 3*c + 1); + return values; + } + + BoundaryTypes boundaryTypesAtPos(const GlobalPosition& globalPos) const + { + BoundaryTypes values; + values.setAllNeumann(); + return values; + } + + NumEqVector neumannAtPos(const GlobalPosition& globalPos) const + { return { 0.0, 0.0 }; } + + Scalar mobility() const + { return mobility_; } + + Scalar surfaceTension() const + { return surfaceTension_; } + +private: + Scalar mobility_; + Scalar surfaceTension_; + Scalar energyScale_; +}; + +} // end namespace Dumux + +////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// Test case properties/traits /// (often in a separate file properties.hh) // +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +#include <dune/grid/yaspgrid.hh> +#include <dumux/discretization/box.hh> +#include "model.hh" + +namespace Dumux::Properties { + +namespace TTag { +struct CahnHilliardTest { using InheritsFrom = std::tuple<CahnHilliardModel, BoxModel>; }; +} // end namespace TTag + +template<class TypeTag> +struct Grid<TypeTag, TTag::CahnHilliardTest> +{ using type = Dune::YaspGrid<2>; }; + +template<class TypeTag> +struct Problem<TypeTag, TTag::CahnHilliardTest> +{ using type = CahnHilliardTestProblem<TypeTag>; }; + +// Enable caching +template<class TypeTag> +struct EnableGridVolumeVariablesCache<TypeTag, TTag::CahnHilliardTest> +{ static constexpr bool value = true; }; + +template<class TypeTag> +struct EnableGridFluxVariablesCache<TypeTag, TTag::CahnHilliardTest> +{ static constexpr bool value = true; }; + +template<class TypeTag> +struct EnableGridGeometryCache<TypeTag, TTag::CahnHilliardTest> +{ static constexpr bool value = true; }; + +} // end namespace Dumux::Properties + +////////////////////////////////////////////////////////////////////////////// + +#include <random> + +#include <dumux/common/initialize.hh> +#include <dumux/common/properties.hh> +#include <dumux/common/parameters.hh> + +#include <dumux/io/vtkoutputmodule.hh> +#include <dumux/io/grid/gridmanager_yasp.hh> + +#include <dumux/linear/linearsolvertraits.hh> +#include <dumux/linear/linearalgebratraits.hh> +#include <dumux/linear/istlsolvers.hh> +#include <dumux/nonlinear/newtonsolver.hh> +#include <dumux/assembly/fvassembler.hh> + +struct MinScatter +{ + template<class A, class B> + static void apply(A& a, const B& b) + { a[0] = std::min(a[0], b[0]); } +}; + +int main(int argc, char** argv) +{ + using namespace Dumux; + + // define the type tag for this problem + using TypeTag = Properties::TTag::CahnHilliardTest; + + // maybe initialize MPI and/or multithreading backend + Dumux::initialize(argc, argv); + + // initialize parameter tree + Parameters::init(argc, argv); + + // initialize the grid + GridManager<GetPropType<TypeTag, Properties::Grid>> gridManager; + gridManager.init(); + + // we compute on the leaf grid view + const auto& leafGridView = gridManager.grid().leafGridView(); + + // create the finite volume grid geometry + using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>; + auto gridGeometry = std::make_shared<GridGeometry>(leafGridView); + + // the problem (initial and boundary conditions) + using Problem = GetPropType<TypeTag, Properties::Problem>; + auto problem = std::make_shared<Problem>(gridGeometry); + + // the solution vector + using SolutionVector = GetPropType<TypeTag, Properties::SolutionVector>; + SolutionVector sol(gridGeometry->numDofs()); + + // create a random initial field + std::mt19937 gen(0); // seed is 0 for deterministic results + std::uniform_real_distribution<> dis(0.0, 1.0); + for (int n = 0; n < sol.size(); ++n) + { + sol[n][0] = 0.42 + 0.02*(0.5-dis(gen)) + leafGridView.comm().rank(); + sol[n][1] = 0.0; + } + + // take the value of the processor with the minimum rank + VectorCommDataHandle< + typename GridGeometry::VertexMapper, + SolutionVector, + GridGeometry::GridView::dimension, + MinScatter + > minHandle(gridGeometry->vertexMapper(), sol); + leafGridView.communicate(minHandle, Dune::All_All_Interface, Dune::ForwardCommunication); + + // remove processor offset + for (int n = 0; n < sol.size(); ++n) + sol[n][0] -= std::floor(sol[n][0]); + + // copy the vector to store state of previous time step + auto oldSol = sol; + + // the grid variables + using GridVariables = GetPropType<TypeTag, Properties::GridVariables>; + auto gridVariables = std::make_shared<GridVariables>(problem, gridGeometry); + gridVariables->init(sol); + + // initialize the vtk output module + VtkOutputModule<GridVariables, SolutionVector> vtkWriter(*gridVariables, sol, problem->name()); + vtkWriter.addVolumeVariable([](const auto& vv){ return vv.concentration(); }, "c"); + vtkWriter.addVolumeVariable([](const auto& vv){ return vv.chemicalPotential(); }, "mu"); + vtkWriter.write(0.0); + + // get some time loop parameters + using Scalar = GetPropType<TypeTag, Properties::Scalar>; + const auto tEnd = getParam<Scalar>("TimeLoop.TEnd"); + const auto dt = getParam<Scalar>("TimeLoop.InitialTimeStepSize"); + const auto maxDt = getParam<Scalar>("TimeLoop.MaxTimeStepSize"); + + // instantiate time loop + auto timeLoop = std::make_shared<CheckPointTimeLoop<Scalar>>(0.0, dt, tEnd); + timeLoop->setMaxTimeStepSize(maxDt); + + // the assembler with time loop for a transient problem + using Assembler = FVAssembler<TypeTag, DiffMethod::numeric>; + auto assembler = std::make_shared<Assembler>(problem, gridGeometry, gridVariables, timeLoop, oldSol); + + // the linear solver + using LinearSolver = SSORBiCGSTABIstlSolver< + LinearSolverTraits<GridGeometry>, + LinearAlgebraTraitsFromAssembler<Assembler> + >; + auto linearSolver = std::make_shared<LinearSolver>(gridGeometry->gridView(), gridGeometry->dofMapper()); + + // the solver + using Solver = Dumux::NewtonSolver<Assembler, LinearSolver>; + Solver solver(assembler, linearSolver); + + // time loop + timeLoop->start(); do + { + // assemble & solve + solver.solve(sol, *timeLoop); + + // make the new solution the old solution + oldSol = sol; + 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 the Newton solver + timeLoop->setTimeStepSize(solver.suggestTimeStepSize(timeLoop->timeStepSize())); + + } while (!timeLoop->finished()); + + timeLoop->finalize(leafGridView.comm()); + + return 0; +} diff --git a/examples/cahn_hilliard/model.hh b/examples/cahn_hilliard/model.hh new file mode 100644 index 0000000000000000000000000000000000000000..51b5eb62be61a8a886a9696ee5378ddad6ef2df8 --- /dev/null +++ b/examples/cahn_hilliard/model.hh @@ -0,0 +1,255 @@ +// -*- 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/>. * + *****************************************************************************/ + +#ifndef DUMUX_EXAMPLES_CAHN_HILLIARD_MODEL_HH +#define DUMUX_EXAMPLES_CAHN_HILLIARD_MODEL_HH + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// VolumeVariables //////// (often in a separate file volumevariables.hh) //// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +namespace Dumux { + +template <class Traits> +class CahnHilliardModelVolumeVariables +{ + using Scalar = typename Traits::PrimaryVariables::value_type; + static_assert(Traits::PrimaryVariables::dimension == Traits::ModelTraits::numEq()); +public: + //! export the type used for the primary variables + using PrimaryVariables = typename Traits::PrimaryVariables; + //! export the indices type + using Indices = typename Traits::ModelTraits::Indices; + + /*! + * \brief Update all quantities for a given control volume + */ + template<class ElementSolution, class Problem, class Element, class SubControlVolume> + void update(const ElementSolution& elemSol, + const Problem& problem, + const Element& element, + const SubControlVolume& scv) + { + priVars_ = elemSol[scv.indexInElement()]; + } + + Scalar concentration() const + { return priVars_[Indices::concentrationIdx]; } + + Scalar chemicalPotential() const + { return priVars_[Indices::chemicalPotentialIdx]; } + + Scalar priVar(const int pvIdx) const + { return priVars_[pvIdx]; } + + const PrimaryVariables& priVars() const + { return priVars_; } + + // for compatibility with more general models + Scalar extrusionFactor() const + { return 1.0; } + +private: + PrimaryVariables priVars_; +}; + +} // end namespace Dumux + +////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// LocalResidual //////////// (often in a separate file localresidual.hh) //// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +#include <dumux/common/math.hh> +#include <dumux/common/properties.hh> +#include <dumux/common/numeqvector.hh> + +namespace Dumux { + +template<class TypeTag> +class CahnHilliardModelLocalResidual +: public GetPropType<TypeTag, Properties::BaseLocalResidual> +{ + // the base local residual is selected depending on the chosen discretization scheme + using ParentType = GetPropType<TypeTag, Properties::BaseLocalResidual>; + using Scalar = GetPropType<TypeTag, Properties::Scalar>; + using Problem = GetPropType<TypeTag, Properties::Problem>; + + using NumEqVector = Dumux::NumEqVector<GetPropType<TypeTag, Properties::PrimaryVariables>>; + using VolumeVariables = typename GetPropType<TypeTag, Properties::GridVolumeVariables>::VolumeVariables; + + using ElementVolumeVariables = typename GetPropType<TypeTag, Properties::GridVolumeVariables>::LocalView; + using ElementFluxVariablesCache = typename GetPropType<TypeTag, Properties::GridFluxVariablesCache>::LocalView; + using FVElementGeometry = typename GetPropType<TypeTag, Properties::GridGeometry>::LocalView; + + using SubControlVolume = typename FVElementGeometry::SubControlVolume; + using SubControlVolumeFace = typename FVElementGeometry::SubControlVolumeFace; + using GridView = typename GetPropType<TypeTag, Properties::GridGeometry>::GridView; + using Element = typename GridView::template Codim<0>::Entity; + + using ModelTraits = GetPropType<TypeTag, Properties::ModelTraits>; + using Indices = typename ModelTraits::Indices; + static constexpr int dimWorld = GridView::dimensionworld; +public: + using ParentType::ParentType; + + /*! + * \brief Evaluate the rate of change of all conserved quantities + */ + NumEqVector computeStorage(const Problem& problem, + const SubControlVolume& scv, + const VolumeVariables& volVars) const + { + NumEqVector storage; + storage[Indices::massBalanceEqIdx] = volVars.concentration(); + storage[Indices::chemicalPotentialEqIdx] = 0.0; + return storage; + } + + /*! + * \brief Evaluate the fluxes over a face of a sub control volume + * Here we evaluate the flow rate, F1 = -M∇mu·n A, F2 = -gamma∇c·n A + * + * TODO: why is this called flux, if we expect it to be integrated already? + * computeFluxIntegral? + */ + NumEqVector computeFlux(const Problem& problem, + const Element& element, + const FVElementGeometry& fvGeometry, + const ElementVolumeVariables& elemVolVars, + const SubControlVolumeFace& scvf, + const ElementFluxVariablesCache& elemFluxVarsCache) const + { + const auto& fluxVarCache = elemFluxVarsCache[scvf]; + Dune::FieldVector<Scalar, dimWorld> gradConcentration(0.0); + Dune::FieldVector<Scalar, dimWorld> gradChemicalPotential(0.0); + for (const auto& scv : scvs(fvGeometry)) + { + const auto& volVars = elemVolVars[scv]; + gradConcentration.axpy(volVars.concentration(), fluxVarCache.gradN(scv.indexInElement())); + gradChemicalPotential.axpy(volVars.chemicalPotential(), fluxVarCache.gradN(scv.indexInElement())); + } + + const auto M = problem.mobility(); + const auto gamma = problem.surfaceTension(); + + NumEqVector flux; + flux[Indices::massBalanceEqIdx] = -1.0*vtmv(scvf.unitOuterNormal(), M, gradChemicalPotential)*scvf.area(); + flux[Indices::chemicalPotentialEqIdx] = -1.0*vtmv(scvf.unitOuterNormal(), gamma, gradConcentration)*scvf.area(); + return flux; + } + + /*! + * \brief Calculate the source term of the equation + */ + NumEqVector computeSource(const Problem& problem, + const Element& element, + const FVElementGeometry& fvGeometry, + const ElementVolumeVariables& elemVolVars, + const SubControlVolume &scv) const + { + NumEqVector source(0.0); + + source[Indices::chemicalPotentialEqIdx] = elemVolVars[scv].chemicalPotential(); + + // add contributions from problem (e.g. double well potential) + source += problem.source(element, fvGeometry, elemVolVars, scv); + + return source; + } +}; + +} // end namespace Dumux +////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// Model properties/traits /////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +#include <dumux/common/properties.hh> + +namespace Dumux::Properties { + +namespace TTag { +struct CahnHilliardModel {}; +} // end namespace TTag + +//! Set the default type of scalar values to double +template<class TypeTag> +struct Scalar<TypeTag, TTag:: CahnHilliardModel > +{ using type = double; }; + +//! Set the default primary variable vector to a vector of size of number of equations +template<class TypeTag> +struct PrimaryVariables<TypeTag, TTag:: CahnHilliardModel > +{ + using type = Dune::FieldVector< + GetPropType<TypeTag, Properties::Scalar>, + GetPropType<TypeTag, Properties::ModelTraits>::numEq() + >; +}; + +//! Set the model traits property +template<class TypeTag> +struct ModelTraits<TypeTag, TTag::CahnHilliardModel> +{ + struct Traits + { + struct Indices + { + static constexpr int concentrationIdx = 0; + static constexpr int chemicalPotentialIdx = 1; + + static constexpr int massBalanceEqIdx = 0; + static constexpr int chemicalPotentialEqIdx = 1; + }; + + static constexpr int numEq() { return 2; } + }; + + using type = Traits; +}; + +template<class TypeTag> +struct LocalResidual<TypeTag, TTag::CahnHilliardModel> +{ using type = CahnHilliardModelLocalResidual<TypeTag>; }; + +//! Set the volume variables property +template<class TypeTag> +struct VolumeVariables<TypeTag, TTag::CahnHilliardModel> +{ + struct Traits + { + using PrimaryVariables = GetPropType<TypeTag, Properties::PrimaryVariables>; + using ModelTraits = GetPropType<TypeTag, Properties::ModelTraits>; + }; + + using type = CahnHilliardModelVolumeVariables<Traits>; +}; + +} // end namespace Dumux::Properties + +#endif diff --git a/examples/cahn_hilliard/params.input b/examples/cahn_hilliard/params.input new file mode 100644 index 0000000000000000000000000000000000000000..f9d41a960d4dd43dd177449a3ffdba6ea82425c4 --- /dev/null +++ b/examples/cahn_hilliard/params.input @@ -0,0 +1,19 @@ +[TimeLoop] +TEnd = 1.0 +InitialTimeStepSize = 0.0025 +MaxTimeStepSize = 0.01 + +[Grid] +LowerLeft = 0 0 +UpperRight = 1 1 +Cells = 100 100 +Overlap = 2 + +[Problem] +Name = cahn_hilliard +Mobility = 0.0001 +SurfaceTension = 0.01 +EnergyScale = 100 + +[Newton] +EnablePartialReassembly = true