From 4ff1ea718d6d43d7b2669d2601e7de295c7c67a5 Mon Sep 17 00:00:00 2001 From: Ivan Buntic <st116086@stud.uni-stuttgart.de> Date: Wed, 26 Jul 2023 11:23:00 +0000 Subject: [PATCH] Add solution checker, checks if the solution is updated with the exercise --- .create_all_patches.sh | 28 + .create_single_patch.sh | 62 + .developerREADME.md | 13 + .gitlab-ci.yml | 7 + .patches/apply_patches.sh | 26 + .patches/exercise-basic/exercise-basic.patch | 1405 +++++++++++++++++ .../exercise-biomineralization.patch | 559 +++++++ .../exercise-coupling-ff-pm.patch | 1399 ++++++++++++++++ .../exercise-dunemodule.patch | 112 ++ .../exercise-fluidsystem.patch | 617 ++++++++ .../exercise-fractures.patch | 748 +++++++++ .patches/exercise-grids/exercise-grids.patch | 351 ++++ .../exercise-mainfile/exercise-mainfile.patch | 982 ++++++++++++ .patches/exercise-model/exercise-model.patch | 574 +++++++ .../exercise-properties.patch | 180 +++ .../exercise-runtimeparams.patch | 284 ++++ 16 files changed, 7347 insertions(+) create mode 100755 .create_all_patches.sh create mode 100755 .create_single_patch.sh create mode 100644 .developerREADME.md create mode 100755 .patches/apply_patches.sh create mode 100644 .patches/exercise-basic/exercise-basic.patch create mode 100644 .patches/exercise-biomineralization/exercise-biomineralization.patch create mode 100644 .patches/exercise-coupling-ff-pm/exercise-coupling-ff-pm.patch create mode 100644 .patches/exercise-dunemodule/exercise-dunemodule.patch create mode 100644 .patches/exercise-fluidsystem/exercise-fluidsystem.patch create mode 100644 .patches/exercise-fractures/exercise-fractures.patch create mode 100644 .patches/exercise-grids/exercise-grids.patch create mode 100644 .patches/exercise-mainfile/exercise-mainfile.patch create mode 100644 .patches/exercise-model/exercise-model.patch create mode 100644 .patches/exercise-properties/exercise-properties.patch create mode 100644 .patches/exercise-runtimeparams/exercise-runtimeparams.patch diff --git a/.create_all_patches.sh b/.create_all_patches.sh new file mode 100755 index 00000000..6494d879 --- /dev/null +++ b/.create_all_patches.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +#This is to be executed at the root folder of dumux-course, no input arguments required. The script creates patches for all exercises stored in the exercise/ folder and with a name starting with exercise-* + +exerciseFolder=exercises/ + +# Iterate over all subdirectories +for exercise in $(find $exerciseFolder -maxdepth 1 -type d -name "exercise-*") +do + #crop path to get exercise name + exerciseName=${exercise%/} # this removes the trailing slash of the path - % removes smallest suffix matching the given pattern + exerciseName=${exercise#exercises/} # this remove the leading exercises/ of the path - # removes the smallest prefix matching the given pattern + + #check if the exercise-folder exists. Should always exist, as we iterate over them + if [ ! -d exercises/$exerciseName ] + then + echo + echo "exercises/$exerciseName does NOT exist. Terminating." + exit 1 + fi + + echo "Generating diff for $exerciseName. Storing the patch file into patches/$exerciseName/$exerciseName.patch" + diff -ruN $exercise exercises/solution/$exerciseName > .patches/$exerciseName/$exerciseName.patch + +done + + + diff --git a/.create_single_patch.sh b/.create_single_patch.sh new file mode 100755 index 00000000..c2cd9bbb --- /dev/null +++ b/.create_single_patch.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +#This is to be executed at the root folder of dumux-course. Via the necessary option -x you name the exercise folder you would like to create a patch on. E.g.: +#./.create_single_patch.sh -x exercise-basic + +#read in the parameter -x from the terminal +while getopts "x:" opt +do + case $opt in + x) + exerciseName=$OPTARG + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + exit 1 + ;; + esac +done + +#if -x option is empty, throw an error +if [ -z "$exerciseName" ] +then + echo "ERROR: -x option must be set" + exit 1 +fi + +#flag for remembering if a patch should be created using git +shouldRunDiff=true + +#check if the exercise-folder exists. Should always exist, as we iterate over them +if [ ! -d exercises/$exerciseName ] +then + echo "exercises/$exerciseName does NOT exist. Terminating." + exit 1 +fi + +#check if the respective solution to the exercise exists. If not, check if it is required. In any case where the answer is no, set the flag to false to only generate an empty patch +if [ ! -d exercises/solution/$exerciseName ] +then + echo "ERROR: exercises/$exerciseName exists but exercises/solution/$exerciseName does NOT exist." + shouldRunDiff=false + + # Prompt the user for input - is this solution required? + while true; do + read -p "Does $exerciseName require a solution (y/n)?" yn + case $yn in + [Yy]* ) echo "Please create a solution to the exercise first."; exit;; + [Nn]* ) echo "Continuing..."; break;; + * ) echo "Please answer y or n.";; + esac + done +fi + +#depending on the state of shouldRunDiff - create a patch using git diff or an empty patch +if [ $shouldRunDiff = true ] +then + echo "Generating diff for $exerciseName. Storing the patch file into .patches/$exerciseName/$exerciseName.patch" + diff -ruN exercises/$exerciseName exercises/solution/$exerciseName > .patches/$exerciseName/$exerciseName.patch +else + echo "Skipping diff generation for $exerciseName due to previous conflicts. Creating an empty patch..." + touch .patches/$exerciseName/$exerciseName.patch +fi diff --git a/.developerREADME.md b/.developerREADME.md new file mode 100644 index 00000000..e1e38b18 --- /dev/null +++ b/.developerREADME.md @@ -0,0 +1,13 @@ +# Notes for developers + +This README provides instructions on how to handle changing exercises and their respective solutions. + +## Instructions + +The Gitlab CI now also includes a check to ensure that the stored patches, located in .patches/, match the current exercises and their respective solutions. Each exercise has one associated patch that maps it to its respective solution. If you change an exercise without updating the patch, the CI will complain that the exercise and solution are out of sync. Keep in mind, that this is only a reminder to update the respective solution once you changed an exercise. Of course, you do not have to adapt the solution if it is not necessary. But in any case, whether changing the exercise, the solution or both, you always need to update the respective patch before pushing the remote repository. + +To this end, there are two possible ways of generating the patch: +1. In the root folder of dumux-course, there is the hidden script `.create_all_patches.sh` (can show it in terminal via `ls -a`). You can simply run `./.create_all_patches.sh` which will create patches for all exercises. However, this might be prone to errors, if you changed other exercises too by accident. +2. In the root folder of dumux-course, there is another hidden script `.create_single_patch.sh` (can show it in terminal via `ls -a`). For this script, you need to provide a specific exercise name for which you want to generate the patch. As an example, for generating the patch for the exercise exercise-basic, you would need to run: `./.create_single_patch.sh -x exercise-basic` in the root folder of dumux-course. + +TLDR: Before pushing to the remote repository, always generate a new patch, when changing an exercise and/or a solution by using one of the scripts. diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ef81e7db..5ef05b15 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,7 @@ stages: - trigger pipelines - deploy + - solution checker variables: IMAGE_REGISTRY_URL: $CI_REGISTRY/dumux-repositories/dumux-docker-ci @@ -60,3 +61,9 @@ pages: - public only: - master + +apply_patches: + stage: solution checker + image: $IMAGE_REGISTRY_URL/full:dune-$DUMUX_CI_DUNE_LATEST_RELEASE-gcc-ubuntu-20.04 + script: + - sh .patches/apply_patches.sh diff --git a/.patches/apply_patches.sh b/.patches/apply_patches.sh new file mode 100755 index 00000000..8b4de3d6 --- /dev/null +++ b/.patches/apply_patches.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +exerciseFolder=exercises/ + +# Iterate over all subdirectories +for exercise in $(find $exerciseFolder -maxdepth 1 -type d -name "exercise-*") +do + #crop path to get exercise name + exerciseName=${exercise%/} # this removes the trailing slash of the path - % removes smallest suffix matching the given pattern + exerciseName=${exercise#exercises/} # this remove the leading exercises/ of the path - # removes the smallest prefix matching the given pattern + + #for all exercises, apply the respective patches within the respective exercise folder + patch -s -p2 -d $exercise < .patches/$exerciseName/$exerciseName.patch + + #for all exercises, apply the compare the created solution via patching and the already existing solution in the solution folder + diff -ruN $exercise exercises/solution/$exerciseName > $exercise/$exerciseName.patch + + #if the patch file is empty, everything went ok and the stored patch matches the existing exercise and solution. If it was forgotten to update the patch, the created patch file for comparing the solutions will not be empty. + if [ ! -s $exercise/$exerciseName.patch ] + then + echo "The solution for $exerciseName seems to be up to date." + else + echo "You forgot to update the exercise or the solution for $exerciseName!" + exit + fi +done diff --git a/.patches/exercise-basic/exercise-basic.patch b/.patches/exercise-basic/exercise-basic.patch new file mode 100644 index 00000000..93e1b993 --- /dev/null +++ b/.patches/exercise-basic/exercise-basic.patch @@ -0,0 +1,1405 @@ +diff -ruN exercises/exercise-basic/2p2cmain.cc exercises/solution/exercise-basic/2p2cmain.cc +--- exercises/exercise-basic/2p2cmain.cc 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-basic/2p2cmain.cc 1970-01-01 01:00:00.000000000 +0100 +@@ -1,154 +0,0 @@ +-// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +-// vi: set et ts=4 sw=4 sts=4: +-/***************************************************************************** +- * See the file COPYING for full copying permissions. * +- * * +- * This program is free software: you can redistribute it and/or modify * +- * it under the terms of the GNU General Public License as published by * +- * the Free Software Foundation, either version 3 of the License, or * +- * (at your option) any later version. * +- * * +- * This program is distributed in the hope that it will be useful, * +- * but WITHOUT ANY WARRANTY; without even the implied warranty of * +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +- * GNU General Public License for more details. * +- * * +- * You should have received a copy of the GNU General Public License * +- * along with this program. If not, see <http://www.gnu.org/licenses/>. * +- *****************************************************************************/ +-/*! +- * \file +- * \brief The main file for the 2p2c porousmediumflow problem in exercise-basic +- */ +-#include <config.h> +- +-#include <iostream> +- +-#include <dumux/common/initialize.hh> +-#include <dumux/common/properties.hh> +-#include <dumux/common/parameters.hh> +- +-#include <dumux/linear/istlsolvers.hh> +-#include <dumux/linear/linearsolvertraits.hh> +-#include <dumux/linear/linearalgebratraits.hh> +-#include <dumux/nonlinear/newtonsolver.hh> +- +-#include <dumux/assembly/fvassembler.hh> +-#include <dumux/assembly/diffmethod.hh> +- +-#include <dumux/io/vtkoutputmodule.hh> +-#include <dumux/io/grid/gridmanager_yasp.hh> +- +-// The properties file, where the compile time options are defined +-#include "properties2p2c.hh" +- +-//////////////////////// +-// the main function +-//////////////////////// +-int main(int argc, char** argv) +-{ +- using namespace Dumux; +- +- // define the type tag for this problem +- using TypeTag = Properties::TTag::Injection2p2cCC; +- +- // initialize MPI+X, finalize is done automatically on exit +- Dumux::initialize(argc, argv); +- +- // parse command line arguments and input file +- Parameters::init(argc, argv); +- +- // try to create a grid (from the given grid file or the input file) +- GridManager<GetPropType<TypeTag, Properties::Grid>> gridManager; +- gridManager.init(); +- +- //////////////////////////////////////////////////////////// +- // run instationary non-linear problem on this grid +- //////////////////////////////////////////////////////////// +- +- // 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 x; +- problem->applyInitialSolution(x); +- auto xOld = x; +- +- // the grid variables +- using GridVariables = GetPropType<TypeTag, Properties::GridVariables>; +- auto gridVariables = std::make_shared<GridVariables>(problem, gridGeometry); +- gridVariables->init(x); +- +- // initialize the vtk output module +- using IOFields = GetPropType<TypeTag, Properties::IOFields>; +- VtkOutputModule<GridVariables, SolutionVector> vtkWriter(*gridVariables, x, problem->name()); +- using VelocityOutput = GetPropType<TypeTag, Properties::VelocityOutput>; +- vtkWriter.addVelocityOutput(std::make_shared<VelocityOutput>(*gridVariables)); +- IOFields::initOutputModule(vtkWriter); //!< Add model specific output fields +- vtkWriter.write(0.0); +- +- // instantiate time loop +- using Scalar = GetPropType<TypeTag, Properties::Scalar>; +- const auto tEnd = getParam<Scalar>("TimeLoop.TEnd"); +- const auto maxDt = getParam<Scalar>("TimeLoop.MaxTimeStepSize"); +- const auto dt = getParam<Scalar>("TimeLoop.DtInitial"); +- auto timeLoop = std::make_shared<TimeLoop<Scalar>>(0.0, 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, gridGeometry, gridVariables, timeLoop, xOld); +- +- // the linear solver +- using LinearSolver = AMGBiCGSTABIstlSolver<LinearSolverTraits<GridGeometry>, LinearAlgebraTraitsFromAssembler<Assembler>>; +- auto linearSolver = std::make_shared<LinearSolver>(gridGeometry->gridView(), gridGeometry->dofMapper()); +- +- // the non-linear solver +-// using PrimaryVariableSwitch = GetPropType<TypeTag, Properties::PrimaryVariableSwitch>; +- using NewtonSolver = NewtonSolver<Assembler, LinearSolver>; +- NewtonSolver nonLinearSolver(assembler, linearSolver); +- +- // time loop +- timeLoop->start(); +- while (!timeLoop->finished()) +- { +- //set time in problem (is used in time-dependent Neumann boundary condition) +- problem->setTime(timeLoop->time()+timeLoop->timeStepSize()); +- +- // solve the non-linear system with time step control +- nonLinearSolver.solve(x, *timeLoop); +- +- // make the new solution the old solution +- xOld = x; +- gridVariables->advanceTimeStep(); +- +- // advance to the time loop to the next step +- timeLoop->advanceTimeStep(); +- +- // report statistics of this time step +- timeLoop->reportTimeStep(); +- +- // set new dt as suggested by the newton solver +- timeLoop->setTimeStepSize(nonLinearSolver.suggestTimeStepSize(timeLoop->timeStepSize())); +- +- // output to vtk +- vtkWriter.write(timeLoop->time()); +- } +- +- timeLoop->finalize(leafGridView.comm()); +- +- // print parameter report +- if (leafGridView.comm().rank() == 0) +- Parameters::print(); +- +- return 0; +-} // end main +diff -ruN exercises/exercise-basic/2pmain.cc exercises/solution/exercise-basic/2pmain.cc +--- exercises/exercise-basic/2pmain.cc 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-basic/2pmain.cc 1970-01-01 01:00:00.000000000 +0100 +@@ -1,157 +0,0 @@ +-// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +-// vi: set et ts=4 sw=4 sts=4: +-/***************************************************************************** +- * See the file COPYING for full copying permissions. * +- * * +- * This program is free software: you can redistribute it and/or modify * +- * it under the terms of the GNU General Public License as published by * +- * the Free Software Foundation, either version 3 of the License, or * +- * (at your option) any later version. * +- * * +- * This program is distributed in the hope that it will be useful, * +- * but WITHOUT ANY WARRANTY; without even the implied warranty of * +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +- * GNU General Public License for more details. * +- * * +- * You should have received a copy of the GNU General Public License * +- * along with this program. If not, see <http://www.gnu.org/licenses/>. * +- *****************************************************************************/ +-/*! +- * \file +- * \brief The main file for the two-phase porousmediumflow problem of exercise-basic +- */ +-#include <config.h> +- +-#include <iostream> +- +-#include <dumux/common/initialize.hh> +-#include <dumux/common/properties.hh> +-#include <dumux/common/parameters.hh> +- +-#include <dumux/linear/istlsolvers.hh> +-#include <dumux/linear/linearsolvertraits.hh> +-#include <dumux/linear/linearalgebratraits.hh> +-#include <dumux/nonlinear/newtonsolver.hh> +- +-#include <dumux/assembly/fvassembler.hh> +-#include <dumux/assembly/diffmethod.hh> +- +-#include <dumux/io/vtkoutputmodule.hh> +-#include <dumux/io/grid/gridmanager_yasp.hh> +- +-// The properties file, where the compile time options are defined +-#include "properties2p.hh" +- +-//////////////////////// +-// the main function +-//////////////////////// +-int main(int argc, char** argv) +-{ +- using namespace Dumux; +- +- // define the type tag for this problem +- using TypeTag = Properties::TTag::Injection2pCC; +- +- /// initialize MPI+X, finalize is done automatically on exit +- Dumux::initialize(argc, argv); +- +- // parse command line arguments and input file +- Parameters::init(argc, argv); +- +- // try to create a grid (from the given grid file or the input file) +- GridManager<GetPropType<TypeTag, Properties::Grid>> gridManager; +- gridManager.init(); +- +- //////////////////////////////////////////////////////////// +- // run instationary non-linear problem on this grid +- //////////////////////////////////////////////////////////// +- +- // 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 x; +- problem->applyInitialSolution(x); +- auto xOld = x; +- +- // the grid variables +- using GridVariables = GetPropType<TypeTag, Properties::GridVariables>; +- auto gridVariables = std::make_shared<GridVariables>(problem, gridGeometry); +- gridVariables->init(x); +- +- // initialize the vtk output module +- using IOFields = GetPropType<TypeTag, Properties::IOFields>; +- +- // use non-conforming output for the test with interface solver +- const auto ncOutput = getParam<bool>("Problem.UseNonConformingOutput", false); +- VtkOutputModule<GridVariables, SolutionVector> vtkWriter(*gridVariables, x, problem->name(), "", +- ncOutput ? Dune::VTK::nonconforming : Dune::VTK::conforming); +- using VelocityOutput = GetPropType<TypeTag, Properties::VelocityOutput>; +- vtkWriter.addVelocityOutput(std::make_shared<VelocityOutput>(*gridVariables)); +- IOFields::initOutputModule(vtkWriter); //!< Add model specific output fields +- vtkWriter.write(0.0); +- +- // instantiate time loop +- using Scalar = GetPropType<TypeTag, Properties::Scalar>; +- const auto tEnd = getParam<Scalar>("TimeLoop.TEnd"); +- const auto maxDt = getParam<Scalar>("TimeLoop.MaxTimeStepSize"); +- const auto dt = getParam<Scalar>("TimeLoop.DtInitial"); +- auto timeLoop = std::make_shared<TimeLoop<Scalar>>(0.0, 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, gridGeometry, gridVariables, timeLoop, xOld); +- +- // the linear solver +- using LinearSolver = AMGBiCGSTABIstlSolver<LinearSolverTraits<GridGeometry>, LinearAlgebraTraitsFromAssembler<Assembler>>; +- auto linearSolver = std::make_shared<LinearSolver>(gridGeometry->gridView(), gridGeometry->dofMapper()); +- +- // the non-linear solver +- using NewtonSolver = Dumux::NewtonSolver<Assembler, LinearSolver>; +- NewtonSolver nonLinearSolver(assembler, linearSolver); +- +- // time loop +- timeLoop->start(); +- while (!timeLoop->finished()) +- { +- //set time in problem (is used in time-dependent Neumann boundary condition) +- problem->setTime(timeLoop->time()+timeLoop->timeStepSize()); +- +- // solve the non-linear system with time step control +- nonLinearSolver.solve(x, *timeLoop); +- +- // make the new solution the old solution +- xOld = x; +- gridVariables->advanceTimeStep(); +- +- // advance to the time loop to the next step +- timeLoop->advanceTimeStep(); +- +- // report statistics of this time step +- timeLoop->reportTimeStep(); +- +- // set new dt as suggested by the newton solver +- timeLoop->setTimeStepSize(nonLinearSolver.suggestTimeStepSize(timeLoop->timeStepSize())); +- +- // output to vtk +- vtkWriter.write(timeLoop->time()); +- } +- +- timeLoop->finalize(leafGridView.comm()); +- +- // print parameter report +- if (leafGridView.comm().rank() == 0) +- Parameters::print(); +- +- return 0; +-} // end main +diff -ruN exercises/exercise-basic/2pnimain.cc exercises/solution/exercise-basic/2pnimain.cc +--- exercises/exercise-basic/2pnimain.cc 1970-01-01 01:00:00.000000000 +0100 ++++ exercises/solution/exercise-basic/2pnimain.cc 2023-04-26 14:37:49.789150797 +0200 +@@ -0,0 +1,150 @@ ++// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- ++// vi: set et ts=4 sw=4 sts=4: ++/***************************************************************************** ++ * See the file COPYING for full copying permissions. * ++ * * ++ * This program is free software: you can redistribute it and/or modify * ++ * it under the terms of the GNU General Public License as published by * ++ * the Free Software Foundation, either version 3 of the License, or * ++ * (at your option) any later version. * ++ * * ++ * This program is distributed in the hope that it will be useful, * ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of * ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * ++ * GNU General Public License for more details. * ++ * * ++ * You should have received a copy of the GNU General Public License * ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ++ *****************************************************************************/ ++/*! ++ * \file ++ * \brief The solution main file for the two-phase porousmediumflow problem of exercise-basic ++ */ ++#include <config.h> ++ ++#include <iostream> ++ ++#include <dumux/common/initialize.hh> ++#include <dumux/common/properties.hh> ++#include <dumux/common/parameters.hh> ++ ++#include <dumux/linear/istlsolvers.hh> ++#include <dumux/linear/linearsolvertraits.hh> ++#include <dumux/linear/linearalgebratraits.hh> ++#include <dumux/nonlinear/newtonsolver.hh> ++ ++#include <dumux/assembly/fvassembler.hh> ++#include <dumux/assembly/diffmethod.hh> ++ ++#include <dumux/io/vtkoutputmodule.hh> ++#include <dumux/io/grid/gridmanager_yasp.hh> ++ ++// The properties file, where the compile time options are defined ++#include "properties2pni.hh" ++ ++int main(int argc, char** argv) ++{ ++ using namespace Dumux; ++ ++ // define the type tag for this problem ++ using TypeTag = Properties::TTag::Injection2pNICC; ++ ++ // initialize MPI+X, finalize is done automatically on exit ++ Dumux::initialize(argc, argv); ++ ++ // parse command line arguments and input file ++ Parameters::init(argc, argv); ++ ++ // try to create a grid (from the given grid file or the input file) ++ GridManager<GetPropType<TypeTag, Properties::Grid>> gridManager; ++ gridManager.init(); ++ ++ //////////////////////////////////////////////////////////// ++ // run instationary non-linear problem on this grid ++ //////////////////////////////////////////////////////////// ++ ++ // 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 x; ++ problem->applyInitialSolution(x); ++ auto xOld = x; ++ ++ // the grid variables ++ using GridVariables = GetPropType<TypeTag, Properties::GridVariables>; ++ auto gridVariables = std::make_shared<GridVariables>(problem, gridGeometry); ++ gridVariables->init(x); ++ ++ // initialize the vtk output module ++ using IOFields = GetPropType<TypeTag, Properties::IOFields>; ++ VtkOutputModule<GridVariables, SolutionVector> vtkWriter(*gridVariables, x, problem->name()); ++ using VelocityOutput = GetPropType<TypeTag, Properties::VelocityOutput>; ++ vtkWriter.addVelocityOutput(std::make_shared<VelocityOutput>(*gridVariables)); ++ IOFields::initOutputModule(vtkWriter); //!< Add model specific output fields ++ vtkWriter.write(0.0); ++ ++ // instantiate time loop ++ using Scalar = GetPropType<TypeTag, Properties::Scalar>; ++ const auto tEnd = getParam<Scalar>("TimeLoop.TEnd"); ++ const auto maxDt = getParam<Scalar>("TimeLoop.MaxTimeStepSize"); ++ const auto dt = getParam<Scalar>("TimeLoop.DtInitial"); ++ auto timeLoop = std::make_shared<TimeLoop<Scalar>>(0.0, 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, gridGeometry, gridVariables, timeLoop, xOld); ++ ++ // the linear solver ++ using LinearSolver = AMGBiCGSTABIstlSolver<LinearSolverTraits<GridGeometry>, LinearAlgebraTraitsFromAssembler<Assembler>>; ++ auto linearSolver = std::make_shared<LinearSolver>(gridGeometry->gridView(), gridGeometry->dofMapper()); ++ ++ // the non-linear solver ++ using NewtonSolver = Dumux::NewtonSolver<Assembler, LinearSolver>; ++ NewtonSolver nonLinearSolver(assembler, linearSolver); ++ ++ // time loop ++ timeLoop->start(); ++ while (!timeLoop->finished()) ++ { ++ //set time in problem (is used in time-dependent Neumann boundary condition) ++ problem->setTime(timeLoop->time()+timeLoop->timeStepSize()); ++ ++ // solve the non-linear system with time step control ++ nonLinearSolver.solve(x, *timeLoop); ++ ++ // make the new solution the old solution ++ xOld = x; ++ gridVariables->advanceTimeStep(); ++ ++ // advance to the time loop to the next step ++ timeLoop->advanceTimeStep(); ++ ++ // report statistics of this time step ++ timeLoop->reportTimeStep(); ++ ++ // set new dt as suggested by the newton solver ++ timeLoop->setTimeStepSize(nonLinearSolver.suggestTimeStepSize(timeLoop->timeStepSize())); ++ ++ // output to vtk ++ vtkWriter.write(timeLoop->time()); ++ } ++ ++ timeLoop->finalize(leafGridView.comm()); ++ ++ // print parameter report ++ if (leafGridView.comm().rank() == 0) ++ Parameters::print(); ++ ++ return 0; ++} // end main +diff -ruN exercises/exercise-basic/CMakeLists.txt exercises/solution/exercise-basic/CMakeLists.txt +--- exercises/exercise-basic/CMakeLists.txt 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-basic/CMakeLists.txt 2023-04-26 14:37:49.789150797 +0200 +@@ -1,13 +1,6 @@ +-# the immiscible two-phase simulation program +-dumux_add_test(NAME exercise_basic_2p +- SOURCES 2pmain.cc) +- +-# the compositional two-phase two-component simulation program +-dumux_add_test(NAME exercise_basic_2p2c +- SOURCES 2p2cmain.cc) +- +-# here, add the two-phase non-isothermal simulation program +- ++# the two-phase non-isothermal simulation program ++dumux_add_test(NAME exercise_basic_2pni_solution ++ SOURCES 2pnimain.cc) + + # add a symlink for each input file + add_input_file_links() +diff -ruN exercises/exercise-basic/injection2p2cproblem.hh exercises/solution/exercise-basic/injection2p2cproblem.hh +--- exercises/exercise-basic/injection2p2cproblem.hh 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-basic/injection2p2cproblem.hh 1970-01-01 01:00:00.000000000 +0100 +@@ -1,229 +0,0 @@ +-// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +-// vi: set et ts=4 sw=4 sts=4: +-/***************************************************************************** +- * See the file COPYING for full copying permissions. * +- * * +- * This program is free software: you can redistribute it and/or modify * +- * it under the terms of the GNU General Public License as published by * +- * the Free Software Foundation, either version 3 of the License, or * +- * (at your option) any later version. * +- * * +- * This program is distributed in the hope that it will be useful, * +- * but WITHOUT ANY WARRANTY; without even the implied warranty of * +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +- * GNU General Public License for more details. * +- * * +- * You should have received a copy of the GNU General Public License * +- * along with this program. If not, see <http://www.gnu.org/licenses/>. * +- *****************************************************************************/ +-/*! +- * \file +- * +- * \brief The two-phase porousmediumflow problem for exercise-basic +- */ +- +-#ifndef DUMUX_EX_BASIC_PROBLEM_2P2C_HH +-#define DUMUX_EX_BASIC_PROBLEM_2P2C_HH +- +-#include <dumux/common/properties.hh> +-#include <dumux/common/boundarytypes.hh> +-#include <dumux/common/numeqvector.hh> +-#include <dumux/porousmediumflow/problem.hh> +-#include <dumux/material/binarycoefficients/h2o_n2.hh> +- +-namespace Dumux { +- +-/*! +- * \ingroup TwoPTwoCModel +- * \ingroup ImplicitTestProblems +- * \brief Gas injection problem where a gas (here nitrogen) is injected into a fully +- * water saturated medium. During buoyancy driven upward migration the gas +- * passes a high temperature area. +- * +- * The domain is sized 60 m times 40 m. +- * +- * For the mass conservation equation Neumann boundary conditions are used on +- * the top, on the bottom and on the right of the domain, while Dirichlet conditions +- * apply on the left boundary. +- * +- * Gas is injected at the right boundary from 7 m to 15 m at a rate of +- * 0.001 kg/(s m), the remaining Neumann boundaries are no-flow +- * boundaries. +- * +- * At the Dirichlet boundaries a hydrostatic pressure and a gas saturation of zero a +- * +- * This problem uses the \ref TwoPModel model. +- */ +-template<class TypeTag> +-class Injection2p2cProblem : public PorousMediumFlowProblem<TypeTag> +-{ +- using ParentType = PorousMediumFlowProblem<TypeTag>; +- using Scalar = GetPropType<TypeTag, Properties::Scalar>; +- using Indices = typename GetPropType<TypeTag, Properties::ModelTraits>::Indices; +- using PrimaryVariables = GetPropType<TypeTag, Properties::PrimaryVariables>; +- using BoundaryTypes = Dumux::BoundaryTypes<GetPropType<TypeTag, Properties::ModelTraits>::numEq()>; +- using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>; +- using FVElementGeometry = typename GridGeometry::LocalView; +- using GridView = typename GridGeometry::GridView; +- using FluidSystem = GetPropType<TypeTag, Properties::FluidSystem>; +- using NumEqVector = Dumux::NumEqVector<PrimaryVariables>; +- +- static constexpr int dimWorld = GridView::dimensionworld; +- using Element = typename GridView::template Codim<0>::Entity; +- using GlobalPosition = typename Element::Geometry::GlobalCoordinate; +- +-public: +- Injection2p2cProblem(std::shared_ptr<const GridGeometry> gridGeometry) +- : ParentType(gridGeometry) +- { +- // initialize the tables of the fluid system +- FluidSystem::init(/*tempMin=*/273.15, +- /*tempMax=*/423.15, +- /*numTemp=*/50, +- /*pMin=*/0.0, +- /*pMax=*/30e6, +- /*numP=*/300); +- +- // name of the problem and output file +- // getParam<TYPE>("GROUPNAME.PARAMNAME") reads and sets parameter PARAMNAME +- // of type TYPE given in the group GROUPNAME from the input file +- name_ = getParam<std::string>("Problem.Name"); +- // depth of the aquifer, units: m +- aquiferDepth_ = getParam<Scalar>("Problem.AquiferDepth"); +- // the duration of the injection, units: second +- injectionDuration_ = getParam<Scalar>("Problem.InjectionDuration"); +- } +- +- /*! +- * \brief Returns the problem name +- * +- * This is used as a prefix for files generated by the simulation. +- */ +- std::string name() const +- { return name_+"-2p2c"; } +- +- /*! +- * \brief Specifies which kind of boundary condition should be +- * used for which equation on a given boundary segment. +- * +- * \param globalPos The position for which the bc type should be evaluated +- */ +- BoundaryTypes boundaryTypesAtPos(const GlobalPosition &globalPos) const +- { +- BoundaryTypes bcTypes; +- if (globalPos[0] < eps_) +- bcTypes.setAllDirichlet(); +- else +- bcTypes.setAllNeumann(); +- +- return bcTypes; +- } +- +- /*! +- * \brief Evaluates the boundary conditions for a Dirichlet +- * boundary segment +- * +- * \param globalPos The global position +- */ +- PrimaryVariables dirichletAtPos(const GlobalPosition &globalPos) const +- { +- return initialAtPos(globalPos); +- } +- +- /*! +- * \brief Evaluate the boundary conditions for a Neumann +- * boundary segment. +- * +- * \param globalPos The position of the integration point of the boundary segment. +- */ +- NumEqVector neumannAtPos(const GlobalPosition &globalPos) const +- { +- // initialize values to zero, i.e. no-flow Neumann boundary conditions +- NumEqVector values(0.0); +- +- // if we are inside the injection zone set inflow Neumann boundary conditions +- if (injectionActive() && onInjectionBoundary(globalPos)) +- { +- // TODO: dumux-course-task +- //instead of setting -1e-4 here directly use totalAreaSpecificInflow_ in the computation +- +- // inject nitrogen. negative values mean injection +- // convert from units kg/(s*m^2) to mole/(s*m^2) +- values[Indices::conti0EqIdx + FluidSystem::N2Idx] = -1e-4/FluidSystem::molarMass(FluidSystem::N2Idx); +- values[Indices::conti0EqIdx + FluidSystem::H2OIdx] = 0.0; +- } +- +- return values; +- } +- +- /*! +- * \brief Evaluate the source term for all phases within a given +- * sub-control-volume. +- * +- * \param globalPos The position for which the source term should be evaluated +- */ +- NumEqVector sourceAtPos(const GlobalPosition &globalPos) const +- { +- return NumEqVector(0.0); +- } +- +- /*! +- * \brief Evaluate the initial value for a control volume. +- * +- * \param globalPos The position for which the initial condition should be evaluated +- */ +- PrimaryVariables initialAtPos(const GlobalPosition &globalPos) const +- { +- PrimaryVariables values(0.0); +- values.setState(Indices::firstPhaseOnly); +- // get the water density at atmospheric conditions +- const Scalar densityW = FluidSystem::H2O::liquidDensity(this->spatialParams().temperatureAtPos(globalPos), 1.0e5); +- +- // assume an initially hydrostatic liquid pressure profile +- // note: we subtract rho_w*g*h because g is defined negative +- const Scalar pw = 1.0e5 - densityW*this->spatialParams().gravity(globalPos)[dimWorld-1]*(aquiferDepth_ - globalPos[dimWorld-1]); +- +- // initially we have some nitrogen dissolved +- // saturation mole fraction would be +- // moleFracLiquidN2 = (pw + pc + p_vap^sat)/henry; +- const Scalar moleFracLiquidN2 = pw*0.95/BinaryCoeff::H2O_N2::henry(this->spatialParams().temperatureAtPos(globalPos)); +- +- // note that because we start with a single phase system the primary variables +- // are pl and x^w_N2. This will switch as soon after we start injecting to a two +- // phase system so the primary variables will be pl and Sn (nonwetting saturation). +- values[Indices::pressureIdx] = pw; +- values[Indices::switchIdx] = moleFracLiquidN2; +- +- return values; +- } +- +- //! set the time for the time dependent boundary conditions (called from main) +- void setTime(Scalar time) +- { time_ = time; } +- +- //! Return true if the injection is currently active +- bool injectionActive() const +- { return time_ < injectionDuration_; } +- +- //! Return true if the given position is in the injection boundary region +- bool onInjectionBoundary(const GlobalPosition& globalPos) const +- { +- return globalPos[1] < 15. + eps_ +- && globalPos[1] > 7. - eps_ +- && globalPos[0] > this->gridGeometry().bBoxMax()[0] - eps_; +- } +- +-private: +- static constexpr Scalar eps_ = 1e-6; +- std::string name_; //! Problem name +- Scalar aquiferDepth_; //! Depth of the aquifer in m +- Scalar injectionDuration_; //! Duration of the injection in seconds +- Scalar time_; +- //TODO: dumux-course-task +- //define the Scalar totalAreaSpecificInflow_ here +- +-}; +- +-} //end namespace Dumux +- +-#endif +diff -ruN exercises/exercise-basic/injection2pniproblem.hh exercises/solution/exercise-basic/injection2pniproblem.hh +--- exercises/exercise-basic/injection2pniproblem.hh 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-basic/injection2pniproblem.hh 2023-04-26 14:37:49.789150797 +0200 +@@ -19,7 +19,7 @@ + /*! + * \file + * +- * \brief The two-phase nonisothermal porousmediumflow problem for exercise-basic ++ * \brief The solution of the two-phase nonisothermal porousmediumflow problem for exercise-basic + */ + + #ifndef DUMUX_EX_BASIC_PROBLEM_2PNI_HH +@@ -31,7 +31,6 @@ + #include <dumux/porousmediumflow/problem.hh> + + namespace Dumux { +- + /*! + * \ingroup TwoPModel + * \ingroup ImplicitTestProblems +@@ -46,7 +45,7 @@ + * apply on the left boundary. + * + * Gas is injected at the right boundary from 7 m to 15 m at a rate of +- * 0.0001 kg/(s m), the remaining Neumann boundaries are no-flow ++ * 0.001 kg/(s m), the remaining Neumann boundaries are no-flow + * boundaries. + * + * At the Dirichlet boundaries a hydrostatic pressure and a gas saturation of zero a +@@ -57,13 +56,13 @@ + class Injection2PNIProblem : public PorousMediumFlowProblem<TypeTag> + { + using ParentType = PorousMediumFlowProblem<TypeTag>; ++ using GridView = typename GetPropType<TypeTag, Properties::GridGeometry>::GridView; + using Scalar = GetPropType<TypeTag, Properties::Scalar>; + using Indices = typename GetPropType<TypeTag, Properties::ModelTraits>::Indices; + using PrimaryVariables = GetPropType<TypeTag, Properties::PrimaryVariables>; + using BoundaryTypes = Dumux::BoundaryTypes<GetPropType<TypeTag, Properties::ModelTraits>::numEq()>; + using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>; +- using FVElementGeometry = typename GridGeometry::LocalView; +- using GridView = typename GridGeometry::GridView; ++ using FVElementGeometry = typename GetPropType<TypeTag, Properties::GridGeometry>::LocalView; + using FluidSystem = GetPropType<TypeTag, Properties::FluidSystem>; + using NumEqVector = Dumux::NumEqVector<PrimaryVariables>; + +@@ -115,14 +114,6 @@ + else + bcTypes.setAllNeumann(); + +- /*! +- * TODO:dumux-course-task: +- * dumux-course-task: +- * set Dirichlet conditions for the energy equation on the left boundary +- * and Neumann everywhere else +- * think about: is there anything necessary to do here? +- */ +- + return bcTypes; + } + +@@ -135,13 +126,6 @@ + PrimaryVariables dirichletAtPos(const GlobalPosition &globalPos) const + { + return initialAtPos(globalPos); +- +- /*! +- * TODO:dumux-course-task: +- * dumux-course-task: +- * set Dirichlet conditions for the energy equation on the left boundary +- * think about: is there anything necessary to do here? +- */ + } + + /*! +@@ -156,19 +140,14 @@ + NumEqVector values(0.0); + + // if we are inside the injection zone set inflow Neumann boundary conditions +- if (injectionActive() && onInjectionBoundary(globalPos)) ++ if (injectionActive() && onInjectionBoundary(globalPos)) + { ++ + // inject nitrogen. negative values mean injection + // units kg/(s*m^2) + values[Indices::conti0EqIdx + FluidSystem::N2Idx] = -1e-4; + values[Indices::conti0EqIdx + FluidSystem::H2OIdx] = 0.0; +- +- /*! +- * TODO:dumux-course-task: +- * dumux-course-task: +- * set Neumann noflow conditions for the energy equation everywhere else except the left boundary +- * hint: use Indices::energyEqIdx) for that (if required) +- */ ++ values[Indices::energyEqIdx] = 0.0; + } + + return values; +@@ -204,20 +183,17 @@ + values[Indices::pressureIdx] = pw; + values[Indices::saturationIdx] = 0.0; + +- /*! +- * TODO:dumux-course-task: +- * set a temperature gradient of 0.03 K per m beginning at 283 K here. +- * Hint: you can use aquiferDepth_ and the globalPos similar to the pressure gradient +- * use globalPos[0] and globalPos[1] to implement the high temperature lens with 380 K +- * Hint : use Indices::temperatureIdx to address the initial values for temperature +- */ ++ values[Indices::temperatureIdx] = 283.0 + (aquiferDepth_ - globalPos[1])*0.03; ++ if (globalPos[0] > 20 - eps_ && globalPos[0] < 30 + eps_ && globalPos[1] > 5 - eps_ && globalPos[1] < 35 + eps_) ++ values[Indices::temperatureIdx] = 380; ++ + return values; + } + + //! set the time for the time dependent boundary conditions (called from main) + void setTime(Scalar time) + { time_ = time; } +- ++ + //! Return true if the injection is currently active + bool injectionActive() const + { return time_ < injectionDuration_; } +diff -ruN exercises/exercise-basic/injection2pproblem.hh exercises/solution/exercise-basic/injection2pproblem.hh +--- exercises/exercise-basic/injection2pproblem.hh 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-basic/injection2pproblem.hh 1970-01-01 01:00:00.000000000 +0100 +@@ -1,223 +0,0 @@ +-// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +-// vi: set et ts=4 sw=4 sts=4: +-/***************************************************************************** +- * See the file COPYING for full copying permissions. * +- * * +- * This program is free software: you can redistribute it and/or modify * +- * it under the terms of the GNU General Public License as published by * +- * the Free Software Foundation, either version 3 of the License, or * +- * (at your option) any later version. * +- * * +- * This program is distributed in the hope that it will be useful, * +- * but WITHOUT ANY WARRANTY; without even the implied warranty of * +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +- * GNU General Public License for more details. * +- * * +- * You should have received a copy of the GNU General Public License * +- * along with this program. If not, see <http://www.gnu.org/licenses/>. * +- *****************************************************************************/ +-/*! +- * \file +- * +- * \brief The two-phase porousmediumflow problem for exercise-basic +- */ +- +-#ifndef DUMUX_EX_BASIC_PROBLEM_2P_HH +-#define DUMUX_EX_BASIC_PROBLEM_2P_HH +- +-#include <dumux/common/properties.hh> +-#include <dumux/common/boundarytypes.hh> +-#include <dumux/common/numeqvector.hh> +-#include <dumux/porousmediumflow/problem.hh> +- +-namespace Dumux { +- +-/*! +- * \ingroup TwoPModel +- * \ingroup ImplicitTestProblems +- * \brief Gas injection problem where a gas (here nitrogen) is injected into a fully +- * water saturated medium. During buoyancy driven upward migration the gas +- * passes a high temperature area. +- * +- * The domain is sized 60 m times 40 m. +- * +- * For the mass conservation equation neumann boundary conditions are used on +- * the top, on the bottom and on the right of the domain, while dirichlet conditions +- * apply on the left boundary. +- * +- * Gas is injected at the right boundary from 7 m to 15 m at a rate of +- * 0.001 kg/(s m), the remaining neumann boundaries are no-flow +- * boundaries. +- * +- * At the dirichlet boundaries a hydrostatic pressure and a gas saturation of zero a +- * +- * This problem uses the \ref TwoPModel model. +- */ +-template<class TypeTag> +-class Injection2PProblem : public PorousMediumFlowProblem<TypeTag> +-{ +- using ParentType = PorousMediumFlowProblem<TypeTag>; +- using GridView = typename GetPropType<TypeTag, Properties::GridGeometry>::GridView; +- using Scalar = GetPropType<TypeTag, Properties::Scalar>; +- using Indices = typename GetPropType<TypeTag, Properties::ModelTraits>::Indices; +- using PrimaryVariables = GetPropType<TypeTag, Properties::PrimaryVariables>; +- using BoundaryTypes = Dumux::BoundaryTypes<GetPropType<TypeTag, Properties::ModelTraits>::numEq()>; +- using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>; +- using FVElementGeometry = typename GetPropType<TypeTag, Properties::GridGeometry>::LocalView; +- using FluidSystem = GetPropType<TypeTag, Properties::FluidSystem>; +- using NumEqVector = Dumux::NumEqVector<PrimaryVariables>; +- +- static constexpr int dimWorld = GridView::dimensionworld; +- using Element = typename GridView::template Codim<0>::Entity; +- using GlobalPosition = typename Element::Geometry::GlobalCoordinate; +- +-public: +- Injection2PProblem(std::shared_ptr<const GridGeometry> gridGeometry) +- : ParentType(gridGeometry) +- { +- // initialize the tables of the fluid system +- FluidSystem::init(/*tempMin=*/273.15, +- /*tempMax=*/423.15, +- /*numTemp=*/50, +- /*pMin=*/0.0, +- /*pMax=*/30e6, +- /*numP=*/300); +- +- // name of the problem and output file +- // getParam<TYPE>("GROUPNAME.PARAMNAME") reads and sets parameter PARAMNAME +- // of type TYPE given in the group GROUPNAME from the input file +- name_ = getParam<std::string>("Problem.Name"); +- // depth of the aquifer, units: m +- aquiferDepth_ = getParam<Scalar>("Problem.AquiferDepth"); +- // the duration of the injection, units: second +- injectionDuration_ = getParam<Scalar>("Problem.InjectionDuration"); +- } +- +- +- /*! +- * \brief Returns the problem name +- * +- * This is used as a prefix for files generated by the simulation. +- */ +- std::string name() const +- { return name_+"-2p"; } +- +- +- /*! +- * \brief Specifies which kind of boundary condition should be +- * used for which equation on a given boundary segment. +- * +- * \param globalPos The position for which the bc type should be evaluated +- */ +- BoundaryTypes boundaryTypesAtPos(const GlobalPosition &globalPos) const +- { +- BoundaryTypes bcTypes; +- // set the left of the domain (with the global position in "0 = x" direction as a Dirichlet boundary +- if (globalPos[0] < eps_) +- bcTypes.setAllDirichlet(); +- // set all other as Neumann boundaries +- else +- bcTypes.setAllNeumann(); +- +- return bcTypes; +- } +- +- /*! +- * \brief Evaluates the boundary conditions for a Dirichlet +- * boundary segment +- * +- * \param globalPos The global position +- */ +- PrimaryVariables dirichletAtPos(const GlobalPosition &globalPos) const +- { +- return initialAtPos(globalPos); +- } +- +- /*! +- * \brief Evaluate the boundary conditions for a neumann +- * boundary segment. +- * +- * \param globalPos The position of the integration point of the boundary segment. +- */ +- NumEqVector neumannAtPos(const GlobalPosition &globalPos) const +- { +- // initialize values to zero, i.e. no-flow Neumann boundary conditions +- NumEqVector values(0.0); +- +- // if we are inside the injection zone set inflow Neumann boundary conditions +- // using < boundary + eps_ or > boundary - eps_ is safer for floating point comparisons +- // than using <= or >= as it is robust with regard to imprecision introduced by rounding errors. +- if (injectionActive() && onInjectionBoundary(globalPos)) +- { +- // inject nitrogen. negative values mean injection +- // units kg/(s*m^2) +- values[Indices::conti0EqIdx + FluidSystem::N2Idx] = -1e-4; +- values[Indices::conti0EqIdx + FluidSystem::H2OIdx] = 0.0; +- } +- +- return values; +- } +- +- +- /*! +- * \brief Evaluate the source term for all phases within a given +- * sub-control-volume. +- * +- * \param globalPos The position for which the source term should be evaluated +- */ +- NumEqVector sourceAtPos(const GlobalPosition &globalPos) const +- { +- return NumEqVector(0.0); +- } +- +- /*! +- * \brief Evaluate the initial value for a control volume. +- * +- * \param globalPos The position for which the initial condition should be evaluated +- */ +- PrimaryVariables initialAtPos(const GlobalPosition &globalPos) const +- { +- PrimaryVariables values(0.0); +- +- // get the water density at atmospheric conditions +- const Scalar densityW = FluidSystem::H2O::liquidDensity(this->spatialParams().temperatureAtPos(globalPos), 1.0e5); +- +- // assume an initially hydrostatic liquid pressure profile +- // note: we subtract rho_w*g*h because g is defined negative +- const Scalar pw = 1.0e5 - densityW*this->spatialParams().gravity(globalPos)[dimWorld-1]*(aquiferDepth_ - globalPos[dimWorld-1]); +- +- values[Indices::pressureIdx] = pw; +- values[Indices::saturationIdx] = 0.0; +- +- return values; +- } +- +- // \} +- +- //! set the time for the time dependent boundary conditions (called from main) +- void setTime(Scalar time) +- { time_ = time; } +- +- //! Return true if the injection is currently active +- bool injectionActive() const +- { return time_ < injectionDuration_; } +- +- //! Return true if the given position is in the injection boundary region +- bool onInjectionBoundary(const GlobalPosition& globalPos) const +- { +- return globalPos[1] < 15. + eps_ +- && globalPos[1] > 7. - eps_ +- && globalPos[0] > this->gridGeometry().bBoxMax()[0] - eps_; +- } +- +-private: +- static constexpr Scalar eps_ = 1e-6; +- std::string name_; //! Problem name +- Scalar aquiferDepth_; //! Depth of the aquifer in m +- Scalar injectionDuration_; //! Duration of the injection in seconds +- Scalar time_; +-}; +- +-} //end namespace Dumux +- +-#endif +diff -ruN exercises/exercise-basic/params.input exercises/solution/exercise-basic/params.input +--- exercises/exercise-basic/params.input 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-basic/params.input 2023-04-26 14:37:49.789150797 +0200 +@@ -1,6 +1,6 @@ + [TimeLoop] + DtInitial = 3600 # in seconds +-TEnd = 3.154e9 # in seconds, i.e one hundred years ++TEnd = 3.154e9 # in seconds, i.e ten years + + [Grid] + UpperRight = 60 40 +@@ -24,7 +24,7 @@ + Aquifer.Snr = 0.0 + + # these parameters are only used in the nonisothermal model. Uncomment them for that +-#[Component] +-#SolidDensity = 2700 # solid density of granite +-#SolidThermalConductivity = 2.8 # solid thermal conducitivity of granite +-#SolidHeatCapacity = 790 # solid heat capacity of granite ++[Component] ++SolidDensity = 2700 # solid density of granite ++SolidThermalConductivity = 2.8 # solid thermal conducitivity of granite ++SolidHeatCapacity = 790 # solid heat capacity of granite +diff -ruN exercises/exercise-basic/properties2p2c.hh exercises/solution/exercise-basic/properties2p2c.hh +--- exercises/exercise-basic/properties2p2c.hh 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-basic/properties2p2c.hh 1970-01-01 01:00:00.000000000 +0100 +@@ -1,78 +0,0 @@ +-// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +-// vi: set et ts=4 sw=4 sts=4: +-/***************************************************************************** +- * See the file COPYING for full copying permissions. * +- * * +- * This program is free software: you can redistribute it and/or modify * +- * it under the terms of the GNU General Public License as published by * +- * the Free Software Foundation, either version 3 of the License, or * +- * (at your option) any later version. * +- * * +- * This program is distributed in the hope that it will be useful, * +- * but WITHOUT ANY WARRANTY; without even the implied warranty of * +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +- * GNU General Public License for more details. * +- * * +- * You should have received a copy of the GNU General Public License * +- * along with this program. If not, see <http://www.gnu.org/licenses/>. * +- *****************************************************************************/ +-/*! +- * \file +- * +- * \brief The two-phase two-component porousmediumflow properties file for exercise-basic +- */ +- +-#ifndef DUMUX_EX_BASIC_PROPERTIES_2P2C_HH +-#define DUMUX_EX_BASIC_PROPERTIES_2P2C_HH +- +-#include <dune/grid/yaspgrid.hh> +- +-#include <dumux/discretization/cctpfa.hh> +-#include <dumux/porousmediumflow/2p2c/model.hh> +-#include <dumux/material/fluidsystems/h2on2.hh> +- +-#include "injection2p2cproblem.hh" +-#include "injection2pspatialparams.hh" +- +-namespace Dumux::Properties { +- +-// Create new type tags +-namespace TTag { +-struct Injection2p2c { using InheritsFrom = std::tuple<TwoPTwoC>; }; +-struct Injection2p2cCC { using InheritsFrom = std::tuple<Injection2p2c, CCTpfaModel>; }; +-} // end namespace TTag +- +-// Set the grid type +-template<class TypeTag> +-struct Grid<TypeTag, TTag::Injection2p2c> { using type = Dune::YaspGrid<2>; }; +- +-// Set the problem property +-template<class TypeTag> +-struct Problem<TypeTag, TTag::Injection2p2c> { using type = Injection2p2cProblem<TypeTag>; }; +- +-// Set the spatial parameters +-template<class TypeTag> +-struct SpatialParams<TypeTag, TTag::Injection2p2c> +-{ +-private: +- using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>; +- using Scalar = GetPropType<TypeTag, Properties::Scalar>; +-public: +- using type = InjectionSpatialParams<GridGeometry, Scalar>; +-}; +- +-// Set fluid configuration +-template<class TypeTag> +-struct FluidSystem<TypeTag, TTag::Injection2p2c> +-{ +- using type = FluidSystems::H2ON2< GetPropType<TypeTag, Properties::Scalar>, +- FluidSystems::H2ON2DefaultPolicy</*fastButSimplifiedRelations=*/ true> >; +-}; +- +-// Define whether mole (true) or mass (false) fractions are used +-template<class TypeTag> +-struct UseMoles<TypeTag, TTag::Injection2p2c> { static constexpr bool value = true; }; +- +-} // end namespace Dumux::Properties +- +-#endif +diff -ruN exercises/exercise-basic/properties2p.hh exercises/solution/exercise-basic/properties2p.hh +--- exercises/exercise-basic/properties2p.hh 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-basic/properties2p.hh 1970-01-01 01:00:00.000000000 +0100 +@@ -1,75 +0,0 @@ +-// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +-// vi: set et ts=4 sw=4 sts=4: +-/***************************************************************************** +- * See the file COPYING for full copying permissions. * +- * * +- * This program is free software: you can redistribute it and/or modify * +- * it under the terms of the GNU General Public License as published by * +- * the Free Software Foundation, either version 3 of the License, or * +- * (at your option) any later version. * +- * * +- * This program is distributed in the hope that it will be useful, * +- * but WITHOUT ANY WARRANTY; without even the implied warranty of * +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +- * GNU General Public License for more details. * +- * * +- * You should have received a copy of the GNU General Public License * +- * along with this program. If not, see <http://www.gnu.org/licenses/>. * +- *****************************************************************************/ +-/*! +- * \file +- * +- * \brief The two-phase porousmediumflow properties file for exercise-basic +- */ +- +-#ifndef DUMUX_EX_BASIC_PROPERTIES_2P_HH +-#define DUMUX_EX_BASIC_PROPERTIES_2P_HH +- +-#include <dune/grid/yaspgrid.hh> +- +-#include <dumux/discretization/cctpfa.hh> +-#include <dumux/porousmediumflow/2p/model.hh> +-#include <dumux/material/fluidsystems/h2on2.hh> +- +-#include "injection2pproblem.hh" +-#include "injection2pspatialparams.hh" +- +-namespace Dumux::Properties { +- +-// define the TypeTag for this problem with a cell-centered two-point flux approximation spatial discretization. +-// Create new type tags +-namespace TTag { +-struct Injection2p { using InheritsFrom = std::tuple<TwoP>; }; +-struct Injection2pCC { using InheritsFrom = std::tuple<Injection2p, CCTpfaModel>; }; +-} // end namespace TTag +- +-// Set the grid type +-template<class TypeTag> +-struct Grid<TypeTag, TTag::Injection2p> { using type = Dune::YaspGrid<2>; }; +- +-// Set the problem property +-template<class TypeTag> +-struct Problem<TypeTag, TTag::Injection2p> { using type = Injection2PProblem<TypeTag>; }; +- +-// Set the spatial parameters +-template<class TypeTag> +-struct SpatialParams<TypeTag, TTag::Injection2p> +-{ +-private: +- using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>; +- using Scalar = GetPropType<TypeTag, Properties::Scalar>; +-public: +- using type = InjectionSpatialParams<GridGeometry, Scalar>; +-}; +- +-// Set fluid configuration +-template<class TypeTag> +-struct FluidSystem<TypeTag, TTag::Injection2p> +-{ +- using type = FluidSystems::H2ON2< GetPropType<TypeTag, Properties::Scalar>, +- FluidSystems::H2ON2DefaultPolicy</*fastButSimplifiedRelations=*/ true> >; +-}; +- +-} // end namespace Dumux::Properties +- +-#endif +diff -ruN exercises/exercise-basic/properties2pni.hh exercises/solution/exercise-basic/properties2pni.hh +--- exercises/exercise-basic/properties2pni.hh 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-basic/properties2pni.hh 2023-04-26 14:37:49.789150797 +0200 +@@ -31,18 +31,14 @@ + #include <dumux/porousmediumflow/2p/model.hh> + #include <dumux/material/fluidsystems/h2on2.hh> + +-#include "injection2pniproblem.hh" + #include "injection2pspatialparams.hh" ++#include "injection2pniproblem.hh" + + namespace Dumux::Properties { + +- /*! +-* TODO:dumux-course-task 4 +-* Inherit from the TwoPNI model instead of TwoP here +-*/ + // Create new type tags + namespace TTag { +-struct Injection2pNITypeTag { using InheritsFrom = std::tuple<TwoP>; }; ++struct Injection2pNITypeTag { using InheritsFrom = std::tuple<TwoPNI>; }; + struct Injection2pNICC { using InheritsFrom = std::tuple<Injection2pNITypeTag, CCTpfaModel>; }; + } // end namespace TTag + +@@ -69,8 +65,8 @@ + template<class TypeTag> + struct FluidSystem<TypeTag, TTag::Injection2pNITypeTag> + { +- using type = FluidSystems::H2ON2< GetPropType<TypeTag, Properties::Scalar>, +- FluidSystems::H2ON2DefaultPolicy</*fastButSimplifiedRelations=*/true> >; ++ using type = FluidSystems::H2ON2<GetPropType<TypeTag, Properties::Scalar>, ++ FluidSystems::H2ON2DefaultPolicy</*fastButSimplifiedRelations=*/true>>; + }; + + } // end namespace Dumux::Properties +diff -ruN exercises/exercise-basic/README.md exercises/solution/exercise-basic/README.md +--- exercises/exercise-basic/README.md 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-basic/README.md 1970-01-01 01:00:00.000000000 +0100 +@@ -1,106 +0,0 @@ +-# Exercise Basics (DuMuX course) +-<br> +- +-## Problem set-up +- +-N$_2$ is injected in an aquifer previously saturated with water with an injection rate of 0.0001 kg/(s*m$^2$). +-The aquifer is situated 2700 m below sea level and the domain size is 60 m x 40 m. It consists of two layers, a moderately permeable one ($\Omega_1$) and a lower permeable one ($\Omega_2$). +- +-<img src="https://git.iws.uni-stuttgart.de/dumux-repositories/dumux-course/raw/master/exercises/extradoc/exercise1_setup.png" width="1000"> +- +-## Preparing the exercise +- +-* Navigate to the directory `dumux-course/exercises/exercise-basic` +- +-This exercise deals with two problems: a two-phase immiscible problem (__2p__) and a two-phase compositional problem (__2p2c__). They both set up the same scenario with the difference that the 2p2c assumes a miscible fluid state for the two fluids (water and gaseous N$_2$) and the 2p model assumes an immiscible fluid state. +- +-<br><br> +-### Task 1: Getting familiar with the code +-<hr> +- +-Locate all the files you will need for this exercise +-* The __main file__ for the __2p__ problem : `2pmain.cc` +-* The __main file__ for the __2p2c__ problem : `2p2cmain.cc` +-* The __problem file__ for the __2p__ problem: `injection2pproblem.hh` +-* The __problem file__ for the __2p2c__ problem: `injection2p2cproblem.hh` +-* The __properties file__ for the __2p__ problem: `properties2p.hh` +-* The __properties file__ for the __2p2c__ problem: `properties2p2c.hh` +-* The shared __spatial parameters file__: `injection2pspatialparams.hh` +-* The shared __input file__: `params.input` +- +-<br><br> +-### Task 2: Compiling and running an executable +-<hr> +- +-* Change to the build-directory +- +-```bash +-cd ../../build-cmake/exercises/exercise-basic +-``` +- +-* Compile both executables `exercise_basic_2p` and `exercise_basic_2p2c` +- +-```bash +-make exercise_basic_2p exercise_basic_2p2c +-``` +- +-* Execute the two problems and inspect the result +- +-```bash +-./exercise_basic_2p params.input +-./exercise_basic_2p2c params.input +-``` +- +-* you can look at the results with paraview +- +-```bash +-paraview injection-2p2c.pvd +-``` +- +-<br><br> +-### Task 3: Setting up a new executable (for a non-isothermal simulation) +-<hr> +- +-* Copy the main file `2pmain.cc` and rename it to `2pnimain.cc` +-* In `2pnimain.cc`, include the header `properties2pni.hh` instead of `properties2p.hh`. +-* In `2pnimain.cc`, change `Injection2pCC` to `Injection2pNICC` in the line `using TypeTag = Properties::TTag::Injection2pNICC;` +-* Add a new executable in `CMakeLists.txt` by adding the lines +- +-```cmake +-# the two-phase non-isothermal simulation program +-dumux_add_test(NAME exercise_basic_2pni +- SOURCES 2pnimain.cc) +-``` +- +-* Test that everything compiles without error +- +-```bash +-make # should rerun cmake +-make exercise_basic_2pni # builds new executable +-``` +- +-<br><br> +-### Task 4: Setting up a non-isothermal __2pni__ test problem +-<hr> +- +-* Open the files `injection2pniproblem.hh` and `properties2pni.hh`. +-These are copies of the `injection2pproblem.hh` and `properties2p.hh` files, with some useful comments on how to implement a non-isothermal model. +-Look for comments containing +- +-```c++ +-// TODO: dumux-course-task 4 +-``` +- +-* The following set-up should be realized: +- +- __Boundary conditions:__ Dirichlet conditions at the left boundary. For the primary variable __temperature__ use a varying temperature of <br/> +-$`\displaystyle T(y) = 283~\text{K} + 0.03~\frac{\text{K}}{\text{m}} \cdot \left( d_\text{aquifer} - y \right)`$, <br/> +-with the aquifer depth +-$\displaystyle d_\text{aquifer}=2700~\text{m}$. +-Assign Neumann no-flow for the energy balance to the rest of the boundaries. +- +- __Initial conditions:__ The same temperature gradient as in the boundary conditions with an exception in the subdomain (20 < x < 30, 5 < y < 35), where you assign a constant initial temperature of 380 K. +- +-<img src="https://git.iws.uni-stuttgart.de/dumux-repositories/dumux-course/raw/master/exercises/extradoc/exercise1_nonisothermal.png" width="800"> +- +-The non-isothermal model requires additional parameters like the thermal conductivity of the solid component. They are already implemented and set in `params.input`, you just need to _uncomment_ them. diff --git a/.patches/exercise-biomineralization/exercise-biomineralization.patch b/.patches/exercise-biomineralization/exercise-biomineralization.patch new file mode 100644 index 00000000..90d31967 --- /dev/null +++ b/.patches/exercise-biomineralization/exercise-biomineralization.patch @@ -0,0 +1,559 @@ +diff -ruN exercises/exercise-biomineralization/biominproblem.hh exercises/solution/exercise-biomineralization/biominproblem.hh +--- exercises/exercise-biomineralization/biominproblem.hh 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-biomineralization/biominproblem.hh 2023-04-26 14:37:49.789150797 +0200 +@@ -31,10 +31,12 @@ + + // TODO: dumux-course-task + // include chemistry file here ++#include "chemistry/simplebiominreactions.hh" // chemical reactions + + namespace Dumux { + + /*! ++ * + * \brief Problem biomineralization (MICP) in an experimental setup. + */ + template <class TypeTag> +@@ -65,6 +67,7 @@ + using SubControlVolume = typename FVElementGeometry::SubControlVolume; + // TODO: dumux-course-task + // set the chemistry TypeTag ++ using Chemistry = typename Dumux::SimpleBiominReactions<NumEqVector, VolumeVariables>; + + static constexpr int numComponents = FluidSystem::numComponents; + static constexpr int pressureIdx = Indices::pressureIdx; +@@ -182,11 +185,12 @@ + NumEqVector values(0.0); + + Scalar waterFlux = injVolumeflux_; // divide by area if area not 1! [m/s] ++ Scalar gasFlux = injCO2_; // divide by area if area not 1! [m/s] + + // Set values for Ca + urea injection above aquitard. + // Use negative values for injection. + if(globalPos[0] < eps_ +- && globalPos[1] > 11.0 - eps_ ++ && globalPos[1] > 11.0 + eps_ + && globalPos[1] < 12.0 + eps_ + && time_ < injBioTime_) + { +@@ -196,9 +200,15 @@ + } + // TODO: dumux-course-task + // Set CO2 injection below aquitard after the injBioTime ++ else if(globalPos[0] < eps_ ++ && globalPos[1] > 2.0 + eps_ ++ && globalPos[1] < 3.0 + eps_ ++ && time_ > injBioTime_ ) ++ { ++ values[conti0EqIdx + CO2Idx] = - gasFlux / FluidSystem::molarMass(CO2Idx); ++ } + else + { +- // Scalar gasFlux = injCO2_; // divide by area if area not 1! [m/s] + values = 0.0; //mol/m²/s + } + +@@ -265,11 +275,13 @@ + // set Chemistry + // set VolumeVariables + // call reactionSource() in chemistry ++ Chemistry chemistry; ++ const auto& volVars = elemVolVars[scv]; ++ chemistry.reactionSource(source, volVars); + + return source; + } + +- + const std::vector<Scalar>& getKxx() + { + return Kxx_; +diff -ruN exercises/exercise-biomineralization/biominspatialparams.hh exercises/solution/exercise-biomineralization/biominspatialparams.hh +--- exercises/exercise-biomineralization/biominspatialparams.hh 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-biomineralization/biominspatialparams.hh 2023-04-26 14:37:49.789150797 +0200 +@@ -30,7 +30,7 @@ + #include <dumux/material/fluidmatrixinteractions/porosityprecipitation.hh> + // TODO: dumux-course-task + // include the new permeability law (power law) instead of Kozeny-Carman +-#include <dumux/material/fluidmatrixinteractions/permeabilitykozenycarman.hh> ++#include "fluidmatrixinteractions/permeabilitypowerlaw.hh" //the power-law porosity-permeability relation + + #include <dumux/discretization/method.hh> + +@@ -285,7 +285,7 @@ + + // TODO: dumux-course-task + // define the power law as the permeability law +- PermeabilityKozenyCarman<PermeabilityType> permLaw_; ++ PermeabilityPowerLaw<PermeabilityType> permLaw_; + PoroLaw poroLaw_; + + Scalar initialPorosity_; +diff -ruN exercises/exercise-biomineralization/chemistry/simplebiominreactions.hh exercises/solution/exercise-biomineralization/chemistry/simplebiominreactions.hh +--- exercises/exercise-biomineralization/chemistry/simplebiominreactions.hh 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-biomineralization/chemistry/simplebiominreactions.hh 2023-04-26 14:37:49.789150797 +0200 +@@ -25,7 +25,6 @@ + #define DUMUX_BIOMIN_REACTIONS_HH + + #include <dumux/common/parameters.hh> +-// #include <dumux-course/exercises/exercise-biomineralization/components/urea.hh> + + namespace Dumux { + +@@ -94,6 +93,7 @@ + + // TODO: dumux-course-task + // implement mass of biofilm ++ Scalar massBiofilm = densityBiofilm * volFracBiofilm; + + Scalar molalityUrea = moleFracToMolality(volVars.moleFraction(liquidPhaseIdx,UreaIdx), + xwCa, +@@ -101,7 +101,8 @@ + + // TODO: dumux-course-task + // compute rate of ureolysis by implementing Z_urease,biofilm and r_urea +- Scalar rurea = 0.0; ++ Scalar Zub = kub_ * massBiofilm; // [kg urease/m³] ++ Scalar rurea = kurease_ * Zub * molalityUrea / (ku_ + molalityUrea); // [mol/m³s] + + // compute/set dissolution and precipitation rate of calcite + Scalar rprec = 0.0; +@@ -111,11 +112,11 @@ + // TODO: dumux-course-task + // update terms according to stochiometry + q[H2OIdx] += 0.0; +- q[CO2Idx] += 0.0; +- q[CaIdx] += 0.0; +- q[UreaIdx] += 0.0; ++ q[CO2Idx] += rurea - rprec ; ++ q[CaIdx] += - rprec; ++ q[UreaIdx] += - rurea; + q[BiofilmIdx] += 0.0; +- q[CalciteIdx] += 0.0; ++ q[CalciteIdx] += rprec; + } + + private: +diff -ruN exercises/exercise-biomineralization/CMakeLists.txt exercises/solution/exercise-biomineralization/CMakeLists.txt +--- exercises/exercise-biomineralization/CMakeLists.txt 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-biomineralization/CMakeLists.txt 2023-04-26 14:37:49.789150797 +0200 +@@ -1,7 +1,7 @@ + # executables for exercisebiomin +-dumux_add_test(NAME exercise_biomin ++dumux_add_test(NAME exercise_biomin_solution + SOURCES main.cc +- CMD_ARGS -Injection.InjBioTime 1 -Injection.InjCO2Time 0) ++ CMD_ARGS -Injection.InjBioTime 1 -Injection.InjCO2Time 0.3) + + # add a symlink for each input file + add_input_file_links() +diff -ruN exercises/exercise-biomineralization/components/biofilm.hh exercises/solution/exercise-biomineralization/components/biofilm.hh +--- exercises/exercise-biomineralization/components/biofilm.hh 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-biomineralization/components/biofilm.hh 2023-04-26 14:37:49.789150797 +0200 +@@ -24,9 +24,10 @@ + #ifndef DUMUX_BIOFILM_HH + #define DUMUX_BIOFILM_HH + ++#include <dumux/common/parameters.hh> ++ + #include <dumux/material/components/base.hh> + #include <dumux/material/components/solid.hh> +-#include <dumux/common/parameters.hh> + + namespace Dumux::Components { + +diff -ruN exercises/exercise-biomineralization/components/urea.hh exercises/solution/exercise-biomineralization/components/urea.hh +--- exercises/exercise-biomineralization/components/urea.hh 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-biomineralization/components/urea.hh 2023-04-26 14:37:49.789150797 +0200 +@@ -26,8 +26,7 @@ + + #include <dumux/material/components/base.hh> + +-namespace Dumux { +-namespace Components { ++namespace Dumux::Components { + + /*! + * \ingroup Components +@@ -52,7 +51,5 @@ + + }; + +-} // end namespace Components +-} // end namespace Dumux +- ++} // end namespace Dumux::Components + #endif +diff -ruN exercises/exercise-biomineralization/fluidmatrixinteractions/permeabilitypowerlaw.hh exercises/solution/exercise-biomineralization/fluidmatrixinteractions/permeabilitypowerlaw.hh +--- exercises/exercise-biomineralization/fluidmatrixinteractions/permeabilitypowerlaw.hh 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-biomineralization/fluidmatrixinteractions/permeabilitypowerlaw.hh 2023-04-26 14:37:49.789150797 +0200 +@@ -52,10 +52,11 @@ + using std::pow; + // TODO: dumux-course-task + // read the exponent for the power law from the input file ++ const Scalar exponent = getParam<Scalar>("PowerLaw.Exponent", 5.0); + + // TODO: dumux-course-task + // return the updated permeability according to K=K_0*(poro/refPoro)^exponent +- return refPerm; ++ return refPerm * pow((poro)/(refPoro), exponent); + } + }; + +diff -ruN exercises/exercise-biomineralization/fluidsystems/biomin.hh exercises/solution/exercise-biomineralization/fluidsystems/biomin.hh +--- exercises/exercise-biomineralization/fluidsystems/biomin.hh 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-biomineralization/fluidsystems/biomin.hh 2023-04-26 14:37:49.789150797 +0200 +@@ -231,7 +231,7 @@ + static void init() + { + init(/*startTemp=*/295.15, /*endTemp=*/305.15, /*tempSteps=*/10, +- /*startPressure=*/1e4, /*endPressure=*/1e6, /*pressureSteps=*/200); ++ /*startPressure=*/1e4, /*endPressure=*/1e6, /*pressureSteps=*/2000); + + } + +diff -ruN exercises/exercise-biomineralization/params.input exercises/solution/exercise-biomineralization/params.input +--- exercises/exercise-biomineralization/params.input 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-biomineralization/params.input 2023-04-26 14:37:49.789150797 +0200 +@@ -16,10 +16,10 @@ + InitBiofilm = 0.05 # [-] initial volumefraction biofilm + + [Injection] +-InjVolumeflux = 1e-5 # [m³/s] = [ml/min] /[s/min] /[ml/m³] +-InjBioTime = 10 # [days] ++InjVolumeflux = 1e-4 # [m³/s] = [ml/min] /[s/min] /[ml/m³] ++InjBioTime = 12 # [days] + InjCO2 = 1e-2 # [kg/s] gasFlux +-InjCO2Time = 4 # [days] duration of CO2 injection until the end of the simulation ++InjCO2Time = 4 # [days] + ConcCa = 40 # [kg/m³] injected calcium concentration (max: 50) + ConcUrea = 60 # [kg/m³] injected urea concentration (max: 80) + +@@ -35,7 +35,6 @@ + Aquitard.Swr = 0.0 + Aquitard.Snr = 0.0 + +- + [BioCoefficients] + RhoBiofilm = 6.9 # [kg/m³] density of biofilm + +@@ -48,4 +47,6 @@ + Salinity = 0.1 + + #TODO: dumux-course-task +-# add the power law's exponent parameter PowerLaw.Exponent = 5.0 ++# add the power law's exponent parameter PowerLaw.Exponent = 5 ++[PowerLaw] ++Exponent = 5.0 +diff -ruN exercises/exercise-biomineralization/properties.hh exercises/solution/exercise-biomineralization/properties.hh +--- exercises/exercise-biomineralization/properties.hh 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-biomineralization/properties.hh 2023-04-26 14:37:49.789150797 +0200 +@@ -30,17 +30,20 @@ + #include <dumux/discretization/cctpfa.hh> + #include <dumux/porousmediumflow/2pncmin/model.hh> + #include <dumux/porousmediumflow/problem.hh> +-#include <dumux/material/components/simpleco2.hh> //!< Simplified CO2 component based on ideal gas law + // TODO: dumux-course-task + // include the CO2 component and tabulated values from DuMux +-#include "solidsystems/biominsolidphase.hh" // The biomineralization solid system ++#include <dumux/material/components/co2.hh> //!< CO2 component for use with tabulated values ++#include <dumux/material/components/defaultco2table.hh> //! Provides the precalculated tabulated values of CO2 density and enthalpy. + ++#include "solidsystems/biominsolidphase.hh" // The biomineralization solid system + #include "fluidsystems/biomin.hh" // The biomineralization fluid system + + #include "biominspatialparams.hh" // Spatially dependent parameters + #include "biominproblem.hh" + + namespace Dumux { ++ ++ + namespace Properties { + + //! Create new type tag for the problem +@@ -66,7 +69,7 @@ + using Scalar = GetPropType<TypeTag, Properties::Scalar>; + // TODO: dumux-course-task + // use the CO2 component with tabulated values +- using CO2Impl = Components::SimpleCO2<Scalar>; ++ using CO2Impl = Components::CO2<Scalar, GeneratedCO2Tables::CO2Tables>; + using H2OType = Components::TabulatedComponent<Components::H2O<Scalar>>; + public: + using type = FluidSystems::BioMin<Scalar, CO2Impl, H2OType>; +diff -ruN exercises/exercise-biomineralization/README.md exercises/solution/exercise-biomineralization/README.md +--- exercises/exercise-biomineralization/README.md 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-biomineralization/README.md 1970-01-01 01:00:00.000000000 +0100 +@@ -1,245 +0,0 @@ +-# Exercise Biomineralization (DuMuX Course) +- +-The aim of this exercise is to get a first glimpse at the _DuMu<sup>x</sup>_ way of implementing mineralization and reaction processes. In the scope of this exercise, the setting of boundary conditions is revisited and a new reaction term is implemented. +- +-## Problem set-up +- +-The domain has a size of 20 x 15 m and contains a sealing aquitard in the middle. The aquitard is interrupted by a "fault zone" and thereby connects the upper drinking water aquifer and the lower CO<sub>2<sub>-storage aquifer. Initially, the domain is fully saturated with water and biofilm is present in the lower CO<sub>2<sub>-storage aquifer. Calcium and urea are injected in the upper drinking water aquifer by means of a Neumann boundary condition. The remaining parts of the upper boundary and the entire lower boundary are modelled with Neumann no-flow conditions, while on the right-hand side a Dirichlet boundary conditions is assigned, which fixes there the the initial values. +- +- +-Disclaimer: Please note, that this is not a realistic scenario. No one would think of storing gaseous CO<sub>2<sub> in this subcritical setting. +- +- +- +- +-## Preparing the exercise +- +-* Navigate to the directory `dumux-course/exercises/exercise-biomineralization` +- +-### 1. Getting familiar with the code +- +-Locate all the files you will need for this exercise +-* The __main file__ : `main.cc` +-* The __input file__: `params.input` +-* The __problem file__ : `biominproblem.hh` +-* The __properties file__: `properties.hh` +-* The __spatial parameters file__: `biominspatialparams.hh` +- +-Furthermore you will find the following folders: +-* `chemistry`: Provides a way to formulate reactions via source/sink terms. +-* `components`: Provides some additional components, e.g. biofilm and urea. +-* `fluidsystems`: Stores headers containing data/methods on fluid mixtures of pure components. +-* `fluidmatrixinteractions`: Stores headers containing methods on fluid-solid interactions, i.e. the permeability law. +-* `solidsystems`: Stores headers containing data/methods on solid mixtures of the components. +- +-To find out more on chemistry, components, fluidsystems, fluidmatrixinteractions, and solidsystems implementations, you may have a look at the folder `dumux/material`. +- +-__Special note on solidsystems:__ +-There are two types of solid components: reactive and inert. For each reactive component, one mass balance is solved. The inert components compose the "unchanging" (inert) rock matrix. +- +-### 2. Implement a chemical equation +- +-In the following, the basic steps required to set the new chemical equation are outlined. Here, this is done in the __chemistry__ folder in the prepared file: `simplebiominreactions.hh` within the function ``reactionSource()``. +-Please be aware, that the chemistry file already provides some convenience functions (e.g. ``moleFracToMolality()``). +- +-__Task__ +- +-Add a kinetic reaction by first calculating the current mass of available biofilm. Note that the density and volume fraction of biofilm are already defined for you. +- +-$`\displaystyle mass_{biofilm} = \rho_{biofilm} * \phi_{biofilm}`$ +- +-Next, we want to implement the rate of ureolysis. This can be done with the following simplified equation: +- +-$`\displaystyle r_{urea} = k_{urease} * Z_{urease,biofilm} * m_{urea} / (K_{urea} + m_{urea})`$, +- +-where $`\displaystyle r_{urea}`$ is the rate of ureolysis, $`\displaystyle k_{urease}`$ the urease enzyme activity, $`\displaystyle m_{urea}`$ the molality of urea and $`\displaystyle K_{urea}`$ the half-saturation constant for urea for the ureolysis rate. +- +-Note, that the urease concentration $`\displaystyle Z_{urease,biofilm}`$ has a kinetic term of urease production per biofilm : +- +-$`\displaystyle Z_{urease,biofilm} = k_{urease,biofilm} * mass_{biofilm}`$ +- +-The last step is defining the source term for each component according to the chemical reaction equations: +- +-$`\displaystyle \mathrm{CO(NH_{2})_{2} + 2 H_{2}O \rightarrow 2 NH_{3} + H_{2}CO_{3}}`$ +- +-$`\displaystyle \mathrm{Ca^{2+} + CO_{3}^{2-} \rightarrow CaCO_{3}}`$ +- +-Alternatively, it can be written in terms of the total chemical reaction equation, in which the appearance of inorganic carbon species cancels out: +- +-$`\displaystyle \mathrm{Ca^{2+} + CO(NH_{2})_{2} + 2 H_{2}O \rightarrow 2 NH_{4}^{+} + CaCO_{3}}`$ +- +-which, written in terms of our primary variables are: +- +-Urea + 2 Water → (2 Ammonia) + Total Carbon +- +-Calcium ion + Total Carbon → Calcite +- +-Calcium ion + Urea + 2 Water → (2 Ammonium ions) + Calcite +- +-Note that for the sake of having a simplified chemistry for this dumux-course example, the component Ammonium is not considered as part of the reaction. Thus, you cannot set its source term, even though it is produced in the real reaction. +-Similarly, we only account for "Total Carbon", which is the sum of all carbon species +-($`\mathrm{CO_{2}}`$, $`\mathrm{H_{2}CO_{3}}`$, $`\mathrm{HCO_{3}^{-}}`$, and $`\mathrm{CO_{3}^{2-}}`$). +-Further, we assume that the overall reaction has reached an equilibrium state, i.e. every mole of urea hydrolyzed will lead to a mole of Calcite precipitating, and thus the precipitation rate simplifies to $`\displaystyle r_{prec} = r_{urea}`$. +-In reality, the initial geochemistry might be far away from conditions at which Calcite precipitates, e.g. due to low pH values at which the "Total Carbon" is mainly present as bicarbonate, $`\mathrm{HCO_{3}^{-}}`$, not taking part in the Calcite precipitation reaction. +-To reach the overall reaction's equilibrium state, the pH value needs to be increased first by ureolysis. +-However, to calculate the detailed precipitation rate of Calcite, we would first need to determine how much of the aggregate species "Total Carbon" is present in the form of each of its sub species, $`\mathrm{CO_{2}}`$, $`\mathrm{H_{2}CO_{3}}`$, $`\mathrm{HCO_{3}^{-}}`$, and $`\mathrm{CO_{3}^{2-}}`$. +-Further, we would need to account for all involved complex aqueous geochemistry to be able to determine the activities of both Calcium and Carbonate ions, which also impact the precipitation rate. +-We feel that this very specific chemistry goes beyond what is necessary for this dumux-course exercise and simplify the chemistry also with the motivation to save on the run time, which accounting for the detailed, complex geochemistry would increase significantly. +-The assumption of the overall reaction being at an equilibrium is used in many models for biomineralization. +- +-### 3. Make use of your newly created chemical equation +- +-To employ your newly created chemical equation, the chemistry file has to be included in your problem file. +- +-```c++ +-#include "chemistry/simplebiominreactions.hh" // chemical reactions +-``` +- +-Additionally the TypeTag of your chemistry file needs to be set in the problem file, within the class ``BioMinProblem``: +- +-```c++ +-using Chemistry = typename Dumux::SimpleBiominReactions<NumEqVector, VolumeVariables>; +-``` +- +-__Task__ +- +-Now, the source/sink term can be updated in the problem file in its function ``source()``. You can access the newly created chemistry file and call the ``reactionSource()``-function from it. Make sure to call the ``chemistry.reactionSource()``-function with the correct arguments. Return the updated source terms in the end. +-The volume variables can be set using the element volume variables and the sub control volume: +- +-```c++ +-const auto& volVars = elemVolVars[scv]; +-``` +- +-In order to compile and execute the program, change to the build-directory +- +-```bash +-cd build-cmake/exercises/exercise-biomineralization +-``` +- +-and type +- +-```bash +-make exercise_biomin +-./exercise_biomin +-``` +- +-### 4. Seal leakage pathway in the aquitard +- +- In the input file, you will find some parameters concerning the mineralization process. We want to seal the leakage pathway in the aquitard. The leakage pathway is assumed to be sealed when the porosity is reduced to `0.07` or less. +- +- __Task:__ +- +- Vary input parameters in the input file to seal the leakage pathway. The overall injection duration in days (`InjBioTime`), the initial biomass (`InitBiofilm`), the overall injection rate (`InjVolumeflux`) and the injected concentrations of urea and calcium (`ConcUrea` and `ConcCa`) are available for variation. When changing the concentrations, keep in mind that both urea and calcium are needed for the reaction and their mass ratio should be 2 calcium to 3 urea for a good molar ratio of 1 mol urea per 1 mol of calcium, see also the molar masses in the component files. +- +- The result for the porosity should look like this: +- +-  +- +-### 5. CO<sub>2</sub> injection to test aquitard integrity +- +-Now, the sealed aquitard is tested with a CO<sub>2<sub>-Injection into the lower CO<sub>2<sub>-storage aquifer. +- +-__Task:__ +- +-Implement a new boundary condition on the left boundary, injecting CO<sub>2<sub> from 2 m to 3 m from the bottom. Make sure, that the injection time for the calcium and urea is finished. You can use the predefined value `gasFlux` directly and divide it by the molar mass of CO<sub>2<sub>. +-Run two simulations and compare them side by side by creating two input files, or overwriting the input file in the command line: +-```bash +-./exercise_biomin -Problem.Name biominNoUrea -Injection.ConcUrea 0 +-``` +-The result for the biomineralization process during the CO<sub>2<sub> injection should look like this: +- +- +- +-### 6. Change the permeability law +- +-Now, we want to change the way the change in permeability due to the precipitation is calculated. While the initially used Kozeny-Carman relation is widely used, another common relation is the so-called Power Law. +- +-__Task:__ +- +-Implement the Power-Law relation to create a new permeability law. +-For this, you can copy the existing header `dumux/material/fluidmatrixinteractions/permeabilitykozenycarman.hh` to the folder `fluidmatrixinteractions` in the exercise, rename it and within the file adapt the guarding macro, the class name, and the calculations for updating the permeability. +-Alternatively, you can work with the pre-prepared header `permeabilitypowerlaw.hh` in the folder `fluidmatrixinteractions`, in which only the calculations for updating the permeability are left to be modified. +-The equation of the Power Law is defined as: +- +-$`\displaystyle K = K_0 \left(\frac{\phi}{\phi_0}\right)^\eta`$ +- +-```c++ +- const Scalar exponent = getParam<Scalar>("PowerLaw.Exponent", 5.0); +-``` +- +-```c++ +- const Scalar factor = pow(poro/refPoro, exponent); +-``` +- +-As a special feature, we would like the exponent $`\displaystyle \eta=5`$ to be a run-time parameter read from the input file, as this allows easy modification of the parameter and potentially fit it. +-This is useful, as field-scale porosity-permeability relations might be quite uncertain. +-Adapt the input file `params.input` accordingly. +- +-Finally, the header `permeabilitypowerlaw.hh` needs to be included in the spatial parameters header `biominspatialparams.hh` and the permeability law set to the new implementation of the Power Law. +- +-```c++ +- PermeabilityPowerLaw<PermeabilityType> permLaw_; +-``` +- +-Note: As both the Kozeny-Carman and the Power-Law relation use the same parameters, there is no need to change the permeability function calling `evaluatePermeability(refPerm, refPoro, poro)` in `biominspatialparams.hh`: +- +-```c++ +- template<class ElementSolution> +- PermeabilityType permeability(const Element& element, +- const SubControlVolume& scv, +- const ElementSolution& elemSol) const +- { +- const auto refPerm = referencePermeability(element, scv); +- const auto refPoro = referencePorosity(element, scv); +- const auto poro = porosity(element, scv, elemSol); +- return permLaw_.evaluatePermeability(refPerm, refPoro, poro); +- } +-``` +- +-What is the effect of the exchanged permeability calculation on the results, especially the leakage of CO<sub>2<sub>? What if the exponent would be smaller, e.g. $`\displaystyle \eta=2`$, which would mean that the precipitation is less efficient in sealing the leakage? +-You can again run two simulations and compare them side by side by creating two input files, or overwriting the input file parameter in the command line: +-```bash +-./exercise_biomin -Problem.Name biominPowerLawExponent2 -PowerLaw.Exponent 2.0 +-``` +- +-### 7. Use tabulated CO<sub>2</sub> values instead of SimpleCO2 +- +-So far we have been using a simplified component for CO<sub>2</sub>, which is based on the ideal gas law. Due to the conditions present in this exercise this is not too inaccurate, but for real applications of CO<sub>2<sub> storage changes to the model are required. We use tabulated data for density and enthalpy of CO<sub>2<sub>, accessed through `GeneratedCO2Tables::CO2Tables` and `Components::CO2` from DuMu<sup>x</sup>. +- +-__Task:__ +- +-The CO<sub>2<sub> component is used in the fluidsystem, which is defined in `properties.hh`. Replace the component `SimpleCO2` with `CO2` defined in `dumux/material/components/co2.hh`, with a CO<sub>2<sub> table as an additional template parameter. Use the the table defined in `dumux/material/components/defaultco2table.hh`, noting the different namespace. Take care to include the appropriate headers. +- +-```c++ +-#include <dumux/material/components/co2.hh> //!< CO2 component for use with tabulated values +-#include <dumux/material/components/defaultco2table.hh> //!< Provides the precalculated tabulated values of CO2 density and enthalpy. +-``` +- +-```c++ +-template<class TypeTag> +-struct FluidSystem<TypeTag, TTag::ExerciseBioMin> +-{ +-private: +- using Scalar = GetPropType<TypeTag, Properties::Scalar>; +- using CO2Impl = Components::CO2<Scalar, GeneratedCO2Tables::CO2Tables>; +- using H2OType = Components::TabulatedComponent<Components::H2O<Scalar>>; +-public: +- using type = FluidSystems::BioMin<Scalar, CO2Impl, H2OType>; +-}; +-``` +- +-### Bonus: Paraview Magic: Compare different results using Programmable Filter +- +-In the last step, the manual comparison of the results can be quite difficult. Paraview offers the option to use programmable python filters. To use them, make sure that two result files with __different names__ are loaded. Mark both of them and click on `Filters --> Alphabetical --> Programmable Filter`. Now, a new field opens on the left side. Copy the following lines there: +- +-```python +-S_gas_0 = inputs[0].CellData['S_gas']; +-S_gas_1 = inputs[1].CellData['S_gas']; +-output.CellData.append(abs(S_gas_0-S_gas_1),'diffS_gas'); +-``` +- +-Click `Apply` and select `diffS_gas` as new output. You should now see the difference between the two result files. You can also change the output to a not absolute value by changing the last line to: +- +-```python +-output.CellData.append((S_gas_0-S_gas_1),'diffS_gas'); +-``` +diff -ruN exercises/exercise-biomineralization/solidsystems/biominsolidphase.hh exercises/solution/exercise-biomineralization/solidsystems/biominsolidphase.hh +--- exercises/exercise-biomineralization/solidsystems/biominsolidphase.hh 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-biomineralization/solidsystems/biominsolidphase.hh 2023-04-26 14:37:49.789150797 +0200 +@@ -27,12 +27,12 @@ + #include <string> + #include <dune/common/exceptions.hh> + +-#include "../components/biofilm.hh" ++#include <dumux/common/parameters.hh> + #include <dumux/material/components/calcite.hh> + #include <dumux/material/components/granite.hh> ++#include "../components/biofilm.hh" + +-namespace Dumux { +-namespace SolidSystems { ++namespace Dumux::SolidSystems { + + /*! + * \ingroup SolidSystems +@@ -195,7 +195,6 @@ + + }; + +-} // end namespace SolidSystems +-} // end namespace Dumux ++} // end namespace Dumux::SolidSystems + + #endif diff --git a/.patches/exercise-coupling-ff-pm/exercise-coupling-ff-pm.patch b/.patches/exercise-coupling-ff-pm/exercise-coupling-ff-pm.patch new file mode 100644 index 00000000..e13d4e57 --- /dev/null +++ b/.patches/exercise-coupling-ff-pm/exercise-coupling-ff-pm.patch @@ -0,0 +1,1399 @@ +diff -ruN exercises/exercise-coupling-ff-pm/interface/CMakeLists.txt exercises/solution/exercise-coupling-ff-pm/interface/CMakeLists.txt +--- exercises/exercise-coupling-ff-pm/interface/CMakeLists.txt 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-coupling-ff-pm/interface/CMakeLists.txt 2023-04-26 14:37:49.789150797 +0200 +@@ -1,7 +1,27 @@ +-# executables for ex_interface_coupling_ff-pm +-dumux_add_test(NAME exercise_interface_coupling_ff-pm ++dumux_add_test(NAME exercise_interface_coupling_ff-pm_original + SOURCES main.cc +- LABELS ffpm) ++ LABELS ffpm ++ COMPILE_DEFINITIONS EXNUMBER=0) ++ ++dumux_add_test(NAME exercise_interface_coupling_ff-pm_a_solution ++ SOURCES main.cc ++ LABELS ffpm ++ COMPILE_DEFINITIONS EXNUMBER=1) ++ ++dumux_add_test(NAME exercise_interface_coupling_ff-pm_b_solution ++ SOURCES main.cc ++ LABELS ffpm ++ COMPILE_DEFINITIONS EXNUMBER=2) ++ ++dumux_add_test(NAME exercise_interface_coupling_ff-pm_c_solution ++ SOURCES main.cc ++ LABELS ffpm ++ COMPILE_DEFINITIONS EXNUMBER=3) ++ ++dumux_add_test(NAME exercise_interface_coupling_ff-pm_extra_solution ++ SOURCES main.cc ++ LABELS ffpm ++ COMPILE_DEFINITIONS EXNUMBER=4) + + # add a symlink for each input file + add_input_file_links() +diff -ruN exercises/exercise-coupling-ff-pm/interface/freeflowsubproblem.hh exercises/solution/exercise-coupling-ff-pm/interface/freeflowsubproblem.hh +--- exercises/exercise-coupling-ff-pm/interface/freeflowsubproblem.hh 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-coupling-ff-pm/interface/freeflowsubproblem.hh 2023-06-01 13:25:59.349831633 +0200 +@@ -83,8 +83,7 @@ + + const auto& globalPos = scvf.dofPosition(); + +- // TODO: dumux-course-task 1.A +- // Change the boundary conditions here as described in the exercise ++#if EXNUMBER == 0 // flow from top to bottom + if(onUpperBoundary_(globalPos)) + { + values.setDirichlet(Indices::velocityXIdx); +@@ -97,20 +96,45 @@ + values.setDirichlet(Indices::velocityXIdx); + values.setDirichlet(Indices::velocityYIdx); + } ++#elif EXNUMBER < 4 // flow flom left to right ++ if(onLeftBoundary_(globalPos) || onRightBoundary_(globalPos)) ++ values.setDirichlet(Indices::pressureIdx); ++ else ++ { ++ values.setDirichlet(Indices::velocityXIdx); ++ values.setDirichlet(Indices::velocityYIdx); ++ } ++#else ++ if (onRightBoundary_(globalPos)) ++ values.setDirichlet(Indices::pressureIdx); ++ else ++ { ++ values.setDirichlet(Indices::velocityXIdx); ++ values.setDirichlet(Indices::velocityYIdx); ++ } ++#endif + + // coupling interface + if(couplingManager().isCoupledEntity(CouplingManager::stokesIdx, scvf)) + { + values.setCouplingNeumann(Indices::conti0EqIdx); ++#if EXNUMBER < 3 + values.setCouplingNeumann(Indices::momentumYBalanceIdx); +- // TODO: dumux-course-task 1.B +- // Replace Dirichlet BC with Beavers-Joseph-Saffman slip condition for the tangential momentum balance +- values.setDirichlet(Indices::velocityXIdx); // assume no slip on interface ++#else ++ //consider orientation of face automatically ++ values.setCouplingNeumann(scvf.directionIndex()); ++#endif + +- // TODO: dumux-course-task 1.C ++#if EXNUMBER < 2 ++ values.setDirichlet(Indices::velocityXIdx); // assume no slip on interface ++#elif EXNUMBER == 2 ++ // set the Beaver-Joseph-Saffman slip condition for the tangential momentum balance equation ++ values.setBeaversJoseph(Indices::momentumXBalanceIdx); ++#else + // set the Beaver-Joseph-Saffman slip condition for the tangential momentum balance equation, + // consider orientation of face automatically +- ++ values.setBeaversJoseph(1 - scvf.directionIndex()); ++#endif + } + + return values; +@@ -150,9 +174,13 @@ + if(couplingManager().isCoupledEntity(CouplingManager::stokesIdx, scvf)) + { + values[Indices::conti0EqIdx] = couplingManager().couplingData().massCouplingCondition(element, fvGeometry, elemVolVars, elemFaceVars, scvf); ++#if EXNUMBER < 3 + values[Indices::momentumYBalanceIdx] = couplingManager().couplingData().momentumCouplingCondition(element, fvGeometry, elemVolVars, elemFaceVars, scvf); +- } ++#else ++ values[scvf.directionIndex()] = couplingManager().couplingData().momentumCouplingCondition(element, fvGeometry, elemVolVars, elemFaceVars, scvf); ++#endif + ++ } + return values; + } + +@@ -172,10 +200,18 @@ + PrimaryVariables initialAtPos(const GlobalPosition &globalPos) const + { + PrimaryVariables values(0.0); ++#if EXNUMBER == 0 + values[Indices::velocityYIdx] = -1e-6 * globalPos[0] * (this->gridGeometry().bBoxMax()[0] - globalPos[0]); +- // TODO: dumux-course-task 1.A +- // Set fixed pressures on the left and right boundary +- ++#elif EXNUMBER == 4 ++ values[Indices::velocityXIdx] = 1e-6 * (globalPos[1] - this->gridGeometry().bBoxMin()[1]) ++ * (this->gridGeometry().bBoxMax()[1] - globalPos[1]); ++#else ++ // set fixed pressures on the left and right boundary ++ if(onLeftBoundary_(globalPos)) ++ values[Indices::pressureIdx] = deltaP_; ++ if(onRightBoundary_(globalPos)) ++ values[Indices::pressureIdx] = 0.0; ++#endif + return values; + } + +diff -ruN exercises/exercise-coupling-ff-pm/interface/main.cc exercises/solution/exercise-coupling-ff-pm/interface/main.cc +--- exercises/exercise-coupling-ff-pm/interface/main.cc 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-coupling-ff-pm/interface/main.cc 2023-06-01 13:25:59.349831633 +0200 +@@ -64,10 +64,8 @@ + using FreeflowTypeTag = Properties::TTag::FreeflowOneP; + using PorousMediumTypeTag = Properties::TTag::PorousMediumFlowOneP; + +- //TODO: dumux-course-task 1.C +- // ******************** comment-out this section for the last exercise **************** // +- +- // create two individual grids (from the given grid file or the input file) ++#if EXNUMBER < 3 ++ // try to create a grid (from the given grid file or the input file) + // for both sub-domains + using PorousMediumGridManager = Dumux::GridManager<GetPropType<PorousMediumTypeTag, Properties::Grid>>; + PorousMediumGridManager porousMediumGridManager; +@@ -80,57 +78,51 @@ + // we compute on the leaf grid view + const auto& porousMediumGridView = porousMediumGridManager.grid().leafGridView(); + const auto& freeflowGridView = freeflowGridManager.grid().leafGridView(); ++#else ++ // use dune-subgrid to create the individual grids ++ static constexpr int dim = 2; ++ using HostGrid = Dune::YaspGrid<2, Dune::TensorProductCoordinates<double, dim> >; ++ using HostGridManager = Dumux::GridManager<HostGrid>; ++ HostGridManager hostGridManager; ++ hostGridManager.init(); ++ auto& hostGrid = hostGridManager.grid(); ++ ++ struct Params ++ { ++ double amplitude = getParam<double>("Grid.Amplitude"); ++ double baseline = getParam<double>("Grid.Baseline"); ++ double offset = getParam<double>("Grid.Offset"); ++ double scaling = getParam<double>("Grid.Scaling"); ++ }; ++ ++ Params params; ++ ++ auto elementSelectorFreeflow = [&](const auto& element) ++ { ++ double interface = params.amplitude * std::sin(( element.geometry().center()[0] -params.offset) / params.scaling * 2.0 * M_PI) + params.baseline; ++ return element.geometry().center()[1] > interface; ++ }; ++ ++ auto elementSelectorPorousMedium = [&](const auto& element) ++ { ++ double interface = params.amplitude * std::sin(( element.geometry().center()[0] - params.offset) / params.scaling * 2.0 * M_PI) + params.baseline; ++ return element.geometry().center()[1] < interface; ++ }; ++ ++ using SubGrid = Dune::SubGrid<dim, HostGrid>; ++ ++ Dumux::GridManager<SubGrid> subGridManagerFreeflow; ++ Dumux::GridManager<SubGrid> subGridManagerPorousMedium; ++ ++ // initialize subgrids ++ subGridManagerFreeflow.init(hostGrid, elementSelectorFreeflow, "Freeflow"); ++ subGridManagerPorousMedium.init(hostGrid, elementSelectorPorousMedium, "PorousMedium"); + +- // ************************************************************************************ // +- +- +- // ******************** uncomment this section for the last exercise ****************** // +- +-// // use dune-subgrid to create the individual grids +-// static constexpr int dim = 2; +-// using HostGrid = Dune::YaspGrid<2, Dune::TensorProductCoordinates<double, dim> >; +-// using HostGridManager = Dumux::GridManager<HostGrid>; +-// HostGridManager hostGridManager; +-// hostGridManager.init(); +-// auto& hostGrid = hostGridManager.grid(); +-// +-// struct Params +-// { +-// double amplitude = getParam<double>("Grid.Amplitude"); +-// double baseline = getParam<double>("Grid.Baseline"); +-// double offset = getParam<double>("Grid.Offset"); +-// double scaling = getParam<double>("Grid.Scaling"); +-// }; +-// +-// Params params; +-// +-// auto elementSelectorFreeflow = [&](const auto& element) +-// { +-// double interface = params.amplitude * std::sin(( element.geometry().center()[0] -params.offset) / params.scaling * 2.0 * M_PI) + params.baseline; +-// return element.geometry().center()[1] > interface; +-// }; +-// +-// auto elementSelectorPorousMedium = [&](const auto& element) +-// { +-// double interface = params.amplitude * std::sin(( element.geometry().center()[0] - params.offset) / params.scaling * 2.0 * M_PI) + params.baseline; +-// return element.geometry().center()[1] < interface; +-// }; +-// +-// using SubGrid = Dune::SubGrid<dim, HostGrid>; +-// +-// Dumux::GridManager<SubGrid> subGridManagerFreeflow; +-// Dumux::GridManager<SubGrid> subGridManagerPorousMedium; +-// +-// // initialize subgrids +-// subGridManagerFreeflow.init(hostGrid, elementSelectorFreeflow, "Freeflow"); +-// subGridManagerPorousMedium.init(hostGrid, elementSelectorPorousMedium, "PorousMedium"); +-// +-// // we compute on the leaf grid view +-// const auto& porousMediumGridView = subGridManagerPorousMedium.grid().leafGridView(); +-// const auto& freeflowGridView = subGridManagerFreeflow.grid().leafGridView(); +- +- // ************************************************************************************ // ++ // we compute on the leaf grid view ++ const auto& porousMediumGridView = subGridManagerPorousMedium.grid().leafGridView(); ++ const auto& freeflowGridView = subGridManagerFreeflow.grid().leafGridView(); + ++#endif + + // create the finite volume grid geometry + using FreeflowFVGridGeometry = GetPropType<FreeflowTypeTag, Properties::GridGeometry>; +@@ -183,9 +175,10 @@ + StaggeredVtkOutputModule<FreeflowGridVariables, decltype(freeflowSol)> freeflowVtkWriter(*freeflowGridVariables, freeflowSol, freeflowName); + GetPropType<FreeflowTypeTag, Properties::IOFields>::initOutputModule(freeflowVtkWriter); + +- //TODO: dumux-course-task 1.B +- //****** uncomment the add analytical solution of v_x *****// +- // freeflowVtkWriter.addField(freeflowProblem->getAnalyticalVelocityX(), "analyticalV_x"); ++#if EXNUMBER >= 2 ++ freeflowVtkWriter.addField(freeflowProblem->getAnalyticalVelocityX(), "analyticalV_x"); ++#endif ++ + freeflowVtkWriter.write(0.0); + + using PorousMediumSolutionVector = GetPropType<PorousMediumTypeTag, Properties::SolutionVector>; +diff -ruN exercises/exercise-coupling-ff-pm/interface/porousmediumsubproblem.hh exercises/solution/exercise-coupling-ff-pm/interface/porousmediumsubproblem.hh +--- exercises/exercise-coupling-ff-pm/interface/porousmediumsubproblem.hh 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-coupling-ff-pm/interface/porousmediumsubproblem.hh 2023-06-01 13:25:59.349831633 +0200 +@@ -80,13 +80,13 @@ + // set Neumann BCs to all boundaries first + values.setAllNeumann(); + +- // set the coupling boundary condition at the interface +- if (couplingManager().isCoupledEntity(CouplingManager::darcyIdx, scvf)) +- values.setAllCouplingNeumann(); +- +- // set a Dirichlet boundary condition at the bottom ++#if EXNUMBER == 0 // flow from top to bottom + if (onLowerBoundary_(scvf.center())) + values.setAllDirichlet(); ++#endif ++ ++ if (couplingManager().isCoupledEntity(CouplingManager::darcyIdx, scvf)) ++ values.setAllCouplingNeumann(); + + return values; + } +diff -ruN exercises/exercise-coupling-ff-pm/interface/properties.hh exercises/solution/exercise-coupling-ff-pm/interface/properties.hh +--- exercises/exercise-coupling-ff-pm/interface/properties.hh 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-coupling-ff-pm/interface/properties.hh 2023-06-01 13:25:59.349831633 +0200 +@@ -28,9 +28,9 @@ + #include <dumux/multidomain/staggeredtraits.hh> + #include <dumux/multidomain/boundary/stokesdarcy/couplingmanager.hh> + +-// TODO: dumux-course-task 1.C +-//****** uncomment for the last exercise *****// +-// #include <dumux/io/grid/gridmanager_sub.hh> ++#if EXNUMBER >= 3 ++#include <dumux/io/grid/gridmanager_sub.hh> ++#endif + + #include <dumux/material/fluidsystems/1pliquid.hh> + #include <dumux/material/components/simpleh2o.hh> +@@ -98,13 +98,12 @@ + using Scalar = GetPropType<TypeTag, Properties::Scalar>; + using TensorGrid = Dune::YaspGrid<2, Dune::TensorProductCoordinates<Scalar, dim> >; + +-// TODO: dumux-course-task 1.C +-//****** comment out for the last exercise *****// ++#if EXNUMBER < 3 // use "normal" grid + using type = TensorGrid; +- +-//****** uncomment for the last exercise *****// +- // using HostGrid = TensorGrid; +- // using type = Dune::SubGrid<dim, HostGrid>; ++#else // use dune-subgrid ++ using HostGrid = TensorGrid; ++ using type = Dune::SubGrid<dim, HostGrid>; ++#endif + }; + template<class TypeTag> + struct Grid<TypeTag, TTag::FreeflowOneP> +@@ -113,13 +112,12 @@ + using Scalar = GetPropType<TypeTag, Properties::Scalar>; + using TensorGrid = Dune::YaspGrid<2, Dune::TensorProductCoordinates<Scalar, dim> >; + +-// TODO: dumux-course-task 1.C +-//****** comment out for the last exercise *****// ++#if EXNUMBER < 3 // use "normal" grid + using type = TensorGrid; +- +-//****** uncomment for the last exercise *****// +- // using HostGrid = TensorGrid; +- // using type = Dune::SubGrid<dim, HostGrid>; ++#else // use dune-subgrid ++ using HostGrid = TensorGrid; ++ using type = Dune::SubGrid<dim, HostGrid>; ++#endif + }; + + template<class TypeTag> +diff -ruN exercises/exercise-coupling-ff-pm/models/CMakeLists.txt exercises/solution/exercise-coupling-ff-pm/models/CMakeLists.txt +--- exercises/exercise-coupling-ff-pm/models/CMakeLists.txt 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-coupling-ff-pm/models/CMakeLists.txt 2023-04-26 14:37:49.789150797 +0200 +@@ -1,6 +1,29 @@ +-# executables for ex_interface_coupling_ff-pm +-dumux_add_test(NAME exercise_models_coupling_ff-pm ++dumux_add_test(NAME exercise_models_coupling_ff-pm_original + SOURCES main.cc +- LABELS ffpm) ++ LABELS ffpm ++ COMPILE_DEFINITIONS EXNUMBER=0 ++ COMMAND ${CMAKE_CURRENT_BINARY_DIR}/exercise_models_coupling_ff-pm_original ++ CMD_ARGS -Problem.ExportStorage false -Problem.PlotStorage 0 -Problem.ExportFluxes 0) ++ ++dumux_add_test(NAME exercise_models_coupling_ff-pm_a_solution ++ SOURCES main.cc ++ LABELS ffpm ++ COMPILE_DEFINITIONS EXNUMBER=1 ++ COMMAND ${CMAKE_CURRENT_BINARY_DIR}/exercise_models_coupling_ff-pm_a_solution ++ CMD_ARGS -Problem.ExportStorage false -Problem.PlotStorage 0 -Problem.ExportFluxes 0) ++ ++dumux_add_test(NAME exercise_models_coupling_ff-pm_b_solution ++ SOURCES main.cc ++ LABELS ffpm ++ COMPILE_DEFINITIONS EXNUMBER=2 ++ COMMAND ${CMAKE_CURRENT_BINARY_DIR}/exercise_models_coupling_ff-pm_b_solution ++ CMD_ARGS -Problem.ExportStorage false -Problem.PlotStorage 0 -Problem.ExportFluxes 0) ++ ++dumux_add_test(NAME exercise_models_coupling_ff-pm_c_solution ++ SOURCES main.cc ++ LABELS ffpm ++ COMPILE_DEFINITIONS EXNUMBER=3 ++ COMMAND ${CMAKE_CURRENT_BINARY_DIR}/exercise_models_coupling_ff-pm_c_solution ++ CMD_ARGS -Problem.ExportStorage false -Problem.PlotStorage 0 -Problem.ExportFluxes 0) + + dune_symlink_to_source_files(FILES "params.input" plotFluxes.py) +diff -ruN exercises/exercise-coupling-ff-pm/models/main.cc exercises/solution/exercise-coupling-ff-pm/models/main.cc +--- exercises/exercise-coupling-ff-pm/models/main.cc 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-coupling-ff-pm/models/main.cc 2023-06-01 13:25:59.349831633 +0200 +@@ -139,9 +139,15 @@ + auto porousMediumGridVariables = std::make_shared<PorousMediumGridVariables>(porousMediumProblem, porousMediumFvGridGeometry); + porousMediumGridVariables->init(sol[porousMediumIdx]); + +- // intialize the vtk output module +- const auto freeflowName = getParam<std::string>("Problem.Name") + "_" + freeflowProblem->name(); +- const auto porousMediumName = getParam<std::string>("Problem.Name") + "_" + porousMediumProblem->name(); ++ // initialize the vtk output module ++#if EXNUMBER >= 1 ++ const std::array<std::string, 3> part = {"a", "b", "c"}; ++ const auto freeflowName = "sol_" + part[EXNUMBER-1] + "_" + getParam<std::string>("Problem.Name") + "_" + freeflowProblem->name(); ++ const auto porousMediumName = "sol_" + part[EXNUMBER-1] + "_" + getParam<std::string>("Problem.Name") + "_" + porousMediumProblem->name(); ++#else ++ const auto freeflowName = "orig_" + getParam<std::string>("Problem.Name") + "_" + freeflowProblem->name(); ++ const auto porousMediumName = "orig_" + getParam<std::string>("Problem.Name") + "_" + porousMediumProblem->name(); ++#endif + + StaggeredVtkOutputModule<FreeflowGridVariables, decltype(freeflowSol)> freeflowVtkWriter(*freeflowGridVariables, freeflowSol, freeflowName); + GetPropType<FreeflowTypeTag, Properties::IOFields>::initOutputModule(freeflowVtkWriter); +diff -ruN exercises/exercise-coupling-ff-pm/models/params.input exercises/solution/exercise-coupling-ff-pm/models/params.input +--- exercises/exercise-coupling-ff-pm/models/params.input 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-coupling-ff-pm/models/params.input 2023-06-01 13:25:59.349831633 +0200 +@@ -1,5 +1,5 @@ + [TimeLoop] +-DtInitial = 1 # s ++DtInitial = 100 # s + EpisodeLength = -360 # s # 0.25 days + TEnd = 256000 # s # 2 days + +diff -ruN exercises/exercise-coupling-ff-pm/models/porousmediumsubproblem.hh exercises/solution/exercise-coupling-ff-pm/models/porousmediumsubproblem.hh +--- exercises/exercise-coupling-ff-pm/models/porousmediumsubproblem.hh 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-coupling-ff-pm/models/porousmediumsubproblem.hh 2023-06-01 13:25:59.349831633 +0200 +@@ -64,10 +64,15 @@ + // primary variable indices + static constexpr int conti0EqIdx = Indices::conti0EqIdx; + static constexpr int pressureIdx = Indices::pressureIdx; ++#if EXNUMBER >= 3 ++ static constexpr int saturationIdx = Indices::switchIdx; ++ static constexpr int transportCompIdx = Indices::switchIdx; ++#elif EXNUMBER >= 1 ++ static constexpr int transportCompIdx = Indices::switchIdx; ++#else + static constexpr int phaseIdx = 0; +- // TODO: dumux-course-task 2.A +- // set the `transportCompIdx` to `Indices::switchIdx`. + static constexpr int transportCompIdx = 1; ++#endif + + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = Dune::FieldVector<Scalar, dimworld>; +@@ -82,7 +87,11 @@ + eps_(1e-7), + couplingManager_(couplingManager) + { ++#if EXNUMBER >= 3 ++ saturation_ = getParamFromGroup<Scalar>(this->paramGroup(), "Problem.Saturation"); ++#else + moleFraction_ = getParamFromGroup<Scalar>(this->paramGroup(), "Problem.MoleFraction"); ++#endif + + // initialize storage output file (.csv) + exportStorage_ = getParamFromGroup<bool>(this->paramGroup(), "Problem.ExportStorage", false); +@@ -125,8 +134,10 @@ + void startStorageEvaluation(const SolutionVector& curSol, + const GridVariables& gridVariables) + { +- // TODO: dumux-course-task 2.B +- // Initialize `initialWaterContent_` and assign that to `lastWaterMass_`. ++#if EXNUMBER >= 2 ++ initialWaterContent_ = evaluateWaterMassStorageTerm(curSol, gridVariables); ++ lastWaterMass_ = initialWaterContent_; ++#endif + } + + void startFluxEvaluation() +@@ -205,13 +216,23 @@ + + for (auto&& scv : scvs(fvGeometry)) + { +- // const auto& volVars = elemVolVars[scv]; +- +- // TODO: dumux-course-task 2.B +- // Insert calculation of the water mass here +- waterMass += 0.0; ++ for(int phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++phaseIdx) ++ { ++ // insert calculation of the water mass here ++#if EXNUMBER >= 2 ++ const auto& volVars = elemVolVars[scv]; ++ waterMass += volVars.massFraction(phaseIdx, FluidSystem::H2OIdx) * volVars.density(phaseIdx) ++ * volVars.saturation(phaseIdx) * volVars.porosity() ++ * scv.volume() * volVars.extrusionFactor(); ++#else ++ waterMass += 0.0; ++#endif ++ } + } + } ++#if EXNUMBER >= 2 ++ std::cout << "Mass of water is: " << waterMass << std::endl; ++#endif + + Scalar cumMassLoss = initialWaterContent_ - waterMass; + Scalar evaporationRate = (lastWaterMass_ - waterMass) * 86400 +@@ -251,9 +272,12 @@ + if (!couplingManager().isCoupledEntity(CouplingManager::darcyIdx, scvf)) + continue; + +- // TODO: dumux-course-task 2.B +- // Use "massCouplingCondition" from the couplingManager here +- NumEqVector flux(0.0); ++#if EXNUMBER >= 2 ++ NumEqVector flux = couplingManager().couplingData().massCouplingCondition(element, fvGeometry, elemVolVars, scvf) ++ * scvf.area() * elemVolVars[scvf.insideScvIdx()].extrusionFactor() * FluidSystem::molarMass(1) * -1.0 * 86400.0; ++#else ++ NumEqVector flux(0.0); // add "massCouplingCondition" from the couplingManager here ++#endif + faceEvaporation.push_back(flux[transportCompIdx]); + } + } +@@ -345,12 +369,16 @@ + static const Scalar freeflowPressure = getParamFromGroup<Scalar>("Freeflow", "Problem.Pressure"); + + PrimaryVariables values(0.0); +- +- // TODO: dumux-course-task 2.A +- // Declare here which phases are present. +- +- values[Indices::pressureIdx] = freeflowPressure; ++#if EXNUMBER >= 3 ++ values.setState(3/*bothPhases*/); ++ values[saturationIdx] = saturation_; ++#elif EXNUMBER >= 1 ++ values.setState(2/*secondPhaseOnly*/); + values[transportCompIdx] = moleFraction_; ++#else ++ values[transportCompIdx] = moleFraction_; ++#endif ++ values[pressureIdx] = freeflowPressure; + return values; + } + +@@ -385,7 +413,12 @@ + { return globalPos[1] > this->gridGeometry().bBoxMax()[1] - eps_; } + + Scalar eps_; ++#if EXNUMBER >= 3 ++ Scalar saturation_; ++#else + Scalar moleFraction_; ++#endif ++ + TimeLoopPtr timeLoop_; + std::shared_ptr<CouplingManager> couplingManager_; + +diff -ruN exercises/exercise-coupling-ff-pm/models/properties.hh exercises/solution/exercise-coupling-ff-pm/models/properties.hh +--- exercises/exercise-coupling-ff-pm/models/properties.hh 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-coupling-ff-pm/models/properties.hh 2023-06-01 13:25:59.349831633 +0200 +@@ -34,15 +34,17 @@ + #include <dumux/material/fluidsystems/h2oair.hh> + + // Porous medium flow domain +-// TODO: dumux-course-task 2.A +-// Include 2pnc model here +-#include <dumux/porousmediumflow/1pnc/model.hh> + #include <dumux/discretization/cctpfa.hh> + #include <dumux/material/fluidmatrixinteractions/diffusivityconstanttortuosity.hh> + +-// TODO: dumux-course-task 2.A +-// Include spatial params for a 2-phase system ++#if EXNUMBER >= 1 ++#include <dumux/porousmediumflow/2pnc/model.hh> ++#include "../2pspatialparams.hh" ++#else ++#include <dumux/porousmediumflow/1pnc/model.hh> + #include "../1pspatialparams.hh" ++#endif ++ + #include "porousmediumsubproblem.hh" + + // Free-flow +@@ -55,10 +57,12 @@ + + // Create new type tags + namespace TTag { +-// TODO: dumux-course-task 2.A +-// Change the inheritance such that the correct model is used. +-struct PorousMediumOnePNC { using InheritsFrom = std::tuple<OnePNC, CCTpfaModel>; }; + struct FreeflowNC { using InheritsFrom = std::tuple<NavierStokesNC, StaggeredFreeFlowModel>; }; ++#if EXNUMBER >= 1 ++struct PorousMediumOnePNC { using InheritsFrom = std::tuple<TwoPNC, CCTpfaModel>; }; ++#else ++struct PorousMediumOnePNC { using InheritsFrom = std::tuple<OnePNC, CCTpfaModel>; }; ++#endif + } // end namespace TTag + + // Set the coupling manager +@@ -82,13 +86,15 @@ + struct Problem<TypeTag, TTag::FreeflowNC> { using type = Dumux::FreeFlowSubProblem<TypeTag> ; }; + + // The fluid system +-// TODO: dumux-course-task 2.A +-// Change to property of the `FluidSystem` such that `H2OAir` is used directly. + template<class TypeTag> + struct FluidSystem<TypeTag, TTag::PorousMediumOnePNC> + { + using H2OAir = FluidSystems::H2OAir<GetPropType<TypeTag, Properties::Scalar>>; ++#if EXNUMBER == 0 + using type = FluidSystems::OnePAdapter<H2OAir, H2OAir::gasPhaseIdx>; ++#else ++ using type = H2OAir; ++#endif + }; + template<class TypeTag> + struct FluidSystem<TypeTag, TTag::FreeflowNC> +@@ -120,16 +126,25 @@ + struct EffectiveDiffusivityModel<TypeTag, TTag::PorousMediumOnePNC> + { using type = DiffusivityConstantTortuosity<GetPropType<TypeTag, Properties::Scalar>>; }; + +-// TODO: dumux-course-task 2.A +-// Define new formulation for primary variables here. ++#if EXNUMBER >= 1 ++//! Set the default formulation to pw-Sn: This can be over written in the problem. ++template<class TypeTag> ++struct Formulation<TypeTag, TTag::PorousMediumOnePNC> ++{ static constexpr auto value = TwoPFormulation::p1s0; }; ++#endif + + // Set the spatial parameters type ++#if EXNUMBER >= 1 ++template<class TypeTag> ++struct SpatialParams<TypeTag, TTag::PorousMediumOnePNC> { ++ using type = TwoPSpatialParams<GetPropType<TypeTag, GridGeometry>, GetPropType<TypeTag, Scalar>>; ++}; ++#else + template<class TypeTag> +-// TODO: dumux-course-task 2.A +-// Adapt the spatial params here. + struct SpatialParams<TypeTag, TTag::PorousMediumOnePNC> { + using type = OnePSpatialParams<GetPropType<TypeTag, GridGeometry>, GetPropType<TypeTag, Scalar>>; + }; ++#endif + + template<class TypeTag> + struct EnableGridGeometryCache<TypeTag, TTag::FreeflowNC> { static constexpr bool value = true; }; +diff -ruN exercises/exercise-coupling-ff-pm/README.md exercises/solution/exercise-coupling-ff-pm/README.md +--- exercises/exercise-coupling-ff-pm/README.md 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-coupling-ff-pm/README.md 1970-01-01 01:00:00.000000000 +0100 +@@ -1,439 +0,0 @@ +-# Exercise Coupling free flow/porous medium flow (DuMuX Course) +- +-The aim of this exercise is to get familiar with setting up coupled free flow/porous medium flow problems. +- +-## Problem set-up +- +-The model domain consists of two non-overlapping subdomains. +-Free flow is modeled in the upper subdomain, while the lower subdomain models a flow in a porous medium. +-Both single-phase flow and two-phase flow will be considered in the porous domain. +- +- +-### 0. Getting familiar with the code +- +-* Navigate to the directory `exercises/exercise-coupling-ff-pm` +- +-There are three sub folders: `interface` (Exercise 1), `models` (Exercise 2) and `turbulence` (Exercise 3). +- +-The task-related files for the simulation exercises are as follows: +-* Three __main files__ for the three sub-tasks :`interface/main.cc`, `models/main.cc`, `turbulence/main.cc`, +-* Three __free flow problem files__: `interface/freeflowsubproblem.hh`, `models/freeflowsubproblem.hh`, `turbulence/freeflowsubproblem.hh` +-* Three __porous medium flow problem files__: `interface/porousmediumsubproblem.hh`, `models/porousmediumsubproblem.hh`, `turbulence/porousmediumsubproblem.hh` +-* Three __properties files__: `interface/properties.hh`, `models/properties.hh`, `turbulence/properties.hh` +-* The __input files__: `interface/params.input`, `models/params.input`, `turbulence/params.input`, +-* The __spatial parameters files__: `1pspatialparams.hh`, `2pspatialparams.hh` +- +-In the main file, `TypeTags` for both submodels are defined, `FreeflowTypeTag` and `PorousMediumTypeTag`. These `TypeTags` collect all of the properties associated with each subproblem. +-The same applies for types such as `GridManager`, `FVGridGeometry`, `Problem`, etc... +-Since we use a monolithic coupling scheme, there is only one `Assembler` and one `NewtonSolver`, which help to assemble and solve the full coupled problem. +- +-The problem files very much look like the "regular", uncoupled problem files seen in previous exercises, with the exception that they hold a pointer to the `CouplingManager`. +-This allows them to evaluate the coupling conditions and to exchange information between the coupled models. +-The coupling conditions are realized technically in terms of boundary conditions. For instance, +-in `interface/freeflowsubproblem.hh`, `couplingNeumann` boundary conditions are set, which means that the free flow model evaluates the +-mass and momentum fluxes coming from the porous domain and uses these values as boundary conditions at the interface. +- +-Note that certain checks are performed when combining different models, e.g., the fluid system has to be the same for both domains +-and the sub-control-volume faces at the interface have to match. +- +-We will use a staggered grid to discretize the free-flow domain and a cell-centered finite volume method for the porous medium domain. +-Keep in mind that the staggered grid implementation distinguishes between face variables (velocity components) and +-cell center variables (all other variables). +-For this reason one distinguishes between `CouplingManager::stokesCellCenterIdx` and `CouplingManager::stokesFaceIdx` indices (see `main.cc`), while for the porous medium all variables can be accessed with `CouplingManager::darcyIdx`. +- +-__Task__: +-Take a closer look at the above listed files before moving to the next part of the exercise. +- +- +-### 1. Changing the interface +- +-In this part of the exercise, a simple coupled system consisting of a one-phase (1p) free flow and a one-phase flow in a porous medium is set up. Both subproblems have no-flow boundaries at the sides. +-Currently, a velocity profile is set on the upper free flow boundary, which leads to a vertical flow into the porous medium: +- +- +- +-* We will first change the flow direction such that the free flow is parallel to the porous medium. +-* Afterwards, the Beavers-Joseph-Saffman condition will be used as an interface condition for the tangential momentum transfer. +-* Last, we change the flat interface between the two domains to a wave-shaped one. +- +-__Task A: Change the flow direction__ +- +-Open the file `interface/freeflowsubproblem.hh` and navigate to the part, where the types of boundary condition are set. +-Instead of applying a fixed velocity profile at the top of the domain, we want to use fixed pressure boundary conditions +-at the left and right side of the free flow domain, while the top represents an impermeable wall. +- +-Set a Dirichlet boundary condition for the pressure at the left and right side of the domain: +-``` cpp +-if(onLeftBoundary_(globalPos) || onRightBoundary_(globalPos)) +- values.setDirichlet(Indices::pressureIdx); +- +-``` +- +-Set a Dirichlet boundary condition for the velocities at the top: +-``` cpp +-if(onUpperBoundary_(globalPos)) +-{ +- values.setDirichlet(Indices::velocityXIdx); +- values.setDirichlet(Indices::velocityYIdx); +-} +-``` +- +-Keep the coupling boundary condition: +-``` cpp +-if(couplingManager().isCoupledEntity(CouplingManager::stokesIdx, scvf)) +-{ +- values.setCouplingNeumann(Indices::conti0EqIdx); +- values.setCouplingNeumann(Indices::momentumYBalanceIdx); +- values.setDirichlet(Indices::velocityXIdx); // assume no slip on interface +-} +-``` +- +-Having changed the types of boundary conditions, we must now assign the correct values for them. +- +-Set a no-slip, no-flow condition for the velocity at the top: +-``` cpp +-values[Indices::velocityXIdx] = 0.0; +-values[Indices::velocityYIdx] = 0.0; +-``` +-Apply a fixed pressure difference between the inlet and outlet, e.g.: +-``` cpp +-if(onLeftBoundary_(globalPos)) +- values[Indices::pressureIdx] = deltaP_; +-if(onRightBoundary_(globalPos)) +- values[Indices::pressureIdx] = 0.0; +-``` +- +-For changing the flow direction, the boundary conditions for the porous medium have to be changed as well. +- +-Use Neumann no-flow boundaries everywhere, keep the coupling conditions. +-``` cpp +-values.setAllNeumann(); +- +-if (couplingManager().isCoupledEntity(CouplingManager::darcyIdx, scvf)) +- values.setAllCouplingNeumann(); +-``` +- +-This should make the flow go from left to right. +-You can also delete the initial vertical velocity from `initialAtPos()`, to be consistent with the new boundary conditions. +- +-__Task B: Include slip-condition__ +- +-However, we are still missing one important feature: +-at the moment, the velocity component tangential to the interface gets a no-slip condition. +-In the next step we want to implement the Beavers-Joseph-Saffman slip condition at the interface: +- +-$`\frac{\partial v_x}{\partial y} = \frac{\alpha}{\sqrt K} (v_x - q_{pm})\quad`$ at $`\quad y=0`$ +- +-with $`\quad q_{pm}=0`$. +- +-To include this, just replace the no-slip condition at the interface +-``` cpp +-values.setDirichlet(Indices::velocityXIdx); // assume no slip on interface +-``` +-with a Beavers-Joseph-Saffman (BJS) boundary condition for the respective momentum balance: +-``` cpp +-values.setBeaversJoseph(Indices::momentumXBalanceIdx); +-``` +- +-at the position where the coupling boundary conditions are set in `interface/freeflowsubproblem.hh`. +- +-To check if the simulation behaves as expected, we can compare the velocity profile $`v_x(y)`$ with the analytical solution provided by [Beavers and Joseph (1967)](https://doi.org/10.1017/S0022112067001375). +-For doing so, we uncomment the following line in `main.cc` in the subfolder `interface`. +-```cpp +-freeflowVtkWriter.addField(freeflowProblem->getAnalyticalVelocityX(), "analyticalV_x"); +-``` +- +-After re-compiling and re-running the executable, we should be able to see also +-the analytical solution of $`v_x`$ on the free flow domain. Play around with the grid resolution to see how that affects the velocity profile. +- +-__Task C: Change the shape of interface__ +- +-Now we want to include a non-flat interface between the two domains. +-We use [`dune-subgrid`](https://doi.org/10.1007/s00607-009-0067-2) to construct two grids for the two domains from one common host grid. Thus, for the following tasks subgrid needs to be correctly installed. +-Our hostgrid will be a Dune-Yasp grid (`Dune::YaspGrid<2, Dune::TensorProductCoordinates<double, dim> >`) +-and the bounds and resolution of the domain will be set in the `params.input`file under the group `[Grid]`. +-This hostgrid, along with `elementSelector` functions defining some spatial cut of this domain, are passed to the grid manager to create each subdomain grid. +- +-To introduce this, open `main.cc` in the subfolder `interface` again and search for `TODO: dumux-course-task 1.C`. +-Comment out the first code block and uncomment the second. +-This will instantiate a host grid and define two helper lambda functions that are used to choose elements from to host grid for the respective sub grid. +-In the given case, the domain is split in two halves, separated by a sinusoidal interface. +- +-```cpp +-auto elementSelectorFreeflow = [&](const auto& element) +-{ +- double interface = params.amplitude * std::sin(( element.geometry().center()[0] - params.offset) / params.scaling * 2.0 * M_PI) + params.baseline; +- return element.geometry().center()[1] > interface; +-}; +- +-auto elementSelectorPorousMedium = [&](const auto& element) +-{ +- double interface = params.amplitude * std::sin(( element.geometry().center()[0] - params.offset) / params.scaling * 2.0 * M_PI) + params.baseline; +- return element.geometry().center()[1] < interface; +-}; +-``` +- +-Make sure that you have uncommented the line for including the grid manager in the properties file, i.e. +-```cpp +-#include <dumux/io/grid/gridmanager_sub.hh> +-``` +- +-and do the changes in the respective lines for the `Grid` property. +- +-The problem should now compile. However, a runtime error occurs due to the coupling conditions. +-So far, we assumed a flat interface, therefore the normal momentum coupling condition +- +- $`[\sigma \cdot \mathbf{n}]^{FF} = p^{PM}`$ +- +- was always set for a fixed $`\mathbf{n} = (0,1)^T`$. We need to account for the curvature of the interface and thus replace +- ```cpp +-values.setCouplingNeumann(Indices::momentumYBalanceIdx); +- ``` +- with +- ```cpp +-values.setCouplingNeumann(scvf.directionIndex()); +- ``` +-in `freeflowsubproblem.hh` in the subfolder `interface`. +- +-The same is true for the BJS condition, however, here we need to consider the tangential direction: +-```cpp +-values.setBeaversJoseph(1 - scvf.directionIndex()); +-``` +- +-The final result should look something like this: +- +- +- +-*Extra Points:* +-Rather than enforcing a pressure difference across the domain, an inflow velocity profile could be set. +-What changes to the left boundary conditions in the free-flow domain would you make to introduce this? What conditions can be enforced on the right boundary? +-Hint: A relation between velocity and position is used for the vertical velocity component in the original form of the `initialAtPos` method. +- +-### 2. Changing the porous medium model +- +-In this part of the exercise, the coupled system will be extended such that the transport of components +-in both domains is included and the presence of a second phase in the porous medium domain is considered. +-This enables the simulation of the drying of a porous medium (however no energy balance is included yet). +- +-This part of the exercise consists of the following steps: +-* replacing the 1pnc porous-medium model by a 2pnc porous-medium model, +-* adding the output for fluxes and storage terms (having gnuplot is recommended), +-* enable a variable switch for a complete drying. +- +-We start with an example in which the transport of water vapor in the gas phase is considered. +-The porous medium is filled with gas, initially the mole fraction of water vapor is $`x^w_g = 0.1`$. +-Above the porous medium, a dry gas flows and by diffusion, the porous medium dries out. +-(Don't mind the compiler warning, we will deal with it in task B.) +- +- +-__Task A: Change the model__: +- +-In the first task, the porous-medium model will be changed from a 1p2c system to a 2p2c system. +-Although a 2p2c system is plugged in, we still want to simulate the same situation as before, i.e., air with water vapor in both domains. +-The following changes have to be made in the porous-medium model (`models/properties.hh`): +-* Include the 2pnc model: include the respective headers and inherit from the new model `TwoPNC` +-* Exchange the spatial parameters for the 1-phase system by those for a 2-phase system. +-* Since two phases are involved now, we do not need to use the `OnePAdapter` anymore. Change the property of the `FluidSystem` such that `H2OAir` is used directly. +- Afterwards, set the `transportCompIdx` to `Indices::switchIdx` in `porousmediumsubproblem.hh`. +- +-One big difference between the 1p and 2p model is, that the primary variables for which +-the problem is solved, are not fixed. +-It is possible to use different formulations, e.g. with the gas pressure and the +-liquid saturation as primary variables (p_g-S_l -> `p1s0`) or vice versa. +-* Set the property +- +-``` +-template<class TypeTag> +-struct Formulation<TypeTag, TTag::PorousMediumOnePNC> +-{ static constexpr auto value = TwoPFormulation::p1s0; }; +-``` +- in the properties file. +- +-In contrast to the formulation, which stays the same during one simulation, the meaning of +-the primary variables may vary during one simulation. +-In the case under investigation, we want to use the gas pressure and the liquid saturation as primary variables. +-However, if only the gas phase is present, the liquid saturation is always zero. +-In this case, the chosen formulation will set the given value as the mole fraction of water vapor in the gas phase. +-* To tell to program which phases are present in which parts of the domain at the beginning of the simulation, +- you have to call `values.setState(MY_PHASE_PRESENCE);` in `initialAtPos()`. `MY_PHASE_PRESENCE` should be replaced with the correct value for the case where only a gas-phase (second phase of the fluid system) is present. +- Have a look at the `indices.hh` in the `2pnc` model (as the 2p2c model is a special case of the 2pnc model) in the subfolder `porousmediumflow` in your DuMuX directory. (hint: the numbering of phase indices begins with 0, the numbering of the phase presence states begins with 1. Take a look at your formulation to find out which phase index to use for the gas phase.) +- +-__Task B: Add output__: +- +-In the next step, we want to add some output to the simulation. The standard method for providing simulation output for visualization is via a `VtkWriter`. These tools take the grid geometries of each domain, as well as the solutions and write spatial output that one can view in visualization tools such as paraview. +- +-Although this Vtk output is very useful, some output is more suited for other forms of visualization. +-Two examples of data output formats are `.csv` files and `.json` files. +-From these files, data can be visualized using many different tools. +-In this exercise, data exported to a `.csv` file will be plotted using gnuplot, and data exported to a `.json` output will be plotted using the matplotlib python library. +- +-First as example output data, we want to investigate the water mass in the (porous-medium) system. +-There are two ways to evaluate this: (1) the total storage of water mass in the porous medium domain, and (2) the water vapor flux along the interface. +-The total storage can be plotted over time, whereas the water vapor flux will vary spatially along the interface as well as over time. +- +-First, we evaluate the storage term of the water component. +-* Have a look at the function `evaluateWaterMassStorageTerm()` in the `porousmediumsubproblem.hh`. +- Then implement a calculation of the total water mass: +- $`M^\text{w}:=\sum_{\alpha \in \textrm{g,l}} \left( \phi S_\alpha X^\text{w}_\alpha \varrho_\alpha V_\textrm{scv} \right)`$. +-* Afterwards, adapt the method `startStorageEvaluation()` such that the variable `initialWaterContent_` is +- initialized correctly using the `evaluateWaterMassStorageTerm()` method and assign that value also to the variable `lastWaterMass_`. +- +-After these functions are correctly implemented, the evaporation in [mm/d] is calculated in the code as +-$`e = \frac{\textrm{d}\, M^\text{w}}{\textrm{d}\, t} / A_\textrm{interface}`$. +-(all boundaries have Neumann no-flow conditions and no sources are present, meaning the water can only leave the system +-via the porous-medium free-flow interface.) +- +-During the simulation a `.csv` file is created (enable by setting `[Problem.ExportStorage] = true` in the `params.input` file). In addition, a gnuplot script `StorageOverTime.gp` is written and the mass loss and cumulative mass are plotted over time (`StorageOverTime.png`), when plotting is enabled with `[Problem.PlotStorage] = true`. +- +-Second, we want to know the distribution of the water mass fluxes across the interface. +-* Have a look at the function `evaluateInterfaceFluxes()` in the porous medium problem. +- Use the facilities therein to return the values of `massCouplingCondition()` from the `couplingManager` +- for each coupling scvf. Multiply this with the relevant face areas, extrusion factor, mass fraction, and seconds per day to get the [mm/d] units as seen in the storage evaluation. +-* When the `[Problem.ExportFluxes] = true` parameter is enabled, simulation data will be exported for this simulation to a `.json` file. +- This file can flexibly handle data of different types, which in this case is helpful as we have both temporal and spatial data. +-* Use the python plotting script `plotFluxes.py` to visualize the flux distribution across the surface at different times. +- This script uses `matplotlib`, a very popular python based visualization library. +- +-Compile and run the simulation and take a look at the results. +- +-__Task C: Let it dry out__: +- +-In this exercise we want to completely dry an initial water-filled porous medium. +-- Change the initial condition such that it is started with a two-phase system. +- Set the initial saturation to $`S_\text{l} = 0.1`$. +- +-If one wants to simulate the complete drying of a porous medium, the standard capillary pressure-saturation +-relationships have to be regularized. If no regularization is used, then the capillary pressure would +-approach infinity when the liquid saturation goes to zero. This means that water can flow to the +-interface from any region of the porous medium and of course this poses numerical problems. +-Therefore, the capillary pressure-saturation relationship has to be regularized for low saturations. +-The regularization has a strong influence on how long liquid water is present at the interface, see +-[Mosthaf (2014)](http://dx.doi.org/10.18419/opus-519). +-* Take a look at how the regularization is set in the `2pspatialparams.hh` and see how adapting +- the parameter for `pcLowSw` and `pcHighSw` affects the results. +- +-The porous-medium model can now reach a liquid saturation of zero. As already explained above, +-for this case the liquid saturation cannot serve as primary variable anymore. However, +-manually adapting the primary variable states and values is inconvenient. +-[Class et al. (2002)](http://dx.doi.org/10.1016/S0309-1708(02)00014-3) +-describe an algorithm to switch the primary variables, if phases should appear or disappear during a simulation. +- +-Now you are able to simulate a complete drying of the porous medium. +- +- +-### 3. Use a turbulence model in the free flow domain +- +-Several RANS turbulence models are implemented in DuMu<sup>x</sup>. +-This part of the exercise consists of the following steps: +-* replacing the Navier-Stokes model by the K-Omega SST turbulence model, +-* switching to a symmetry boundary condition, +-* applying a grid refinement towards the interface, +-* subsequently refining the grid (convergence study). +- +-We will work with a `1p2cni/2p2cni` coupled problem, where `ni` stands for non-isothermal. +-All the prepared files can be found in the subfolder `exercise-coupling-ff-pm/turbulence`. +- +-__Task A__: +- +-The file `freeflowsubproblem.hh` is your free flow problem file and `properties.hh` is the properties file within this exercise. +- +-For using the compositional zero equation turbulence model, the following header files need to be included +- in properties file: +-``` +-#include <dumux/freeflow/compositional/sstncmodel.hh> +-``` +-and in problem file: +-``` +-#include <dumux/freeflow/turbulencemodel.hh> +-#include <dumux/freeflow/turbulenceproperties.hh> +-#include <dumux/freeflow/rans/twoeq/sst/problem.hh> +-#include <dumux/freeflow/rans/boundarytypes.hh> +-``` +- +-The includes for the NavierStokesNC model and the NavierStokesProblem are no longer needed and can be removed. +- +-Make sure your free flow problem inherits from the correct parent type: +-* Change the entry in the `FreeflowModel` definition accordingly (multi-component non-isothermal K-Omega SST model, `SSTNCNI`) in the properties file, +-* Adapt the inheritance of the problem class in problem file (Use `RANSProblem<TypeTag>` rather than `NavierStokesStaggeredProblem<TypeTag>`). +- +-Here, the turbulent free flow is wall-bounded, meaning shear stress and turbulence in the flow develop primarily due to the walls. +-The porous medium at the bottom and also the channel wall (upper boundary) act here as such walls. +-Within the problem the location of boundaries representing walls need to be defined. +-To do this, add the following function to the upper and lower boundaries within the `boundaryTypes` function in `freeflowsubproblem.hh`: +-```cpp +- values.setWall(); +-``` +- +-With the locations of the wall designated, and the problem initialized, a series of constant spatial properties, +-in particular the distance to the nearest wall from each cell center, should be initialized. +-To do this, add the following to the `main.cc` file. +-```cpp +- freeflowProblem->updateStaticWallProperties(); +-``` +- +-In addition, there is also a non-local solution-dependent aspect of the turbulence models which is to be updated at the end of each step. +-An example of this is a stencil extended velocity gradient, and other flow properties in relation to the wall. +-These dynamic interactions are to be initialized in the mainfile directly after the `updateStaticWallProperties()` call, +-as well as in the time loop after `// Update dynamic wall properties`: +-```cpp +-freeflowProblem->updateDynamicWallProperties(freeflowSol); +-``` +- +-In addition to designating the locations of walls, +-additional boundary conditions and initial conditions need to be set for the two new primary variables $k$ and $\omega$. +-In the `boundaryTypes` function, set both variables on all walls to be dirichlet, except for the right boundary, which should have outflow conditions. +- +-For the initial conditions, Reynolds number specific base conditions should be applied everywhere. +-In the problem constructor, uncomment the code calculating these terms, +-then apply the `turbulentKineticEnergy_`and `dissipation_` variables to their primary variables in all locations. +-Within the dirichlet function for cell faces (`dirichlet(element, scvf)`), +-we also need to specify that these variables should be fixed to 0 at the wall. +- +-In addition, dirichlet cell constraints for the dissipation or $\omega$ variable will be set for all wall adjacent cells. +-This is done in the `isDirichletCell` function, as well as the `dirichlet` function already, and requires no further changes. +- +-Compile and run your new coupled problem and take a look at the results in Paraview. +-In addition to the standard variables and parameters, you can now analyze turbulence model specific quantities +-(e.g. the turbulent viscosity $`\nu_\textrm{t}`$ or the turbulent diffusivity $`D_\textrm{t}`$) for the free flow domain. +-In paraview you may compare the magnitude of $`D`$ and $`D_\textrm{t}`$ to see where the transport is affected by turbulence. +-The result for the turbulent viscosity should look like this: +- +- +- +-__Task B__: +- +-Instead of computing the whole cross-section of a channel, +-you can use symmetric boundary conditions at the top boundary of your free flow domain by replacing all previous boundary conditions at the top with +-```c++ +-values.setAllSymmetry(); +-``` +- +-In addition, you have to remove the condition `onUpperBoundary_(globalPos)` from the `initialAtPos(globalPos)` method and the `dirichlet(element, scvf)` method. +- +-__Task C__: +- +-Choose `Surface With Edges` instead of `Surface` in the Paraview toolbar to see the discretization grid. +-We will refine the grid now in order to better resolve the processes at the coupling interface. +-Since not much is happening at the upper and lower boundaries of the whole domain, we want to keep the resolution low in these areas to save some computation time. +- +-A grid refinement towards the interface is called _grading_. +-Try different gradings by changing the values in the respective sections in the input file: +-```c++ +-Grading0 = 1.0 +-Grading1 = 1.0 +-``` +- +-__Task D__: +- +-For the grid convergence study, run various simulations with the following grading parameters: +-```c++ +-* [Freeflow.Grid] Grading1 = 1.2, [PorousMedium.Grid] Grading1 = -1.2 +-* [Freeflow.Grid] Grading1 = 1.3, [PorousMedium.Grid] Grading1 = -1.3 +-* [Freeflow.Grid] Grading1 = 1.4, [PorousMedium.Grid] Grading1 = -1.4 +-* [Freeflow.Grid] Grading1 = 1.5, [PorousMedium.Grid] Grading1 = -1.5 +-* [Freeflow.Grid] Grading1 = 1.6, [PorousMedium.Grid] Grading1 = -1.6 +-``` +- +-By changing the parameter `Problem.Name` for each grading factor, you avoid losing the `.vtu` and `.pvd` files of the previous simulation runs. +-Check the first lines of the output to see how the grading factors change the height of your grid cells. +-Compare the velocity fields with Paraview. +diff -ruN exercises/exercise-coupling-ff-pm/turbulence/CMakeLists.txt exercises/solution/exercise-coupling-ff-pm/turbulence/CMakeLists.txt +--- exercises/exercise-coupling-ff-pm/turbulence/CMakeLists.txt 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-coupling-ff-pm/turbulence/CMakeLists.txt 2023-04-26 14:37:49.789150797 +0200 +@@ -1,7 +1,17 @@ +-# executables for ex_interface_coupling_ff-pm +-dumux_add_test(NAME exercise_turbulence_coupling_ff-pm ++dumux_add_test(NAME exercise_turbulence_coupling_ff-pm_original + SOURCES main.cc +- LABELS ffpm) ++ LABELS ffpm ++ COMPILE_DEFINITIONS EXNUMBER=0) ++ ++dumux_add_test(NAME exercise_turbulence_coupling_ff-pm_a_solution ++ SOURCES main.cc ++ LABELS ffpm ++ COMPILE_DEFINITIONS EXNUMBER=1) ++ ++dumux_add_test(NAME exercise_turbulence_coupling_ff-pm_b_solution ++ SOURCES main.cc ++ LABELS ffpm ++ COMPILE_DEFINITIONS EXNUMBER=2) + + # add a symlink for each input file + add_input_file_links() +diff -ruN exercises/exercise-coupling-ff-pm/turbulence/freeflowsubproblem.hh exercises/solution/exercise-coupling-ff-pm/turbulence/freeflowsubproblem.hh +--- exercises/exercise-coupling-ff-pm/turbulence/freeflowsubproblem.hh 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-coupling-ff-pm/turbulence/freeflowsubproblem.hh 2023-06-01 13:25:59.349831633 +0200 +@@ -20,8 +20,9 @@ + * \file + * \brief The free-flow sub problem + */ +-#ifndef DUMUX_FREEFLOW_TURBULENCE_SUBPROBLEM_HH +-#define DUMUX_FREEFLOW_TURBULENCE_SUBPROBLEM_HH ++#ifndef DUMUX_FREEFLOW_TURBULENCE_SUBPROBLEM_SOL_HH ++#define DUMUX_FREEFLOW_TURBULENCE_SUBPROBLEM_SOL_HH ++ + + #include <dumux/common/properties.hh> + #include <dumux/common/boundarytypes.hh> +@@ -29,21 +30,30 @@ + #include <dumux/common/numeqvector.hh> + #include <dumux/multidomain/boundary/stokesdarcy/couplingdata.hh> + +-// TODO: dumux-course-task 3.A +-// Include headers for turbulence problem (rans) here. ++#if EXNUMBER >= 1 ++#include <dumux/freeflow/turbulencemodel.hh> ++#include <dumux/freeflow/turbulenceproperties.hh> ++#include <dumux/freeflow/rans/twoeq/sst/problem.hh> ++#include <dumux/freeflow/rans/boundarytypes.hh> ++#else + #include <dumux/freeflow/navierstokes/staggered/problem.hh> + #include <dumux/freeflow/navierstokes/boundarytypes.hh> ++#endif + + namespace Dumux { + /*! + * \brief The free-flow sub problem + */ + template <class TypeTag> +-// TODO: dumux-course-task 3.A +-// Adapt the inheritance of the problem class. ++#if EXNUMBER >= 1 ++class FreeFlowSubProblem : public RANSProblem<TypeTag> ++{ ++ using ParentType = RANSProblem<TypeTag>; ++#else + class FreeFlowSubProblem : public NavierStokesStaggeredProblem<TypeTag> + { + using ParentType = NavierStokesStaggeredProblem<TypeTag>; ++#endif + + using GridView = typename GetPropType<TypeTag, Properties::GridGeometry>::GridView; + static constexpr auto dimWorld = GridView::dimensionworld; +@@ -51,9 +61,11 @@ + using FluidSystem = GetPropType<TypeTag, Properties::FluidSystem>; + using Indices = typename GetPropType<TypeTag, Properties::ModelTraits>::Indices; + using ModelTraits = GetPropType<TypeTag, Properties::ModelTraits>; +- // TODO: dumux-course-task 3.A +- // Change the boundary types to Dumux::RANSBoundaryTypes<ModelTraits, ModelTraits::numEq()> ++#if EXNUMBER >= 1 ++ using BoundaryTypes = Dumux::RANSBoundaryTypes<ModelTraits, ModelTraits::numEq()>; ++#else + using BoundaryTypes = Dumux::NavierStokesBoundaryTypes<GetPropType<TypeTag, Properties::ModelTraits>::numEq()>; ++#endif + using FVGridGeometry = GetPropType<TypeTag, Properties::GridGeometry>; + using FVElementGeometry = typename FVGridGeometry::LocalView; + using SubControlVolume = typename FVElementGeometry::SubControlVolume; +@@ -90,21 +102,21 @@ + diffCoeffAvgType_ = StokesDarcyCouplingOptions::stringToEnum(DiffusionCoefficientAveragingType{}, + getParamFromGroup<std::string>(this->paramGroup(), + "Problem.InterfaceDiffusionCoefficientAvg")); ++#if EXNUMBER >=1 ++ FluidSystem::init(); ++ Dumux::TurbulenceProperties<Scalar, dimWorld, true> turbulenceProperties; ++ FluidState fluidState; ++ const auto phaseIdx = 0; ++ fluidState.setPressure(phaseIdx, refPressure_); ++ fluidState.setTemperature(this->spatialParams().temperatureAtPos({})); ++ fluidState.setMassFraction(phaseIdx, phaseIdx, 1.0); ++ Scalar density = FluidSystem::density(fluidState, phaseIdx); ++ Scalar kinematicViscosity = FluidSystem::viscosity(fluidState, phaseIdx) / density; ++ Scalar diameter = this->gridGeometry().bBoxMax()[1] - this->gridGeometry().bBoxMin()[1]; ++ turbulentKineticEnergy_ = turbulenceProperties.turbulentKineticEnergy(refVelocity_, diameter, kinematicViscosity); ++ dissipation_ = turbulenceProperties.dissipationRate(refVelocity_, diameter, kinematicViscosity); ++#endif + +- // TODO: // ******************** uncomment this section for the first task 3.A ****************** // +- // FluidSystem::init(); +- // Dumux::TurbulenceProperties<Scalar, dimWorld, true> turbulenceProperties; +- // FluidState fluidState; +- // const auto phaseIdx = 0; +- // fluidState.setPressure(phaseIdx, refPressure_); +- // fluidState.setTemperature(this->spatialParams().temperatureAtPos({})); +- // fluidState.setMassFraction(phaseIdx, phaseIdx, 1.0); +- // Scalar density = FluidSystem::density(fluidState, phaseIdx); +- // Scalar kinematicViscosity = FluidSystem::viscosity(fluidState, phaseIdx) / density; +- // Scalar diameter = this->gridGeometry().bBoxMax()[1] - this->gridGeometry().bBoxMin()[1]; +- // turbulentKineticEnergy_ = turbulenceProperties.turbulentKineticEnergy(refVelocity_, diameter, kinematicViscosity); +- // dissipation_ = turbulenceProperties.dissipationRate(refVelocity_, diameter, kinematicViscosity); +- // // ************************************************************************************* // + } + + /*! +@@ -121,6 +133,20 @@ + + const auto& globalPos = scvf.center(); + ++#if EXNUMBER >=1 ++ if(onRightBoundary_(globalPos)) ++ { ++ values.setOutflow(Indices::turbulentKineticEnergyEqIdx); ++ values.setOutflow(Indices::dissipationEqIdx); ++ } ++ else ++ { ++ // walls and inflow ++ values.setDirichlet(Indices::turbulentKineticEnergyIdx); ++ values.setDirichlet(Indices::dissipationIdx); ++ } ++#endif ++ + if (onLeftBoundary_(globalPos)) + { + values.setDirichlet(Indices::velocityXIdx); +@@ -129,9 +155,6 @@ + values.setDirichlet(Indices::energyEqIdx); + } + +- // TODO: dumux-course-task 3.A +- // set wall conditions for the turbulence model at the walls (values.setWall()) corresponding to the upper and lower boundary +- // set boundary conditions for the turbulence model primary variables everywhere (outflow on right boundary, otherwise dirichlet) + if (onLowerBoundary_(globalPos)) + { + values.setDirichlet(Indices::velocityXIdx); +@@ -139,17 +162,29 @@ + values.setNeumann(Indices::conti0EqIdx); + values.setNeumann(Indices::conti0EqIdx + 1); + values.setNeumann(Indices::energyEqIdx); ++#if EXNUMBER >=1 ++ values.setWall(); ++#endif + } + + if (onUpperBoundary_(globalPos)) + { +- // TODO: dumux-course-task 3.B +- // Replace all conditions here with symmetric BCs. ++#if EXNUMBER >=2 ++ values.setAllSymmetry(); ++#elif EXNUMBER >= 1 ++ values.setWall(); ++ values.setDirichlet(Indices::velocityXIdx); ++ values.setDirichlet(Indices::velocityYIdx); ++ values.setNeumann(Indices::conti0EqIdx); ++ values.setNeumann(Indices::conti0EqIdx + 1); ++ values.setNeumann(Indices::energyEqIdx); ++#else + values.setDirichlet(Indices::velocityXIdx); + values.setDirichlet(Indices::velocityYIdx); + values.setNeumann(Indices::conti0EqIdx); + values.setNeumann(Indices::conti0EqIdx + 1); + values.setNeumann(Indices::energyEqIdx); ++#endif + } + + if (onRightBoundary_(globalPos)) +@@ -182,16 +217,19 @@ + const auto globalPos = scvf.ipGlobal(); + PrimaryVariables values(initialAtPos(globalPos)); + +- // TODO: dumux-course-task 3.A +- // Add dirichlet conditions setting TKE and Dissipation to zero on the upper and lower walls. +- // TODO: dumux-course-task 3.B +- // Remove the condition `onUpperBoundary_(globalPos)` here. +- // if (onUpperBoundary_(globalPos) || onLowerBoundary_(globalPos)) +- // { +- // values[Indices::turbulentKineticEnergyIdx] = 0.0; +- // values[Indices::dissipationIdx] = 0.0; +- // } +- ++#if EXNUMBER == 1 ++ if (onUpperBoundary_(globalPos) || onLowerBoundary_(globalPos)) ++ { ++ values[Indices::turbulentKineticEnergyIdx] = 0.0; ++ values[Indices::dissipationIdx] = 0.0; ++ } ++#elif EXNUMBER >= 2 ++ if (onLowerBoundary_(globalPos)) ++ { ++ values[Indices::turbulentKineticEnergyIdx] = 0.0; ++ values[Indices::dissipationIdx] = 0.0; ++ } ++#endif + return values; + } + +@@ -298,15 +336,18 @@ + values[Indices::velocityXIdx] = refVelocity(); + values[Indices::temperatureIdx] = refTemperature(); + +- // TODO: dumux-course-task 3.A +- // Set initial conditions for the TKE and the Dissipation. Values calculated in the constructor +- // values[Indices::turbulentKineticEnergyIdx] = TODO??; +- // values[Indices::dissipationIdx] = TODO??; ++#if EXNUMBER >= 1 ++ values[Indices::turbulentKineticEnergyIdx] = turbulentKineticEnergy_; ++ values[Indices::dissipationIdx] = dissipation_; ++#endif + +- // TODO: dumux-course-task 3.B +- // Remove the condition `onUpperBoundary_(globalPos)` here. ++#if EXNUMBER >= 2 ++ if(onLowerBoundary_(globalPos)) ++ values[Indices::velocityXIdx] = 0.0; ++#else + if(onUpperBoundary_(globalPos) || onLowerBoundary_(globalPos)) + values[Indices::velocityXIdx] = 0.0; ++#endif + + return values; + } +diff -ruN exercises/exercise-coupling-ff-pm/turbulence/main.cc exercises/solution/exercise-coupling-ff-pm/turbulence/main.cc +--- exercises/exercise-coupling-ff-pm/turbulence/main.cc 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-coupling-ff-pm/turbulence/main.cc 2023-06-01 13:25:59.349831633 +0200 +@@ -133,12 +133,12 @@ + auto solOld = sol; + + couplingManager->init(freeflowProblem, porousMediumProblem, sol); +- +- // TODO: dumux-course-task 3.A +- // Update static wall properties +- +- // TODO: dumux-course-task 3.A +- // Update dynamic wall properties ++ // TODO: update static wall properties ++ // TODO: update dynamic wall properties ++#if EXNUMBER >= 1 ++ freeflowProblem->updateStaticWallProperties(); ++ freeflowProblem->updateDynamicWallProperties(freeflowSol); ++#endif + + // the grid variables + using FreeflowGridVariables = GetPropType<FreeflowTypeTag, Properties::GridVariables>; +@@ -192,8 +192,10 @@ + // make the new solution the old solution + solOld = sol; + +- // TODO: dumux-course-task 3.A +- // Update dynamic wall properties ++#if EXNUMBER >= 1 ++ // TODO: update dynamic wall properties ++ freeflowProblem->updateDynamicWallProperties(freeflowSol); ++#endif + + // post time step treatment of PorousMedium problem + porousMediumProblem->postTimeStep(sol[porousMediumIdx], *porousMediumGridVariables, timeLoop->timeStepSize()); +diff -ruN exercises/exercise-coupling-ff-pm/turbulence/params.input exercises/solution/exercise-coupling-ff-pm/turbulence/params.input +--- exercises/exercise-coupling-ff-pm/turbulence/params.input 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-coupling-ff-pm/turbulence/params.input 2023-06-01 13:25:59.349831633 +0200 +@@ -69,3 +69,6 @@ + SolidDensity = 2700 + SolidThermalConductivity = 2.8 + SolidHeatCapacity = 790 ++ ++[RANS] ++IsFlatWallBounded = True +diff -ruN exercises/exercise-coupling-ff-pm/turbulence/properties.hh exercises/solution/exercise-coupling-ff-pm/turbulence/properties.hh +--- exercises/exercise-coupling-ff-pm/turbulence/properties.hh 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-coupling-ff-pm/turbulence/properties.hh 2023-06-01 13:25:59.349831633 +0200 +@@ -20,8 +20,8 @@ + * \file + * \brief The coupled exercise properties file or the turbulent case. + */ +-#ifndef DUMUX_EXERCISE_COUPLED_TURBULENCE_PROPERTIES_HH +-#define DUMUX_EXERCISE_COUPLED_TURBULENCE_PROPERTIES_HH ++#ifndef DUMUX_EXERCISE_COUPLING_TURBULENCE_PROPERTIES_HH ++#define DUMUX_EXERCISE_COUPLING_TURBULENCE_PROPERTIES_HH + + // Both domains + #include <dune/grid/yaspgrid.hh> +@@ -40,9 +40,12 @@ + + // Free-flow domain + #include <dumux/discretization/staggered/freeflow/properties.hh> +-// TODO: dumux-course-task 3.A +-// Include headers for compositional zero equation turbulence model here. ++ ++#if EXNUMBER >= 1 ++#include <dumux/freeflow/compositional/sstncmodel.hh> ++#else + #include <dumux/freeflow/compositional/navierstokesncmodel.hh> ++#endif + + #include"freeflowsubproblem.hh" + +@@ -51,9 +54,11 @@ + // Create new type tags + namespace TTag { + struct PorousMediumFlowModel { using InheritsFrom = std::tuple<TwoPTwoCNI, CCTpfaModel>; }; +-// TODO: dumux-course-task 3.A +-// Change the entry in the `FreeflowModel` definition accordingly. ++#if EXNUMBER >= 1 ++struct FreeflowModel { using InheritsFrom = std::tuple<SSTNCNI, StaggeredFreeFlowModel>; }; ++#else + struct FreeflowModel { using InheritsFrom = std::tuple<NavierStokesNCNI, StaggeredFreeFlowModel>; }; ++#endif + } // end namespace TTag + + // Set the coupling manager diff --git a/.patches/exercise-dunemodule/exercise-dunemodule.patch b/.patches/exercise-dunemodule/exercise-dunemodule.patch new file mode 100644 index 00000000..71ed023c --- /dev/null +++ b/.patches/exercise-dunemodule/exercise-dunemodule.patch @@ -0,0 +1,112 @@ +diff -ruN exercises/exercise-dunemodule/README.md exercises/solution/exercise-dunemodule/README.md +--- exercises/exercise-dunemodule/README.md 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-dunemodule/README.md 1970-01-01 01:00:00.000000000 +0100 +@@ -1,108 +0,0 @@ +-# Exercise New Dune Module (DuMuX course) +- +-This exercise describes how to create a new DuMuX module +-and how to create a corresponding GitLab project. +- +-This is the suggested +-workflow to develop code on top of DuMuX. +- +-### Task 1: Create new dune module +-<hr> +- +-* Execute the following command (bash environment) in the top-folder, i.e. above the dumux folder +- +-```bash +-./dune-common/bin/duneproject +-``` +- +-* Follow the introductions and specify +- * as name of the new module: `dumux-example` +- * as module dependencies: `dumux` +- * a version at your choice (the version of your project, not of dumux.) +- * your email address +- +-<br><br><br> +-### Task 2: Rerun dunecontrol to configure your new project +-<hr> +- +-The following command will configure your new module +- +-```bash +-./dune-common/bin/dunecontrol --opts=<opts file> --only=dumux-example all +-``` +- +-You need to run this command in the folder with content dumux, dumux-course, dune-common, dune-geometry, dune-grid, dune-istl, etc. `<opts file>` needs to be replaced (please replace the angle brackets also) by an options file, e.g., by `./dumux/cmake.opts`. Have a look at the comments in this file to see how you can adapt it to your needs. +- +-<br><br><br> +-### Task 3: Create a new test case within your new DuMuX module +-<hr> +- +-* Create a new folder (in your module folder), e.g., `appl` +- +-```bash +-mkdir appl +-``` +- +-* Copy some test case from the dumux module, e.g., test_1p from test/porousmediumflow/1p/compressible/stationary in your new folder (e.g., `appl`) +-* Copy the problem file, spatialparams file, properties file, cc source file and input file +- +-* Adjust the CMakeLists.txt file within the dumux-example (or your module name)-folder to include your new subdirectory +- +-* Add a new CMakeLists.txt in the folder `appl` with the content +- +-```cmake +-# add a new finite volume 1p test +-dumux_add_test(NAME test_1p_compressible_stationary_tpfa +- SOURCES main.cc +- COMPILE_DEFINITIONS TYPETAG=OnePCompressibleTpfa +- CMD_ARGS params.input) +- +-# add a symlink for the input file +-dune_symlink_to_source_files(FILES "params.input") +- +-``` +- +-* Reconfigure your module by running in the topmost directory of your new module +- +-```bash +-cmake build-cmake +-``` +- +-* Build and execute the test problem +- +-```bash +-cd build-cmake +-make build_tests +-cd appl +-./test_1p_compressible_stationary_tpfa params.input +-``` +- +-<br><br><br> +-### Task 4: Create a new GitLab project +-<hr> +- +-* Login with your username and password at https://git.iws.uni-stuttgart.de/ +- +-Note: If you don't have an account create one. We allow anyone to host repositories +-on our GitLab instance as long as it is DuMuX related. If you created a GitLab account recently or just now, one of +-the administrators first has to increase the number of permitted personal projects for your account before you can create +-your own project. +- +-* Click the **New project** button. +- +-* Then choose to **Create blank project**. +- +-* Specify your project name, untick the box *Initialize repository with a README* and click the **Create project** button. +- +-* Follow the given instructions for an *existing folder*. +- +-Hint: if you have not done so already, be sure to inform your computer of your git account with the following commands: +-```bash +-git config --global user.name "FIRST_NAME LAST_NAME" +-git config --global user.email "YOUR_EMAIL_ADDRESS" +-``` +- +-**Important**: Before executing the `git add .` command, you should add your cmake build folder to `.gitignore`. +-The easiest way to do so is to copy the `.gitignore` file from the dumux module into your module path. If everything +-worked, executing `git status` should not show `build-cmake` anymore. Never put your executables or other build files +-under version control. Only source files (`*.hh`, `*.cc`, `*.input`, `CMakeLists.txt`) should be under version control. diff --git a/.patches/exercise-fluidsystem/exercise-fluidsystem.patch b/.patches/exercise-fluidsystem/exercise-fluidsystem.patch new file mode 100644 index 00000000..5defb315 --- /dev/null +++ b/.patches/exercise-fluidsystem/exercise-fluidsystem.patch @@ -0,0 +1,617 @@ +diff -ruN exercises/exercise-fluidsystem/2pproperties.hh exercises/solution/exercise-fluidsystem/2pproperties.hh +--- exercises/exercise-fluidsystem/2pproperties.hh 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-fluidsystem/2pproperties.hh 2023-04-26 14:37:49.793150817 +0200 +@@ -37,10 +37,8 @@ + #include "spatialparams.hh" + + // The components that will be created in this exercise +-// TODO: dumux-course-task 2.2 +-//****** Include the corresponding header for the task *****// + #include "components/myincompressiblecomponent.hh" +-// #include "components/mycompressiblecomponent.hh" ++#include "components/mycompressiblecomponent.hh" + + // We will only have liquid phases here + #include <dumux/material/fluidsystems/1pliquid.hh> +@@ -89,10 +87,10 @@ + using Scalar = GetPropType<TypeTag, Properties::Scalar>; + using TabulatedH2O = Components::TabulatedComponent<Components::H2O<Scalar>>; + using LiquidWaterPhase = typename FluidSystems::OnePLiquid<Scalar, TabulatedH2O>; +- +- // TODO: dumux-course-task 2.2 +- //****** Select the corresponding component for the task *****// +- // Uncomment first line and comment second line for using the compressible component ++ /*! ++ * Uncomment first line and comment second line for using the incompressible component ++ * Uncomment second line and comment first line for using the compressible component ++ */ + using LiquidMyComponentPhase = typename FluidSystems::OnePLiquid<Scalar, MyIncompressibleComponent<Scalar> >; + // using LiquidMyComponentPhase = typename FluidSystems::OnePLiquid<Scalar, MyCompressibleComponent<Scalar> >; + +diff -ruN exercises/exercise-fluidsystem/aparams.input exercises/solution/exercise-fluidsystem/aparams.input +--- exercises/exercise-fluidsystem/aparams.input 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-fluidsystem/aparams.input 2023-04-26 14:37:49.793150817 +0200 +@@ -3,7 +3,7 @@ + DtInitial = 10 # initial time step size [s] + + [Problem] +-Name = exercise-fluidsystem_a # name will be given to e.g. to the vtk result files ++Name = exercise-fluidsystem_a_solution # name will be given to e.g. to the vtk result files + + [SpatialParams] + BrooksCoreyPcEntry = 5.0e2 # Pa +@@ -20,4 +20,4 @@ + Cells = 60 60 # x-/y-resolution of the grid + + [Output] +-PlotDensity = false # plot density over pressure for your component ++PlotDensity = true # plot density over pressure for your component +diff -ruN exercises/exercise-fluidsystem/bparams.input exercises/solution/exercise-fluidsystem/bparams.input +--- exercises/exercise-fluidsystem/bparams.input 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-fluidsystem/bparams.input 2023-04-26 14:37:49.793150817 +0200 +@@ -3,7 +3,7 @@ + DtInitial = 10 # initial time step size [s] + + [Problem] +-Name = exercise-fluidsystem_b # name will be given to e.g. to the vtk result files ++Name = exercise-fluidsystem_b_solution # name will be given to e.g. to the vtk result files + + [SpatialParams] + BrooksCoreyPcEntry = 5.0e2 # Pa +diff -ruN exercises/exercise-fluidsystem/CMakeLists.txt exercises/solution/exercise-fluidsystem/CMakeLists.txt +--- exercises/exercise-fluidsystem/CMakeLists.txt 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-fluidsystem/CMakeLists.txt 2023-04-26 14:37:49.793150817 +0200 +@@ -1,17 +1,15 @@ + # executables for exercise part a & b + #part a: 2pproblem +-dumux_add_test(NAME exercise_fluidsystem_a ++dumux_add_test(NAME exercise_fluidsystem_a_solution + SOURCES main.cc +- COMPILE_DEFINITIONS TYPETAG=ExerciseFluidsystemTwoP +- COMPILE_ONLY) # for testing purposes, ignore for the exercise ++ CMD_ARGS aparams.input -Output.PlotDensity false ++ COMPILE_DEFINITIONS TYPETAG=ExerciseFluidsystemTwoP) + + #part b: 2p2cproblem +-dumux_add_test(NAME exercise_fluidsystem_b ++dumux_add_test(NAME exercise_fluidsystem_b_solution + SOURCES main.cc +- COMPILE_DEFINITIONS TYPETAG=ExerciseFluidsystemTwoPTwoC +- COMPILE_ONLY) # for testing purposes, ignore for the exercise ++ CMD_ARGS bparams.input ++ COMPILE_DEFINITIONS TYPETAG=ExerciseFluidsystemTwoPTwoC) + + # add a symlink for each input file + add_input_file_links() +-# add a symlink for the grids folder +-dune_symlink_to_source_files(FILES grids) +diff -ruN exercises/exercise-fluidsystem/components/mycompressiblecomponent.hh exercises/solution/exercise-fluidsystem/components/mycompressiblecomponent.hh +--- exercises/exercise-fluidsystem/components/mycompressiblecomponent.hh 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-fluidsystem/components/mycompressiblecomponent.hh 2023-04-26 14:37:49.793150817 +0200 +@@ -50,7 +50,7 @@ + static std::string name() + { return "MyCompressibleComponent"; } + +- /*! ++ /*! + * \brief Returns true if the liquid phase is assumed to be compressible + */ + static constexpr bool liquidIsCompressible() +@@ -61,11 +61,7 @@ + */ + static Scalar molarMass() + { +- // TODO: dumux-course-task 2.2 +- // Copy the methods implemented in MyIncompressibleComponent and substitute +- // the density calculation by the expression given in the exercise description. +- // Replace the line below by a meaningful return statement +- DUNE_THROW(Dune::NotImplemented, "Todo: implement molar mass"); ++ return 131.39e-3; // [kg/mol] + } + + /*! +@@ -77,9 +73,12 @@ + */ + static Scalar liquidDensity(Scalar temperature, Scalar pressure) + { +- // TODO: dumux-course-task 2.2: +- // Replace the line below by a meaningful return statement +- DUNE_THROW(Dune::NotImplemented, "Todo: implement liquid density"); ++ static const Scalar rho_min = 1440; ++ static const Scalar rho_max = 1480; ++ static const Scalar k = 5e-7; ++ ++ using std::exp; ++ return rho_min + (rho_max - rho_min)/(1 + rho_min*exp(-1.0*k*(rho_max - rho_min)*pressure)); // [kg/m^3] + } + + /*! +@@ -103,9 +102,7 @@ + */ + static Scalar liquidViscosity(Scalar temperature, Scalar pressure) + { +- // TODO: dumux-course-task 2.2: +- // Replace the line below by a meaningful return statement +- DUNE_THROW(Dune::NotImplemented, "Todo: implement liquid viscosity"); ++ return 5.7e-4;// [Pa*s] + } + + /*! +@@ -116,9 +113,7 @@ + */ + static Scalar vaporPressure(Scalar t) + { +- // TODO: dumux-course-task 3 +- // Replace the line below by a meaningful return statement +- DUNE_THROW(Dune::NotImplemented, "Todo: implement vapour pressure"); ++ return 3900; // [Pa] (at 20C) + } + }; + +diff -ruN exercises/exercise-fluidsystem/components/myincompressiblecomponent.hh exercises/solution/exercise-fluidsystem/components/myincompressiblecomponent.hh +--- exercises/exercise-fluidsystem/components/myincompressiblecomponent.hh 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-fluidsystem/components/myincompressiblecomponent.hh 2023-04-26 14:37:49.793150817 +0200 +@@ -19,7 +19,7 @@ + /*! + * \file + * \ingroup Components +- * \brief A fictitious component to be implemented in exercise-fluidsystem. ++ * \brief A fictitious component to be implemented in exercise-fluidsystem a. + */ + #ifndef DUMUX_MYINCOMPRESSIBLECOMPONENT_HH + #define DUMUX_MYINCOMPRESSIBLECOMPONENT_HH +@@ -33,7 +33,7 @@ + { + /*! + * \ingroup Components +- * \brief A fictitious component to be implemented in exercise-fluidsystem. ++ * \brief A fictitious component to be implemented in exercise-fluidsystem a. + * + * \tparam Scalar The type used for scalar values + */ +@@ -60,10 +60,7 @@ + */ + static Scalar molarMass() + { +- // TODO: dumux-course-task 2.1 +- // Implement the methods for the component data given in the exercise description. +- // Replace the line below by a meaningful return statement +- DUNE_THROW(Dune::NotImplemented, "Todo: implement molarMass()"); ++ return 131.39e-3; // [kg/mol] + } + + /*! +@@ -75,10 +72,7 @@ + */ + static Scalar liquidDensity(Scalar temperature, Scalar pressure) + { +- // TODO: dumux-course-task 2.1 +- // Implement the methods for the component data given in the exercise description. +- // Replace the line below by a meaningful return statement +- DUNE_THROW(Dune::NotImplemented, "Todo: implement liquidDensity()"); ++ return 1460.0; // [kg/m^3] + } + + /*! +@@ -102,10 +96,7 @@ + */ + static Scalar liquidViscosity(Scalar temperature, Scalar pressure) + { +- // TODO: dumux-course-task 2.1 +- // Implement the methods for the component data given in the exercise description. +- // Replace the line below by a meaningful return statement +- DUNE_THROW(Dune::NotImplemented, "Todo: implement liquidViscosity()"); ++ return 5.7e-4;// [Pa*s] + } + }; + +diff -ruN exercises/exercise-fluidsystem/components/plotdensityfunction.py exercises/solution/exercise-fluidsystem/components/plotdensityfunction.py +--- exercises/exercise-fluidsystem/components/plotdensityfunction.py 1970-01-01 01:00:00.000000000 +0100 ++++ exercises/solution/exercise-fluidsystem/components/plotdensityfunction.py 2023-04-26 14:37:49.793150817 +0200 +@@ -0,0 +1,19 @@ ++#!usr/bin/env python ++import numpy as np ++import matplotlib.pyplot as plt ++ ++# function to calculate rho dependent on pressure ++rho_min = 1440; ++rho_max = 1480; ++k = 5e-7; ++ ++def rho(p): ++ return rho_min + (rho_max - rho_min)/(1 + rho_min*np.exp(-1.0*k*(rho_max - rho_min)*p)); ++ ++# sample pressure in range (1e4, 1e7) and compute corresponding densities ++p = np.logspace(4, 7, 100) ++r = rho(p) ++ ++# plot density vs. pressure ++plt.semilogx(p, r) ++plt.show() +diff -ruN exercises/exercise-fluidsystem/fluidsystems/h2omycompressiblecomponent.hh exercises/solution/exercise-fluidsystem/fluidsystems/h2omycompressiblecomponent.hh +--- exercises/exercise-fluidsystem/fluidsystems/h2omycompressiblecomponent.hh 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-fluidsystem/fluidsystems/h2omycompressiblecomponent.hh 2023-04-26 14:37:49.793150817 +0200 +@@ -31,15 +31,15 @@ + + #include <dumux/material/fluidsystems/base.hh> + +-// the fictitious component that was created in exercise-fluidsystem a +-#include <exercises/exercise-fluidsystem/components/mycompressiblecomponent.hh> ++// the fictitious component that was created in exercise 3a ++#include <exercises/solution/exercise-fluidsystem/components/mycompressiblecomponent.hh> + + // the binary coefficients corresponding to this fluid system +-#include <exercises/exercise-fluidsystem/binarycoefficients/h2omycompressiblecomponent.hh> ++#include <exercises/solution/exercise-fluidsystem/binarycoefficients/h2omycompressiblecomponent.hh> + + namespace Dumux { + namespace FluidSystems { +- ++ + /*! + * \brief A compositional fluid consisting of two liquid phases, + * which are water and a fictitious component from exercise-fluidsystem a. +@@ -51,7 +51,7 @@ + { + using ThisType = H2OMyCompressibleComponent<Scalar, H2OType>; + using Base = Dumux::FluidSystems::Base<Scalar, ThisType>; +- ++ + public: + using H2O = H2OType; + using MyCompressibleComponent = Dumux::MyCompressibleComponent<Scalar>; +@@ -121,11 +121,13 @@ + assert(0 <= phaseIdx && phaseIdx < numPhases); + return true; + } +- static constexpr bool isGas(int phaseIdx) ++ ++ static constexpr bool isGas(int phaseIdx) + { + assert(0 <= phaseIdx && phaseIdx < numPhases); + return phaseIdx == phase1Idx; + } ++ + static constexpr bool isIdealGas(int phaseIdx) + { return H2O::gasIsIdeal() && MyCompressibleComponent::gasIsIdeal(); } + +@@ -223,16 +225,13 @@ + // See: doctoral thesis of Steffen Ochs 2007 + // Steam injection into saturated porous media : process analysis including experimental and numerical investigations + // http://elib.uni-stuttgart.de/bitstream/11682/271/1/Diss_Ochs_OPUS.pdf ++ Scalar rholH2O = H2O::liquidDensity(fluidState.temperature(phaseIdx), fluidState.pressure(phaseIdx)); ++ Scalar clH2O = rholH2O/H2O::molarMass(); ++ Scalar x_H2O = fluidState.moleFraction(phase0Idx, H2OIdx); ++ Scalar x_myComp = fluidState.moleFraction(phase0Idx, NAPLIdx); + +- // Scalar rholH2O = H2O::liquidDensity(fluidState.temperature(phaseIdx), fluidState.pressure(phaseIdx)); +- // Scalar clH2O = rholH2O/H2O::molarMass(); +- // Scalar x_H2O = fluidState.moleFraction(phase0Idx, H2OIdx); +- // Scalar x_myComp = fluidState.moleFraction(phase0Idx, NAPLIdx); +- +- // TODO: dumux-course-task 3 +- // Implement the composition-dependent water density from the exercise sheet. +- +- DUNE_THROW(Dune::NotImplemented, "Todo: implement composition-dependent density"); ++ // return composition-dependent water phase density ++ return clH2O*(H2O::molarMass()*x_H2O + MyCompressibleComponent::molarMass()*x_myComp); + } + else + { +diff -ruN exercises/exercise-fluidsystem/main.cc exercises/solution/exercise-fluidsystem/main.cc +--- exercises/exercise-fluidsystem/main.cc 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-fluidsystem/main.cc 2023-04-26 14:37:49.793150817 +0200 +@@ -51,8 +51,8 @@ + + // define the type tag for this problem + // TYPETAG is set in CMakeLists.txt as compile time definition +- // alternatively you could write `using TypeTag = Properties::TTag::ExerciseFluidsystemBoxTwoP;` +- // then, for the 2p2c problem you have to change this line to `using TypeTag = Properties::TTag::ExerciseFluidsystemBoxTwoPTwoC;` ++ // alternatively you could write `using TypeTag = Properties::TTag::ExerciseFluidsystemTwoP;` ++ // then, for the 2p2c problem you have to change this line to `using TypeTag = Properties::TTag::ExerciseFluidsystemTwoPTwoC;` + // and recompile the executable + using TypeTag = Properties::TTag::TYPETAG; + +@@ -92,13 +92,11 @@ + auto gridVariables = std::make_shared<GridVariables>(problem, gridGeometry); + gridVariables->init(x); + +- + // get some time loop parameters + using Scalar = GetPropType<TypeTag, Properties::Scalar>; + const auto tEnd = getParam<Scalar>("TimeLoop.TEnd"); + const auto maxDt = getParam<Scalar>("TimeLoop.MaxTimeStepSize"); + auto dt = getParam<Scalar>("TimeLoop.DtInitial"); +- + // initialize the vtk output module + using IOFields = GetPropType<TypeTag, Properties::IOFields>; + VtkOutputModule<GridVariables, SolutionVector> vtkWriter(*gridVariables, x, problem->name()); +diff -ruN exercises/exercise-fluidsystem/README.md exercises/solution/exercise-fluidsystem/README.md +--- exercises/exercise-fluidsystem/README.md 2023-06-01 14:31:33.149062999 +0200 ++++ exercises/solution/exercise-fluidsystem/README.md 1970-01-01 01:00:00.000000000 +0100 +@@ -1,266 +0,0 @@ +-# Exercise Fluidsystem (DuMuX Course) +- +-The aim of this exercise is to get familiar with the _DuMu<sup>x</sup>_ way of implementing new components and using them in fluid systems (immiscible/mixture). In the scope of this exercise, different two-phase (liquid-liquid) fluid systems are implemented. A new fictitious component is implemented and used to describe a immiscible liquid phase. The second phase will only consist of water (Section 2). Furthermore, the mixture of the two components is implemented as two miscible phases (Section 3). +- +-## Problem set-up +- +-The domain has a size of 60 x 60 m and contains two low-permeable lenses. Initially, the domain is fully water saturated and the fictitious component is injected through the middle portion of the upper boundary by means of a Neumann boundary condition. The remaining parts of the upper and the entire lower boundary are Neumann no-flow while on the two lateral sides Dirichlet boundary conditions are applied (hydrostatic conditions for the pressure and zero saturation). +- +- +- +- +-## Preparing the exercise +- +-* Navigate to the directory `dumux-course/exercises/exercise-fluidsystem` +- +-### 1. Getting familiar with the code +- +-Locate all the files you will need for this exercise +-* The shared __main file__ : `main.cc` +-* The __input file__ for part a: `aparams.input` +-* The __problem file__ for part a: `2pproblem.hh` +-* The __properties file__ for part a: `2pproperties.hh` +-* The __input file__ for part b: `bparams.input` +-* The __problem file__ for part b: `2p2cproblem.hh` +-* The __properties file__ for part b: `2p2cproperties.hh` +-* The __spatial parameters file__: `spatialparams.hh` +- +-Furthermore you will find the following folders: +-* `binarycoefficients`: Stores headers containing data/methods on binary mixtures +-* `components`: Stores headers containing data/methods on pure components +-* `fluidsystems`: Stores headers containing data/methods on mixtures of pure components. Uses methods from `binarycoefficients`. +- +-To see more components, fluidsystems and binarycoefficients implementations, have a look at the folder `dumux/material`. +- +-### 2. Implement a new component +- +-In the following, the basic steps required to set the desired fluid system are outlined. Here, this is done in the __properties file__, i.e. for this part of the exercise the code shown below is taken from the `2pproperties.hh` file. +- +-In this part of the exercise we will consider a system consisting of two immiscible phases. Therefore, the _TypeTag_ for this problem (`ExerciseFluidsystemTwoP`) derives from +-the `TwoP` _TypeTag_ (immiscible two-phase model properties) and the `BoxModel` _TypeTag_ (specifies properties of the discretization scheme). +- +-```c++ +-// Create new type tags +-namespace TTag { +-struct ExerciseFluidsystemTwoP { using InheritsFrom = std::tuple<TwoP, BoxModel>; }; +-} // end namespace TTag +-``` +- +-In order to be able to derive from these _TypeTags_, the declarations of the `TwoP` _TypeTag_ and `BoxModel` _TypeTag_ have to be included. +-The `TwoP` _TypeTag_ can be found in the `2p/model.hh` header: +- +-```c++ +-// The numerical model +-#include <dumux/porousmediumflow/2p/model.hh> +-``` +- +-while the `BoxModel` _TypeTag_ can be found in the `discretization/box.hh` header: +- +-```c++ +-// The box discretization +-#include <dumux/discretization/box.hh> +-``` +- +-For a cell-centered scheme, you could derive from `CCTpfaModel` or `CCMpfaModel` instead (and, of course, include the right headers). +- +-One of the two phases should only contain the component water. We want to precompute tables on which the properties are then interpolated in order to save computational time. Since we need this part in both problem file and properties file, we have to include the following two headers in our problem file, i.e. `2pproblem.hh` file, and the properties file has access to them through problem file. +- +-```c++ +-// The water component +-#include <dumux/material/components/tabulatedcomponent.hh> +-#include <dumux/material/components/h2o.hh> +-``` +-The other phase that will be created only contains our new component, where we want to implement an incompressible and a compressible variant. +-The respective headers are prepared, but still incomplete. The compressible variant is still commented so that compilation does not fail when finishing the incompressible variant. +- +-```c++ +-// The components that will be created in this exercise +-#include "components/myincompressiblecomponent.hh" +-// #include "components/mycompressiblecomponent.hh" +-``` +-As mentioned above, we want to simulate two non-mixing phases (2pimmiscible). The respective fluid system is found in: +- +-```c++ +-// The two-phase immiscible fluid system +-#include <dumux/material/fluidsystems/2pimmiscible.hh> +-``` +- +-This fluid system expects __phases__ as input and so far we have only included the components, which contain data on the pure component for all physical states. Thus, we need to include +- +-```c++ +-// We will only have liquid phases here +-#include <dumux/material/fluidsystems/1pliquid.hh> +-``` +- +-which creates a _liquid phase_ from a given component. Finally, using all of the included classes we set the fluid system property by choosing that the water phase is liquid (`OnePLiquid`) and consists of the tabulated water component, and +-the other phase is liquid as well and consists of the incompressible fictitious component. Both will make up the immiscible fluid system (`TwoPImmiscible`), consisting of two liquids each consisting of one component: +- +- +-```c++ +-// we use the immiscible fluid system here +-template<class TypeTag> +-struct FluidSystem<TypeTag, TTag::ExerciseFluidsystemTwoP> +-{ +-private: +- using Scalar = GetPropType<TypeTag, Properties::Scalar>; +- using TabulatedH2O = Components::TabulatedComponent<Components::H2O<Scalar>>; +- using LiquidWaterPhase = typename FluidSystems::OnePLiquid<Scalar, TabulatedH2O>; +- +- // TODO: dumux-course-task 2.2 +- //****** Select the corresponding component for the task *****// +- // Uncomment first line and comment second line for using the compressible component +- using LiquidMyComponentPhase = typename FluidSystems::OnePLiquid<Scalar, MyIncompressibleComponent<Scalar> >; +- // using LiquidMyComponentPhase = typename FluidSystems::OnePLiquid<Scalar, MyCompressibleComponent<Scalar> >; +- +-public: +- using type = typename FluidSystems::TwoPImmiscible<Scalar, LiquidWaterPhase, LiquidMyComponentPhase>; +-}; +-``` +- +-### 2.1. Incompressible component +- +-Open the file `myincompressiblecomponent.hh`. A component should always derive from the _Base_ class - thus we include `dumux/material/components/base.hh`-, which defines the interface of a _DuMuX_ component with possibly required functions to be overloaded by the actual implementation. Additionally it is required for liquids to derive from the _Liquid_ class (see `dumux/material/components/liquid.hh`), for gases to derive from the _Gas_ class (see `dumux/material/components/gas.hh`) and for solids to derive from the _Solid_ class (see `dumux/material/components/solid.hh`), with functions specific to liquid, gas or solid. +- +-```c++ +-/*! +- * \ingroup Components +- * \brief A ficitious component to be implemented in exercise 3. +- * +- * \tparam Scalar The type used for scalar values +- */ +-template <class Scalar> +-class MyIncompressibleComponent +-: public Components::Base<Scalar, MyIncompressibleComponent<Scalar> > +-, public Components::Liquid<Scalar, MyIncompressibleComponent<Scalar> > +-``` +- +-__Task__: +- +-Implement an incompressible component into the file `myincompressiblecomponent.hh`, which has the following specifications: +- +-| Parameter | unit | value | +-| -----| --------| -------- | +-| $`M`$ | $`kg/mol`$ | $`131.39 \cdot 10^{-3}`$ | +-| $`\rho_{liquid}`$ | $`kg/m^3`$ | $`1460`$ | +-| $`\mu_{liquid}`$ | $`Pa \cdot s`$ | $`5.7 \cdot 10^{-4}`$ | +- +-In order to do so, have a look at the files `dumux/material/components/base.hh` and `dumux/material/components/liquid.hh` to see how the interfaces are defined and overload them accordingly. +- +-In order to execute the program, change to the build directory and compile and execute the program by typing +- +-```bash +-cd build-cmake/exercises/exercise-fluidsystem +-make exercise_fluidsystem_a +-./exercise_fluidsystem_a aparams.input +-``` +- +-The saturation distribution of the nonwetting phase S$`_n`$ (the phase consisting of our fictitious incompressible component) at the final simulation time should look like this: +- +- +- +-### 2.2. Compressible component +- +-We now want to implement a pressure-dependent density for our component. Open the file `mycompressiblecomponent.hh` and copy in the functions you implemented for the incompressible variant. Now substitute the method that returns the density by the following expression: +- +-$`\displaystyle \rho_{MyComp} = \rho_{min} + \frac{ \rho_{max} - \rho_{min} }{ 1 + \rho_{min}*e^{-1.0*k*(\rho_{max} - \rho_{min})*p} } `$ +- +-where $`p`$ is the pressure and $`\rho_{min} = 1440 `$, $`\rho_{max} = 1480 `$ and $`k = 5 \cdot 10^{-7} `$. Also, make sure the header is included in the `2pproperties.hh` file by uncommenting the corresponding line. Furthermore, the new component has to be set as a liquid phase in the fluid system. To do so, search for `TODO: dumux-course-task 2.2`. Comment out the corresponding line and uncomment the other. The density distribution of this phase (rhoN) at the final simulation time should look like this: +- +- +- +-You can plot the density of the phase consisting of your compressible component by setting `PlotDensity` in `aparams.input` to `true` and starting the simulation again. +-Compare the gnuplot output to the following plot of the density function from above: +- +- +- +-### 3. Implement a new fluid system +- +-The problem file and properties file for this part of the exercise are `2p2cproblem.hh` and `2p2cproperties.hh`, respectively. +-We now want to implement a new fluid system, which still consists of two liquid phases. However, one phase consists mainly of water and the other consists mainly of the previously implemented compressible component. We will now consider compositional effects, which is why we have to derive our _TypeTag_ (`ExerciseFluidsystemTwoPTwoC`) from a _TypeTag_ (`TwoPTwoC`) that holds the miscible two-phase +- two-component model properties: +- +-```c++ +-// The numerical model +-#include <dumux/porousmediumflow/2p2c/model.hh> +-``` +- +-```c++ +-// Create a new type tag for the problem +-namespace TTag { +-struct ExerciseFluidsystemTwoPTwoC { using InheritsFrom = std::tuple<TwoPTwoC, BoxModel>; }; +-} // end namespace TTag +-``` +- +-The new fluid system is to be implemented in the file `fluidsystems/h2omycompressiblecomponent.hh`. This is already included in the problem and the fluid system property is set accordingly. +- +-```c++ +-// The fluid system that is created in this exercise +-#include "fluidsystems/h2omycompressiblecomponent.hh" +-``` +- +-```c++ +-// The fluid system property +-template<class TypeTag> +-struct FluidSystem<TypeTag, TTag::ExerciseFluidsystemTwoPTwoC> +-{ +-private: +- using Scalar = GetPropType<TypeTag, Properties::Scalar>; +-public: +- using type = FluidSystems::H2OMyCompressibleComponent<Scalar>; +-}; +-``` +- +-In the `fluidsystems/h2omycompressiblecomponent.hh` file, your implemented compressible component and the binary coefficient files are already included. +- +-```c++ +-// the ficitious component that was created in exercise-fluidsystem a +-#include <exercises/exercise-fluidsystem/components/mycompressiblecomponent.hh> +- +-// the binary coefficients corresponding to this fluid system +-#include <exercises/exercise-fluidsystem/binarycoefficients/h2omycompressiblecomponent.hh> +-``` +- +-__Task__: +- +-Under the assumption that one molecule of `MyCompressibleComponent` displaces exactly one molecule of water, the density of the mostly-water-phase can be expressed as follows: +- +-$` \rho_{w} = \frac{ \rho_{w, pure} }{ M_{H_2O} }*(M_{H_2O}*x_{H_2O} + M_{MyComponent}*x_{MyComponent}) `$ +- +-Implement this dependency in the `density()` method in the fluid system. In order to compile and execute the program, run the following commands: +- +-```bash +-cd build-cmake/exercises/exercise-fluidsystem +-make exercise_fluidsystem_b +-./exercise_fluidsystem_b bparams.input +-``` +- +-You will observe an error message and an abortion of the program. This is due to the fact that in order for the constraint solver and other mechanisms in the two-phase two-component model to work, an additional functionality in the component has to be implemented: the model has to know the vapour pressure. As in the previous exercise, check the `dumux/material/components/base.hh` file for this function and implement it into `mycompressiblecomponent.hh`. For the vapour pressure, use a value of $`3900`$ Pa. +- +-### 4. Change wettability of the porous medium +- +-In the `spatialparams.hh` file, we can find the following function, with which we can specify which phase of the fluid system is to be considered as the wetting phase at a given position within the domain: +- +-```cpp +-/*! +- * \brief Function for defining which phase is to be considered as the wetting phase. +- * +- * \return the wetting phase index +- * \param globalPos The position of the center of the element +- */ +-template<class FluidSystem> +-int wettingPhaseAtPos(const GlobalPosition& globalPos) const +-{ +- // Our fluid system is H2OMyCompressibleComponent +- // We want to define water as the wetting phase in +- // the entire domain (see fluid system for the phase indices) +- return FluidSystem::phase0Idx; +-} +-``` +- +-Change this function such that the phase of our mostly-new-component-phase is the wetting phase __only__ within the lenses. Execute the program of task 3 again: +- +-```bash +-cd build-cmake/exercises/exercise-fluidsystem +-make exercise_fluidsystem_b +-./exercise_fluidsystem_b exercise_fluidsystem_b.input +-``` +diff -ruN exercises/exercise-fluidsystem/spatialparams.hh exercises/solution/exercise-fluidsystem/spatialparams.hh +--- exercises/exercise-fluidsystem/spatialparams.hh 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-fluidsystem/spatialparams.hh 2023-04-26 14:37:49.793150817 +0200 +@@ -129,9 +129,12 @@ + // We want to define water as the wetting phase in + // the entire domain (see fluid system for the phase indices) + +- // TODO: dumux-course-task 4 +- // Adapt the following line so that the phase of our new component is +- // the wetting phase, only within the lenses. ++ // For the last task (change of wettability), uncomment ++ // the two commented lines below ++ ++ //if (isInLens(globalPos)) ++ // return FluidSystem::phase1Idx; ++ + return FluidSystem::phase0Idx; + } + diff --git a/.patches/exercise-fractures/exercise-fractures.patch b/.patches/exercise-fractures/exercise-fractures.patch new file mode 100644 index 00000000..a0385d1f --- /dev/null +++ b/.patches/exercise-fractures/exercise-fractures.patch @@ -0,0 +1,748 @@ +diff -ruN exercises/exercise-fractures/CMakeLists.txt exercises/solution/exercise-fractures/CMakeLists.txt +--- exercises/exercise-fractures/CMakeLists.txt 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-fractures/CMakeLists.txt 2023-04-26 14:37:49.793150817 +0200 +@@ -1,6 +1,23 @@ +-dumux_add_test(NAME exercise_fractures ++# executable for the exercise ++add_executable(exercisefractures_solution EXCLUDE_FROM_ALL main.cc) ++ ++dumux_add_test(NAME exercisefractures_solution_a ++ CMAKE_GUARD "( dune-foamgrid_FOUND AND dune-alugrid_FOUND )" ++ TARGET exercisefractures_solution ++ COMMAND ./exercisefractures_solution ++ CMD_ARGS exercise_fractures_solution_a.input) ++ ++dumux_add_test(NAME exercisefractures_solution_b ++ CMAKE_GUARD "( dune-foamgrid_FOUND AND dune-alugrid_FOUND )" ++ TARGET exercisefractures_solution ++ COMMAND ./exercisefractures_solution ++ CMD_ARGS exercise_fractures_solution_b.input) ++ ++dumux_add_test(NAME exercisefractures_solution_c + CMAKE_GUARD "( dune-foamgrid_FOUND AND dune-alugrid_FOUND )" +- SOURCES main.cc) ++ TARGET exercisefractures_solution ++ COMMAND ./exercisefractures_solution ++ CMD_ARGS exercise_fractures_solution_c.input) + + # add a symlink for each input file + add_input_file_links() +diff -ruN exercises/exercise-fractures/exercise_fractures_solution_a.input exercises/solution/exercise-fractures/exercise_fractures_solution_a.input +--- exercises/exercise-fractures/exercise_fractures_solution_a.input 1970-01-01 01:00:00.000000000 +0100 ++++ exercises/solution/exercise-fractures/exercise_fractures_solution_a.input 2023-04-26 14:37:49.793150817 +0200 +@@ -0,0 +1,41 @@ ++[TimeLoop] ++TEnd = 30000 # [s] ++DtInitial = 10 # [s] ++MaxTimeStepSize = 2500 # [s] ++ ++[Problem] ++EnableGravity = false ++IsExercisePartA = true ++IsExercisePartB = false ++IsExercisePartC = false ++ ++[Grid] ++File = ./grids/complex.msh ++DomainMarkers = true # enable domain markers ++ ++[Matrix] ++Problem.Name = matrix ++Problem.BoundaryOverPressure = 5e5 ++Problem.BoundarySaturation = 0.5 ++SpatialParams.Permeability = 1e-12 ++SpatialParams.Porosity = 0.1 ++SpatialParams.VanGenuchtenAlpha = 1e-3 ++SpatialParams.VanGenuchtenN = 3 ++SpatialParams.Snr = 0.0 ++SpatialParams.Swr = 0.0 ++ ++[Fracture] ++Problem.Name = fractures ++SpatialParams.Aperture = 1e-1 ++SpatialParams.Permeability = 1e-7 ++SpatialParams.PermeabilityBarrier = 1e-16 ++SpatialParams.Porosity = 0.85 ++SpatialParams.PorosityBarrier = 0.05 ++SpatialParams.VanGenuchtenAlpha = 1e-1 ++SpatialParams.VanGenuchtenN = 2 ++SpatialParams.Swr = 0.0 ++SpatialParams.Snr = 0.0 ++SpatialParams.Barrier.VanGenuchtenAlpha = 1e-4 ++SpatialParams.Barrier.VanGenuchtenN = 2.5 ++SpatialParams.Barrier.Snr = 0.0 ++SpatialParams.Barrier.Swr = 0.0 +diff -ruN exercises/exercise-fractures/exercise_fractures_solution_b.input exercises/solution/exercise-fractures/exercise_fractures_solution_b.input +--- exercises/exercise-fractures/exercise_fractures_solution_b.input 1970-01-01 01:00:00.000000000 +0100 ++++ exercises/solution/exercise-fractures/exercise_fractures_solution_b.input 2023-04-26 14:37:49.793150817 +0200 +@@ -0,0 +1,41 @@ ++[TimeLoop] ++TEnd = 30000 # [s] ++DtInitial = 10 # [s] ++MaxTimeStepSize = 2500 # [s] ++ ++[Problem] ++EnableGravity = false ++IsExercisePartA = false ++IsExercisePartB = true ++IsExercisePartC = false ++ ++[Grid] ++File = ./grids/complex.msh ++DomainMarkers = true # enable domain markers ++ ++[Matrix] ++Problem.Name = matrix ++Problem.BoundaryOverPressure = 5e5 ++Problem.BoundarySaturation = 0.5 ++SpatialParams.Permeability = 1e-12 ++SpatialParams.Porosity = 0.1 ++SpatialParams.VanGenuchtenAlpha = 1e-3 ++SpatialParams.VanGenuchtenN = 3 ++SpatialParams.Snr = 0.0 ++SpatialParams.Swr = 0.0 ++ ++[Fracture] ++Problem.Name = fractures ++SpatialParams.Aperture = 1e-1 ++SpatialParams.Permeability = 1e-7 ++SpatialParams.PermeabilityBarrier = 1e-16 ++SpatialParams.Porosity = 0.85 ++SpatialParams.PorosityBarrier = 0.05 ++SpatialParams.VanGenuchtenAlpha = 1e-1 ++SpatialParams.VanGenuchtenN = 2 ++SpatialParams.Swr = 0.0 ++SpatialParams.Snr = 0.0 ++SpatialParams.Barrier.VanGenuchtenAlpha = 1e-4 ++SpatialParams.Barrier.VanGenuchtenN = 2.5 ++SpatialParams.Barrier.Snr = 0.0 ++SpatialParams.Barrier.Swr = 0.0 +diff -ruN exercises/exercise-fractures/exercise_fractures_solution_c.input exercises/solution/exercise-fractures/exercise_fractures_solution_c.input +--- exercises/exercise-fractures/exercise_fractures_solution_c.input 1970-01-01 01:00:00.000000000 +0100 ++++ exercises/solution/exercise-fractures/exercise_fractures_solution_c.input 2023-04-26 14:37:49.793150817 +0200 +@@ -0,0 +1,41 @@ ++[TimeLoop] ++TEnd = 30000 # [s] ++DtInitial = 10 # [s] ++MaxTimeStepSize = 2500 # [s] ++ ++[Problem] ++EnableGravity = false ++IsExercisePartA = false ++IsExercisePartB = false ++IsExercisePartC = true ++ ++[Grid] ++File = ./grids/complex.msh ++DomainMarkers = true # enable domain markers ++ ++[Matrix] ++Problem.Name = matrix ++Problem.BoundaryOverPressure = 5e5 ++Problem.BoundarySaturation = 0.5 ++SpatialParams.Permeability = 1e-12 ++SpatialParams.Porosity = 0.1 ++SpatialParams.VanGenuchtenAlpha = 1e-3 ++SpatialParams.VanGenuchtenN = 3 ++SpatialParams.Snr = 0.0 ++SpatialParams.Swr = 0.0 ++ ++[Fracture] ++Problem.Name = fractures ++SpatialParams.Aperture = 1e-1 ++SpatialParams.Permeability = 1e-7 ++SpatialParams.PermeabilityBarrier = 1e-16 ++SpatialParams.Porosity = 0.85 ++SpatialParams.PorosityBarrier = 0.05 ++SpatialParams.VanGenuchtenAlpha = 1e-1 ++SpatialParams.VanGenuchtenN = 2 ++SpatialParams.Swr = 0.0 ++SpatialParams.Snr = 0.0 ++SpatialParams.Barrier.VanGenuchtenAlpha = 1e-4 ++SpatialParams.Barrier.VanGenuchtenN = 2.5 ++SpatialParams.Barrier.Snr = 0.0 ++SpatialParams.Barrier.Swr = 0.0 +diff -ruN exercises/exercise-fractures/fractureproblem.hh exercises/solution/exercise-fractures/fractureproblem.hh +--- exercises/exercise-fractures/fractureproblem.hh 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-fractures/fractureproblem.hh 2023-04-26 14:37:49.793150817 +0200 +@@ -71,6 +71,10 @@ + std::shared_ptr<typename ParentType::SpatialParams> spatialParams, + const std::string& paramGroup) + : ParentType(gridGeometry, spatialParams, paramGroup) ++ , aperture_(getParamFromGroup<Scalar>(paramGroup, "SpatialParams.Aperture")) ++ , isExercisePartA_(getParamFromGroup<bool>(paramGroup, "Problem.IsExercisePartA")) ++ , isExercisePartB_(getParamFromGroup<bool>(paramGroup, "Problem.IsExercisePartB")) ++ , isExercisePartC_(getParamFromGroup<bool>(paramGroup, "Problem.IsExercisePartC")) + { + // initialize the fluid system, i.e. the tabulation + // of water properties. Use the default p/T ranges. +@@ -88,12 +92,12 @@ + // Otherwise, we would lose mass leaving across the fracture tips. + values.setAllNeumann(); + +- // However, there is one fracture reaching the top boundary. For this +- // fracture tip we set Dirichlet Bcs as in the matrix domain +- // TODO dumux-course-task A +- // Change boundary conditions +- if (globalPos[1] > this->gridGeometry().bBoxMax()[1] - 1e-6) +- values.setAllDirichlet(); ++ // However, there is one fracture reaching the top boundary. For that ++ // fracture tip we set Dirichlet Bcs - only in the unmodified state of ++ // the exercise though. In parts a, b & c we consider Neumann here. ++ if (!isExercisePartA_ && !isExercisePartB_ && !isExercisePartC_) ++ if (globalPos[1] > this->gridGeometry().bBoxMax()[1] - 1e-6) ++ values.setAllDirichlet(); + + return values; + } +diff -ruN exercises/exercise-fractures/fracturespatialparams.hh exercises/solution/exercise-fractures/fracturespatialparams.hh +--- exercises/exercise-fractures/fracturespatialparams.hh 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-fractures/fracturespatialparams.hh 2023-04-26 14:37:49.793150817 +0200 +@@ -53,6 +53,7 @@ + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + ++ // use a van-genuchten fluid matrix interaction relationship + using PcKrSwCurve = FluidMatrix::VanGenuchtenDefault<Scalar>; + + // we identify those fractures as barriers, that have a domain marker +@@ -69,8 +70,11 @@ + const std::string& paramGroup) + : ParentType(gridGeometry) + , gridDataPtr_(gridData) ++ , isExercisePartA_(getParamFromGroup<bool>(paramGroup, "Problem.IsExercisePartA")) ++ , isExercisePartB_(getParamFromGroup<bool>(paramGroup, "Problem.IsExercisePartB")) ++ , isExercisePartC_(getParamFromGroup<bool>(paramGroup, "Problem.IsExercisePartC")) + , pcKrSwCurve_("Fracture.SpatialParams") +- , pcKrSwCurveBarrier_("Fracture.SpatialParams.Barrier") ++ , PcKrSwCurveBarrier_("Fracture.SpatialParams.Barrier") + { + porosity_ = getParamFromGroup<Scalar>(paramGroup, "SpatialParams.Porosity"); + porosityBarrier_ = getParamFromGroup<Scalar>(paramGroup, "SpatialParams.PorosityBarrier"); +@@ -85,9 +89,22 @@ + const SubControlVolume& scv, + const ElementSolution& elemSol) const + { +- // TODO dumux-course-task B +- // Change fracture properties +- return permeability_; ++ // in the unmodified state and exercise part a return the non-barrier parameters ++ if (!isExercisePartB_ && !isExercisePartC_) ++ return permeability_; ++ ++ // in exercise part b always return the barrier parameters ++ else if (isExercisePartB_) ++ return permeabilityBarrier_; ++ ++ // in exercise part 3 return parameters depending on domain marker ++ else ++ { ++ if (getElementDomainMarker(element) == barriersDomainMarker) ++ return permeabilityBarrier_; ++ else ++ return permeability_; ++ } + } + + //! Return the porosity +@@ -96,9 +113,22 @@ + const SubControlVolume& scv, + const ElementSolution& elemSol) const + { +- // TODO dumux-course-task B +- // Change fracture properties +- return porosity_; ++ // in the unmodified state and exercise part a return the non-barrier parameters ++ if (!isExercisePartB_ && !isExercisePartC_) ++ return porosity_; ++ ++ // in exercise part b always return the barrier parameters ++ else if (isExercisePartB_) ++ return porosityBarrier_; ++ ++ // in exercise part 3 return parameters depending on domain marker ++ else ++ { ++ if (getElementDomainMarker(element) == barriersDomainMarker) ++ return porosityBarrier_; ++ else ++ return porosity_; ++ } + } + + /*! +@@ -113,9 +143,22 @@ + const SubControlVolume& scv, + const ElementSolution& elemSol) const + { +- // TODO dumux-course-task B +- // Change fracture properties +- return makeFluidMatrixInteraction(pcKrSwCurve_); ++ // in the unmodified state and exercise part a return the non-barrier parameters ++ if (!isExercisePartB_ && !isExercisePartC_) ++ return makeFluidMatrixInteraction(pcKrSwCurve_); ++ ++ // in exercise part b always return the barrier parameters ++ else if (isExercisePartB_) ++ return makeFluidMatrixInteraction(PcKrSwCurveBarrier_); ++ ++ // in exercise part 3 return parameters depending on domain marker ++ else ++ { ++ if (getElementDomainMarker(element) == barriersDomainMarker) ++ return makeFluidMatrixInteraction(PcKrSwCurveBarrier_); ++ else ++ return makeFluidMatrixInteraction(pcKrSwCurve_); ++ } + } + + //! Water is the wetting phase +@@ -153,13 +196,18 @@ + //! pointer to the grid data (contains domain markers) + std::shared_ptr<const Dumux::GridData<Grid>> gridDataPtr_; + ++ bool isExercisePartA_; ++ bool isExercisePartB_; ++ bool isExercisePartC_; ++ ++ const PcKrSwCurve pcKrSwCurve_; ++ const PcKrSwCurve PcKrSwCurveBarrier_; ++ + Scalar porosity_; + Scalar porosityBarrier_; + Scalar aperture_; + PermeabilityType permeability_; + PermeabilityType permeabilityBarrier_; +- const PcKrSwCurve pcKrSwCurve_; +- const PcKrSwCurve pcKrSwCurveBarrier_; + }; + + } // end namespace Dumux +diff -ruN exercises/exercise-fractures/main.cc exercises/solution/exercise-fractures/main.cc +--- exercises/exercise-fractures/main.cc 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-fractures/main.cc 2023-04-26 14:37:49.793150817 +0200 +@@ -257,4 +257,5 @@ + Parameters::print(); + + return 0; ++ + }// end main +diff -ruN exercises/exercise-fractures/matrixproblem.hh exercises/solution/exercise-fractures/matrixproblem.hh +--- exercises/exercise-fractures/matrixproblem.hh 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-fractures/matrixproblem.hh 2023-04-26 14:37:49.793150817 +0200 +@@ -77,11 +77,18 @@ + : ParentType(gridGeometry, spatialParams, paramGroup) + , boundaryOverPressure_(getParamFromGroup<Scalar>(paramGroup, "Problem.BoundaryOverPressure")) + , boundarySaturation_(getParamFromGroup<Scalar>(paramGroup, "Problem.BoundarySaturation")) ++ , isExercisePartA_(getParamFromGroup<bool>(paramGroup, "Problem.IsExercisePartA")) ++ , isExercisePartB_(getParamFromGroup<bool>(paramGroup, "Problem.IsExercisePartB")) ++ , isExercisePartC_(getParamFromGroup<bool>(paramGroup, "Problem.IsExercisePartC")) + { + // initialize the fluid system, i.e. the tabulation + // of water properties. Use the default p/T ranges. + using FluidSystem = GetPropType<TypeTag, Properties::FluidSystem>; + FluidSystem::init(); ++ ++ // there can only be one exercise part active ++ if (isExercisePartA_ + isExercisePartB_ + isExercisePartC_ > 1) ++ DUNE_THROW(Dune::InvalidStateException, "Invalid combination of activated exercise parts"); + } + + //! Specifies the type of boundary condition at a given position +@@ -89,13 +96,23 @@ + { + BoundaryTypes values; + +- // we consider buoancy-driven upwards migration of nitrogen and set +- // Dirichlet BCs on the top and bottom boundary +- // TODO dumux-course-task A +- // Change boundary conditions and Dirichlet values! +- values.setAllNeumann(); +- if (globalPos[1] < 1e-6 || globalPos[1] > this->gridGeometry().bBoxMax()[1] - 1e-6) +- values.setAllDirichlet(); ++ // in the unmodified state, we consider buoancy-driven upwards migration ++ // of nitrogen and set Dirichlet BCs on the top and bottom boundary ++ if (!isExercisePartA_ && !isExercisePartB_ && !isExercisePartC_) ++ { ++ values.setAllNeumann(); ++ if (globalPos[1] < 1e-6 || globalPos[1] > this->gridGeometry().bBoxMax()[1] - 1e-6) ++ values.setAllDirichlet(); ++ } ++ else ++ { ++ // for exercise part a,b & c we use no-flow boundaries everywhere ++ // except for the lower quarter of the left and the upper quarter of the right boundary ++ values.setAllNeumann(); ++ if ( (globalPos[0] > this->gridGeometry().bBoxMax()[0] - 1e-6 && globalPos[1] > 75.0) || ++ (globalPos[0] < 1e-6 && globalPos[1] < 25.0) ) ++ values.setAllDirichlet(); ++ } + + return values; + } +@@ -119,10 +136,30 @@ + // the interior boundary conditions to Dirichlet. + // IMPORTANT: Note that you will never be asked to set any values at the interior boundaries! + // This simply chooses a different interface condition! +- // TODO dumux-course-task C +- // Change coupling conditions! + values.setAllDirichlet(); + ++ if (isExercisePartB_) ++ values.setAllNeumann(); ++ else if (isExercisePartC_) ++ { ++ // we need to obtain the domain marker of the fracture element that is coupled to this face ++ // therefore we first get the fracture problem from the coupling manager. For this test, we ++ // know that the fracture domain id is 1 (see main file) ++ static constexpr auto fractureDomainId = Dune::index_constant<1>(); ++ const auto& fractureProblem = couplingManager().problem(fractureDomainId); ++ ++ // use helper function in coupling manager to obtain the element this face couples to ++ const auto fractureElement = couplingManager().getLowDimElement(element, scvf); ++ ++ // obtain marker from the spatial params of the fracture problem ++ const auto fractureElementMarker = fractureProblem.spatialParams().getElementDomainMarker(fractureElement); ++ ++ // now define Neumann coupling on those elements that are defined as blocking ++ // fractures, i.e. marker == 2, see .geo file in grids folder ++ if (fractureElementMarker == 2) ++ values.setAllNeumann(); ++ } ++ + return values; + } + +@@ -132,11 +169,23 @@ + // initialize values with the initial conditions + auto values = initialAtPos(globalPos); + +- // nitrogen is in contact with the domain on the center half of the lower boundary +- // TODO dumux-course-task A +- // Change boundary conditions and Dirichlet values! +- if (globalPos[1] < 1e-6 && globalPos[0] > 25.0 && globalPos[0] < 75.0) +- values[saturationIdx] = boundarySaturation_; ++ // In the unmodified state of the exercise nitrogen is in contact ++ // with the domain on the center half of the lower boundary ++ if (!isExercisePartA_ && !isExercisePartB_ && !isExercisePartC_) ++ { ++ if (globalPos[1] < 1e-6 && globalPos[0] > 25.0 && globalPos[0] < 75.0) ++ values[saturationIdx] = boundarySaturation_; ++ } ++ // exercise parts a, b & c ++ else ++ { ++ // apply overpressure on the right Dirichlet boundary segment ++ if (globalPos[0] > this->gridGeometry().bBoxMax()[0] - 1e-6) ++ { ++ values[pressureIdx] += boundaryOverPressure_; ++ values[saturationIdx] = boundarySaturation_; ++ } ++ } + + return values; + } +@@ -170,6 +219,9 @@ + + Scalar boundaryOverPressure_; + Scalar boundarySaturation_; ++ bool isExercisePartA_; ++ bool isExercisePartB_; ++ bool isExercisePartC_; + }; + + } // end namespace Dumux +diff -ruN exercises/exercise-fractures/matrixspatialparams.hh exercises/solution/exercise-fractures/matrixspatialparams.hh +--- exercises/exercise-fractures/matrixspatialparams.hh 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-fractures/matrixspatialparams.hh 2023-04-26 14:37:49.793150817 +0200 +@@ -52,6 +52,7 @@ + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + ++ // use a van-genuchten fluid matrix interaction relationship + using PcKrSwCurve = FluidMatrix::VanGenuchtenDefault<Scalar>; + + public: +diff -ruN exercises/exercise-fractures/params.input exercises/solution/exercise-fractures/params.input +--- exercises/exercise-fractures/params.input 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-fractures/params.input 2023-04-26 14:37:49.793150817 +0200 +@@ -5,6 +5,9 @@ + + [Problem] + EnableGravity = true ++IsExercisePartA = false ++IsExercisePartB = false ++IsExercisePartC = false + + [Grid] + File = ./grids/complex.msh +@@ -16,8 +19,8 @@ + Problem.BoundarySaturation = 0.5 + SpatialParams.Permeability = 1e-12 + SpatialParams.Porosity = 0.1 +-SpatialParams.VanGenuchtenAlpha = 1e-3 +-SpatialParams.VanGenuchtenN = 3 ++SpatialParams.VGAlpha = 1e-3 ++SpatialParams.VGN = 3 + SpatialParams.Snr = 0.0 + SpatialParams.Swr = 0.0 + +@@ -28,11 +31,11 @@ + SpatialParams.PermeabilityBarrier = 1e-16 + SpatialParams.Porosity = 0.85 + SpatialParams.PorosityBarrier = 0.05 +-SpatialParams.VanGenuchtenAlpha = 1e-1 +-SpatialParams.VanGenuchtenN = 2 +-SpatialParams.Swr = 0.0 ++SpatialParams.VGAlpha = 1e-1 ++SpatialParams.VGAlphaBarrier = 1e-4 ++SpatialParams.VGN = 2 ++SpatialParams.VGNBarrier = 2.5 + SpatialParams.Snr = 0.0 +-SpatialParams.Barrier.VanGenuchtenAlpha = 1e-4 +-SpatialParams.Barrier.VanGenuchtenN = 2.5 +-SpatialParams.Barrier.Snr = 0.0 +-SpatialParams.Barrier.Swr = 0.0 ++SpatialParams.SnrBarrier = 0.0 ++SpatialParams.Swr = 0.0 ++SpatialParams.SwrBarrier = 0.0 +diff -ruN exercises/exercise-fractures/properties.hh exercises/solution/exercise-fractures/properties.hh +--- exercises/exercise-fractures/properties.hh 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-fractures/properties.hh 2023-04-26 14:37:49.793150817 +0200 +@@ -57,6 +57,7 @@ + // with tpfa. + #include <dumux/multidomain/facet/cellcentered/tpfa/properties.hh> + ++ + namespace Dumux::Properties { + + // create the type tag node for the matrix and fracture sub-problems +@@ -65,7 +66,7 @@ + struct FractureProblem { using InheritsFrom = std::tuple<TwoP, CCTpfaModel>; }; + } // end namespace TTag + +-// Set the grid type for matrix and fracture sub-domains ++// Set the grid type for the matrix and fracture sub-domains + template<class TypeTag> + struct Grid<TypeTag, TTag::MatrixProblem> { using type = Dune::ALUGrid<2, 2, Dune::simplex, Dune::conforming>; }; + template<class TypeTag> +diff -ruN exercises/exercise-fractures/README.md exercises/solution/exercise-fractures/README.md +--- exercises/exercise-fractures/README.md 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-fractures/README.md 1970-01-01 01:00:00.000000000 +0100 +@@ -1,216 +0,0 @@ +-# Exercise Discrete Fractures (DuMuX Course) +- +-In this exercise we are going to use the _DuMuX multidomain_ framework, and in particular the _facet coupling_ module. This allows you to define coupled problems, in which a (d-1)-dimensional domain lives on the element facets of a d-dimensional domain and the coupling occurs via the transfer fluxes between them. Here, we are going to make use of this in the context of fractured porous media, where the fractures are represented as lower-dimensional entities embedded in a porous matrix. In the _facet coupling_ framework it is required that the grid resolves the fractures by aligning the matrix element facets with the fracture geometries, for which we are using the open-source mesh generator _Gmsh_ ([see _Gmsh_ documentation](http://gmsh.info/doc/texinfo/gmsh.html)). Note that the Gmsh file format (_.msh_) is currently the only supported file format within the _facet coupling_ framework. +- +-## Problem set-up +- +-We consider a domain of 100 x 100 m with a set of ten fractures. This geometry has been taken from [Flemisch et al. (2018)](https://www.sciencedirect.com/science/article/pii/S0309170817300143), but was slightly modified to better fit to the purposes of this exercise. Apart from one fracture extending up to the top boundary, all other fractures are immersed, meaning that the fracture tips lie within the domain (see image below). +- +- +- +-In the initial setup of this exercise, we want to consider buoyancy-driven upwards migration of nitrogen (gas) in a fully water-saturated medium. Thus, as initial conditions we use hydrostatic pressure conditions and a zero nitrogen saturation. On the boundaries we apply Neumann no-flow boundary conditions on the left and right sides, while using Dirichlet boundary conditions on the top and the bottom of the domain. We set the initial conditions as boundary conditions on the Dirichlet segments, except for the middle part of the lower boundary ($`y = 0 \wedge x > 25 \wedge x < 75`$), where we set a non-zero nitrogen saturation (settable via the input file). Through this segment of the lower boundary, nitrogen can intrude the domain and flow upwards driven by buoyancy. +- +-## Preparing the exercise +- +-Navigate to the directory `dumux/exercises/exercise-fractures` and familiarize yourself with the files necessary for this exercise: +- +-* The __main file__: `main.cc` +-* The __input file__: `params.input` +-* The __problem file__ for the matrix domain: `matrixproblem.hh` +-* The __problem file__ for the fracture domain: `fractureproblem.hh` +-* The __spatial parameters file__ for the matrix domain: `matrixspatialparams.hh` +-* The __spatial parameters file__ for the matrix domain: `fracturespatialparams.hh` +-* The __properties file__ for both domains: `properties.hh` +- +-If you want to learn more about the program flow of coupled problems within the _DuMuX multidomain_ framework, take a closer look at the __main file__. Therein, you will find extensive comments above the key instructions that will help you to identify the main differences with respect to an uncoupled _DuMuX_ model. We will now briefly guide you through this, however, this is not necessary for this exercise. Feel free to continue with the section __Running the program__ and come back to this point later. +- +-The basic idea of the _multidomain_ framework is as follows: you define the sub-problems individually, while all data and algorithms with respect to the coupling between the domains is contained in the `CouplingManager` object. This object is chosen depending on the coupling mode, thus, here we are using the implementation for models considering coupling across the element facets, i.e. the class `Dumux::FacetCouplingManager<...>` (see main file). As mentioned earlier, coupling occurs via the transfer fluxes between the d-dimensional matrix domain and the (d-1)-dimensional fracture domain. These fluxes act as additional source/sink terms for the fracture domain, which have to be implemented in the respective `source(...)` function (see `fractureproblem.hh`): +- +-```cpp +-//! Evaluate the source term at a given position +-NumEqVector source(const Element& element, +- const FVElementGeometry& fvGeometry, +- const ElementVolumeVariables& elemVolVars, +- const SubControlVolume& scv) const +-{ +- // evaluate sources from bulk domain using the function in the coupling manager +- auto source = couplingManagerPtr_->evalSourcesFromBulk(element, fvGeometry, elemVolVars, scv); +- +- // these sources are in kg/s, divide by volume and extrusion to have it in kg/s/m³ +- source /= scv.volume()*elemVolVars[scv].extrusionFactor(); +- return source; +-} +-``` +- +-As you can see, we call the convenience function `evalSourcesFromBulk(...)` available in the _facet coupling_ manager, which requires us to have access to it at this point. This access is provided in the __main file__ by handing over a shared pointer to the sub-problems, i.e.: +- +-```cpp +-// the coupling manager (needs the coupling mapper) +-auto couplingManager = std::make_shared<TheCouplingManager>(); +-couplingManager->init(matrixProblem, fractureProblem, couplingMapper, x); +- +-// we have to set coupling manager pointer in sub-problems +-// they also have to be made accessible in them (see e.g. matrixproblem.hh) +-matrixProblem->setCouplingManager(couplingManager); +-fractureProblem->setCouplingManager(couplingManager); +-``` +- +-From the matrix side, the coupling works a bit different. Since the fracture domain lives on the matrix domain's facets, the state inside the fracture (e.g. pressure, temperature etc.) will affect the fluxes computed across the respective element facets. However, flux assembly depends on the underlying finite-volume scheme and is not convenient to implement. We therefore created _TypeTag_ nodes for the matrix sub-problems in models considering facet coupling. By using these _TypeTags_, all necessary modifications for the flux computation on interior boundaries is done for you. For instance, in `properties.hh`, we define the _TypeTag_ for the matrix problem as follows (please read the provided comments): +- +-```cpp +-// We are using the framework for models that consider coupling +-// across the element facets of the bulk domain. This has some +-// properties defined, which we have to inherit here. In this +-// exercise we want to use a cell-centered finite volume scheme +-// with tpfa. +-#include <dumux/multidomain/facet/cellcentered/tpfa/properties.hh> +- +-namespace Dumux::Properties { +- +-// create the type tag node for the matrix and fracture sub-problems +-namespace TTag { +-struct MatrixProblem { using InheritsFrom = std::tuple<CCTpfaFacetCouplingModel, TwoP>; }; +-struct FractureProblem { using InheritsFrom = std::tuple<TwoP, CCTpfaModel>; }; +-} // end namespace TTag +- +-``` +- +-Additionally, we need to provide access to the coupling manager in the matrix problem, so that the flux assembly engine of the matrix domain has access to the state inside the fracture (which the fluxes depend on): +- +-```cpp +-//! returns reference to the coupling manager. +-const CouplingManager& couplingManager() const +-{ return *couplingManagerPtr_; } +-``` +- +-At this point, we are done with the extra work involved in defining the sub-problems. There are two more differences with respect to the main program flow of uncoupled problems: we now have to use the multidomain assembler for the monolithic assembly of the coupled system of equations (see __main file__) +- +-```cpp +-// the assembler for the coupled problem +-using Assembler = MultiDomainFVAssembler<TheMultiDomainTraits, TheCouplingManager, DiffMethod::numeric, /*implicit?*/true>; +-auto assembler = std::make_shared<Assembler>( std::make_tuple(matrixProblem, fractureProblem), +- std::make_tuple(matrixFvGridGeometry, fractureFvGridGeometry), +- std::make_tuple(matrixGridVariables, fractureGridVariables), +- couplingManager, +- timeLoop); +-``` +- +-and a specialized implementation for Newton's method as non-linear solver: +- +-```cpp +-// the non-linear solver +-using NewtonSolver = Dumux::MultiDomainNewtonSolver<Assembler, LinearSolver, TheCouplingManager>; +-auto newtonSolver = std::make_shared<NewtonSolver>(assembler, linearSolver, couplingManager); +-``` +- +-## Running the program +- +-Head to the build directory, compile the exercise and execute the program by typing +- +-```bash +-cd build-cmake/exercises/exercise-fractures +-make exercise_fractures +-./exercise_fractures params.input +-``` +- +-Take a look at the results by opening the files `matrix.pvd` and `fractures.pvd` with _Paraview_. In order to increase the visibility of the fracture solution, you might want to apply the tube filter to it (e.g. with a tube radius of 0.2). The result should look like this: +- +- +- +-You can see how the fractures act as preferential flowpaths in the upwards movement of nitrogen due to their higher permeabilities. Additionally, you can observe comparatively high nitrogen saturations in the fracture tips as a result of the fractures acting as capillary traps due to the lower capillary pressures inside them. Consider turning them into capillary barriers, e.g. by setting __SpatialParams.VGAlpha = 1e-5__ in the __Fracture__ group in the input file (`params.input`). The nitrogen saturations in the fractures (especially in the fracture tips) should now be lower than in the surrounding matrix. Do not forget to reset __SpatialParams.VGAlpha = 1e-1__ in the input file after you are done. +- +-## Task A: Change the boundary conditions and disable gravity +- +-In order for the influence of the fractures to be more visible in the resulting pressure fields, __switch off gravity__ in the input file and change the boundary conditions in the two sub-problems (i.e. modify the function `boundaryTypesAtPos(...)` in `matrixproblem.hh` and `fractureproblem.hh`). We want to define the boundary conditions such that Neumann no-flow boundaries are used everywhere in the fracture domain and everywhere in the matrix domain except for the lower left and the upper right part of the lateral sides (i.e. $`x = 0 \wedge y < 25`$ and $`x = 100 \wedge y > 75`$). +- +-Furthermore, modify the function `dirichletAtPos(...)` in `matrixproblem.hh` such that an overpressure is added to the initial pressure and a non-zero saturation is applied on the right Dirichlet segment ($`x = 100 \wedge y > 75`$). For this you can use the private variables `boundaryOverPressure_` and `boundarySaturation_` which are stored in the matrix problem and are read from the input file. Compile and execute the program again. The resulting water pressure distribution should look like this: +- +- +- +-## Task B: Turn the fractures into barriers +- +-In this part, we want to change the parameterization of the fractures such that they act as both hydraulic and capillary barriers. In the `fracturespatialparams.hh` file you will see that the spatial parameters class already contains a set of parameters with the post-fix _Barrier_, which are read in from the input file in the class constructor. +- +-Use these as return values for porosity, permeability and fluid-matrix interaction parameters. Take a look at the results. +- +-You will see that only little nitrogen enters the domain, but if you display the pressure distribution in the matrix, you will notice that we cannot observe the pressure distribution we would expect. With the fracture permeabilities being this much lower than the matrix permeability, we would expect substantial pressure drops to be visible across them. +- +-This has to do with the chosen coupling condition. The assembly of the transfer fluxes between fracture and matrix across the matrix element facets occurs on the basis of interface conditions, which type can be set in the function `interiorBoundaryTypes(...)`. Note that this function has to be implemented by the matrix problem. Take a look at this function in the `matrixproblem.hh` file and find the following comment: +- +-```cpp +-//! Specifies the type of interior boundary condition +-BoundaryTypes interiorBoundaryTypes(const Element& element, const SubControlVolumeFace& scvf) const +-{ +- BoundaryTypes values; +- +- // Here we set the type of condition to be used on faces that coincide +- // with a fracture. If Neumann is specified, a flux continuity condition +- // on the basis of the normal fracture permeability is evaluated. If this +- // permeability is lower than that of the matrix, this approach is able to +- // represent the resulting pressure jump across the fracture. If Dirichlet is set, +- // the pressure jump across the fracture is neglected and the pressure inside +- // the fracture is directly applied at the interface between fracture and matrix. +- // This assumption is justified for highly-permeable fractures, but lead to erroneous +- // results for low-permeable fractures. +- // Here, we consider "open" fractures for which we cannot define a normal permeability +- // and for which the pressure jump across the fracture is neglectable. Thus, we set +- // the interior boundary conditions to Dirichlet. +- // IMPORTANT: Note that you will never be asked to set any values at the interior boundaries! +- // This simply chooses a different interface condition! +- // TODO dumux-course-task C +- // Change coupling conditions! +- values.setAllDirichlet(); +- +- return values; +-} +-``` +- +-To summarize: when using Neumann-type interior boundary conditions, flux continuity conditions are evaluated on the two interfaces (the two sides of the fracture) between fracture and matrix ([see e.g. Martin et al. (2005)](https://link.springer.com/article/10.1007/s10596-012-9302-6)). Note that these interfaces geometrically coincide but allow for both different pressures and fluxes to prevail on the two sides of the fracture. When using Dirichlet-type interior boundary conditions, the pressure in the fracture is assumed to be invariant in normal direction of the fracture and is directly applied at the interfaces. While the fluxes still can be different on the two interfaces, the pressure is now the same. Thus, this is only valid for fractures that are highly permeable in normal direction. +- +-This means we have to use Neumann-type interior boundary conditions here. Change this accordingly, compile and rerun the exercise again. You should now be able to see jumps in pressure in the matrix domain with an especially prominent one across the first vertical fracture (see image below). +- +- +- +-### Task C: Define both open fractures and barriers +- +-We have seen in the previous task what type of coupling or interior boundary conditions we have to use to describe open fractures and barriers. In this task, we want to define two fractures as barriers while considering the remaining fractures as "open". For this we want to make use of the domain markers available in gmsh. In the file `grids/complex.geo`, you will find that two different __physical indices__ were given to the different fracture segments: +- +-``` +-// conductive fractures get physical index 1 +-Physical Line(1) = {6,7,9,11,12,13,14,15,16,17,18,19,20,21,23,25}; +- +-// blocking fractures get physical index 2 +-Physical Line(2) = {8,10,22,24,26}; +-``` +- +-We now want to give all fracture elements that are tagged with domain marker 2 the properties for barriers and assign the parameters for open fractures to all other elements. The domain markers are read from the grid file and can be obtained from the `GridManager` subsequent to grid creation. The main file of this exercise has been implemented such that these markers are already passed to the spatial parameters of the sub-domains (see `main.cc`, search for `// pass the model parameter group to the spatial params`). In the spatial parameters you will find a convenience function which allows you to obtain the domain marker for a given element (see `fracturespatialparams.hh`): +- +-```cpp +-//! returns the domain marker for an element +-int getElementDomainMarker(const Element& element) const +-{ return gridDataPtr_->getElementDomainMarker(element); } +-``` +- +-The domain markers are also already added to the output (see `main.cc`, search for `// add domain markers to output`). To visualize them, open any of your previously produced results with _Paraview_ and take a look at them by selecting __domainMarker__. +- +-Adjust the functions for permeability, porosity and fluid-matrix interaction parameters in the `fracturespatialparams.hh` file such that they are selected depending on the domain marker of the elements. You will see in the results that the pressure jump across the first vertical fracture is now lower than before, because there are highly permeable fractures crossing it, allowing for a pressure release into the other parts of the domain. +- +-## Additional task: +- +-We can now also set the interior boundary condition depending on the domain marker of the fracture domain elements. What we would like to do is using Dirichlet-type coupling conditions for open fractures and Neumann-type coupling conditions for barriers. This means we need to obtain the domain marker of the fracture domain element to which a matrix element facet couples. Consider using the following piece of code in your `interiorBoundaryTypes(...)` function in `matrixproblem.hh` in order to realize this: +- +-```cpp +-// we need to obtain the domain marker of the fracture element that is coupled to this face +-// therefore we first get the fracture problem from the coupling manager. For this test, we +-// know that the fracture domain id is 1 (see main file) +-static constexpr auto fractureDomainId = Dune::index_constant<1>(); +-const auto& fractureProblem = couplingManager().problem(fractureDomainId); +- +-// use helper function in coupling manager to obtain the element this face couples to +-const auto fractureElement = couplingManager().getLowDimElement(element, scvf); +- +-// obtain marker from the spatial params of the fracture problem +-const auto fractureElementMarker = fractureProblem.spatialParams().getElementDomainMarker(fractureElement); +-``` +- +-Note that this will not have a visible effect on the results, because the permeability chosen for the open fractures is very high, leading to identical results for the two approaches. However, as mentioned above, the use of interior Neumann-type boundary conditions involves the evaluation of flux continuity conditions at the interfaces between fracture and matrix on the basis of the fracture normal permeability. Here, we are defining scalar permeabilities on the fracture with the result that the normal and tangential permeabilities are the same (if you want them to be different, you have to define tensorial permeabilities). The high value for the "open" fractures leads to negligible pressure jumps and produces results that are visually indistinguishable. But, keep in mind that from a physical perspective it makes little sense to define a permeability for an "open" fracture. Using Dirichlet-type interior boundary conditions at the interfaces to "open" fractures is usually the better choice. diff --git a/.patches/exercise-grids/exercise-grids.patch b/.patches/exercise-grids/exercise-grids.patch new file mode 100644 index 00000000..a42ceecc --- /dev/null +++ b/.patches/exercise-grids/exercise-grids.patch @@ -0,0 +1,351 @@ +diff -ruN exercises/exercise-grids/CMakeLists.txt exercises/solution/exercise-grids/CMakeLists.txt +--- exercises/exercise-grids/CMakeLists.txt 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-grids/CMakeLists.txt 2023-04-26 14:37:49.793150817 +0200 +@@ -1,5 +1,5 @@ + # the grid exercise simulation program +-dumux_add_test(NAME exercise_grids ++dumux_add_test(NAME exercise_grids_solution + SOURCES main.cc) + + # add a symlink for each input file +diff -ruN exercises/exercise-grids/grids/grid_structured.dgf exercises/solution/exercise-grids/grids/grid_structured.dgf +--- exercises/exercise-grids/grids/grid_structured.dgf 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-grids/grids/grid_structured.dgf 1970-01-01 01:00:00.000000000 +0100 +@@ -1,6 +0,0 @@ +-DGF +-Interval +-0 0 % first corner +-60 40 % second corner +-30 20 % cells in x and y direction +-# +diff -ruN exercises/exercise-grids/grids/grid_structured.geo exercises/solution/exercise-grids/grids/grid_structured.geo +--- exercises/exercise-grids/grids/grid_structured.geo 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-grids/grids/grid_structured.geo 1970-01-01 01:00:00.000000000 +0100 +@@ -1,30 +0,0 @@ +-X = 60; +-Y = 40; +-Res = 1; +- +-Point(1) = {0,0,0,Res}; +-Point(2) = {X,0,0,Res}; +-Point(3) = {X,Y,0,Res}; +-Point(4) = {0,Y,0,Res}; +- +-Line(5) = {1,2}; +-Line(6) = {2,3}; +-Line(7) = {3,4}; +-Line(8) = {4,1}; +- +-Line Loop(9) = {5,6,7,8}; +- +-Plane Surface(10) = 9; +- +-numCellsX = 24; +-numCellsY = 16; +- +-Transfinite Line{5} = numCellsX + 1; +-Transfinite Line{6} = numCellsY + 1; +-Transfinite Line{7} = numCellsX + 1; +-Transfinite Line{8} = numCellsY + 1; +- +-Transfinite Surface "*"; +-Recombine Surface "*"; +-Transfinite Volume "*"; +- +diff -ruN exercises/exercise-grids/main.cc exercises/solution/exercise-grids/main.cc +--- exercises/exercise-grids/main.cc 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-grids/main.cc 2023-04-26 14:37:49.797150837 +0200 +@@ -18,7 +18,7 @@ + *****************************************************************************/ + /*! + * \file +- * \brief The main file for the two-phase porousmediumflow problem of exercise-Grids ++ * \brief The main file for the two-phase porousmediumflow problem of exercise Grids + */ + #include <config.h> + +diff -ruN exercises/exercise-grids/params.input exercises/solution/exercise-grids/params.input +--- exercises/exercise-grids/params.input 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-grids/params.input 2023-04-26 14:37:49.797150837 +0200 +@@ -3,13 +3,34 @@ + TEnd = 3.154e9 # in seconds, i.e ten years + + [Grid] +-UpperRight = 60 40 +-Cells = 24 16 ++# UpperRight = 60 40 ++# Cells = 24 16 ++ + # TODO: Task 1: Globally refine your grid ++# Refinement = 2 ++ + # TODO: Task 2: Develop even grid input parameters that can be split into zones ++# Positions0 = 0 60 ++# Positions1 = 0 40 ++# Cells0 = 24 ++# Cells1 = 16 ++# Grading0 = 1.0 ++# Grading1 = 1.0 ++ + # TODO: Task 3: Using the zoning and grading parameters, redevelop your grid to optimize your refinement in the areas that matter. ++Positions0 = 0 40 60 ++Positions1 = 0 25 30 35 40 ++Cells0 = 10 14 ++Cells1 = 10 6 6 1 ++Grading0 = 1.0 -1.25 ++Grading1 = 1.0 -1.2 1.2 1.0 ++ + # TODO: Task 4: Run your simulation the provided structured grid file ./grids/grid_structured.msh ++# File = ./grids/grid_structured.msh ++# File = ./grids/grid_structured.dgf ++ + # TODO: Task 5: Run your simulation the provided structured grid file ./grids/grid_unstructured.msh ++#File = ./grids/grid_unstructured.msh + + [Problem] + Name = grid_exercise +@@ -27,4 +48,4 @@ + Aquifer.BrooksCoreyPcEntry = 1e4 # Pa + Aquifer.BrooksCoreyLambda = 2.0 + Aquifer.Swr = 0.2 +-Aquifer.Snr = 0.0 +\ No newline at end of file ++Aquifer.Snr = 0.0 +diff -ruN exercises/exercise-grids/problem.hh exercises/solution/exercise-grids/problem.hh +--- exercises/exercise-grids/problem.hh 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-grids/problem.hh 2023-04-26 14:37:49.797150837 +0200 +@@ -134,7 +134,7 @@ + } + + /*! +- * \brief Evaluate the boundary conditions for a neumann ++ * \brief Evaluate the boundary conditions for a Neumann + * boundary segment. + * + * \param globalPos The position of the integration point of the boundary segment. +diff -ruN exercises/exercise-grids/properties.hh exercises/solution/exercise-grids/properties.hh +--- exercises/exercise-grids/properties.hh 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-grids/properties.hh 2023-04-26 14:37:49.797150837 +0200 +@@ -17,9 +17,9 @@ + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ + /*! +- * \file ++ * \file + * +- * \brief The properties file for exercise-grids ++ * \brief The two-phase porousmediumflow properties file for exercise-grids + */ + + #ifndef DUMUX_EX_GRIDS_PROPERTIES_HH +@@ -36,7 +36,6 @@ + #include "problem.hh" + + namespace Dumux::Properties { +- + // define the TypeTag for this problem with a cell-centered two-point flux approximation spatial discretization. + // Create new type tags + namespace TTag { +@@ -44,17 +43,20 @@ + struct Injection2pCC { using InheritsFrom = std::tuple<Injection2p, CCTpfaModel>; }; + } // end namespace TTag + +-// Set the grid type ++//Set the grid type ++// template<class TypeTag> ++// struct Grid<TypeTag, TTag::Injection2p> { using type = Dune::YaspGrid<2>; }; ++// TODO: Task 2: Replace the above Grid Property definition with a more flexible grid (Use Dune::TensorProductCoordinates) + template<class TypeTag> +-struct Grid<TypeTag, TTag::Injection2p> { using type = Dune::YaspGrid<2>; }; +-// TODO: dumux-course-task 2 +-//Replace the above Grid Property definition with a more flexible grid (Use Dune::TensorProductCoordinates) +- +-// TODO: dumux-course-task 4 +-// Replace the above Grid Property definition to read in a external structured grid via a .msh file (Use Dune::ALUGrid and Dune:cube) ++struct Grid<TypeTag, TTag::Injection2p> { using type = Dune::YaspGrid<2, Dune::TensorProductCoordinates<double, 2> >; }; + +-// TODO: dumux-course-task 5 +-// Replace the above Grid Property definition to read in a external unstructured grid via a .msh file (Use Dune::ALUGrid and Dune::simplex) ++// TODO: Task 4: Replace the above Grid Property definition to read in a external structured grid via a .msh file (Use Dune::ALUGrid and Dune:cube) ++// template<class TypeTag> ++// struct Grid<TypeTag, TTag::Injection2p> { using type = Dune::ALUGrid<2, 2, Dune::cube, Dune::nonconforming>; }; ++ ++// TODO: Task 5: Replace the above Grid Property definition to read in a external unstructured grid via a .msh file (Use Dune::ALUGrid and Dune::simplex) ++// template<class TypeTag> ++// struct Grid<TypeTag, TTag::Injection2p> { using type = Dune::ALUGrid<2, 2, Dune::simplex, Dune::nonconforming>; }; + + // Set the problem property + template<class TypeTag> +diff -ruN exercises/exercise-grids/README.md exercises/solution/exercise-grids/README.md +--- exercises/exercise-grids/README.md 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-grids/README.md 1970-01-01 01:00:00.000000000 +0100 +@@ -1,169 +0,0 @@ +-# Exercise Grids (DuMuX course) +-<br> +- +-## Problem set-up +- +-Here we will expand on what we've covered in the basics exercise, and the problem set up will remain the same. +- +-## Preparing the exercise +- +-* Navigate to the directory `dumux-course/exercises/exercise-grids/` +- +-We'll be editing the following files during this exercise: +-* The __input file__: `params.input`, +-* the __problem file__: `problem.hh`, +-* and the __properties file__: `properties.hh`. +- +-Currently our grid is defined using the following input parameters in the input file: +- +-```bash +-[Grid] +-UpperRight = 60 40 +-Cells = 24 16 +-``` +- +-The upper right point of our domain is defined using the first parameter, and all directions are split into a number of equally sized cells defined by the second parameter. +- +-As you may have noticed, the resolution of our grid isn't quite optimal. A finer grid could help to show a more realistic picture of the physics. +- +-<br><br> +-### Task 1: Applying a Global Refinement +-<hr> +- +-A simple strategy for addressing this resolution issue would be a global refinement. +-In the `[Grid]` section of your input file, you can add a parameter `Refinement`. +-This will apply a global refinement to the grid you provided. A refinement of value 2 will split each cell into two cells per dimension. +- +-* > __Task 1__: Apply a global refinement of value 2. Make note of the increase in simulation run time. +- +-The simulation should take much more time. Luckily there are other ways to resolve your grid that will not be as computationally expensive. +- +-<br><br> +-### Task 2: Changing the Grid Type +-<hr> +- +-Up until now, we have used a basic uniform structured grid. This grid is provided in the Dune environment and is called the YASP grid (Yet Another Structured Parallel Grid). +- +-In the properties file (`properties.hh`), the grid properties are defined using the following command. +- +-```c++ +-// Set the grid type +-template<class TypeTag> +-struct Grid<TypeTag, TTag::Injection2pTypeTag> { using type = Dune::YaspGrid<2>; }; +-``` +- +-This sets the Grid, which belongs to the `Injection2pTypeTag` type tag, and calls the manager with a basic `YaspGrid` in the second dimension. +- +-If we look in the grid manager header file, `dumux/dumux/io/grid/gridmanager_yasp.hh`, +-we will see that there are a few grid development options. Until now, we have used the most basic definition. +- +-Let's change our grid manager to a slightly more complicated version. We will still call a YaspGrid, but can use the Dune coordinates system named TensorProductCoordinates rather than the basic version. The following class can be found in `dumux/dumux/io/grid/gridmanager_yasp.hh`. +- +-```c++ +-GridManager<Dune::YaspGrid<dim, Dune::TensorProductCoordinates<ctype, dim> >> +-``` +- +-A YaspGrid, of dimension `dim` is used, and the coordinate system `Dune::TensorProductCoordinates`, with arguments coordinate type `ctype` and dimension `dim`, is provided as a secondary argument. This allows us to switch to a grid format that allows for more cells in certain zones, and a cell grading. +- +-To use this format, we need to set the grid property in our properties file accordingly. Our problem dimension is 2 (`dim`), and we will provide the coordinates as doubles (`ctype`). +-To switch the property we should replace the `Dune::YaspGrid<2>` argument with `Dune::YaspGrid<dim, Dune::TensorProductCoordinates<ctype, dim> >`, and the `dim` and `ctype` with `2` and `double` +- +-In our input file (`params.input`), we can now specify more specific Grid sizing directions: Positions, Cells, and Grading, each of which can be defined per direction. +- +-In order to redevelop the exact same grid that was build beforehand, we can enter the following parameters to the Grid section of the input file. +- +-```bash +-[Grid] +-Positions0 = 0 60 +-Positions1 = 0 40 +-Cells0 = 24 +-Cells1 = 16 +-Grading0 = 1.0 +-Grading1 = 1.0 +-``` +- +-* > __Task 2__: Following the format shown above, change the grid type specified in `properties.hh`. Make the required changes to the input file and run the simulation. +- +-<br><br> +-### Task 3: Grid Zoning and Grading +-<hr> +- +-The `Positions0` and `Positions1` parameters will segment the grid into zones split at the values provided. In this case, the X-direction +-will be made of one zone between 0 and 60, and the Y-direction will be split into one zone between 0 and 40. +- +-```bash +-[Grid] +-Positions0 = 0 60 #x-coordinate +-Positions1 = 0 40 #y-coordinate +-``` +- +-The `Cells0` and `Cells1` parameters will define a number of cells to each of the zones. The first number provided will be applied to the first zone. +-The first zone in X-direction is split into 24 cells. The first zone in Y-direction is split into 16 cells. +- +-```bash +-[Grid] +-Cells0 = 24 #number of cells in x-direction +-Cells1 = 16 #number of cells in y-driection +-``` +- +-The `Grading0` and `Grading1` will apply a cell size grading throughout the zone in which it is applied. +-The value `1.0` will split the domain evenly such that each cell has the same size in this direction. +-Any other values will distribute the cell size unevenly throughout the domain. +-These sizes are defined such that the next cell in the domain will be sized as the grading value multiplied by the previous cell size. +- +-```bash +-[Grid] +-Grading0 = 1.0 +-Grading1 = 1.0 +-``` +-Positive values will make the cells closer to the start of the zone small, and those further from the start of the zone larger. +-Negative values will make the cells closer to the start of the zone large, and those further from the start of the zone smaller. +- +-Considering that the majority of the action in this simulation will occur along the interface between the aquifer and the aquitard, and along the injection axis, we should provide more resolution in these areas, and less in the other areas. +- +-* > __Task 3__: Using the Zone definitions described above, edit your grid to focus your simulation on the areas in greatest concern. +- +-One example of this would be the following definition. +- +-```bash +-[Grid] +-Positions0 = 0 45 60 +-Positions1 = 0 25 30 35 40 +-Cells0 = 9 10 +-Cells1 = 5 5 5 2 +-Grading0 = 1.0 -1.3 +-Grading1 = 1.0 -1.3 1.3 1.0 +-``` +- +-<br><br> +-### Task 4: Reading in a Structured Grid (*.dgf or *.msh grid files) +-<hr> +- +-DuMu$^\mathsf{X}$ can also read in grids from external files. There are two supported file types: `.dgf` grids, and `.msh` grids. +-* __`.dgf`__ grids (DUNE Grid Format grids) are developed within the dune environment and are often used in dumux tests. +-(see: [DUNE Grid Format grids](https://www.dune-project.org/doxygen/2.4.1/group__DuneGridFormatParser.html)) +-* __`.msh`__ grids are made from the open-source Gmsh software, and is also supported by dumux. +-(see: [Gmsh](http://gmsh.info//) and [Gmsh reference manual](http://gmsh.info//doc/texinfo/gmsh.html)) +- +-We can read in simple `.dgf` files using basic YaspGrids, but if we want to use more complicated external grids, we would need to use other grid formats, like `UGGrid` or `ALUGrid`. +-For this example, we can rewrite our Grid Properties to develop an `ALUGrid`. The grid manager class (see `dumux/dumux/io/grid/gridmanager_alu.hh`) takes the following form: +-```c++ +-GridManager<Dune::ALUGrid<dim, dimworld, elType, refinementType>> +-``` +-Our `dim` and `dimworld` will both be 2, we can use `Dune::cube` as our `elType` (element type), and for the `refinementType` we can use `Dune::nonconforming`. +- +-There are two external structured grid files located in the `grid/` folder (`grid_structured.msh` and `grid_structured.dgf`). A path to one of these grids should be included in the input file. +- +- +-* > __Task 4__: Edit the grid properties to set up an ALUGrid made of cubes. Call one of the structured meshes via the input file. +- +- +-<br><br> +-### Task 5: Reading in a Unstructured Grid (*.dgf or *.msh grid files) +-<hr> +- +-An example of an unstructured grid is located in the `grid/` folder (`grid_unstructured.msh`) +- +-This grid is made up of triangles (simplices of dimension 2). This element type can be set in the grid properties section. +- +-* > __Task 5__: Change the grid property element type to include `Dune::simplex` instead of `Dune::cube`. Read in the unstructured grid via the input file and run the simulation. diff --git a/.patches/exercise-mainfile/exercise-mainfile.patch b/.patches/exercise-mainfile/exercise-mainfile.patch new file mode 100644 index 00000000..0aea734b --- /dev/null +++ b/.patches/exercise-mainfile/exercise-mainfile.patch @@ -0,0 +1,982 @@ +diff -ruN exercises/exercise-mainfile/1pproblem.hh exercises/solution/exercise-mainfile/1pproblem.hh +--- exercises/exercise-mainfile/1pproblem.hh 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-mainfile/1pproblem.hh 2023-04-26 14:37:49.797150837 +0200 +@@ -24,9 +24,9 @@ + #ifndef DUMUX_EX_MAINFILE_ONEP_TEST_PROBLEM_HH + #define DUMUX_EX_MAINFILE_ONEP_TEST_PROBLEM_HH + ++#include <dumux/porousmediumflow/problem.hh> + #include <dumux/common/properties.hh> + #include <dumux/common/boundarytypes.hh> +-#include <dumux/porousmediumflow/problem.hh> + + namespace Dumux { + +@@ -55,7 +55,7 @@ + OnePTestProblem(std::shared_ptr<const GridGeometry> gridGeometry) + : ParentType(gridGeometry) + { +- FluidSystem::Component::init(/*tempMin=*/273.15, ++ FluidSystem::Component::init(/*tempMin=*/272.15, + /*tempMax=*/294.15, + /*numTemp=*/10, + /*pMin=*/1.0e4, +@@ -104,7 +104,6 @@ + { + return PrimaryVariables(1.0e5); + } +- + }; + + } // end namespace Dumux +diff -ruN exercises/exercise-mainfile/CMakeLists.txt exercises/solution/exercise-mainfile/CMakeLists.txt +--- exercises/exercise-mainfile/CMakeLists.txt 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-mainfile/CMakeLists.txt 2023-04-26 14:37:49.797150837 +0200 +@@ -1,12 +1,9 @@ + # the one-phase simulation program +-dumux_add_test(NAME exercise_mainfile_a +- SOURCES exercise1pamain.cc) ++dumux_add_test(NAME exercise_mainfile_a_solution ++ SOURCES exercise1pa_solution_main.cc ++ COMPILE_DEFINITIONS TYPETAG=OnePIncompressible) + +-dumux_add_test(NAME exercise_mainfile_b +- SOURCES exercise1pbmain.cc) +- +-dumux_add_test(NAME exercise_mainfile_c +- SOURCES exercise1pcmain.cc) ++# here, add the two-phase non-isothermal simulation program + + # add a symlink for each input file + add_input_file_links() +diff -ruN exercises/exercise-mainfile/exercise1pamain.cc exercises/solution/exercise-mainfile/exercise1pamain.cc +--- exercises/exercise-mainfile/exercise1pamain.cc 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-mainfile/exercise1pamain.cc 1970-01-01 01:00:00.000000000 +0100 +@@ -1,142 +0,0 @@ +-// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +-// vi: set et ts=4 sw=4 sts=4: +-/***************************************************************************** +- * See the file COPYING for full copying permissions. * +- * * +- * This program is free software: you can redistribute it and/or modify * +- * it under the terms of the GNU General Public License as published by * +- * the Free Software Foundation, either version 3 of the License, or * +- * (at your option) any later version. * +- * * +- * This program is distributed in the hope that it will be useful, * +- * but WITHOUT ANY WARRANTY; without even the implied warranty of * +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +- * GNU General Public License for more details. * +- * * +- * You should have received a copy of the GNU General Public License * +- * along with this program. If not, see <http://www.gnu.org/licenses/>. * +- *****************************************************************************/ +-/*! +- * \file +- * +- * \brief test for the one-phase CC model +- */ +-#include <config.h> +- +-#include "properties.hh" +- +-#include <iostream> +- +-#include <dune/common/timer.hh> +- +-#include <dumux/common/initialize.hh> +-#include <dumux/common/properties.hh> +-#include <dumux/common/parameters.hh> +- +-#include <dumux/linear/istlsolvers.hh> +-#include <dumux/linear/linearalgebratraits.hh> +-#include <dumux/linear/linearsolvertraits.hh> +- +-#include <dumux/assembly/fvassembler.hh> +- +-#include <dumux/io/vtkoutputmodule.hh> +-#include <dumux/io/grid/gridmanager_yasp.hh> +- +-int main(int argc, char** argv) +-{ +- using namespace Dumux; +- +- // define the type tag for this problem +- using TypeTag = Properties::TTag::OnePIncompressible; +- +- //////////////////////////////////////////////////////////// +- //////////////////////////////////////////////////////////// +- +- // initialize MPI+x, finalize is done automatically on exit +- Dumux::initialize(argc, argv); +- +- // initialize parameter tree +- Parameters::init(argc, argv); +- +- ////////////////////////////////////////////////////////////////////// +- // try to create a grid (from the given grid file or the input file) +- ///////////////////////////////////////////////////////////////////// +- +- GridManager<GetPropType<TypeTag, Properties::Grid>> gridManager; +- gridManager.init(); +- +- //////////////////////////////////////////////////////////// +- // run stationary linear problem on this grid +- //////////////////////////////////////////////////////////// +- +- // 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 x(gridGeometry->numDofs()); +- +- // the grid variables +- using GridVariables = GetPropType<TypeTag, Properties::GridVariables>; +- auto gridVariables = std::make_shared<GridVariables>(problem, gridGeometry); +- gridVariables->init(x); +- +- // initialize the vtk output module +- VtkOutputModule<GridVariables, SolutionVector> vtkWriter(*gridVariables, x, problem->name()); +- using VelocityOutput = GetPropType<TypeTag, Properties::VelocityOutput>; +- vtkWriter.addVelocityOutput(std::make_shared<VelocityOutput>(*gridVariables)); +- using IOFields = GetPropType<TypeTag, Properties::IOFields>; +- IOFields::initOutputModule(vtkWriter); //!< Add model specific output fields +- vtkWriter.write(0.0); +- +- Dune::Timer timer; +- +- // TODO: dumux-course-task 3 +- // Change the differentiation method to analytic by changing from DiffMethod::numeric to DiffMethod::analytic +- +- // the assembler for stationary problems +- using Assembler = FVAssembler<TypeTag, DiffMethod::numeric>; +- auto assembler = std::make_shared<Assembler>(problem, gridGeometry, gridVariables); +- +- // the linear solver +- using LinearSolver = ILUBiCGSTABIstlSolver<LinearSolverTraits<GridGeometry>, LinearAlgebraTraitsFromAssembler<Assembler>>; +- auto linearSolver = std::make_shared<LinearSolver>(gridGeometry->gridView(), gridGeometry->dofMapper()); +- +- // the discretization matrices for stationary linear problems +- using JacobianMatrix = GetPropType<TypeTag, Properties::JacobianMatrix>; +- auto A = std::make_shared<JacobianMatrix>(); +- auto r = std::make_shared<SolutionVector>(); +- assembler->setLinearSystem(A, r); +- assembler->assembleJacobianAndResidual(x); +- +- // we solve Ax = -r to save update and copy +- (*r) *= -1.0; +- linearSolver->solve(*A, x, *r); +- +- // the grid variables need to be up to date for subsequent output +- gridVariables->update(x); +- +- // write vtk output +- vtkWriter.write(1.0); +- +- timer.stop(); +- +- const auto& comm = leafGridView.comm(); +- std::cout << "Simulation took " << timer.elapsed() << " seconds on " +- << comm.size() << " processes.\n" +- << "The cumulative CPU time was " << timer.elapsed()*comm.size() << " seconds.\n"; +- +- if (leafGridView.comm().rank() == 0) +- Parameters::print(); +- +- return 0; +- +-}// end main +diff -ruN exercises/exercise-mainfile/exercise1pa_solution_main.cc exercises/solution/exercise-mainfile/exercise1pa_solution_main.cc +--- exercises/exercise-mainfile/exercise1pa_solution_main.cc 1970-01-01 01:00:00.000000000 +0100 ++++ exercises/solution/exercise-mainfile/exercise1pa_solution_main.cc 2023-04-26 14:37:49.797150837 +0200 +@@ -0,0 +1,143 @@ ++// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- ++// vi: set et ts=4 sw=4 sts=4: ++/***************************************************************************** ++ * See the file COPYING for full copying permissions. * ++ * * ++ * This program is free software: you can redistribute it and/or modify * ++ * it under the terms of the GNU General Public License as published by * ++ * the Free Software Foundation, either version 3 of the License, or * ++ * (at your option) any later version. * ++ * * ++ * This program is distributed in the hope that it will be useful, * ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of * ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * ++ * GNU General Public License for more details. * ++ * * ++ * You should have received a copy of the GNU General Public License * ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ++ *****************************************************************************/ ++/*! ++ * \file ++ * ++ * \brief test for the one-phase CC model ++ */ ++#include <config.h> ++ ++#include "1pproblem.hh" ++#include "properties.hh" ++ ++#include <iostream> ++ ++#include <dune/common/timer.hh> ++ ++#include <dumux/common/initialize.hh> ++#include <dumux/common/properties.hh> ++#include <dumux/common/parameters.hh> ++ ++#include <dumux/linear/istlsolvers.hh> ++#include <dumux/linear/linearalgebratraits.hh> ++#include <dumux/linear/linearsolvertraits.hh> ++ ++#include <dumux/assembly/fvassembler.hh> ++ ++#include <dumux/io/vtkoutputmodule.hh> ++#include <dumux/io/grid/gridmanager_yasp.hh> ++ ++int main(int argc, char** argv) ++{ ++ using namespace Dumux; ++ ++ // define the type tag for this problem ++ using TypeTag = Properties::TTag::OnePIncompressible; ++ ++ //////////////////////////////////////////////////////////// ++ //////////////////////////////////////////////////////////// ++ ++ // initialize MPI+x, finalize is done automatically on exit ++ Dumux::initialize(argc, argv); ++ ++ // initialize parameter tree ++ Parameters::init(argc, argv); ++ ++ ////////////////////////////////////////////////////////////////////// ++ // try to create a grid (from the given grid file or the input file) ++ ///////////////////////////////////////////////////////////////////// ++ ++ GridManager<GetPropType<TypeTag, Properties::Grid>> gridManager; ++ gridManager.init(); ++ ++ //////////////////////////////////////////////////////////// ++ // run stationary linear problem on this grid ++ //////////////////////////////////////////////////////////// ++ ++ // 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 x(gridGeometry->numDofs()); ++ ++ // the grid variables ++ using GridVariables = GetPropType<TypeTag, Properties::GridVariables>; ++ auto gridVariables = std::make_shared<GridVariables>(problem, gridGeometry); ++ gridVariables->init(x); ++ ++ // initialize the vtk output module ++ VtkOutputModule<GridVariables, SolutionVector> vtkWriter(*gridVariables, x, problem->name()); ++ using VelocityOutput = GetPropType<TypeTag, Properties::VelocityOutput>; ++ vtkWriter.addVelocityOutput(std::make_shared<VelocityOutput>(*gridVariables)); ++ using IOFields = GetPropType<TypeTag, Properties::IOFields>; ++ IOFields::initOutputModule(vtkWriter); //!< Add model specific output fields ++ vtkWriter.write(0.0); ++ ++ Dune::Timer timer; ++ ++ // TODO: dumux-course-task ++ // change the differentiation method to analytic by changing from DiffMethod::numeric to DiffMethod::analytic ++ ++ // the assembler for stationary problems ++ using Assembler = FVAssembler<TypeTag, DiffMethod::analytic>; ++ auto assembler = std::make_shared<Assembler>(problem, gridGeometry, gridVariables); ++ ++ // the linear solver ++ using LinearSolver = ILUBiCGSTABIstlSolver<LinearSolverTraits<GridGeometry>, LinearAlgebraTraitsFromAssembler<Assembler>>; ++ auto linearSolver = std::make_shared<LinearSolver>(gridGeometry->gridView(), gridGeometry->dofMapper()); ++ ++ // the discretization matrices for stationary linear problems ++ using JacobianMatrix = GetPropType<TypeTag, Properties::JacobianMatrix>; ++ auto A = std::make_shared<JacobianMatrix>(); ++ auto r = std::make_shared<SolutionVector>(); ++ assembler->setLinearSystem(A, r); ++ assembler->assembleJacobianAndResidual(x); ++ ++ // we solve Ax = -r to save update and copy ++ (*r) *= -1.0; ++ linearSolver->solve(*A, x, *r); ++ ++ // the grid variables need to be up to date for subsequent output ++ gridVariables->update(x); ++ ++ // write vtk output ++ vtkWriter.write(1.0); ++ ++ timer.stop(); ++ ++ const auto& comm = leafGridView.comm(); ++ std::cout << "Simulation took " << timer.elapsed() << " seconds on " ++ << comm.size() << " processes.\n" ++ << "The cumulative CPU time was " << timer.elapsed()*comm.size() << " seconds.\n"; ++ ++ if (leafGridView.comm().rank() == 0) ++ Parameters::print(); ++ ++ return 0; ++ ++}// end main +diff -ruN exercises/exercise-mainfile/exercise1pbmain.cc exercises/solution/exercise-mainfile/exercise1pbmain.cc +--- exercises/exercise-mainfile/exercise1pbmain.cc 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-mainfile/exercise1pbmain.cc 1970-01-01 01:00:00.000000000 +0100 +@@ -1,132 +0,0 @@ +-// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +-// vi: set et ts=4 sw=4 sts=4: +-/***************************************************************************** +- * See the file COPYING for full copying permissions. * +- * * +- * This program is free software: you can redistribute it and/or modify * +- * it under the terms of the GNU General Public License as published by * +- * the Free Software Foundation, either version 3 of the License, or * +- * (at your option) any later version. * +- * * +- * This program is distributed in the hope that it will be useful, * +- * but WITHOUT ANY WARRANTY; without even the implied warranty of * +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +- * GNU General Public License for more details. * +- * * +- * You should have received a copy of the GNU General Public License * +- * along with this program. If not, see <http://www.gnu.org/licenses/>. * +- *****************************************************************************/ +-/*! +- * \file +- * +- * \brief test for the one-phase CC model +- */ +-#include <config.h> +- +-#include "properties.hh" +- +-#include <iostream> +- +-#include <dune/common/timer.hh> +- +-#include <dumux/common/initialize.hh> +-#include <dumux/common/properties.hh> +-#include <dumux/common/parameters.hh> +- +-#include <dumux/linear/istlsolvers.hh> +-#include <dumux/linear/linearalgebratraits.hh> +-#include <dumux/linear/linearsolvertraits.hh> +-#include <dumux/nonlinear/newtonsolver.hh> +- +-#include <dumux/assembly/fvassembler.hh> +- +-#include <dumux/io/vtkoutputmodule.hh> +-#include <dumux/io/grid/gridmanager_yasp.hh> +- +-int main(int argc, char** argv) +-{ +- using namespace Dumux; +- +- // define the type tag for this problem +- using TypeTag = Properties::TTag::OnePCompressible; +- +- //////////////////////////////////////////////////////////// +- //////////////////////////////////////////////////////////// +- +- // initialize MPI+x, finalize is done automatically on exit +- Dumux::initialize(argc, argv); +- +- // initialize parameter tree +- Parameters::init(argc, argv); +- +- ////////////////////////////////////////////////////////////////////// +- // try to create a grid (from the given grid file or the input file) +- ///////////////////////////////////////////////////////////////////// +- +- GridManager<GetPropType<TypeTag, Properties::Grid>> gridManager; +- gridManager.init(); +- +- //////////////////////////////////////////////////////////// +- // run stationary non-linear problem on this grid +- //////////////////////////////////////////////////////////// +- +- // 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 x(gridGeometry->numDofs()); +- +- // the grid variables +- using GridVariables = GetPropType<TypeTag, Properties::GridVariables>; +- auto gridVariables = std::make_shared<GridVariables>(problem, gridGeometry); +- gridVariables->init(x); +- +- // initialize the vtk output module +- VtkOutputModule<GridVariables, SolutionVector> vtkWriter(*gridVariables, x, problem->name()); +- using VelocityOutput = GetPropType<TypeTag, Properties::VelocityOutput>; +- vtkWriter.addVelocityOutput(std::make_shared<VelocityOutput>(*gridVariables)); +- using IOFields = GetPropType<TypeTag, Properties::IOFields>; +- IOFields::initOutputModule(vtkWriter); //!< Add model specific output fields +- vtkWriter.write(0.0); +- +- Dune::Timer timer; +- // the assembler for stationary problems +- using Assembler = FVAssembler<TypeTag, DiffMethod::numeric>; +- auto assembler = std::make_shared<Assembler>(problem, gridGeometry, gridVariables); +- +- // the linear solver +- using LinearSolver = ILUBiCGSTABIstlSolver<LinearSolverTraits<GridGeometry>, LinearAlgebraTraitsFromAssembler<Assembler>>; +- auto linearSolver = std::make_shared<LinearSolver>(gridGeometry->gridView(), gridGeometry->dofMapper()); +- +- // the non-linear solver +- using NewtonSolver = Dumux::NewtonSolver<Assembler, LinearSolver>; +- NewtonSolver nonLinearSolver(assembler, linearSolver); +- +- // linearize & solve +- nonLinearSolver.solve(x); +- +- // write vtk output +- vtkWriter.write(1.0); +- +- timer.stop(); +- +- const auto& comm = leafGridView.comm(); +- std::cout << "Simulation took " << timer.elapsed() << " seconds on " +- << comm.size() << " processes.\n" +- << "The cumulative CPU time was " << timer.elapsed()*comm.size() << " seconds.\n"; +- +- if (leafGridView.comm().rank() == 0) +- Parameters::print(); +- +- return 0; +- +-}// end main +diff -ruN exercises/exercise-mainfile/exercise1pcmain.cc exercises/solution/exercise-mainfile/exercise1pcmain.cc +--- exercises/exercise-mainfile/exercise1pcmain.cc 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-mainfile/exercise1pcmain.cc 1970-01-01 01:00:00.000000000 +0100 +@@ -1,158 +0,0 @@ +-// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +-// vi: set et ts=4 sw=4 sts=4: +-/***************************************************************************** +- * See the file COPYING for full copying permissions. * +- * * +- * This program is free software: you can redistribute it and/or modify * +- * it under the terms of the GNU General Public License as published by * +- * the Free Software Foundation, either version 3 of the License, or * +- * (at your option) any later version. * +- * * +- * This program is distributed in the hope that it will be useful, * +- * but WITHOUT ANY WARRANTY; without even the implied warranty of * +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +- * GNU General Public License for more details. * +- * * +- * You should have received a copy of the GNU General Public License * +- * along with this program. If not, see <http://www.gnu.org/licenses/>. * +- *****************************************************************************/ +-/*! +- * \file +- * +- * \brief test for the one-phase CC model +- */ +-#include <config.h> +- +-#include "properties.hh" +- +-#include <iostream> +- +-#include <dumux/common/initialize.hh> +-#include <dumux/common/properties.hh> +-#include <dumux/common/parameters.hh> +- +-#include <dumux/linear/istlsolvers.hh> +-#include <dumux/linear/linearalgebratraits.hh> +-#include <dumux/linear/linearsolvertraits.hh> +-#include <dumux/nonlinear/newtonsolver.hh> +- +-#include <dumux/assembly/fvassembler.hh> +- +-#include <dumux/io/vtkoutputmodule.hh> +-#include <dumux/io/grid/gridmanager_yasp.hh> +- +-int main(int argc, char** argv) +-{ +- using namespace Dumux; +- +- // define the type tag for this problem +- using TypeTag = Properties::TTag::OnePCompressible; +- +- //////////////////////////////////////////////////////////// +- //////////////////////////////////////////////////////////// +- +- // initialize MPI+x, finalize is done automatically on exit +- Dumux::initialize(argc, argv); +- +- // initialize parameter tree +- Parameters::init(argc, argv); +- +- ////////////////////////////////////////////////////////////////////// +- // try to create a grid (from the given grid file or the input file) +- ///////////////////////////////////////////////////////////////////// +- +- GridManager<GetPropType<TypeTag, Properties::Grid>> gridManager; +- gridManager.init(); +- +- //////////////////////////////////////////////////////////// +- // run instationary non-linear problem on this grid +- //////////////////////////////////////////////////////////// +- +- // 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 x(gridGeometry->numDofs()); +- problem->applyInitialSolution(x); +- auto xOld = x; +- +- // the grid variables +- using GridVariables = GetPropType<TypeTag, Properties::GridVariables>; +- auto gridVariables = std::make_shared<GridVariables>(problem, gridGeometry); +- gridVariables->init(x); +- +- // initialize the vtk output module +- VtkOutputModule<GridVariables, SolutionVector> vtkWriter(*gridVariables, x, problem->name()); +- using VelocityOutput = GetPropType<TypeTag, Properties::VelocityOutput>; +- vtkWriter.addVelocityOutput(std::make_shared<VelocityOutput>(*gridVariables)); +- using IOFields = GetPropType<TypeTag, Properties::IOFields>; +- IOFields::initOutputModule(vtkWriter); //!< Add model specific output fields +- vtkWriter.write(0.0); +- +- // get some time loop parameters +- using Scalar = GetPropType<TypeTag, Properties::Scalar>; +- auto tEnd = getParam<Scalar>("TimeLoop.TEnd"); +- auto dt = getParam<Scalar>("TimeLoop.DtInitial"); +- 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 instationary problem +- using Assembler = FVAssembler<TypeTag, DiffMethod::numeric>; +- auto assembler = std::make_shared<Assembler>(problem, gridGeometry, gridVariables, timeLoop, xOld); +- +- // the linear solver +- using LinearSolver = ILUBiCGSTABIstlSolver<LinearSolverTraits<GridGeometry>, LinearAlgebraTraitsFromAssembler<Assembler>>; +- auto linearSolver = std::make_shared<LinearSolver>(gridGeometry->gridView(), gridGeometry->dofMapper()); +- +- // the non-linear solver +- using NewtonSolver = Dumux::NewtonSolver<Assembler, LinearSolver>; +- NewtonSolver nonLinearSolver(assembler, linearSolver); +- +- // set some check points for the time loop +- timeLoop->setPeriodicCheckPoint(tEnd/10.0); +- +- // time loop +- timeLoop->start(); do +- { +- // linearize & solve +- nonLinearSolver.solve(x, *timeLoop); +- +- // make the new solution the old solution +- xOld = x; +- gridVariables->advanceTimeStep(); +- +- // advance to the time loop to the next step +- timeLoop->advanceTimeStep(); +- +- // write vtk output +- if (timeLoop->isCheckPoint()) +- vtkWriter.write(timeLoop->time()); +- +- // report statistics of this time step +- timeLoop->reportTimeStep(); +- +- // set new dt as suggested by the newton solver +- timeLoop->setTimeStepSize(nonLinearSolver.suggestTimeStepSize(timeLoop->timeStepSize())); +- +- } while (!timeLoop->finished()); +- +- timeLoop->finalize(leafGridView.comm()); +- +- if (leafGridView.comm().rank() == 0) +- Parameters::print(); +- +- return 0; +- +-}// end main +diff -ruN exercises/exercise-mainfile/exercise_mainfile_a.input exercises/solution/exercise-mainfile/exercise_mainfile_a.input +--- exercises/exercise-mainfile/exercise_mainfile_a.input 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-mainfile/exercise_mainfile_a.input 1970-01-01 01:00:00.000000000 +0100 +@@ -1,18 +0,0 @@ +-[Grid] +-LowerLeft = 0 0 +-UpperRight = 1 1 +-Cells = 20 20 +- +-[Problem] +-Name = 1p_incompressible_stationary +- +-[SpatialParams] +-LensLowerLeft = 0.2 0.2 +-LensUpperRight = 0.8 0.8 +- +-Permeability = 1e-10 # [m^2] +-PermeabilityLens = 1e-12 # [m^2] +- +-[Assembly.NumericDifference] +-PriVarMagnitude = 1e5 +-BaseEpsilon = 1e-10 +diff -ruN exercises/exercise-mainfile/exercise_mainfile_a_solution.input exercises/solution/exercise-mainfile/exercise_mainfile_a_solution.input +--- exercises/exercise-mainfile/exercise_mainfile_a_solution.input 1970-01-01 01:00:00.000000000 +0100 ++++ exercises/solution/exercise-mainfile/exercise_mainfile_a_solution.input 2023-04-26 14:37:49.797150837 +0200 +@@ -0,0 +1,17 @@ ++[Grid] ++LowerLeft = 0 0 ++UpperRight = 1 1 ++Cells = 20 20 ++ ++[Problem] ++Name = 1p_incompressible_stationary ++ ++[SpatialParams] ++LensLowerLeft = 0.2 0.2 ++LensUpperRight = 0.8 0.8 ++ ++Permeability = 1e-10 # [m^2] ++PermeabilityLens = 1e-12 # [m^2] ++ ++[Assembly.NumericDifference] ++PriVarMagnitude = 1e5 +diff -ruN exercises/exercise-mainfile/exercise_mainfile_b.input exercises/solution/exercise-mainfile/exercise_mainfile_b.input +--- exercises/exercise-mainfile/exercise_mainfile_b.input 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-mainfile/exercise_mainfile_b.input 1970-01-01 01:00:00.000000000 +0100 +@@ -1,17 +0,0 @@ +-[Grid] +-LowerLeft = 0 0 +-UpperRight = 1 1 +-Cells = 20 20 +- +-[Problem] +-Name = 1p_compressible_stationary +- +-[SpatialParams] +-LensLowerLeft = 0.2 0.2 +-LensUpperRight = 0.8 0.8 +- +-Permeability = 1e-10 # [m^2] +-PermeabilityLens = 1e-12 # [m^2] +- +-[Assembly.NumericDifference] +-PriVarMagnitude = 1e5 +diff -ruN exercises/exercise-mainfile/exercise_mainfile_c.input exercises/solution/exercise-mainfile/exercise_mainfile_c.input +--- exercises/exercise-mainfile/exercise_mainfile_c.input 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-mainfile/exercise_mainfile_c.input 1970-01-01 01:00:00.000000000 +0100 +@@ -1,21 +0,0 @@ +-[TimeLoop] +-TEnd = 0.1 +-DtInitial = 0.01 +- +-[Grid] +-LowerLeft = 0 0 +-UpperRight = 1 1 +-Cells = 20 20 +- +-[Problem] +-Name = 1p_compressible_instationary +- +-[SpatialParams] +-LensLowerLeft = 0.2 0.2 +-LensUpperRight = 0.8 0.8 +- +-Permeability = 1e-10 # [m^2] +-PermeabilityLens = 1e-12 # [m^2] +- +-[Assembly.NumericDifference] +-PriVarMagnitude = 1e5 +\ No newline at end of file +diff -ruN exercises/exercise-mainfile/properties.hh exercises/solution/exercise-mainfile/properties.hh +--- exercises/exercise-mainfile/properties.hh 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-mainfile/properties.hh 2023-04-26 15:09:50.146538947 +0200 +@@ -35,20 +35,19 @@ + #include <dumux/discretization/ccmpfa.hh> + #include <dumux/discretization/box.hh> + +- + #include <dumux/porousmediumflow/1p/model.hh> +-// TODO: dumux-course-task 3 +-// uncomment the incompressiblelocalresidual which is a specialization of the standard immiscible localresidual for one phase incompressible cases and provides an analytic jacobian. +-// #include <dumux/porousmediumflow/1p/incompressiblelocalresidual.hh> ++// TODO: dumux-course-task ++// uncomment the incompressiblelocalresidual which is a specialization of the standard immisible localresidual for one phase incompressible cases and provides an analytic jacobian. ++#include <dumux/porousmediumflow/1p/incompressiblelocalresidual.hh> + + #include "1pspatialparams.hh" + #include "1pproblem.hh" + + namespace Dumux::Properties { + +-// Create the new type tag nodes: ++// Create the new type tag nodes. + // Here we define the incompressible type tag as well as the compressible type tag. +-// The incompressible uses a different fluidsystem than the compressible ++// The incompressible uses a different fluidsystem than the compressible. + namespace TTag { + struct OnePBase { using InheritsFrom = std::tuple<OneP>; }; + struct OnePIncompressible { using InheritsFrom = std::tuple<OnePBase, CCTpfaModel>; }; +@@ -79,10 +78,10 @@ + using type = FluidSystems::OnePLiquid<Scalar, Components::SimpleH2O<Scalar> >; + }; + +-// TODO: dumux-course-task 3 ++// TODO: dumux-course-task + // set the OneP Incompressible local residual for the OnePIncompressible type tag. This provides an analytic jacobian to be used for the analytic solution. Change that by setting: +-// template<class TypeTag> +-// struct LocalResidual<TypeTag, TTag::OnePIncompressible> { using type = OnePIncompressibleLocalResidual<TypeTag>; }; ++template<class TypeTag> ++struct LocalResidual<TypeTag, TTag::OnePIncompressible> { using type = OnePIncompressibleLocalResidual<TypeTag>; }; + + + // the fluid system for compressible tests +diff -ruN exercises/exercise-mainfile/README.md exercises/solution/exercise-mainfile/README.md +--- exercises/exercise-mainfile/README.md 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-mainfile/README.md 1970-01-01 01:00:00.000000000 +0100 +@@ -1,203 +0,0 @@ +-# Exercise Mainfiles (DuMuX course) +-<br> +- +-## Problem set-up +- +-This exercise will make you familiar with the program sequence in DuMu<sup>X</sup> and how different levels of complexity can be realized in the main file according to the complexity of your physical problem. +- +-In order to do so, there are three examples of one phase flow problems. Two examples (a and b) are stationary problems and the third example (c) is an instationary problem. +- +-The stationary examples differ in the `FluidSystem` they are using, which means they differ in the fluid properties (e.g. density, thermal conductivity etc). The first problem (a) uses an incompressible fluid, i.e. the density does not change when pressure changes. This makes it possible to solve the system linearly. The second problem uses a compressible fluid, that means the density is a function of pressure and we need to use a nonlinear solver. +- +-To summarize, the problems differ in: +-* exercise mainfile a: a one-phase incompressible, stationary problem +-* exercise mainfile b: a one-phase compressible, stationary problem +-* exercise mainfile c: a one-phase compressible, instationary problem +- +-The problem set-up for all three examples is always the same: It is a two dimensional problem and the domain is $1 m$ by $1 m$. It is a heterogeneous set-up with a lens in the middle of the domain which has a lower permeability ($1\cdot 10^{-12} m^2$ compared to $1\cdot 10^{-10} m^2$ in the rest of the domain). +- +-<img src="https://git.iws.uni-stuttgart.de/dumux-repositories/dumux-course/raw/master/exercises/extradoc/exercise1_1p_setup.png" width="1000"> +- +-In the beginning, there is a uniform pressure of $1\cdot 10^5 Pa$ in the whole domain. On the top and the bottom border, dirichlet boundary conditions are set with a pressure of $1\cdot 10^5 Pa$ on top and $2 \cdot 10^5 Pa$ on the bottom. At the sides, there is no in- or outflow and there are no source terms. +- +-## Preparing the exercise +- +-* Navigate to the directory `dumux-course/exercises/exercise-mainfile` +- +-<br><br> +-### Task 1: Getting familiar with the code +-<hr> +- +-Locate all the files you will need for this exercise +-* The __main file__ for the __incompressible, stationary__ problem : `exercise1pamain.cc` +-* The __main file__ for the __compressible, stationary__ problem : `exercise1pbmain.cc` +-* The __main file__ for the __compressible, instationary__ problem : `exercise1pcmain.cc` +-* The shared __problem file__: `1pproblem.hh` +-* The shared __properties file__: `properties.hh` +-* The shared __spatial parameters file__: `1pspatialparams.hh` +-* The __input file__ for the __incompressible, stationary__ problem: `exercise_mainfile_a.input` +-* The __input file__ for the __compressible, stationary__ problem: `exercise_mainfile_b.input` +-* The __input file__ for the __compressible, instationary__ problem: `exercise_mainfile_c.input` +- +-Please pay special attention to the similarities and differences in the three main files. +-The first main file is solved linearly and does not need a newton solver or any other nonlinear solver method. +-The second problem is a nonlinear problem and uses newton's method to solve the system. +-The third problem is nonlinear and additionally instationary. +-Therefore, a time loop needs to be included in the main file. +- +-The general structure of any main file in DuMuX is: +- +-* the specific problem `TypeTag` is defined for the problem. This example shows the `TypeTag` for the `CompressibleProblem`: +- +-```c++ +-// define the type tag for this problem +-using TypeTag = Properties::TTag::OnePCompressible; +-``` +-The `TypeTag` is created in the `properties.hh`. There, you can see that it inherits from the __OneP__ and additionally from the __CCTpfaModel__. +-The latter defines the discretization method, which is in this case the cell-centered tpfa method. +- +-* A gridmanager tries to create the grid either from a grid file or the input file: +- +-```c++ +-GridManager<GetPropType<TypeTag, Properties::Grid>> gridManager; +-gridManager.init(); +-``` +-* We create the finite volume grid geometry, the problem, solution vector and the grid variables and initialize them. +-Additionally, we initialize the vtk output. Each model has a predefined model specific output with relevant parameters for that model: +- +-```c++ +-// 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>(fvGridGeometry); +- +-// the solution vector +-using SolutionVector = GetPropType<TypeTag, Properties::SolutionVector>; +-SolutionVector x(fvGridGeometry->numDofs()); +- +-// the grid variables +-using GridVariables = GetPropType<TypeTag, Properties::GridVariables>; +-auto gridVariables = std::make_shared<GridVariables>(problem, fvGridGeometry); +-gridVariables->init(x); +- +-// initialize the vtk output module +-VtkOutputModule<GridVariables, SolutionVector> vtkWriter(*gridVariables, x, problem->name()); +-using VelocityOutput = GetPropType<TypeTag, Properties::VelocityOutput>; +-vtkWriter.addVelocityOutput(std::make_shared<VelocityOutput>(*gridVariables)); +-using IOFields = GetPropType<TypeTag, Properties::IOFields>; +-IOFields::initOutputModule(vtkWriter); //!< Add model specific output fields +-vtkWriter.write(0.0); +-``` +- +-* Then, we need to assemble and solve the system. Depending on the problem, this can be done with a linear solver or a nonlinear solver. +-If the problem is time dependent, we additionally need a time loop. An example for that is given in `exercise1pcmain.cc`: +- +-```c++ +-// get some time loop parameters +-using Scalar = GetPropType<TypeTag, Properties::Scalar>; +-auto tEnd = getParam<Scalar>("TimeLoop.TEnd"); +-auto dt = getParam<Scalar>("TimeLoop.DtInitial"); +-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 instationary problem +-using Assembler = FVAssembler<TypeTag, DiffMethod::numeric>; +-auto assembler = std::make_shared<Assembler>(problem, fvGridGeometry, gridVariables, timeLoop); +- +-// the linear solver +-using LinearSolver = ILUBiCGSTABIstlSolver<LinearSolverTraits<GridGeometry>, LinearAlgebraTraitsFromAssembler<Assembler>>; +-auto linearSolver = std::make_shared<LinearSolver>(gridGeometry->gridView(), gridGeometry->dofMapper()); +- +-// the non-linear solver +-using NewtonSolver = Dumux::NewtonSolver<Assembler, LinearSolver>; +-NewtonSolver nonLinearSolver(assembler, linearSolver); +- +-// set some check points for the time loop +-timeLoop->setPeriodicCheckPoint(tEnd/10.0); +- +-// time loop +-timeLoop->start(); do +-{ +- // linearize & solve +- nonLinearSolver.solve(x, *timeLoop); +- +- // make the new solution the old solution +- xOld = x; +- gridVariables->advanceTimeStep(); +- +- // advance to the time loop to the next step +- timeLoop->advanceTimeStep(); +- +- // write vtk output +- if (timeLoop->isCheckPoint()) +- vtkWriter.write(timeLoop->time()); +- +- // report statistics of this time step +- timeLoop->reportTimeStep(); +- +- // set new dt as suggested by the newton solver +- timeLoop->setTimeStepSize(nonLinearSolver.suggestTimeStepSize(timeLoop->timeStepSize())); +- +-} while (!timeLoop->finished()); +- +-timeLoop->finalize(leafGridView.comm()); +-``` +- +-<br><br><br> +-### Task 2: Compiling and running an executable +-<hr> +- +-* Change to the build-directory +- +-```bash +-cd ../../build-cmake/exercises/exercise-mainfile +-``` +- +-* Compile all three executables `exercise_mainfile_a` and `exercise_mainfile_b` and `exercise_mainfile_c` +- +-```bash +-make exercise_mainfile_a exercise_mainfile_b exercise_mainfile_c +-``` +- +-* Execute the three problems and inspect the result +- +-```bash +-./exercise_mainfile_a +-./exercise_mainfile_b +-./exercise_mainfile_c +-``` +- +-* You can look at the results (e.g. for the first example) with paraview: +- +-```bash +-paraview 1p_incompressible_stationary.pvd +-``` +- +-<br><br><br> +-### Task 3: Analytical differentiation +-<hr> +- +-In the input file `exercise_mainfile_a.input`, you will see that there is a variable `BaseEpsilon`. +-This defines the base value for the epsilon used in the numeric differentiation. +-If that value is too small, you will see that the solution of the numeric differentiation is not correct. +-Change that value to $1 \cdot 10^{-15}$ and have a look at the solution. +- +-For the incompressible one phase problem, it is also possible to have an analytic solution method. +-In this case, the epsilon does not play a role anymore, since the derivatives are calculated analytically. +-To implement that, follow the tips in the `exercise1pamain.cc` and the `properties.hh` marked by: +- +-```c++ +-// TODO: dumux-course-task 3 +-``` +-For the analytic solution of your immiscible problem, you need analytic solutions for the derivatives of the jacobian. +-For that, we have a special local residual, the `OnePIncompressibleLocalResidual` which provides that. +-You just need to include `incompressiblelocalresidual.hh` in your `properties.hh` +-and use that instead of the `immisciblelocalresidual.hh` which is used as a default for all immiscible models. +- +-Additionally, you need to set the differentiation method in the main file `exercise1pamain.cc` to analytic. diff --git a/.patches/exercise-model/exercise-model.patch b/.patches/exercise-model/exercise-model.patch new file mode 100644 index 00000000..3c11af95 --- /dev/null +++ b/.patches/exercise-model/exercise-model.patch @@ -0,0 +1,574 @@ +diff -ruN exercises/exercise-model/CMakeLists.txt exercises/solution/exercise-model/CMakeLists.txt +--- exercises/exercise-model/CMakeLists.txt 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-model/CMakeLists.txt 2023-04-26 14:37:49.797150837 +0200 +@@ -1,4 +1,4 @@ +-set(CMAKE_BUILD_TYPE Release) + dune_symlink_to_source_files(FILES images params.input) +-dumux_add_test(NAME exercise_nonlineardiffusion ++ ++dumux_add_test(NAME exercise_nonlineardiffusion_sol + SOURCES main.cc) +diff -ruN exercises/exercise-model/main.cc exercises/solution/exercise-model/main.cc +--- exercises/exercise-model/main.cc 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-model/main.cc 2023-04-26 14:37:49.797150837 +0200 +@@ -41,6 +41,8 @@ + #include <dumux/nonlinear/newtonsolver.hh> + #include <dumux/assembly/fvassembler.hh> + ++#include "model.hh" ++ + /*! + * \ingroup NonlinearDiffusion + * \brief Test problem for image denoising using a nonlinear diffusion model +@@ -90,7 +92,7 @@ + struct NonlinearDiffusionTest + { + // TODO: We need to set our model by replacing ModelTypeTag +- using InheritsFrom = std::tuple</*ModelTypeTag,*/BoxModel>; ++ using InheritsFrom = std::tuple<NonlinearDiffusionModel, BoxModel>; + + using Scalar = double; + using Grid = Dune::YaspGrid<2>; +@@ -148,63 +150,62 @@ + // the problem for the boundary conditions, a solution vector and a grid variables instance. + auto gridGeometry = std::make_shared<GridGeometry>(gridManager.grid().leafGridView()); + +- // TODO: Uncomment when the model is implemented +- // using Scalar = GetPropType<TypeTag, Properties::Scalar>; +- // using Problem = GetPropType<TypeTag, Properties::Problem>; +- // using SolutionVector = GetPropType<TypeTag, Properties::SolutionVector>; +- // using GridVariables = GetPropType<TypeTag, Properties::GridVariables>; +- // +- // auto problem = std::make_shared<Problem>(gridGeometry); +- // auto sol = createInitialSolution<SolutionVector>(*gridGeometry, imageData); +- // auto gridVariables = std::make_shared<GridVariables>(problem, gridGeometry); +- // gridVariables->init(sol); ++ using Scalar = GetPropType<TypeTag, Properties::Scalar>; ++ using Problem = GetPropType<TypeTag, Properties::Problem>; ++ using SolutionVector = GetPropType<TypeTag, Properties::SolutionVector>; ++ using GridVariables = GetPropType<TypeTag, Properties::GridVariables>; ++ ++ auto problem = std::make_shared<Problem>(gridGeometry); ++ auto sol = createInitialSolution<SolutionVector>(*gridGeometry, imageData); ++ auto gridVariables = std::make_shared<GridVariables>(problem, gridGeometry); ++ gridVariables->init(sol); + + // We initialize the VTK output module and write out the initial concentration field +- // VtkOutputModule<GridVariables, SolutionVector> vtkWriter(*gridVariables, sol, problem->name()); +- // vtkWriter.addVolumeVariable([](const auto& vv){ return vv.priVar(0); }, "imageIntensity"); +- // vtkWriter.write(0.0); ++ VtkOutputModule<GridVariables, SolutionVector> vtkWriter(*gridVariables, sol, problem->name()); ++ vtkWriter.addVolumeVariable([](const auto& vv){ return vv.imageIntensity(); }, "imageIntensity"); ++ vtkWriter.write(0.0); + + // We instantiate time loop using start and end time as well as + // the time step size from the parameter tree (`params.input`) +- // auto timeLoop = std::make_shared<CheckPointTimeLoop<Scalar>>( +- // getParam<Scalar>("TimeLoop.TStart", 0.0), +- // getParam<Scalar>("TimeLoop.Dt"), +- // getParam<Scalar>("TimeLoop.TEnd") +- // ); ++ auto timeLoop = std::make_shared<CheckPointTimeLoop<Scalar>>( ++ getParam<Scalar>("TimeLoop.TStart", 0.0), ++ getParam<Scalar>("TimeLoop.Dt"), ++ getParam<Scalar>("TimeLoop.TEnd") ++ ); + + // Next, we choose the type of assembler, linear solver and nonlinear solver + // and construct instances of these classes. +- // using Assembler = FVAssembler<TypeTag, DiffMethod::numeric>; +- // using LinearSolver = SSORCGIstlSolver<LinearSolverTraits<GridGeometry>, LinearAlgebraTraitsFromAssembler<Assembler>>; +- // using Solver = Dumux::NewtonSolver<Assembler, LinearSolver>; ++ using Assembler = FVAssembler<TypeTag, DiffMethod::numeric>; ++ using LinearSolver = SSORCGIstlSolver<LinearSolverTraits<GridGeometry>, LinearAlgebraTraitsFromAssembler<Assembler>>; ++ using Solver = Dumux::NewtonSolver<Assembler, LinearSolver>; ++ ++ auto oldSol = sol; // copy the vector to store state of previous time step ++ auto assembler = std::make_shared<Assembler>(problem, gridGeometry, gridVariables, timeLoop, oldSol); ++ auto linearSolver = std::make_shared<LinearSolver>(gridGeometry->gridView(), gridGeometry->dofMapper()); ++ Solver solver(assembler, linearSolver); + +- // auto oldSol = sol; // copy the vector to store state of previous time step +- // auto assembler = std::make_shared<Assembler>(problem, gridGeometry, gridVariables, timeLoop, oldSol); +- // auto linearSolver = std::make_shared<LinearSolver>(gridGeometry->gridView(), gridGeometry->dofMapper()); +- // Solver solver(assembler, linearSolver); +- +- // // time loop +- // timeLoop->start(); do +- // { +- // // assemble & solve +- // solver.solve(sol); ++ // time loop ++ timeLoop->start(); do ++ { ++ // assemble & solve ++ solver.solve(sol); + +- // // make the new solution the old solution +- // oldSol = sol; +- // gridVariables->advanceTimeStep(); ++ // make the new solution the old solution ++ oldSol = sol; ++ gridVariables->advanceTimeStep(); + +- // // advance to the time loop to the next step +- // timeLoop->advanceTimeStep(); ++ // advance to the time loop to the next step ++ timeLoop->advanceTimeStep(); + +- // // write VTK output (writes out the concentration field) +- // vtkWriter.write(timeLoop->time()); ++ // write VTK output (writes out the concentration field) ++ vtkWriter.write(timeLoop->time()); + +- // // report statistics of this time step +- // timeLoop->reportTimeStep(); ++ // report statistics of this time step ++ timeLoop->reportTimeStep(); + +- // } while (!timeLoop->finished()); ++ } while (!timeLoop->finished()); + +- // timeLoop->finalize(gridGeometry->gridView().comm()); ++ timeLoop->finalize(gridGeometry->gridView().comm()); + + return 0; + }// end main +diff -ruN exercises/exercise-model/model.hh exercises/solution/exercise-model/model.hh +--- exercises/exercise-model/model.hh 1970-01-01 01:00:00.000000000 +0100 ++++ exercises/solution/exercise-model/model.hh 2023-04-26 14:37:49.797150837 +0200 +@@ -0,0 +1,222 @@ ++// -*- 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_EXERCISE_NONLINEAR_DIFFUSION_MODEL_HH ++#define DUMUX_EXERCISE_NONLINEAR_DIFFUSION_MODEL_HH ++ ++// In the file `model.hh`, we define the volume variables, the model equations, and ++// set all default model properties. ++ ++#include <dune/common/fvector.hh> ++#include <dumux/common/math.hh> ++#include <dumux/common/properties.hh> ++#include <dumux/common/numeqvector.hh> ++#include <dumux/common/volumevariables.hh> ++#include <dumux/discretization/method.hh> ++ ++// The property tag is simply an empty struct with the name `NonlinearDiffusionModel` ++namespace Dumux::Properties::TTag { ++//! The nonlinear diffusion model tag that we can specialize properties for ++struct NonlinearDiffusionModel {}; ++} // end namespace Dumux::Properties::TTag ++ ++namespace Dumux { ++ ++/*! ++ * \ingroup NonlinearDiffusionModel ++ * \brief Volume averaged quantities required by the nonlinear diffusion model. ++ * This contains the quantities which are related to a control volume ++ */ ++template <class Traits> ++class ImageDenoisingVolumeVariables ++{ ++ using Scalar = typename Traits::PrimaryVariables::value_type; ++public: ++ //! export the type used for the primary variables ++ using PrimaryVariables = typename Traits::PrimaryVariables; ++ using Indices = typename Traits::ModelTraits::Indices; ++ ++ 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()]; ++ } ++ ++ /*! ++ * \brief Returns the image intensity of the control volume. ++ */ ++ Scalar imageIntensity() const ++ { return priVars_[Indices::imageIntensityIdx]; } ++ ++ Scalar priVar(const int pvIdx) const ++ { return priVars_[pvIdx]; } ++ ++ const PrimaryVariables& priVars() const ++ { return priVars_; } ++ ++ Scalar extrusionFactor() const ++ { return 1.0; } ++ ++private: ++ PrimaryVariables priVars_; ++}; ++ ++/*! ++ * \ingroup NonlinearDiffusionModel ++ * \brief Element-wise calculation of the residual and its derivatives ++ * for a nonlinear diffusion problem. ++ */ ++template<class TypeTag> ++class NonlinearDiffusionModelLocalResidual ++: public GetPropType<TypeTag, Properties::BaseLocalResidual> ++{ ++ 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 GridVariables = GetPropType<TypeTag, Properties::GridVariables>; ++ using VolumeVariables = typename GridVariables::GridVolumeVariables::VolumeVariables; ++ using ElementVolumeVariables = typename GridVariables::GridVolumeVariables::LocalView; ++ using ElementFluxVariablesCache = typename GridVariables::GridFluxVariablesCache::LocalView; ++ ++ using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>; ++ using FVElementGeometry = typename GridGeometry::LocalView; ++ using SubControlVolume = typename GridGeometry::SubControlVolume; ++ using SubControlVolumeFace = typename GridGeometry::SubControlVolumeFace; ++ using GridView = typename 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; ++ ++ NumEqVector computeStorage(const Problem& problem, ++ const SubControlVolume& scv, ++ const VolumeVariables& volVars) const ++ { ++ NumEqVector storage; ++ storage[Indices::diffusionEqIdx] = volVars.imageIntensity(); ++ return storage; ++ } ++ ++ NumEqVector computeFlux(const Problem& problem, ++ const Element& element, ++ const FVElementGeometry& fvGeometry, ++ const ElementVolumeVariables& elemVolVars, ++ const SubControlVolumeFace& scvf, ++ const ElementFluxVariablesCache& elemFluxVarsCache) const ++ { ++ static_assert(DiscretizationMethods::isCVFE<typename GridGeometry::DiscretizationMethod>, ++ "This local residual is hard-coded to control-volume finite element schemes"); ++ ++ // Compute ∇c at the integration point of this sub control volume face. ++ const auto& fluxVarCache = elemFluxVarsCache[scvf]; ++ Dune::FieldVector<Scalar, dimWorld> gradConcentration(0.0); ++ for (const auto& scv : scvs(fvGeometry)) ++ { ++ const auto& volVars = elemVolVars[scv]; ++ // v.axpy(a, w) means v <- v + a*w ++ gradConcentration.axpy( ++ volVars.imageIntensity(), ++ fluxVarCache.gradN(scv.indexInElement()) ++ ); ++ } ++ ++ NumEqVector flux; ++ ++ const auto K = problem.conductance(); ++ const auto D = 1.0/(1.0 + gradConcentration.two_norm2()/(K*K)); ++ ++ // Compute the flux with `vtmv` (vector transposed times matrix times vector) or -n^T D ∇c A. ++ // The diffusion coefficient comes from the `problem` (see Part 2 of the example). ++ flux[Indices::diffusionEqIdx] = -1.0*vtmv( ++ scvf.unitOuterNormal(), D, gradConcentration ++ )*scvf.area(); ++ ++ return flux; ++ } ++}; ++} // end namespace Dumux ++ ++// The model properties ++namespace Dumux::Properties { ++ ++// The type of the local residual is the class defined above ++template<class TypeTag> ++struct LocalResidual<TypeTag, TTag::NonlinearDiffusionModel> ++{ using type = NonlinearDiffusionModelLocalResidual<TypeTag>; }; ++ ++// The default scalar type is double ++// we compute with double precision floating point numbers ++template<class TypeTag> ++struct Scalar<TypeTag, TTag::NonlinearDiffusionModel> ++{ using type = double; }; ++ ++// The model traits specify some information about our equation system. ++template<class TypeTag> ++struct ModelTraits<TypeTag, TTag::NonlinearDiffusionModel> ++{ ++ struct type ++ { ++ struct Indices ++ { ++ static constexpr int imageIntensityIdx = 0; ++ static constexpr int diffusionEqIdx = 0; ++ }; ++ ++ static constexpr int numEq() { return 1; } ++ }; ++}; ++ ++// The primary variable vector has entries of type `Scalar` and is ++// as large as the number of equations ++template<class TypeTag> ++struct PrimaryVariables<TypeTag, TTag::NonlinearDiffusionModel> ++{ ++ using type = Dune::FieldVector< ++ GetPropType<TypeTag, Properties::Scalar>, ++ GetPropType<TypeTag, Properties::ModelTraits>::numEq() ++ >; ++}; ++ ++// The volume variables that are used in our model ++template<class TypeTag> ++struct VolumeVariables<TypeTag, TTag::NonlinearDiffusionModel> ++{ ++ struct Traits ++ { ++ using ModelTraits ++ = GetPropType<TypeTag, Properties::ModelTraits>; ++ using PrimaryVariables ++ = GetPropType<TypeTag, Properties::PrimaryVariables>; ++ }; ++ ++ using type = ImageDenoisingVolumeVariables<Traits>; ++}; ++ ++} // end namespace Dumux::Properties ++ ++#endif +diff -ruN exercises/exercise-model/README.md exercises/solution/exercise-model/README.md +--- exercises/exercise-model/README.md 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-model/README.md 1970-01-01 01:00:00.000000000 +0100 +@@ -1,208 +0,0 @@ +-# Exercise Model (DuMuX course) +-The aim of this exercise is it to learn how to set up a new model (new system of equations). +-As an example, we implement a nonlinear diffusion equation model and apply it for image denoising. +-In this exercise, we will only consider the bare minimum of classes to successfully assemble +-and solve such a problem with DuMux. We also implement the model for a specific discretization method +-(the Box method: a vertex-centered finite volume method also known as control-volume finite element method with piece-wise linear basis functions). +- +-## Equation and Problem set-up +- +-We consider a nonlinear diffusion equation on a domain $\Omega \subset \mathbb{R}^2$ and time interval $(0,T]$ with boundary $\partial\Omega$ given by +- +-```math +-\begin{aligned} +-\frac{\partial c}{\partial t} - \nabla \cdot (D(c) \nabla c) &= 0 && \mathrm{in}\; \Omega \times (0,T] \\ +- -D(c) \nabla c \cdot \boldsymbol{n} &= 0 && \mathrm{on}\; \partial\Omega \times (0,T] \\ +- c &= c^0 && \mathrm{for}\; t = 0, +-\end{aligned} +-``` +-where for a position $\mathbf{x} \in \Omega$ and time $t$, $c(\mathbf{x},t)$ denotes the unknown evolving concentration field and $D(c)$ the solution-dependent diffusion coefficient. +- +-In this exercise we want to model image denoising such that the primary variable $c$ corresponds to the **image intensity** and $D$ is given (due to Perona & Malik), as +-```math +-D(c) = \frac{1}{1 + \left(\frac{|| \nabla c ||}{K}\right)^2}, +-``` +-with conductance $K$. +- +-<br><br> +-## Task 1: Getting familiar with the diffusion example +-<hr> +- +-When implementing a new model it is advisable to built upon an already existing model that might be similar and only needs modifications. Since the above equation is a nonlinear diffusion equation, we can built upon the diffusion model implemented in the [diffusion example](https://git.iws.uni-stuttgart.de/dumux-repositories/dumux/-/tree/master/examples/diffusion). +-The diffusion example also derives the discrete equations using the Box method as spatial discretization scheme. +-<hr> +- +-<br><br> +-## Task 2: Set up the model +-<hr> +- +-:arrow_right: Copy the `model.hh` file from the diffusion example into `dumux-course/exercises/exercise-model` and choose appropriate class names. +- +-Do also not forget to change the [include guards](https://en.wikipedia.org/wiki/Include_guard) +-at the beginning of header files (`DUMUX_EXAMPLES_DIFFUSION_MODEL_HH`). +-Include guards have to be unique in the entire application that you compile. (Otherwise some +-code will not be included.) An alternative is using [`#pragma once`](https://en.wikipedia.org/wiki/Pragma_once) +-which is widely supported but not specified by the C++ standard. +- +-First, the goal is to get the simulation running and then add improvements. For this, it is important to have a +-compiling test such that new changes can continuously be tested. +-Thus, in the `computeFlux(...)` function of the local residual, you can start with a hard-coded diffusion coefficient of `1.0` (linear diffusion coefficient function). +-(Replace `problem.diffusionCoefficient()` by `1.0` because our problem class in `main.cc` does not have a `diffusionCoefficient()` interface.) +- +-Each model also needs to define a model type tag for setting model-specific properties. +- +-:arrow_right: Rename the one of the diffusion model (`struct DiffusionModel {};`) to `NonlinearDiffusionModel`. +-and use this new type tag for specializing properties, i.e. within the properties namespace +- +-```c++ +-namespace Dumux::Properties { +- +-// Defined properties, replace TTag::DiffusionModel by TTag::NonlinearDiffusionModel +- +-} // end namespace Dumux::Properties +-``` +- +-<br><br> +-## Task 3: Set up the test case +-<hr> +- +-:arrow_right: Open the file `main.cc`. +- +-There you can find +-1. The problem setup (implements the homogeneous Neumann boundary conditions (zero flux)) +-2. Test case specific property definitions +-3. A function for setting the initial intensity values by using the noisy image data +-4. The main program defining all steps of the program +- +-For reading the image data (given as **pgm** file), the `NetPBMReader` reader is used +-```c++ +-// Read the image +-const auto imageFileName = getParam<std::string>("ImageFile"); +-const auto imageData = NetPBMReader::readPGM(imageFileName); +-``` +- +-### 3.1: Make test type tag inherit properties from model type tag +- +-:arrow_right: Include the header `model.hh` <br> +-:arrow_right: To use the new model for this test case, make +-the test type tag inherit properties from the model type tag +- +-```c++ +-namespace Dumux::Properties::TTag { +- +-struct NonlinearDiffusionTest +-{ +- // TODO: Set new model by replacing ModelTypeTag +- using InheritsFrom = std::tuple</*ModelTypeTag,*/BoxModel>; +- +- // Further property definitions +-}; +-} // end namespace Dumux::Properties::TTag +-``` +- +-### 3.2: Uncomment the commented code in main file +- +-Without a model the previous code could not be compiled. +-The parts of the main function that need a working model are commented. +- +-:arrow_right: Remove the code comments to enable all simulation steps. +- +-### 3.3: Run your application +- +-We hope everything is implemented correctly. +- +-:arrow_right: Compile and run the test case by executing +- +-```bash +-cd build-cmake/exercises/exercise-model +-make exercise_nonlineardiffusion +-./exercise_nonlineardiffusion +-``` +- +-:arrow_right: Fix any compiler errors and warnings. +- +-<br><br> +-## Task 4: Nonlinear diffusion +-<hr> +- +-In the local residual class, instead of using a constant `1.0`, we now +-want to implement the nonlinear diffusion function specified above. With a `Dune::FieldVector` +-you can get the 2-norm of the vector via `v.two_norm()` and its square with `v.two_norm2()`. (The reason +-for having two different interfaces is that the second one is more efficient.) +-Compute the diffusion coefficient and pass it to `vmtv` instead of `1.0`. +-You can get the conductance $K$ via the problem interface `conductance`. +- +-:arrow_right: Add the nonlinear diffusion coefficient computation to the `computeFlux` function +- +-:arrow_right: Compare the `main.cc` with the `main.cc` of the diffusion example. Notice that we +-use a Newton solver as we want to solve nonlinear equations. +- +-As the DuMux assembler uses numeric differentiation to approximate the Jacobian matrix, you do not have to implement +-the derivatives of your residual by hand and implement them. This greatly simplifies implementing +-nonlinear equations. +- +-<br><br> +-## Task 5: Running the image denosing test case +-<hr> +- +-:arrow_right: Compile and run like above. +- +-With the conductance parameter `Problem.Conductance` (see `params.input`) you can regulate the preservation of edges. +-For large conductance, you get almost linear diffusion (Gaussian blurring). For small conductances some edges +-are preserved. The conductance acts as a threshold for the image intensity gradient detected as an edge feature. +- +-__The final result should look like this:__ +-<figure> +- <center> +- <img src="../extradoc/exercisemodel_mri_denoise.gif" alt="denoising"/> +- <figcaption> <b> Fig.1 </b> - Denosing of MRI image using nonlinear diffusion model.</figcaption> +- </center> +-</figure> +- +-<br><br> +-## Task 6: Custom volume variables +-<hr> +- +-The volume variables represent variables of the control volume. So far we have used +-the `BasicVolumeVariables` class that only stores the primary variables at control volumes. +-We have used the interface `priVar` to access the primary variables. In more complex models, +-we often want to +- +-* assign names to our variables to make the local residual more readable +-* evaluate constitutive laws and compute secondary variables from primary variables and use these in the local residual +- +-In this task we will implement a custom volume variable class, while in this simple case it will only be for the first reason. +- +-:arrow_right: Implement your own `ImageDenoisingVolumeVariables` class (either within a new header file or directly in `model.hh`). As basis you can use the `BasicVolumeVariables`, see `dumux/common/volumevariables.hh`. Copy the class into `model.hh` and rename it. +- +-<br> Add a function `imageIntensity()` that returns the first primary variable. <br> +- +-:arrow_right: In order to use the new `ImageDenoisingVolumeVariables` class in your model, set in your model-specific properties (see `dumux-course/exercises/exercise-properties`): +-```c++ +-template<class TypeTag> +-struct VolumeVariables<TypeTag, TTag::NonlinearDiffusionModel> +-{ +- struct Traits +- { +- using PrimaryVariables +- = GetPropType<TypeTag, Properties::PrimaryVariables>; +- }; +- // TODO: set type to your class +- using type = ...; +-}; +-``` +- +-You can now use the interface `imageIntensity` in your local residual instead of the generic `priVar` interface. +- +-In order to simplify the implementation of your custom volume variables, you can +-also inherit from `BasicVolumeVariables` and only implement the additional interface. +- +-Another common modification is to add named indices to a model. +-To this end, we typically add an `Indices` struct to the `ModelTraits` in which the primary variable +-and equation indices are named (e.g. `static constexpr int imageIntensityIdx = 0;`). +-The model traits can be passed to the volume variables class via the Traits class. +- +-:arrow_right: Have a look at the solution of the exercise to see how this is usually implemented. +- +-Names indices allow to address the entries of the local residual vector or the primary variables vector. +-You will see names indices in every DuMux model. diff --git a/.patches/exercise-properties/exercise-properties.patch b/.patches/exercise-properties/exercise-properties.patch new file mode 100644 index 00000000..b5a5784e --- /dev/null +++ b/.patches/exercise-properties/exercise-properties.patch @@ -0,0 +1,180 @@ +diff -ruN exercises/exercise-properties/CMakeLists.txt exercises/solution/exercise-properties/CMakeLists.txt +--- exercises/exercise-properties/CMakeLists.txt 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-properties/CMakeLists.txt 2023-04-26 14:37:49.797150837 +0200 +@@ -1,5 +1,5 @@ +-# the property exercise simulation program +-dumux_add_test(NAME exercise_properties ++# the solution to the properties exercise ++dumux_add_test(NAME exercise_properties_solution + SOURCES main.cc) + + # add a symlink for each input file +diff -ruN exercises/exercise-properties/mylocalresidual.hh exercises/solution/exercise-properties/mylocalresidual.hh +--- exercises/exercise-properties/mylocalresidual.hh 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-properties/mylocalresidual.hh 2023-04-26 14:37:49.797150837 +0200 +@@ -71,8 +71,6 @@ + * \note The volVars can be different to allow computing + * the implicit euler time derivative here + */ +- // TODO: dumux-course-task 3 +- // Eliminate density from the storage term + NumEqVector computeStorage(const Problem& problem, + const SubControlVolume& scv, + const VolumeVariables& volVars) const +@@ -83,7 +81,6 @@ + { + auto eqIdx = conti0EqIdx + phaseIdx; + storage[eqIdx] = volVars.porosity() +- * volVars.density(phaseIdx) + * volVars.saturation(phaseIdx); + + //! The energy storage in the fluid phase with index phaseIdx +@@ -107,8 +104,6 @@ + * \param scvf The sub control volume face to compute the flux on + * \param elemFluxVarsCache The cache related to flux computation + */ +- // TODO: dumux-course-task +- // Eliminate the density from the flux term + NumEqVector computeFlux(const Problem& problem, + const Element& element, + const FVElementGeometry& fvGeometry, +@@ -124,7 +119,7 @@ + { + // the physical quantities for which we perform upwinding + auto upwindTerm = [phaseIdx](const auto& volVars) +- { return volVars.density(phaseIdx)*volVars.mobility(phaseIdx); }; ++ { return volVars.mobility(phaseIdx); }; + + auto eqIdx = conti0EqIdx + phaseIdx; + flux[eqIdx] = fluxVars.advectiveFlux(phaseIdx, upwindTerm); +diff -ruN exercises/exercise-properties/problem.hh exercises/solution/exercise-properties/problem.hh +--- exercises/exercise-properties/problem.hh 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-properties/problem.hh 2023-04-26 14:37:49.797150837 +0200 +@@ -23,6 +23,7 @@ + #ifndef DUMUX_INCOMPRESSIBLE_TWOP_TEST_PROBLEM_HH + #define DUMUX_INCOMPRESSIBLE_TWOP_TEST_PROBLEM_HH + ++#include <dumux/material/components/trichloroethene.hh> + #include <dumux/porousmediumflow/problem.hh> + #include <dumux/common/properties.hh> + #include <dumux/common/boundarytypes.hh> +@@ -119,10 +120,12 @@ + */ + NumEqVector neumannAtPos(const GlobalPosition &globalPos) const + { +-// TODO: reformulate the neumann boundary condition's values in terms of volume instead of mass injected per meter and second + NumEqVector values(0.0); + if (onInlet_(globalPos)) +- values[contiDNAPLEqIdx] = -0.04; // kg / (m * s) ++ { ++ using TCE = Components::Trichloroethene<Scalar>; ++ values[contiDNAPLEqIdx] = -0.04/TCE::liquidDensity(this->spatialParams().temperatureAtPos(globalPos), /*pressure=*/1e5);// m/s ++ } + + return values; + } +diff -ruN exercises/exercise-properties/properties.hh exercises/solution/exercise-properties/properties.hh +--- exercises/exercise-properties/properties.hh 2023-06-01 14:31:33.157063038 +0200 ++++ exercises/solution/exercise-properties/properties.hh 2023-04-26 14:37:49.797150837 +0200 +@@ -39,9 +39,7 @@ + + #include "spatialparams.hh" + #include "problem.hh" +- +-// TODO: dumux-course-task 3 +-// Include the local residual header ++#include "mylocalresidual.hh" + + namespace Dumux::Properties { + +@@ -59,9 +57,8 @@ + template<class TypeTag> + struct Problem<TypeTag, TTag::TwoPIncompressible> { using type = TwoPTestProblem<TypeTag>; }; + +-// TODO: dumux-course-task 3 +-// Use MyLocalResidual as LocalResidual +- ++template<class TypeTag> ++struct LocalResidual<TypeTag, TTag::TwoPIncompressible> { using type = MyLocalResidual<TypeTag>; }; + + // Set the fluid system + template<class TypeTag> +diff -ruN exercises/exercise-properties/README.md exercises/solution/exercise-properties/README.md +--- exercises/exercise-properties/README.md 2023-06-01 14:31:33.153063018 +0200 ++++ exercises/solution/exercise-properties/README.md 1970-01-01 01:00:00.000000000 +0100 +@@ -1,75 +0,0 @@ +-# Exercise Properties (DuMuX course) +-<br> +- +-## Problem set-up +- +-The problem setup is identical to the two-phase incompressible test from DuMu<sup>x</sup>. +- +-## Preparing the exercise +- +-* Navigate to the directory `exercise-properties` +- +-_Exercise Properties_ deals with a two-phase immiscible incompressible problem (__2p__). The goal is to learn how to adapt compile-time parameters by employing the _DuMu<sup>x</sup> property system_. +- +-<br><br> +-### Task 1: Getting familiar with the code +-<hr> +- +-Locate all the files you will need for this exercise +-* The __main file__: `main.cc` +-* The __problem file__: `problem.hh` +-* The __properties file__: `properties.hh` +-* The __spatial parameters file__: `spatialparams.hh` +-* The __input file__: `params.input` +-* One header file containing: +- * a custom __local residual__ in: `mylocalresidual.hh` +- +- +-<br><br><br> +-### Task 2: Compiling and running the program +-<hr> +- +-* Change to the build-directory +- +-```bash +-cd ../../build-cmake/exercises/exercise-properties +-``` +- +-* Compile the executable `exercise_properties` +- +-```bash +-make exercise_properties +-``` +- +-* Run the problem and inspect the result +- +-```bash +-./exercise_properties +-``` +-Note: Because the input file has the same name as the executable, DuMu<sup>x</sup> will find it automatically. +- +- +-<br><br><br> +-### Task 3: Implement a custom local residual +-<hr> +- +-Types that are properties can be changed on the problem level by using the property system. In the following task, we implement our own 2p local residual, i.e. the class that computes the element residual in every Newton iteration. The file `mylocalresidual.hh` contains a copy of the original local residual class used for all immiscible models renamed to `template<class TypeTag> class MyLocalResidual`. +- +-* Make DuMu<sup>x</sup> use this new local residual by including the header `mylocalresidual.hh` and setting the corresponding property in the `Properties` namespace in the file `properties.hh` +- +-```c++ +- +-template<class TypeTag> +-struct LocalResidual<TypeTag, TTag::TwoPIncompressible> +-{ +- using type = MyLocalResidual<TypeTag>; +-}; +-``` +- +-Simplify the original local residual by using the assumption that only incompressible fluid phases are present. As a consequence, one can balance phase volume instead of phase mass: +- +-* Eliminate the density from the `computeStorage` and the `computeFlux` methods. +- +-* Adapt the relevant routine in the problem file such that volume instead of mass is injected. The density of the employed NAPL is 1460 kg/m<sup>3</sup> . For a first try, you can use the hardcoded value. +- +-* Generalize your approach by using the component's `liquidDensity(t, p)` function instead of the hardcoded value. diff --git a/.patches/exercise-runtimeparams/exercise-runtimeparams.patch b/.patches/exercise-runtimeparams/exercise-runtimeparams.patch new file mode 100644 index 00000000..16097f80 --- /dev/null +++ b/.patches/exercise-runtimeparams/exercise-runtimeparams.patch @@ -0,0 +1,284 @@ +diff -ruN exercises/exercise-runtimeparams/CMakeLists.txt exercises/solution/exercise-runtimeparams/CMakeLists.txt +--- exercises/exercise-runtimeparams/CMakeLists.txt 2023-06-01 14:31:33.157063038 +0200 ++++ exercises/solution/exercise-runtimeparams/CMakeLists.txt 2023-04-26 14:37:49.797150837 +0200 +@@ -1,5 +1,5 @@ + # the runtime parameter exercise simulation program +-dumux_add_test(NAME exercise_runtimeparams ++dumux_add_test(NAME exercise_runtimeparams_solution + SOURCES main.cc) + + # add a symlink for each input file +diff -ruN exercises/exercise-runtimeparams/params.input exercises/solution/exercise-runtimeparams/params.input +--- exercises/exercise-runtimeparams/params.input 2023-06-01 14:31:33.157063038 +0200 ++++ exercises/solution/exercise-runtimeparams/params.input 2023-04-26 14:37:49.797150837 +0200 +@@ -12,11 +12,12 @@ + AquiferDepth = 2700.0 # m + InjectionDuration = 2.628e6 # in seconds, i.e. one month + # TODO: Task 2: Create a parameter called "TotalAreaSpecificInflow" ++TotalAreaSpecificInflow = -1e-4 # kg/(s m^2) + + [SpatialParams] + PermeabilityAquitard = 1e-15 # m^2 + # TODO: Task 1: Change the Aquitard's Entry Pressure +-Aquitard.BrooksCoreyPcEntry = 4.5e4 # Pa ++Aquitard.BrooksCoreyPcEntry = 4.5e3 # Pa + Aquitard.BrooksCoreyLambda = 2.0 + Aquitard.Swr = 0.2 + Aquitard.Snr = 0.0 +@@ -24,4 +25,4 @@ + Aquifer.BrooksCoreyPcEntry = 1e4 # Pa + Aquifer.BrooksCoreyLambda = 2.0 + Aquifer.Swr = 0.2 +-Aquifer.Snr = 0.0 ++Aquifer.Snr = 0.0 +\ No newline at end of file +diff -ruN exercises/exercise-runtimeparams/problem.hh exercises/solution/exercise-runtimeparams/problem.hh +--- exercises/exercise-runtimeparams/problem.hh 2023-06-01 14:31:33.157063038 +0200 ++++ exercises/solution/exercise-runtimeparams/problem.hh 2023-04-26 14:37:49.797150837 +0200 +@@ -91,14 +91,15 @@ + aquiferDepth_ = getParam<Scalar>("Problem.AquiferDepth"); + // the duration of the injection, units: second + injectionDuration_ = getParamFromGroup<Scalar>("Problem","InjectionDuration"); +- // TODO: dumux-course-task 2 +- // Set a variable "TotalAreaSpecificInflow" to read in a value from the parameter tree via the input file +- +- // TODO: dumux-course-task 3 +- // Set a default value for the above parameter. +- +- // TODO: dumux-course-task 4 +- // Provide output describing where the parameter value comes from using parameter bool functions. ++ //TODO: Task 2: Set a variable "TotalAreaSpecificInflow" to read in a value from the parameter tree via the input file ++ totalAreaSpecificInflow_ = getParam<Scalar>("Problem.TotalAreaSpecificInflow"); ++ //TODO: Task 3: Set a default value for the above parameter. ++ // totalAreaSpecificInflow_ = getParam<Scalar>("Problem.TotalAreaSpecificInflow", -1e-4); ++ //TODO: Task 4: Provide output describing where the parameter value comes from using parameter bool functions. ++ // if (hasParamInGroup("Problem","TotalAreaSpecificInflow")) ++ // std::cout << "Parameter value is read from the input file." << std::endl; ++ // else ++ // std::cout << "Using the default parameter value." << std::endl; + } + + +@@ -160,9 +161,8 @@ + { + // inject nitrogen. negative values mean injection + // units kg/(s*m^2) +- // TODO: dumux-course-task 2 +- // Incorporate "totalAreaSpecificInflow_" into the injection boundary condition +- values[Indices::conti0EqIdx + FluidSystem::N2Idx]= -1e-4/FluidSystem::molarMass(FluidSystem::N2Idx); ++ //TODO: Task 2: incorporate "totalAreaSpecificInflow_" into the injection boundary condition ++ values[Indices::conti0EqIdx + FluidSystem::N2Idx] = totalAreaSpecificInflow_/FluidSystem::molarMass(FluidSystem::N2Idx); + values[Indices::conti0EqIdx + FluidSystem::H2OIdx] = 0.0; + } + +@@ -203,8 +203,8 @@ + std::string name_; //! Problem name + Scalar aquiferDepth_; //! Depth of the aquifer in m + Scalar injectionDuration_; //! Duration of the injection in seconds +- // TODO: dumux-course-task 2 +- // Set a variable "totalAreaSpecificInflow_" to read in a value from the parameter tree via the input file ++ //TODO: Task 2: Set a variable "totalAreaSpecificInflow_" to read in a value from the parameter tree via the input file ++ Scalar totalAreaSpecificInflow_; //! Rate of the Injection in kg/(s m^2) + Scalar time_; + }; + +diff -ruN exercises/exercise-runtimeparams/properties.hh exercises/solution/exercise-runtimeparams/properties.hh +--- exercises/exercise-runtimeparams/properties.hh 2023-06-01 14:31:33.157063038 +0200 ++++ exercises/solution/exercise-runtimeparams/properties.hh 2023-04-26 14:37:49.797150837 +0200 +@@ -70,6 +70,6 @@ + FluidSystems::H2ON2DefaultPolicy</*fastButSimplifiedRelations=*/ true> >; + }; + +-} //end namespace Dumux::Properties ++} // end namespace Dumux::Properties + + #endif +diff -ruN exercises/exercise-runtimeparams/README.md exercises/solution/exercise-runtimeparams/README.md +--- exercises/exercise-runtimeparams/README.md 2023-06-01 14:31:33.157063038 +0200 ++++ exercises/solution/exercise-runtimeparams/README.md 1970-01-01 01:00:00.000000000 +0100 +@@ -1,171 +0,0 @@ +-# Exercise Runtime Parameters (DuMuX course) +-<br> +- +-## Problem set-up +- +-Here we will expand on what we've covered in the basics exercise (see `dumux-course/exercises/exercise-basic/README.md`), and the problem set up will remain the same. +- +-## Preparing the exercise +- +-Navigate to the directory `dumux-course/exercises/exercise-runtimeparams/` +- +-<br><br> +-### Task 1: Understanding Input Parameters +-<hr> +-For this task we will edit the following files: +- +-* The shared __problem file__: `problem.hh` +-* And the shared __input file__: `params.input` +- +-Parameters can either be directly defined within your program, or specified via the input file. Within every main file, (`*.cc`), the following function is called +- +-```c++ +-// parse command line arguments and input file +-Parameters::init(argc, argv); +-``` +-This will read in the parameters from the input file. +-The input file should either be named the same as the executable file, with a trailing `*.input`, or be named `prarams.input` as this is the standard in our CMake system. +-Alternatively, arbitrarily named input files (e.g. `exercise1.input`) can be explicitly written as the first shell argument after the executable file (here `exercise_runtimeparams`) is called. +- +-```bash +-./exercise_runtimeparams +- #(Calls the file params.input as the default input file.) +-``` +-```bash +-./exercise_runtimeparams exercise1.input +- #(Calls the input file provided (exercise1.input) as the input file.) +-``` +-In the input file `params.input` you can find the following section +- +-```ini +-[SpatialParams] +-PermeabilityAquitard = 1e-15 # m^2 +-Aquitard.BrooksCoreyPcEntry = 4.5e4 # Pa +-PermeabilityAquifer = 1e-12 # m^2 +-Aquifer.BrooksCoreyPcEntry = 1e4 # Pa +-``` +-When a parameter is defined directly within your program, you'll need to recompile your program every time you change the value. When a parameter is passed via the input file, this is not the case. If we decided to vary the entry pressure in our geologic units a few times via the parameters listed above, there would be no need to recompile between simulation runs. +- +-* > __Task 1__: Change the aquitard's entry pressure in the input file to a lower value and compare the results with the previous solution. You do not need to recompile the executable. +- +-<br><br> +-### Task 2: Setting a variable to collect a runtime parameter +-<hr> +- +-Let's free one of the variables in this exercise. Within the class `problem.hh`, in the first line of the Neumann boundary condition definition function `neumannAtPos(...)`, the injection rate is defined as $`1.0 \cdot 10^{-4} kg s^{-1} m^{-2}`$. +- +-```c++ +-// inject nitrogen. negative values mean injection +-// units kg/(s*m^2) +-values[Indices::conti0EqIdx + FluidSystem::N2Idx]= -1e-4/FluidSystem::molarMass(FluidSystem::N2Idx); +-values[Indices::conti0EqIdx + FluidSystem::H2OIdx] = 0.0; +-``` +- +-This parameter may need to change, and if we choose to always change this within the class, we will need to recompile every time. +- +-Instead of hard defining this parameter within the function, we can set a variable to read into our parameter tree via the input file and use this in our function instead. +-To do this, there are two functions defined in `dumux/dumux/common/parameters.hh`, `getParam()` and `getParamFromGroup()`. They use the following format: +- +-```c++ +-variable_ = getParam<TYPE>("GROUPNAME.PARAMNAME"); +-``` +-or +-```c++ +-variable_ = getParamFromGroup<TYPE>("GROUPNAME", "PARAMNAME"); +-``` +- +-`<TYPE>`,`<GROUPNAME>`,`<PARAMNAME>` should be appropriately defined for your variable: +- * `<TYPE>` is the type of the parameter to read (e.g. `Scalar`) +- * `<GROUPNAME>` is the group in the input file (e.g. `Problem`) +- * `<PARAMNAME>` is the name of the parameter in the input file (e.g. `AquiferDepth`) +- +-An example of this is already performed in the problem constructor. The Injection Duration (`injectionDuration_`) is defined via the input file and can then be used later in the problem header. +- +-```c++ +-// depth of the aquifer, units: m +-aquiferDepth_ = getParam<Scalar>("Problem.AquiferDepth"); +-// the duration of the injection, units: second +-injectionDuration_ = getParamFromGroup<Scalar>("Problem","InjectionDuration"); +-``` +- +-The injection duration parameter is located in the `[Problem]` group, is named `InjectionDuration`, and has the type `Scalar`. +- +-This variable should then be defined as a `Scalar` at the bottom of this problem class in the private section. +- +-```c++ +-Scalar injectionDuration_; +-``` +- +-In the input file, within the group `[Problem]`, a value is set to the Parameter name. This is then called in the problem class. +- +-```ini +-[Problem] +-InjectionDuration = 2.628e6 # in seconds, i.e. one month +-``` +- +-* > __Task 2__: The goal is to replace the value `-1e-4` in +- > +- >```c++ +- >values[Indices::conti0EqIdx + FluidSystem::N2Idx]= -1e-4/FluidSystem::molarMass(FluidSystem::N2Idx); +- >``` +- >with a runtime variable. +- > +- > * (2a) Develop a new variable called `totalAreaSpecificInflow_`, +- > * (2b) Assign a value to this variable from a path in the input file, and +- > * (2c) Incorporate this variable into the injection boundary condition. +- > +- > When your problem file and the input file are edited, compile and run the exercise. +- > +- >```bash +- > make exercise_runtimeparams && ./exercise_runtimeparams params.input +- >``` +-<br><br> +-### Task 3: Default Values for Runtime Parameters +-<hr> +- +-In the case that no path to a required runtime parameter is provided in the input file, the program will no longer run. You can try this if you like by removing the `TotalAreaSpecificInflow` from the input file. The resulting error message should look like this: +- +-```bash +-loggingparametertree.hh:316: +-Key Problem.TotalAreaSpecificInflow not found in the parameter tree ---> Abort! +-``` +- +-To avoid this, we can place a default value in the variable definition. Whenever the parameter is specified in the input file, this default value in your class will be overwritten. In the case that no value is provided in the input file, this default value will be used. In order to do this, follow the following template. +- +-```c++ +-variable_ = getParam<TYPE>("GROUPNAME.PARAMNAME", DEFAULTVALUE); +-``` +-or +-```c++ +-variable_ = getParamFromGroup<TYPE>("GROUPNAME","PARAMNAME", DEFAULTVALUE); +-``` +- +-* > __Task 3__: Set up the `totalAreaSpecificInflow_` variable to record a default value of `-1e-4` and run this with and without a provided value of `-1e-3` in the input file. +- +-<br><br> +-### Task 4: Other Runtime Parameter Functions +-<hr> +- +-Setting default values for variables defined with runtime parameters can also lead to problems. If one runtime parameter from the input file is set to multiple different variables, each with a different default value, changing the variable in one location can lead to unexpected changes elsewhere. On top of this, in DuMu<sup>x</sup>, there are a few base variables that are set with default values for all DuMu<sup>x</sup> simulations. These can be found in the header file `dumux/common/parameters.hh` in the function `globalDefaultParameters`. +- +-One way to check this is to use either the `hasParam()` or the `hasParamInGroup()` function. These functions returning `bool`s will check to see if a parameter is read in via the input file. These functions are also both defined in the `dumux/dumux/common/parameters.hh` class, and follow a similar format to that of `getParam()` and `getParamFromGroup()`. +- +-An example of this would look like this: +- +-```c++ +-if (hasParam("GROUPNAME.PARAMNAME")) +- std::cout << "Parameter value is read from the input file." << std::endl; +-else +- std::cout << "Using the default parameter value." << std::endl; +-``` +-or, +-```c++ +-if (hasParamInGroup("GROUPNAME","PARAMNAME")) +- std::cout << "Parameter value is read from the input file." << std::endl; +-else +- std::cout << "Using the default parameter value." << std::endl; +-``` +- +-Using these functions we can better check which parameter values are being included in our program. +- +-* > __Task 4__: Using one of the bool `hasParam` functions, place an output in the problem file to alert the user where the parameter value comes from. +diff -ruN exercises/exercise-runtimeparams/spatialparams.hh exercises/solution/exercise-runtimeparams/spatialparams.hh +--- exercises/exercise-runtimeparams/spatialparams.hh 2023-06-01 14:31:33.157063038 +0200 ++++ exercises/solution/exercise-runtimeparams/spatialparams.hh 2023-04-26 14:37:49.797150837 +0200 +@@ -24,8 +24,8 @@ + * fully implicit model. + */ + +-#ifndef DUMUX_EXRUNTIMEPARAMS_INJECTION_SPATIAL_PARAMS_HH +-#define DUMUX_EXRUNTIMEPARAMS_INJECTION_SPATIAL_PARAMS_HH ++#ifndef DUMUX_RUNTIMEPARAMS_INJECTION_SPATIAL_PARAMS_HH ++#define DUMUX_RUNTIMEPARAMS_INJECTION_SPATIAL_PARAMS_HH + + #include <dumux/porousmediumflow/fvspatialparamsmp.hh> + #include <dumux/material/fluidmatrixinteractions/2p/brookscorey.hh> -- GitLab