diff -ruN exercises/exercise-basic/2pmain.cc exercises/solution/exercise-basic/2pmain.cc --- exercises/exercise-basic/2pmain.cc 2024-07-17 14:02:52.088041093 +0200 +++ exercises/solution/exercise-basic/2pmain.cc 1970-01-01 01:00:00.000000000 +0100 @@ -1,145 +0,0 @@ -// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- -// vi: set et ts=4 sw=4 sts=4: -// -// SPDX-FileCopyrightInfo: Copyright © DuMux-Course contributors, see AUTHORS.md in root folder -// SPDX-License-Identifier: GPL-3.0-or-later -// -/*! - * \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 2024-07-17 14:02:52.092041122 +0200 @@ -0,0 +1,138 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +// +// SPDX-FileCopyrightInfo: Copyright © DuMux-Course contributors, see AUTHORS.md in root folder +// SPDX-License-Identifier: GPL-3.0-or-later +// +/*! + * \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 2024-07-17 14:37:31.109952737 +0200 +++ exercises/solution/exercise-basic/CMakeLists.txt 2024-07-17 14:02:52.092041122 +0200 @@ -1,12 +1,9 @@ # SPDX-FileCopyrightInfo: Copyright © DuMux-Course contributors, see AUTHORS.md in root folder # SPDX-License-Identifier: GPL-3.0-or-later -# the immiscible two-phase simulation program -dumux_add_test(NAME exercise_basic_2p - SOURCES 2pmain.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 2024-07-17 14:37:31.221955214 +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, unit: m - aquiferDepth_ = getParam<Scalar>("Problem.AquiferDepth"); - // The duration of the injection, unit: seconds - 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 units from 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 2024-07-17 14:37:31.221955214 +0200 +++ exercises/solution/exercise-basic/injection2pniproblem.hh 2024-07-17 14:37:31.221955214 +0200 @@ -7,7 +7,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 @@ -55,6 +55,8 @@ using FluidSystem = GetPropType<TypeTag, Properties::FluidSystem>; using NumEqVector = Dumux::NumEqVector<PrimaryVariables>; + using N2 = typename FluidSystem::N2; + static constexpr int dimWorld = GridView::dimensionworld; using Element = typename GridView::template Codim<0>::Entity; using GlobalPosition = typename Element::Geometry::GlobalCoordinate; @@ -103,13 +105,6 @@ else bcTypes.setAllNeumann(); - /*! - * TODO:dumux-course-task 4: - * 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; } @@ -122,12 +117,6 @@ PrimaryVariables dirichletAtPos(const GlobalPosition &globalPos) const { return initialAtPos(globalPos); - - /*! - * TODO:dumux-course-task 4: - * Set Dirichlet conditions for the energy equation on the left boundary. - * Think about: is there anything necessary to do here? - */ } /*! @@ -142,19 +131,20 @@ 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 + const Scalar injectionRate = -1e-4; + + // Inject nitrogen. Negative values mean injection // Unit: kg/(s*m^2) - values[Indices::conti0EqIdx + FluidSystem::N2Idx] = -1e-4; + values[Indices::conti0EqIdx + FluidSystem::N2Idx] = injectionRate; values[Indices::conti0EqIdx + FluidSystem::H2OIdx] = 0.0; - /*! - * TODO:dumux-course-task 4: - * Set Neumann noflow conditions for the energy equation everywhere else except the left boundary. - * Additionally, consider the energy flux at the injection point which is equal to the product of the respective mass flux and the matching enthalpy. Use the function `gasEnthalpy(temperature,pressure)` from the N2 component to access the necessary enthalpy. - * hint: use `Indices::energyEqIdx` to access the entry belonging to the energy flux. - */ + // Energy fluxes are always mass specific + // unit: W/(m^2) + const Scalar temperatureAtInjection = initialAtPos(globalPos)[Indices::temperatureIdx];/*K*/ + const Scalar pressureAtInjection = initialAtPos(globalPos)[Indices::pressureIdx];/*Pa*/ + values[Indices::energyEqIdx] = injectionRate /*kg/(m^2 s)*/ *N2::gasEnthalpy(temperatureAtInjection, pressureAtInjection)/*J/kg*/; } return values; @@ -180,7 +170,7 @@ { PrimaryVariables values(0.0); - // get the water density at atmospheric conditions + // Get the water density at atmospheric conditions const Scalar densityW = FluidSystem::H2O::liquidDensity(283.15, 1.0e5); // Assume an initially hydrostatic liquid pressure profile @@ -190,13 +180,10 @@ values[Indices::pressureIdx] = pw; values[Indices::saturationIdx] = 0.0; - /*! - * TODO:dumux-course-task 4: - * 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.0; + return values; } diff -ruN exercises/exercise-basic/injection2pproblem.hh exercises/solution/exercise-basic/injection2pproblem.hh --- exercises/exercise-basic/injection2pproblem.hh 2024-07-17 14:37:31.221955214 +0200 +++ exercises/solution/exercise-basic/injection2pproblem.hh 1970-01-01 01:00:00.000000000 +0100 @@ -1,211 +0,0 @@ -// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- -// vi: set et ts=4 sw=4 sts=4: -// -// SPDX-FileCopyrightInfo: Copyright © DuMux-Course contributors, see AUTHORS.md in root folder -// SPDX-License-Identifier: GPL-3.0-or-later -// -/*! - * \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, unit: m - aquiferDepth_ = getParam<Scalar>("Problem.AquiferDepth"); - // The duration of the injection, unit: seconds - 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 - // unit: 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 2024-06-26 11:12:30.497879893 +0200 +++ exercises/solution/exercise-basic/params.input 2024-07-17 14:02:52.096041149 +0200 @@ -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/properties2p.hh exercises/solution/exercise-basic/properties2p.hh --- exercises/exercise-basic/properties2p.hh 2024-07-17 14:02:52.088041093 +0200 +++ exercises/solution/exercise-basic/properties2p.hh 1970-01-01 01:00:00.000000000 +0100 @@ -1,63 +0,0 @@ -// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- -// vi: set et ts=4 sw=4 sts=4: -// -// SPDX-FileCopyrightInfo: Copyright © DuMux-Course contributors, see AUTHORS.md in root folder -// SPDX-License-Identifier: GPL-3.0-or-later -// -/*! - * \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 2024-07-17 14:02:52.088041093 +0200 +++ exercises/solution/exercise-basic/properties2pni.hh 2024-07-17 14:02:52.096041149 +0200 @@ -24,13 +24,9 @@ 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 diff -ruN exercises/exercise-basic/README.md exercises/solution/exercise-basic/README.md --- exercises/exercise-basic/README.md 2024-07-17 14:37:31.109952737 +0200 +++ exercises/solution/exercise-basic/README.md 1970-01-01 01:00:00.000000000 +0100 @@ -1,94 +0,0 @@ -# Exercise Basics (DuMuX course) - -## 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 non-isothermal problem (__2pni__). They both set up the same scenario with the difference that the 2pni model introduces an extra energy equation. - -## Task 1: Getting familiar with the code - -Locate all the files you will need for this exercise -* The __main file__ for the __2p__ problem : `2pmain.cc` -* The __problem file__ for the __2p__ problem: `injection2pproblem.hh` -* The __problem file__ for the __2pni__ problem: `injection2pniproblem.hh` -* The __properties file__ for the __2p__ problem: `properties2p.hh` -* The __properties file__ for the __2pni__ problem: `properties2pni.hh` -* The shared __spatial parameters file__: `injection2pspatialparams.hh` -* The shared __input file__: `params.input` - -## Task 2: Compiling and running an executable - -* Change to the build-directory - -```bash -cd ../../build-cmake/exercises/exercise-basic -``` - -* Compile the executable `exercise_basic_2p` - -```bash -make exercise_basic_2p -``` - -* Execute the problem and inspect the result - -```bash -./exercise_basic_2p params.input -``` - -* you can look at the results with paraview - -```bash -paraview injection-2p.pvd -``` - -## Task 3: Setting up a new executable (for a non-isothermal simulation) - -* 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) -``` - -* In the respective build-cmake folder, test that everything compiles without error - -```bash -make # should rerun cmake -make exercise_basic_2pni # builds new executable -``` - -## Task 4: Setting up a non-isothermal __2pni__ test problem - -* 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: - - __Initial conditions:__ 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}$. Additionally, add a subdomain (20 < x < 30, 5 < y < 35), where you assign a constant initial temperature of 380 K. - - __Boundary conditions:__ Dirichlet boundary conditions at the left boundary with the same temperature gradient as in the initial conditions. For the Neumann conditions, assign an energy flux at the injection point of N$_2$ and no-flow conditions for the energy balance to the rest of the boundaries. - -<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.