diff --git a/examples/README.md b/examples/README.md index beb935118d8db8a5d64f7bcedeb2a8c74f17f499..057987caa527b6fde04fa9dc369abf8d6814142e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -224,3 +224,15 @@ __Discretization method:__ Cell-centered finite volumes with two-point flux appr <figure><img src="embedded_network_1d3d/img/network.png" alt="blood vessel network"/></figure></td> </a></td> </tr></table> + +### [:open_file_folder: Example 11: The Cahn-Hilliard model, a nonlinear PDE](cahn_hilliard/README.md) + +<table><tr><td> + +In this example we simulate the separation of two immiscible phases using the Cahn-Hilliard model. + +</td> +<td width="35%"><a href="cahn_hilliard/README.md"> +<figure><img src="cahn_hilliard/img/animation.gif" alt="phase distribution"/></figure></td> +</a></td> +</tr></table> diff --git a/examples/cahn_hilliard/.doc_config b/examples/cahn_hilliard/.doc_config new file mode 100644 index 0000000000000000000000000000000000000000..d1a87cc8a3bb170ad8f1db9821477b8b0f5747cd --- /dev/null +++ b/examples/cahn_hilliard/.doc_config @@ -0,0 +1,27 @@ +{ + "README.md" : [ + "doc/_intro.md" + ], + + "doc/mainfile.md" : [ + "doc/mainfile_intro.md", + "main.cc" + ], + + "doc/modelheader.md" : [ + "doc/modelheader_intro.md", + "model.hh" + ], + + "navigation" : { + "mainpage" : "README.md", + "subpages" : [ + "doc/modelheader.md", + "doc/mainfile.md" + ], + "subtitles" : [ + "Model implementation", + "Main program" + ] + } +} diff --git a/examples/cahn_hilliard/README.md b/examples/cahn_hilliard/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a192a3346b71a7b07ab85a37530f9ed5383889b1 --- /dev/null +++ b/examples/cahn_hilliard/README.md @@ -0,0 +1,70 @@ +<!-- Important: This file has been automatically generated by generate_example_docs.py. Do not edit this file directly! --> + +# Cahn-Hilliard model + +A random initial distribution of two phases separating according to the Cahn-Hilliard model. + +__The main points illustrated in this example are__ +* A base setup for solving a nonlinear partial differential equation + +__Table of contents__. This description is structured as follows: + +[[_TOC_]] + +__Result__. The result will look like below. +You see the concentration variable $c$ capturing the phase distribution after 1 second. + +<figure> + <center> + <img src="img/results_phase_distribution.png" alt="Concentration field modeling phase distribution" width="50%"/> + </center> +</figure> + +## Problem set-up + +A square two-dimensional domain is initialized with a concentration `c`, randomly perturbed by +noise following a uniform distribution between 0.41 and 0.43. +With time the concentration field evolves towards attaining mostly values near to 0 or 1 while +conserving the total concentration, modeling the separation of two immiscible fluids. + +## Model description + +The Cahn-Hilliard model uses a pair of second order nonlinear partial differential equations, +for a concentration field $c$ and a chemical potential $\mu$. The former is conserved and +evolves with a flux proportional to $\nabla \mu$, while the latter depends on the laplacian of +the concentration $\nabla^2 c$ and a nonlinear function of $c$, a derivative of a free energy +functional. + +```math +\frac{\partial c}{\partial t} = M \nabla^2 \mu \\ +\mu = E (4 c^3 - 6 c^2 +2 c) - \gamma \nabla^2 c +``` + +Here $M$ denotes the mobility of the concentration, while the energy scale $E$ and surface tension +$\gamma$ balance the two contributions of the free energy functional. + +## Implementation of a simple nonlinear PDE + +For this example the C++ code is contained in two files, `main.cc` and `model.hh`. +The `model.hh` header contains the `ModelTraits` and properties for the model TypeTag +`CahnHilliardModel`, as well as volumevariables `CahnHilliardModelVolumeVariables` +and the basic local residual `CahnHilliardModelLocalResidual`. +The residual's storage term is extended in the problem definition `CahnHilliardTestProblem` +in the file `main.cc`, which also contains more specific properties of the problem setup (for +type tag `CahnHilliardTest`) as well as the actual simulation loop usually found in a mainfile. + +More details are given in [main.cc](doc/mainfile.md) and [model.hh](doc/modelheader.md). + +Additionally the folder contains `params.input` containing runtime parameters and the build +system file `CMakeLists.txt` defining conditions for the accompanying test. + +## Part 1: Model implementation + +| [:arrow_right: Click to continue with part 1 of the documentation](doc/modelheader.md) | +|---:| + + +## Part 2: Main program + +| [:arrow_right: Click to continue with part 2 of the documentation](doc/mainfile.md) | +|---:| diff --git a/examples/cahn_hilliard/doc/_intro.md b/examples/cahn_hilliard/doc/_intro.md new file mode 100644 index 0000000000000000000000000000000000000000..0c63dab29316bf6ccd1454d672d7b860d40daa61 --- /dev/null +++ b/examples/cahn_hilliard/doc/_intro.md @@ -0,0 +1,57 @@ +# Cahn-Hilliard model + +A random initial distribution of two phases separating according to the Cahn-Hilliard model. + +__The main points illustrated in this example are__ +* A base setup for solving a nonlinear partial differential equation + +__Table of contents__. This description is structured as follows: + +[[_TOC_]] + +__Result__. The result will look like below. +You see the concentration variable $c$ capturing the phase distribution after 1 second. + +<figure> + <center> + <img src="img/results_phase_distribution.png" alt="Concentration field modeling phase distribution" width="50%"/> + </center> +</figure> + +## Problem set-up + +A square two-dimensional domain is initialized with a concentration `c`, randomly perturbed by +noise following a uniform distribution between 0.41 and 0.43. +With time the concentration field evolves towards attaining mostly values near to 0 or 1 while +conserving the total concentration, modeling the separation of two immiscible fluids. + +## Model description + +The Cahn-Hilliard model uses a pair of second order nonlinear partial differential equations, +for a concentration field $c$ and a chemical potential $\mu$. The former is conserved and +evolves with a flux proportional to $\nabla \mu$, while the latter depends on the laplacian of +the concentration $\nabla^2 c$ and a nonlinear function of $c$, a derivative of a free energy +functional. + +```math +\frac{\partial c}{\partial t} = M \nabla^2 \mu \\ +\mu = E (4 c^3 - 6 c^2 +2 c) - \gamma \nabla^2 c +``` + +Here $M$ denotes the mobility of the concentration, while the energy scale $E$ and surface tension +$\gamma$ balance the two contributions of the free energy functional. + +## Implementation of a simple nonlinear PDE + +For this example the C++ code is contained in two files, `main.cc` and `model.hh`. +The `model.hh` header contains the `ModelTraits` and properties for the model TypeTag +`CahnHilliardModel`, as well as volumevariables `CahnHilliardModelVolumeVariables` +and the basic local residual `CahnHilliardModelLocalResidual`. +The residual's storage term is extended in the problem definition `CahnHilliardTestProblem` +in the file `main.cc`, which also contains more specific properties of the problem setup (for +type tag `CahnHilliardTest`) as well as the actual simulation loop usually found in a mainfile. + +More details are given in [main.cc](doc/mainfile.md) and [model.hh](doc/modelheader.md). + +Additionally the folder contains `params.input` containing runtime parameters and the build +system file `CMakeLists.txt` defining conditions for the accompanying test. diff --git a/examples/cahn_hilliard/doc/mainfile.md b/examples/cahn_hilliard/doc/mainfile.md new file mode 100644 index 0000000000000000000000000000000000000000..ef55ebc6065cc5459f824474642d647914ed1435 --- /dev/null +++ b/examples/cahn_hilliard/doc/mainfile.md @@ -0,0 +1,492 @@ +<!-- Important: This file has been automatically generated by generate_example_docs.py. Do not edit this file directly! --> + + +| [:arrow_left: Back to the main documentation](../README.md) | [:arrow_left: Go back to part 1](modelheader.md) | +|---|---:| + + + +# Problem, test properties/traits and main program flow (`main.cc`) +In this example the file `main.cc` contains the problem class `CahnHilliardTestProblem`, +properties and traits specific to the test case as well as the main program flow in the form of +`main` function. + + +```cpp +#include <config.h> +``` + +## Problem +The __problem class__ defines boundary conditions and extends the storage term defined in the +model's localresidual by the derivative of the free energy. + + +<details open> +<summary><b>Click to hide/show the file documentation</b> (or inspect the [source code](../main.cc))</summary> + +### Include headers + + +```cpp +// use the property system and runtime parameters +#include <dumux/common/properties.hh> +#include <dumux/common/parameters.hh> +// common DuMux vector for discretized equations +#include <dumux/common/numeqvector.hh> +// types of boundary conditions to use +#include <dumux/common/boundarytypes.hh> +// generic problem for finite volume simulations +#include <dumux/common/fvproblem.hh> +``` + + +### The problem class `CahnHilliardTestProblem` +In this abstract problem we inherit from the generic `FVProblem`. + + +```cpp +namespace Dumux { + +template<class TypeTag> +class CahnHilliardTestProblem : public FVProblem<TypeTag> +{ +``` + +<details><summary> Click to show alias definitions and local variables</summary> + +```cpp + 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; +``` + +</details> + +```cpp +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"); + } +``` + + +### Problem source term + +Here we implement the derivative of the free energy, setting a source for the equation for +the chemical potential. The `computeSource` function in the local residual adds the terms +defined here. + + +```cpp + 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; + } +``` + + +### Boundary conditions + +For the boundary we choose boundary flux (or Neumann) conditions for all equations and on +every part of the boundary, specifying zero flux everywhere for both equations. + + +```cpp + BoundaryTypes boundaryTypesAtPos(const GlobalPosition& globalPos) const + { + BoundaryTypes values; + values.setAllNeumann(); + return values; + } + + NumEqVector neumannAtPos(const GlobalPosition& globalPos) const + { return { 0.0, 0.0 }; } +``` + + +<details><summary> Click to show coefficients and private variables</summary> +The problem class offers access to the mobility and surface tension coefficients as read from +the parameter file by default `params.input`. + + +```cpp + Scalar mobility() const + { return mobility_; } + + Scalar surfaceTension() const + { return surfaceTension_; } + +private: + Scalar mobility_; + Scalar surfaceTension_; + Scalar energyScale_; +}; + +} // end namespace Dumux +``` + +</details> + +</details> + +## Test case properties/traits +Within the `Dumux::Properties` namespace we specialize properties and traits to the considered +test case by using the test's TypeTag. + + +<details open> +<summary><b>Click to hide/show the file documentation</b> (or inspect the [source code](../main.cc))</summary> + + +### Include headers + + +```cpp +// Include the grid to be used +#include <dune/grid/yaspgrid.hh> +// The header for the box discretization scheme +#include <dumux/discretization/box.hh> +// The model header including the model traits and properties +#include "model.hh" +``` + + +### TypeTag `CahnHilliardTest` + +We define a type tag for the test case, allowing us to further specify properties and traits. To +use those set for the Cahn-Hilliard model we inherit from its type tag. + + +```cpp +namespace Dumux::Properties { + +// Inheriting properties of the Cahn-Hilliard model and the box finite volume discretization +namespace TTag { +struct CahnHilliardTest { using InheritsFrom = std::tuple<CahnHilliardModel, BoxModel>; }; +} // end namespace TTag +``` + + +### Test properties + +We specify a grid to be used in the test, select our problem class and enable caching. + + +```cpp +// Set the grid type +template<class TypeTag> +struct Grid<TypeTag, TTag::CahnHilliardTest> +{ using type = Dune::YaspGrid<2>; }; + +// Select the problem class defined above +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 +``` + + +</details> + +## The main program flow +The main program flow in the `main` function is often the only content of `main.cc`. It sets up +the simulation framework, initializes runtime parameters, creates the grid and storage vectors +for the variables, primary and secondary. It specifies and constructs and assembler, which +assembles the discretized residual and system matrix (Jacobian of the model residual), as well as +linear and nonlinear solvers that solve the resulting linear system and handle the convergence of +nonlinear iterations. The time loop controls the time stepping, with adaptive time step size in +coordination with the nonlinear solver. + + +<details open> +<summary><b>Click to hide/show the file documentation</b> (or inspect the [source code](../main.cc))</summary> + + +### Include headers + + +```cpp +// standard header to generate random initial data +#include <random> +// common DuMux header for parallelization +#include <dumux/common/initialize.hh> +// headers to use the property system and runtime parameters +#include <dumux/common/properties.hh> +#include <dumux/common/parameters.hh> +// module for VTK output, to write out fields of interest +#include <dumux/io/vtkoutputmodule.hh> +// gridmanager for the grid used in the test +#include <dumux/io/grid/gridmanager_yasp.hh> +// headers for linear and non-linear solvers as well as the assembler +#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> +``` + + +### Creating the initial solution + +We define a helper struct and function to handle communication for parallel runs. +For our initial conditions we create a random field of values around a mean of 0.42. +The random values are created with an offset based on the processor rank for communication +purposes, which is removed afterwards. For more information see the description of the +diffusion example +[examples/diffusion/doc/main.md](https://git.iws.uni-stuttgart.de/dumux-repositories/dumux/-/blob/master/examples/diffusion/doc/main.md) + + +```cpp +struct MinScatter +{ + template<class A, class B> + static void apply(A& a, const B& b) + { a[0] = std::min(a[0], b[0]); } +}; + +template<class SolutionVector, class GridGeometry> +SolutionVector createInitialSolution(const GridGeometry& gg) +{ + SolutionVector sol(gg.numDofs()); + + // Generate random number and add processor offset + // For sequential runs `rank` always returns `0`. + 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)) + gg.gridView().comm().rank(); + sol[n][1] = 0.0; + } + + // Take the value of the processor with the minimum rank and subtract the rank offset + if (gg.gridView().comm().size() > 1) + { + Dumux::VectorCommDataHandle< + typename GridGeometry::VertexMapper, + SolutionVector, + GridGeometry::GridView::dimension, + MinScatter + > minHandle(gg.vertexMapper(), sol); + gg.gridView().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]); + } + return sol; +} +``` + + +### The main function + +The main function takes the command line arguments, optionally specifying an input file of +parameters and/or individual key-value pairs of runtime parameters. + + +```cpp +int main(int argc, char** argv) +{ + using namespace Dumux; + + // define the type tag for this problem + using TypeTag = Properties::TTag::CahnHilliardTest; +``` + + +We initialize parallelization backends as well as runtime parameters + + +```cpp + // maybe initialize MPI and/or multithreading backend + Dumux::initialize(argc, argv); + + // initialize parameter tree + Parameters::init(argc, argv); +``` + + +### Grid setup + +Set up the grid as well as a grid geometry to access the (sub-)control-volumes and their +faces + + +```cpp + // 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); +``` + + +### Problem setup + +We instantiate also the problem class according to the test properties + + +```cpp + // the problem (initial and boundary conditions) + using Problem = GetPropType<TypeTag, Properties::Problem>; + auto problem = std::make_shared<Problem>(gridGeometry); +``` + + +### Applying initial conditions + +After writing the initial data to the storage for previous and current time-step, we +initialize the grid variables, also computing secondary variables. + + +```cpp + // the solution vector + using SolutionVector = GetPropType<TypeTag, Properties::SolutionVector>; + auto sol = createInitialSolution<SolutionVector>(*gridGeometry); + // 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 VTK output + + +```cpp + // 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); +``` + + +### Set up time loop + + +```cpp + // 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); +``` + + +### Assembler, linear and nonlinear solver + + +```cpp + // 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 + + +```cpp + // 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()); +``` + + +### Finalize + + +```cpp + timeLoop->finalize(leafGridView.comm()); + + return 0; +} +``` + + +</details> + + +| [:arrow_left: Back to the main documentation](../README.md) | [:arrow_left: Go back to part 1](modelheader.md) | +|---|---:| + diff --git a/examples/cahn_hilliard/doc/mainfile_intro.md b/examples/cahn_hilliard/doc/mainfile_intro.md new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/examples/cahn_hilliard/doc/modelheader.md b/examples/cahn_hilliard/doc/modelheader.md new file mode 100644 index 0000000000000000000000000000000000000000..125ea472c8b868a4e264198cddf2d1234aac2680 --- /dev/null +++ b/examples/cahn_hilliard/doc/modelheader.md @@ -0,0 +1,394 @@ +<!-- Important: This file has been automatically generated by generate_example_docs.py. Do not edit this file directly! --> + + +| [:arrow_left: Back to the main documentation](../README.md) | [:arrow_right: Continue with part 2](mainfile.md) | +|---|---:| + + + +# Volume variables, local residual and model traits (`model.hh`) +In this example the file `model.hh` contains the classes `CahnHilliardModelVolumeVariables` and +`CahnHilliardModelLocalResidual` as well as general model traits and properties. + +## VolumeVariables + +The volume variables store the local element volume variables, both primary and secondary. + + +<details open> +<summary><b>Click to hide/show the file documentation</b> (or inspect the [source code](../model.hh))</summary> + + +### The `CahnHilliardModelVolumeVariables` class + + +```cpp +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; +``` + + +### Update variables + +The `update` function stores the local primary variables of the current solution and +potentially recomputes secondary variables. + + +```cpp + /*! + * \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()]; + } +``` + + +### Access functions + +Named and generic functions to access different primary variables + + +```cpp + 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_; } +``` + + +### Extrusion factor + +The volumevariables are also expected to return the extrusion factor + + +```cpp + // for compatibility with more general models + Scalar extrusionFactor() const + { return 1.0; } +``` + + +### Storage of local variables + + +```cpp +private: + PrimaryVariables priVars_; +}; + +} // end namespace Dumux +``` + + +</details> + +## LocalResidual + +The local residual defines the discretized and integrated partial differential equation through +terms for storage, fluxes and sources, with the residual given as +d/dt storage + div(fluxes) - sources = 0 +The individual terms can be further adjusted or replaced in the more specific problem. + + +<details open> +<summary><b>Click to hide/show the file documentation</b> (or inspect the [source code](../model.hh))</summary> + + +### Include headers + + +```cpp +#include <dumux/common/math.hh> +// use the property system +#include <dumux/common/properties.hh> +// common DuMux vector for discretized equations +#include <dumux/common/numeqvector.hh> +``` + + +### The local residual class `CahnHilliardModelLocalResidual` inherits from a base class set in +the model properties, depending on the discretization scheme. + + +```cpp +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>; +``` + + +<details><summary> Click to show alias definitions</summary> + +```cpp + 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; +``` + +</details> + +### The storage term +The function `computeStorage` receives the volumevariables at the previous or current time +step and computes the value of the storage terms. +In this case the mass balance equation is a conservation equation of the concentration and +the equation for the chemical potential does not have a storage term. + + +```cpp + /*! + * \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; + } +``` + + +### The flux terms +`computeFlux` computes the fluxes over a subcontrolvolumeface, including the integration over +the area of the face. + + +```cpp + /*! + * \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; + } +``` + + +### Source terms + +`computeSource` defines the sources terms at a sub control volume. +We implement a model-specific source term for the chemical potential equation before +deferring further implementation to the problem where we add the derivative of the free +energy. The default implementation of this function also defers the calculation to the +problem. + + +```cpp + /*! + * \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 +``` + + +</details> + +## Model properties/traits + +We set some general model traits and properties, using a TypeTag `CahnHilliardModel`. +For this type tag we define a `ModelTraits` struct and set a number of properties by specifying +further structs within the `Dumux::Properties` namespace. + + +<details open> +<summary><b>Click to hide/show the file documentation</b> (or inspect the [source code](../model.hh))</summary> + + +### Include the header for the property system and common properties and switch to the +`Properties` namespace. + + +```cpp +#include <dumux/common/properties.hh> + +namespace Dumux::Properties { +``` + + +### Define the type tag + + +```cpp +namespace TTag { +struct CahnHilliardModel {}; +} // end namespace TTag +``` + + +### Basic model properties + +Define some general properties to be used for this modedl such as a datatype for scalars and the +default vector for the primary variables. Here we can also use the model traits we specify below. + + +```cpp +//! 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() + >; +}; +``` + + +### Model traits + +We specify general traits of the implemented model, defining indices (often in `indices.hh`) +and the number of equations in the model. + + +```cpp +//! 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; +}; +``` + + +### Further model properties + +Define further properties of the model, selecting the local residual and volumevariables defined +above. + + +```cpp +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 +``` + + +</details> + + +| [:arrow_left: Back to the main documentation](../README.md) | [:arrow_right: Continue with part 2](mainfile.md) | +|---|---:| + diff --git a/examples/cahn_hilliard/doc/modelheader_intro.md b/examples/cahn_hilliard/doc/modelheader_intro.md new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/examples/cahn_hilliard/img/animation.gif b/examples/cahn_hilliard/img/animation.gif new file mode 100644 index 0000000000000000000000000000000000000000..6e266f5f8f146c7780dea6f6c1f4ddb950b49d46 Binary files /dev/null and b/examples/cahn_hilliard/img/animation.gif differ diff --git a/examples/cahn_hilliard/main.cc b/examples/cahn_hilliard/main.cc index fa1bc08904d39562adfd5e244988c5ce95a3916c..4e18dcff990e0bf4b50dbc5fa9cdc45988868260 100644 --- a/examples/cahn_hilliard/main.cc +++ b/examples/cahn_hilliard/main.cc @@ -17,26 +17,43 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * *****************************************************************************/ +// # Problem, test properties/traits and main program flow (`main.cc`) +// In this example the file `main.cc` contains the problem class `CahnHilliardTestProblem`, +// properties and traits specific to the test case as well as the main program flow in the form of +// `main` function. +// #include <config.h> - -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -// Problem //////// (often in a separate file problem.hh) //////////////////// -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - +// ## Problem +// The __problem class__ defines boundary conditions and extends the storage term defined in the +// model's localresidual by the derivative of the free energy. +// +// [[content]] +// ### Include headers +// +// [[codeblock]] +// use the property system and runtime parameters #include <dumux/common/properties.hh> #include <dumux/common/parameters.hh> +// common DuMux vector for discretized equations #include <dumux/common/numeqvector.hh> - +// types of boundary conditions to use #include <dumux/common/boundarytypes.hh> +// generic problem for finite volume simulations #include <dumux/common/fvproblem.hh> - +// [[/codeblock]] +// +// ### The problem class `CahnHilliardTestProblem` +// In this abstract problem we inherit from the generic `FVProblem`. +// +// [[codeblock]] namespace Dumux { template<class TypeTag> class CahnHilliardTestProblem : public FVProblem<TypeTag> { +// [[/codeblock]] +// [[details]] alias definitions and local variables +// [[codeblock]] using ParentType = FVProblem<TypeTag>; using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>; using FVElementGeometry = typename GridGeometry::LocalView; @@ -50,6 +67,9 @@ class CahnHilliardTestProblem : public FVProblem<TypeTag> using NumEqVector = Dumux::NumEqVector<PrimaryVariables>; using BoundaryTypes = Dumux::BoundaryTypes<GetPropType<TypeTag, Properties::ModelTraits>::numEq()>; using Indices = typename GetPropType<TypeTag, Properties::ModelTraits>::Indices; +// [[/codeblock]] +// [[/details]] +// [[codeblock]] public: CahnHilliardTestProblem(std::shared_ptr<const GridGeometry> gridGeometry) : ParentType(gridGeometry) @@ -58,8 +78,15 @@ public: surfaceTension_ = getParam<Scalar>("Problem.SurfaceTension"); energyScale_ = getParam<Scalar>("Problem.EnergyScale"); } - - // here we implement the derivative of the free energy + // [[/codeblock]] + // + // ### Problem source term + // + // Here we implement the derivative of the free energy, setting a source for the equation for + // the chemical potential. The `computeSource` function in the local residual adds the terms + // defined here. + // + // [[codeblock]] template<class ElementVolumeVariables> NumEqVector source(const Element &element, const FVElementGeometry& fvGeometry, @@ -71,7 +98,14 @@ public: values[Indices::chemicalPotentialEqIdx] = -energyScale_*2.0*c*(2.0*c*c - 3*c + 1); return values; } - + // [[/codeblock]] + // + // ### Boundary conditions + // + // For the boundary we choose boundary flux (or Neumann) conditions for all equations and on + // every part of the boundary, specifying zero flux everywhere for both equations. + // + // [[codeblock]] BoundaryTypes boundaryTypesAtPos(const GlobalPosition& globalPos) const { BoundaryTypes values; @@ -81,7 +115,13 @@ public: NumEqVector neumannAtPos(const GlobalPosition& globalPos) const { return { 0.0, 0.0 }; } - + // [[/codeblock]] + // + // [[details]] coefficients and private variables + // The problem class offers access to the mobility and surface tension coefficients as read from + // the parameter file by default `params.input`. + // + // [[codeblock]] Scalar mobility() const { return mobility_; } @@ -95,29 +135,52 @@ private: }; } // end namespace Dumux - -////////////////////////////////////////////////////////////////////////////// - -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -// Test case properties/traits /// (often in a separate file properties.hh) // -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - +// [[/codeblock]] +// [[/details]] +// [[/content]] +// +// ## Test case properties/traits +// Within the `Dumux::Properties` namespace we specialize properties and traits to the considered +// test case by using the test's TypeTag. +// +// [[content]] +// +// ### Include headers +// +// [[codeblock]] +// Include the grid to be used #include <dune/grid/yaspgrid.hh> +// The header for the box discretization scheme #include <dumux/discretization/box.hh> +// The model header including the model traits and properties #include "model.hh" - +// [[/codeblock]] +// +// ### TypeTag `CahnHilliardTest` +// +// We define a type tag for the test case, allowing us to further specify properties and traits. To +// use those set for the Cahn-Hilliard model we inherit from its type tag. +// +// [[codeblock]] namespace Dumux::Properties { +// Inheriting properties of the Cahn-Hilliard model and the box finite volume discretization namespace TTag { struct CahnHilliardTest { using InheritsFrom = std::tuple<CahnHilliardModel, BoxModel>; }; } // end namespace TTag - +// [[/codeblock]] +// +// ### Test properties +// +// We specify a grid to be used in the test, select our problem class and enable caching. +// +// [[codeblock]] +// Set the grid type template<class TypeTag> struct Grid<TypeTag, TTag::CahnHilliardTest> { using type = Dune::YaspGrid<2>; }; +// Select the problem class defined above template<class TypeTag> struct Problem<TypeTag, TTag::CahnHilliardTest> { using type = CahnHilliardTestProblem<TypeTag>; }; @@ -136,24 +199,52 @@ struct EnableGridGeometryCache<TypeTag, TTag::CahnHilliardTest> { static constexpr bool value = true; }; } // end namespace Dumux::Properties - -////////////////////////////////////////////////////////////////////////////// - +// [[/codeblock]] +// [[/content]] +// +// ## The main program flow +// The main program flow in the `main` function is often the only content of `main.cc`. It sets up +// the simulation framework, initializes runtime parameters, creates the grid and storage vectors +// for the variables, primary and secondary. It specifies and constructs and assembler, which +// assembles the discretized residual and system matrix (Jacobian of the model residual), as well as +// linear and nonlinear solvers that solve the resulting linear system and handle the convergence of +// nonlinear iterations. The time loop controls the time stepping, with adaptive time step size in +// coordination with the nonlinear solver. +// +// [[content]] +// +// ### Include headers +// +// [[codeblock]] +// standard header to generate random initial data #include <random> - +// common DuMux header for parallelization #include <dumux/common/initialize.hh> +// headers to use the property system and runtime parameters #include <dumux/common/properties.hh> #include <dumux/common/parameters.hh> - +// module for VTK output, to write out fields of interest #include <dumux/io/vtkoutputmodule.hh> +// gridmanager for the grid used in the test #include <dumux/io/grid/gridmanager_yasp.hh> - +// headers for linear and non-linear solvers as well as the assembler #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> - +// [[/codeblock]] +// +// ### Creating the initial solution +// +// We define a helper struct and function to handle communication for parallel runs. +// For our initial conditions we create a random field of values around a mean of 0.42. +// The random values are created with an offset based on the processor rank for communication +// purposes, which is removed afterwards. For more information see the description of the +// diffusion example +// [examples/diffusion/doc/main.md](https://git.iws.uni-stuttgart.de/dumux-repositories/dumux/-/blob/master/examples/diffusion/doc/main.md) +// +// [[codeblock]] struct MinScatter { template<class A, class B> @@ -161,19 +252,70 @@ struct MinScatter { a[0] = std::min(a[0], b[0]); } }; +template<class SolutionVector, class GridGeometry> +SolutionVector createInitialSolution(const GridGeometry& gg) +{ + SolutionVector sol(gg.numDofs()); + + // Generate random number and add processor offset + // For sequential runs `rank` always returns `0`. + 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)) + gg.gridView().comm().rank(); + sol[n][1] = 0.0; + } + + // Take the value of the processor with the minimum rank and subtract the rank offset + if (gg.gridView().comm().size() > 1) + { + Dumux::VectorCommDataHandle< + typename GridGeometry::VertexMapper, + SolutionVector, + GridGeometry::GridView::dimension, + MinScatter + > minHandle(gg.vertexMapper(), sol); + gg.gridView().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]); + } + return sol; +} +// [[/codeblock]] +// +// ### The main function +// +// The main function takes the command line arguments, optionally specifying an input file of +// parameters and/or individual key-value pairs of runtime parameters. +// +// [[codeblock]] int main(int argc, char** argv) { using namespace Dumux; // define the type tag for this problem using TypeTag = Properties::TTag::CahnHilliardTest; - + // [[/codeblock]] + // + // We initialize parallelization backends as well as runtime parameters + // + // [[codeblock]] // maybe initialize MPI and/or multithreading backend Dumux::initialize(argc, argv); // initialize parameter tree Parameters::init(argc, argv); - + // [[/codeblock]] + // + // ### Grid setup + // + // Set up the grid as well as a grid geometry to access the (sub-)control-volumes and their + // faces + // + // [[codeblock]] // initialize the grid GridManager<GetPropType<TypeTag, Properties::Grid>> gridManager; gridManager.init(); @@ -184,37 +326,27 @@ int main(int argc, char** argv) // create the finite volume grid geometry using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>; auto gridGeometry = std::make_shared<GridGeometry>(leafGridView); - + // [[/codeblock]] + // + // ### Problem setup + // + // We instantiate also the problem class according to the test properties + // + // [[codeblock]] // the problem (initial and boundary conditions) using Problem = GetPropType<TypeTag, Properties::Problem>; auto problem = std::make_shared<Problem>(gridGeometry); - + // [[/codeblock]] + // + // ### Applying initial conditions + // + // After writing the initial data to the storage for previous and current time-step, we + // initialize the grid variables, also computing secondary variables. + // + // [[codeblock]] // 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]); - + auto sol = createInitialSolution<SolutionVector>(*gridGeometry); // copy the vector to store state of previous time step auto oldSol = sol; @@ -222,13 +354,21 @@ int main(int argc, char** argv) using GridVariables = GetPropType<TypeTag, Properties::GridVariables>; auto gridVariables = std::make_shared<GridVariables>(problem, gridGeometry); gridVariables->init(sol); - + // [[/codeblock]] + // + // ### Initialize VTK output + // + // [[codeblock]] // 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); - + // [[/codeblock]] + // + // ### Set up time loop + // + // [[codeblock]] // get some time loop parameters using Scalar = GetPropType<TypeTag, Properties::Scalar>; const auto tEnd = getParam<Scalar>("TimeLoop.TEnd"); @@ -238,7 +378,11 @@ int main(int argc, char** argv) // instantiate time loop auto timeLoop = std::make_shared<CheckPointTimeLoop<Scalar>>(0.0, dt, tEnd); timeLoop->setMaxTimeStepSize(maxDt); - + // [[/codeblock]] + // + // ### Assembler, linear and nonlinear solver + // + // [[codeblock]] // 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); @@ -253,7 +397,11 @@ int main(int argc, char** argv) // the solver using Solver = Dumux::NewtonSolver<Assembler, LinearSolver>; Solver solver(assembler, linearSolver); - + // [[/codeblock]] + // + // ### Time loop + // + // [[codeblock]] // time loop timeLoop->start(); do { @@ -277,8 +425,14 @@ int main(int argc, char** argv) timeLoop->setTimeStepSize(solver.suggestTimeStepSize(timeLoop->timeStepSize())); } while (!timeLoop->finished()); - + // [[/codeblock]] + // + // ### Finalize + // + // [[codeblock]] timeLoop->finalize(leafGridView.comm()); return 0; } +// [[/codeblock]] +// [[/content]] diff --git a/examples/cahn_hilliard/model.hh b/examples/cahn_hilliard/model.hh index 51b5eb62be61a8a886a9696ee5378ddad6ef2df8..9f69f9dadabc0a18945fcdfcddf432aaa6c90272 100644 --- a/examples/cahn_hilliard/model.hh +++ b/examples/cahn_hilliard/model.hh @@ -20,12 +20,19 @@ #ifndef DUMUX_EXAMPLES_CAHN_HILLIARD_MODEL_HH #define DUMUX_EXAMPLES_CAHN_HILLIARD_MODEL_HH -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -// VolumeVariables //////// (often in a separate file volumevariables.hh) //// -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - +// # Volume variables, local residual and model traits (`model.hh`) +// In this example the file `model.hh` contains the classes `CahnHilliardModelVolumeVariables` and +// `CahnHilliardModelLocalResidual` as well as general model traits and properties. +// +// ## VolumeVariables +// +// The volume variables store the local element volume variables, both primary and secondary. +// +// [[content]] +// +// ### The `CahnHilliardModelVolumeVariables` class +// +// [[codeblock]] namespace Dumux { template <class Traits> @@ -38,7 +45,14 @@ public: using PrimaryVariables = typename Traits::PrimaryVariables; //! export the indices type using Indices = typename Traits::ModelTraits::Indices; - + // [[/codeblock]] + // + // ### Update variables + // + // The `update` function stores the local primary variables of the current solution and + // potentially recomputes secondary variables. + // + // [[codeblock]] /*! * \brief Update all quantities for a given control volume */ @@ -50,7 +64,13 @@ public: { priVars_ = elemSol[scv.indexInElement()]; } - + // [[/codeblock]] + // + // ### Access functions + // + // Named and generic functions to access different primary variables + // + // [[codeblock]] Scalar concentration() const { return priVars_[Indices::concentrationIdx]; } @@ -62,29 +82,52 @@ public: const PrimaryVariables& priVars() const { return priVars_; } - + // [[/codeblock]] + // + // ### Extrusion factor + // + // The volumevariables are also expected to return the extrusion factor + // + // [[codeblock]] // for compatibility with more general models Scalar extrusionFactor() const { return 1.0; } - + // [[/codeblock]] + // + // ### Storage of local variables + // + // [[codeblock]] private: PrimaryVariables priVars_; }; } // end namespace Dumux - -////////////////////////////////////////////////////////////////////////////// - -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -// LocalResidual //////////// (often in a separate file localresidual.hh) //// -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - +// [[/codeblock]] +// [[/content]] +// +// ## LocalResidual +// +// The local residual defines the discretized and integrated partial differential equation through +// terms for storage, fluxes and sources, with the residual given as +// d/dt storage + div(fluxes) - sources = 0 +// The individual terms can be further adjusted or replaced in the more specific problem. +// +// [[content]] +// +// ### Include headers +// +// [[codeblock]] #include <dumux/common/math.hh> +// use the property system #include <dumux/common/properties.hh> +// common DuMux vector for discretized equations #include <dumux/common/numeqvector.hh> - +// [[/codeblock]] +// +// ### The local residual class `CahnHilliardModelLocalResidual` inherits from a base class set in +// the model properties, depending on the discretization scheme. +// +// [[codeblock]] namespace Dumux { template<class TypeTag> @@ -93,6 +136,10 @@ class CahnHilliardModelLocalResidual { // the base local residual is selected depending on the chosen discretization scheme using ParentType = GetPropType<TypeTag, Properties::BaseLocalResidual>; + // [[/codeblock]] + // + // [[details]] alias definitions + // [[codeblock]] using Scalar = GetPropType<TypeTag, Properties::Scalar>; using Problem = GetPropType<TypeTag, Properties::Problem>; @@ -113,7 +160,16 @@ class CahnHilliardModelLocalResidual static constexpr int dimWorld = GridView::dimensionworld; public: using ParentType::ParentType; - + // [[/codeblock]] + // [[/details]] + // + // ### The storage term + // The function `computeStorage` receives the volumevariables at the previous or current time + // step and computes the value of the storage terms. + // In this case the mass balance equation is a conservation equation of the concentration and + // the equation for the chemical potential does not have a storage term. + // + // [[codeblock]] /*! * \brief Evaluate the rate of change of all conserved quantities */ @@ -126,7 +182,13 @@ public: storage[Indices::chemicalPotentialEqIdx] = 0.0; return storage; } - + // [[/codeblock]] + // + // ### The flux terms + // `computeFlux` computes the fluxes over a subcontrolvolumeface, including the integration over + // the area of the face. + // + // [[codeblock]] /*! * \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 @@ -159,7 +221,17 @@ public: flux[Indices::chemicalPotentialEqIdx] = -1.0*vtmv(scvf.unitOuterNormal(), gamma, gradConcentration)*scvf.area(); return flux; } - + // [[/codeblock]] + // + // ### Source terms + // + // `computeSource` defines the sources terms at a sub control volume. + // We implement a model-specific source term for the chemical potential equation before + // deferring further implementation to the problem where we add the derivative of the free + // energy. The default implementation of this function also defers the calculation to the + // problem. + // + // [[codeblock]] /*! * \brief Calculate the source term of the equation */ @@ -181,22 +253,40 @@ public: }; } // end namespace Dumux -////////////////////////////////////////////////////////////////////////////// - -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -// Model properties/traits /////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - +// [[/codeblock]] +// [[/content]] +// +// ## Model properties/traits +// +// We set some general model traits and properties, using a TypeTag `CahnHilliardModel`. +// For this type tag we define a `ModelTraits` struct and set a number of properties by specifying +// further structs within the `Dumux::Properties` namespace. +// +// [[content]] +// +// ### Include the header for the property system and common properties and switch to the +// `Properties` namespace. +// +// [[codeblock]] #include <dumux/common/properties.hh> namespace Dumux::Properties { - +// [[/codeblock]] +// +// ### Define the type tag +// +// [[codeblock]] namespace TTag { struct CahnHilliardModel {}; } // end namespace TTag - +// [[/codeblock]] +// +// ### Basic model properties +// +// Define some general properties to be used for this modedl such as a datatype for scalars and the +// default vector for the primary variables. Here we can also use the model traits we specify below. +// +// [[codeblock]] //! Set the default type of scalar values to double template<class TypeTag> struct Scalar<TypeTag, TTag:: CahnHilliardModel > @@ -211,7 +301,14 @@ struct PrimaryVariables<TypeTag, TTag:: CahnHilliardModel > GetPropType<TypeTag, Properties::ModelTraits>::numEq() >; }; - +// [[/codeblock]] +// +// ### Model traits +// +// We specify general traits of the implemented model, defining indices (often in `indices.hh`) +// and the number of equations in the model. +// +// [[codeblock]] //! Set the model traits property template<class TypeTag> struct ModelTraits<TypeTag, TTag::CahnHilliardModel> @@ -232,7 +329,14 @@ struct ModelTraits<TypeTag, TTag::CahnHilliardModel> using type = Traits; }; - +// [[/codeblock]] +// +// ### Further model properties +// +// Define further properties of the model, selecting the local residual and volumevariables defined +// above. +// +// [[codeblock]] template<class TypeTag> struct LocalResidual<TypeTag, TTag::CahnHilliardModel> { using type = CahnHilliardModelLocalResidual<TypeTag>; }; @@ -251,5 +355,7 @@ struct VolumeVariables<TypeTag, TTag::CahnHilliardModel> }; } // end namespace Dumux::Properties +// [[/codeblock]] +// [[/content]] #endif