diff --git a/slides/problem.md b/slides/problem.md index e971ea87f4873e0ee929d35e7683078e148fae81..01bfb01842cdaa81bf22adff39f7a52d24a6dff5 100644 --- a/slides/problem.md +++ b/slides/problem.md @@ -1,10 +1,9 @@ --- -title: Setting up a test problem / application and using the build system (CMake) +title: Setting up a problem / using cmake --- -# Test Problems / Applications - ## Test Problems / Applications + A test problem / application consists of: * A property file (properties.hh) @@ -14,13 +13,15 @@ A test problem / application consists of: * a main file (often main.cc) * a build system file (CMakeLists.txt) -## Example for an immiscible two phase injection test case + +## Example: gas injection / immiscible two phase flow + Mass balance: $\begin{equation} \phi \frac{\partial \varrho_\alpha S_\alpha}{\partial t} - - \text{div} \left\{ \boldsymbol{v}_\alpha \right\} + \nabla \cdot \boldsymbol{v}_\alpha - q_\alpha = 0 \end{equation}$ @@ -28,85 +29,108 @@ $\begin{equation} Momentum balance: $\begin{equation} -\boldsymbol{v}_\alpha = \varrho_\alpha \frac{k_{r\alpha}}{\mu_\alpha} \mathbf{K} \left(\textbf{grad}\, p_\alpha - \varrho_{\alpha} \mathbf{g} \right) +\boldsymbol{v}_\alpha = \varrho_\alpha \frac{k_{r\alpha}}{\mu_\alpha} \mathbf{K} \left(\nabla\, p_\alpha - \varrho_{\alpha} \mathbf{g} \right) \end{equation}$ -# The properties file (properties.hh) + +# Properties <small>(`properties.hh`)</small> ## The properties file -Lists all the properties and dependencies of the current problem. The injection test case inherits from the 2p model: + +Sets all properties of the current problem. The injection test case inherits from the 2p model: ```cpp namespace Dumux::Properties { -// define the TypeTag for this problem with a cell-centered two-point flux approximation spatial discretization. -// Create new type tags + +// define the TypeTag for this problem namespace TTag { struct Injection2p { using InheritsFrom = std::tuple<TwoP>; }; } // end namespace TTag -// Include all necessary properties within the namespace Dumux::Properties + +// Set/Overwrite properties within the namespace Dumux::Properties + } // end namespace Dumux::Properties ``` ## The properties file + Often specifies the discretization method: ```cpp namespace Dumux::Properties { -// define the TypeTag for this problem with a cell-centered two-point flux approximation spatial discretization. -// Create new type tags +// define the TypeTag for this problem with a cell-centered two-point +// flux approximation spatial discretization. namespace TTag { struct Injection2p { using InheritsFrom = std::tuple<TwoP>; }; -struct Injection2pCC { using InheritsFrom = std::tuple<Injection2p, CCTpfaModel>; }; +struct Injection2pCC { + using InheritsFrom = std::tuple<Injection2p, CCTpfaModel>; +}; } // end namespace TTag } // end namespace Dumux::Properties ``` ## The properties file -The grid type: + +Setting the `Grid` type: ```cpp -// Set the grid type template<class TypeTag> -struct Grid<TypeTag, TTag::Injection2p> { using type = Dune::YaspGrid<2>; }; +struct Grid<TypeTag, TTag::Injection2p> +{ using type = Dune::YaspGrid<2>; }; ``` ## The properties file -The problem type: + +Setting our `Problem` type: ```cpp -// Set the problem property template<class TypeTag> -struct Problem<TypeTag, TTag::Injection2p> { using type = InjectionProblem2P<TypeTag>; }; +struct Problem<TypeTag, TTag::Injection2p> +{ using type = InjectionProblem2P<TypeTag>; }; ``` ## The properties file -The spatial parameters: + +Setting our `SpatialParams` type: ```cpp // Set the spatial parameters template<class TypeTag> -struct SpatialParams<TypeTag, TTag::Injection2p> { +struct SpatialParams<TypeTag, TTag::Injection2p> +{ private: - using FVGridGeometry = GetPropType<TypeTag, Properties::FVGridGeometry>; + using FVGridGeometry = GetPropType<TypeTag, + Properties::FVGridGeometry>; using Scalar = GetPropType<TypeTag, Properties::Scalar>; public: - using type = InjectionSpatialParams<FVGridGeometry, Scalar>; }; + using type = InjectionSpatialParams<FVGridGeometry, Scalar>; +}; ``` ## The properties file -The fluidsystem: + +Setting the `FluidSystem` type: ```cpp // Set fluid configuration template<class TypeTag> -struct FluidSystem<TypeTag, TTag::Injection2p> { using type = FluidSystems::H2ON2<GetPropType - <TypeTag, Properties::Scalar>, FluidSystems::H2ON2DefaultPolicy</*fastButSimplifiedRelations=*/true>>; }; +struct FluidSystem<TypeTag, TTag::Injection2p> +{ +private: + using Scalar = GetPropType<TypeTag, Properties::Scalar>; + using Policy = FluidSystems::H2ON2DefaultPolicy< + /*fastButSimplifiedRelations=*/true + >; +public: + using type = FluidSystems::H2ON2<Scalar, Policy>; +}; ``` -## The properties file -The property file can also incorporate many more properties depending on the utilized model and test case. +## -# The problem file (problem.hh) +<h2>The properties file may set many more properties depending on the utilized model and test case.</h2> + +# The problem <small>(`problem.hh`)</small> ## The problem file A problem in DuMu$^\mathsf{x}$ implements a specific model scenario: @@ -115,21 +139,25 @@ A problem in DuMu$^\mathsf{x}$ implements a specific model scenario: template<class TypeTag> class InjectionProblem2P : public PorousMediumFlowProblem<TypeTag> { -// Details of the model scenario (BoundaryConditions, InitialConditions, etc.) + // Details of the model scenario + // - BoundaryConditions + // - InitialConditions + // - etc. } ``` ## The problem file -Defines boundary conditions: + +Defining the types of boundary conditions: ```cpp 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 + // Use Dirichlet boundary conditions on the left if (globalPos[0] < eps_) bcTypes.setAllDirichlet(); - // set all other as Neumann boundaries + // Use Neumann boundary conditions on the rest of the boundary else bcTypes.setAllNeumann(); return bcTypes; @@ -138,99 +166,82 @@ BoundaryTypes boundaryTypesAtPos(const GlobalPosition &globalPos) const ## The problem file -Evaluates boundary conditions: + +Evaluating boundary conditions: ```cpp -PrimaryVariables dirichletAtPos(const GlobalPosition &globalPos) const -{ - return initialAtPos(globalPos); -} +PrimaryVariables dirichletAtPos(const GlobalPosition& globalPos) const +{ return initialAtPos(globalPos); } ``` -## The problem file ```cpp -PrimaryVariables neumannAtPos(const GlobalPosition &globalPos) const +NumEqVector neumannAtPos(const GlobalPosition& globalPos) const { - // initialize values to zero, i.e. no-flow Neumann boundary conditions - PrimaryVariables values(0.0); - - // 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 (time_ < injectionDuration_ && globalPos[1] < 15 + eps_ && globalPos[1] > 7 - eps_ - && globalPos[0] > 0.9*this->fvGridGeometry().bBoxMax()[0]) + NumEqVector values(0.0); + if (injectionActive() && onInjectionBoundary(globalPos)) { - // inject nitrogen. Negative values mean injection + // 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; } ``` ## The problem file -Defines initial conditions + +Defining initial conditions: + ```cpp -PrimaryVariables initialAtPos(const GlobalPosition &globalPos) const +PrimaryVariables initialAtPos(const GlobalPosition& globalPos) const { PrimaryVariables values(0.0); - // get the water density at atmospheric conditions - const Scalar densityW = FluidSystem::H2O::liquidDensity(temperature(), 1.0e5); - - // assume an initially hydrostatic liquid pressure profile - const Scalar pw = 1.0e5 - densityW*this->gravity()[dimWorld-1]*(aquiferDepth_ - globalPos[dimWorld-1]); + const Scalar densityW = FluidSystem::H2O::liquidDensity( + temperature(), 1.0e5 + ); + const Scalar pw = 1.0e5 - densityW*this->gravity()[dimWorld-1]*( + aquiferDepth_ - globalPos[dimWorld-1] + ); values[Indices::pressureIdx] = pw; - values[Indices::saturationIdx] = 0.0; return values; } ``` ## The problem file -Defines source/sink terms + +Defining source/sink terms: ```cpp NumEqVector sourceAtPos(const GlobalPosition &globalPos) const { - NumEqVector source(0.0) - - // The units must be according to either using mole or mass fractions. - // (mole/(m^3*s) or kg/(m^3*s)) - // extract nitrogen at specific point. Positive values mean extraction. - if (globalPos[0] < 5 + eps_ && globalPos[0] > 4 - eps_ && globalPos[1] < 5 + eps_ && globalPos[1] > 4 - eps_) - source[Indices::conti0EqIdx + FluidSystem::N2Idx] = 1e-6; - - return source; + return NumEqVector(0.0); } ``` -# The spatial parameters (spatialparams.hh) +# Spatial Parameters <small>(`spatialparams.hh`)</small> ## The spatial parameters -The spatialparams define spatial parameters of the porous material. -## The spatial parameters -Permeability: +Defining the intrinsic permeability: ```cpp -PermeabilityType permeabilityAtPos(const GlobalPosition& globalPos) const -{ - // here, either aquitard or aquifer permeability are returned, depending on the global position - if (isInAquitard_(globalPos)) - return aquitardK_; - return aquiferK_; -} -private: -// provides a convenient way distinguishing whether a given location is inside the aquitard -bool isInAquitard_(const GlobalPosition &globalPos) const +template<class FVGridGeometry, class Scalar> +class InjectionSpatialParams : ... { - // globalPos[dimWorld-1] is the y direction for 2D grids or the z direction for 3D grids - return globalPos[dimWorld-1] > aquiferHeightFromBottom_ + eps_; -} + auto permeabilityAtPos(const GlobalPosition& globalPos) const + { + if (isInAquitard_(globalPos)) + return aquitardK_; + return aquiferK_; + } ``` ## The spatial parameters -Porosity: + +Defining the porosity: ```cpp Scalar porosityAtPos(const GlobalPosition& globalPos) const @@ -238,28 +249,28 @@ Scalar porosityAtPos(const GlobalPosition& globalPos) const // here, either aquitard or aquifer porosity are returned if (isInAquitard_(globalPos)) return aquitardPorosity_; - return aquiferPorosity_; } ``` ## The spatial parameters + Capillary pressure - saturation relationship: More information in a later lecture on the materialsystem! ```cpp -const auto fluidMatrixInteractionAtPos(const GlobalPosition& globalPos) const +auto fluidMatrixInteractionAtPos(const GlobalPosition& globalPos) const { if (isInAquitard_(globalPos)) return makeFluidMatrixInteraction(aquitardPcKrSwCurve_); - return makeFluidMatrixInteraction(aquiferPcKrSwCurve_); } ``` ## The spatial parameters -Temperature: + +Defining the temperature in the domain: ```cpp Scalar temperatureAtPos(const GlobalPosition& globalPos) const @@ -269,9 +280,10 @@ Scalar temperatureAtPos(const GlobalPosition& globalPos) const } ``` -# The input file (*.input) +# Runtime parameters <small>(`params.input`)</small> ## The input file + DUNE INI syntax: ```cpp @@ -287,14 +299,19 @@ Input files are specified as arguments to the executable `./myexecutable params.input` + ## The input file + If no input file is given it defaults to the file `params.input` or `myexecutablename.input` -Parameters can be overwritten through the command line like via: +Parameters can be overwritten in the command line via: -`./executable –Problem.Name myNewName` +```bash +./executable params.input –Problem.Name myNewName +``` -# The main source file (*.cc) + +# The main file <small>(`2pmain.cc`)</small> ## The main source file * Each problem has a specific main file (`test_name.cc` or `main.cc`) which sets up the program structure and calls assembler and solvers to assemble and solve the PDEs. @@ -302,11 +319,12 @@ Parameters can be overwritten through the command line like via: * The main file usually includes the problem, the solvers, the assembler, the VTK output module and the gridmanager. ## The main source file -Common structure for most main files: + +Startup / parsing runtime parameters: ```cpp // define the type tag for this problem -using TypeTag = Properties::TTag::OnePIncompressible; +using TypeTag = Properties::TTag::Injection2pCC; // maybe initialize MPI and/or multithreading backend const auto& mpiHelper = Dune::MPIHelper::instance(); @@ -320,6 +338,9 @@ Parameters::init(argc, argv); ``` ## The main source file + +Grid creation: + ```cpp // try to create a grid (from the given grid file or the input file) GridManager<GetPropType<TypeTag, Properties::Grid>> gridManager; @@ -327,8 +348,15 @@ gridManager.init(); // we compute on the leaf grid view const auto& leafGridView = gridManager.grid().leafGridView(); +``` + +## The main source file +`GridGeometry` and `Problem` instantiation: + +```cpp // create the finite volume grid geometry +// (represents the chosen discretization scheme) using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>; auto gridGeometry = std::make_shared<GridGeometry>(leafGridView); @@ -337,7 +365,11 @@ using Problem = GetPropType<TypeTag, Properties::Problem>; auto problem = std::make_shared<Problem>(gridGeometry); ``` + ## The main source file + +Initial solution and secondary variables: + ```cpp // the solution vector using SolutionVector = GetPropType<TypeTag, Properties::SolutionVector>; @@ -345,15 +377,29 @@ SolutionVector x(gridGeometry->numDofs()); // the grid variables using GridVariables = GetPropType<TypeTag, Properties::GridVariables>; -auto gridVariables = std::make_shared<GridVariables>(problem, gridGeometry); +auto gridVariables = std::make_shared<GridVariables>( + problem, gridGeometry +); gridVariables->init(x); +``` +## The main source file + +Setting up [VTK](https://vtk.org/) output: + +```cpp // initialize the vtk output module -VtkOutputModule<GridVariables, SolutionVector> vtkWriter(*gridVariables, x, problem->name()); +VtkOutputModule<GridVariables, SolutionVector> vtkWriter( + *gridVariables, x, problem->name() +); + using VelocityOutput = GetPropType<TypeTag, Properties::VelocityOutput>; -vtkWriter.addVelocityOutput(std::make_shared<VelocityOutput>(*gridVariables)); +vtkWriter.addVelocityOutput( + std::make_shared<VelocityOutput>(*gridVariables) +); + using IOFields = GetPropType<TypeTag, Properties::IOFields>; -IOFields::initOutputModule(vtkWriter); //!< Add model specific output fields +IOFields::initOutputModule(vtkWriter); ``` ## The main source file @@ -366,17 +412,14 @@ Differences for various problem cases: ## The main source file -A stationary linear problem: +Assembling the system for a linear problem: ```cpp // 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()); +auto assembler = std::make_shared<Assembler>( + problem, gridGeometry, gridVariables +); // the discretization matrices for stationary linear problems using JacobianMatrix = GetPropType<TypeTag, Properties::JacobianMatrix>; @@ -387,7 +430,18 @@ assembler->assembleJacobianAndResidual(x); ``` ## The main source file + +And solving it: + ```cpp +using LinearSolver = ILUBiCGSTABIstlSolver< + LinearSolverTraits<GridGeometry>, + LinearAlgebraTraitsFromAssembler<Assembler> +>; +auto linearSolver = std::make_shared<LinearSolver>( + gridGeometry->gridView(), gridGeometry->dofMapper() +); + // we solve Ax = -r to save update and copy (*r) *= -1.0; linearSolver->solve(*A, x, *r); @@ -396,45 +450,45 @@ gridVariables->update(x); ``` ## The main source file -A stationary non-linear problem: + +For non-linear problems, use the `NewtonSolver`: ```cpp -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 +auto assembler = ...; // as before +auto linearSolver = ...; // as before + using NewtonSolver = Dumux::NewtonSolver<Assembler, LinearSolver>; NewtonSolver nonLinearSolver(assembler, linearSolver); + // linearize & solve nonLinearSolver.solve(x); ``` ## The main source file -An instationary non-linear problem: + +For instationary problems, pass a `TimeLoop` and a solution vector +carrying the solution on the previous time step to the assembler: ```cpp -// instantiate time loop +SolutionVector xOld = x; + auto timeLoop = std::make_shared<TimeLoop<Scalar>(0.0, dt, tEnd); timeLoop->setMaxTimeStepSize(maxDt); -using Assembler = FVAssembler<TypeTag, DiffMethod::numeric>; -auto assembler = std::make_shared<Assembler>(problem, gridGeometry, gridVariables, timeLoop); -// the linear solver -using LinearSolver = ILUBiCGSTABIstlSolver<LinearSolverTraits<GridGeometry>, - LinearAlgebraTraitsFromAssembler<Assembler>>; -auto linearSolver = std::make_shared<LinearSolver>(gridGeometry->gridView(), gridGeometry->dofMapper()); +using Assembler = FVAssembler<TypeTag, DiffMethod::numeric>; +auto assembler = std::make_shared<Assembler>( + problem, gridGeometry, gridVariables, timeLoop, xOld +); -// the non-linear solver -using NewtonSolver = Dumux::NewtonSolver<Assembler, LinearSolver>; -NewtonSolver nonLinearSolver(assembler, linearSolver); +// assemble linear/non-linear problem as before +// ... ``` ## The main source file + +The skeleton of a time loop: + ```cpp -// time loop timeLoop->start(); do { // Calculate solution within each time step @@ -442,15 +496,13 @@ timeLoop->start(); do ``` ## The main source file -The timeloop for the instationary non-linear problem: + +Solving a time step: ```cpp // time loop timeLoop->start(); do { - // set previous solution for storage evaluations - assembler->setPreviousSolution(xOld); - // linearize & solve nonLinearSolver.solve(x, *timeLoop); @@ -460,18 +512,24 @@ timeLoop->start(); do ``` ## The main source file + +Advancing the time loop to the next step: + ```cpp // 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())); + timeLoop->setTimeStepSize( + nonLinearSolver.suggestTimeStepSize(timeLoop->timeStepSize()) + ); } while (!timeLoop->finished()); + timeLoop->finalize(leafGridView.comm()); ``` -# Build system (CMakeLists.txt) +# Build system <small>(`CMakeLists.txt`)</small> ## Build system - what is CMake? @@ -491,62 +549,74 @@ timeLoop->finalize(leafGridView.comm()); * Build with the script `dune-common/bin/dunecontrol <options>` which takes care of all dependencies and modular dune structure. * Option `all`: build all libraries and executables. -* Option `--opts=<optionfile.opts>` specify e.g. compiler flags, for DuMu$^\mathsf{x}$ the `optionfile` is `dumux/cmake.opts`. -* Option `--build-dir=<build directory>` specify path for out-of-source build. -* Default: every module contains its own build directory `build-cmake/`. +* Option `--opts=<optionfile.opts>` specify e.g. compiler flags; For DuMu$^\mathsf{x}$, you may use `dumux/cmake.opts`. +* Option `--build-dir=<build directory>` specify path for out-of-source build; Default: every module contains its own build directory `build-cmake/`. * You have to reconfigure (possibly deleting all build directories first) whenever a dependency changes or a Dune library is updated. -`./dune-common/bin/dunecontrol --opts=dumux/cmake.opts all` +## Invoking `dunecontrol` + +```bash +./dune-common/bin/dunecontrol --opts=dumux/cmake.opts all +``` ## Build system - important basic commands + * Use `add_subdirectory` for recursively adding subdirectories. * The subdirectory has to contain a `CMakeLists.txt` file (can be empty). * Executables are added via `add_executable(<name> source1 [source2 ...])`. -* Tests are added via `dune_add_test(...)` which also add a test executable to the test suite. +* Tests are added via `dumux_add_test(...)` which also add a test executable to the test suite. * Symlinks can be added via `dune_symlink_to_source_files(FILES file1 [file2 ...])`. ## Build system Simplest incorporation of a test by defining name, source file and command line arguments: ```cmake -dune_add_test(NAME test_2p_incompressible_box -SOURCES test_2p_fv.cc -CMD_ARGS test_2p.input) +dumux_add_test( + NAME test_2p_incompressible_box + SOURCES test_2p_fv.cc + CMD_ARGS test_2p.input +) ``` ## Build system Add extra compile definitions and commands: ```cmake -dune_add_test(NAME test_2p_incompressible_box -SOURCES test_2p_fv.cc -COMPILE_DEFINITIONS TYPETAG=TwoPIncompressibleBox -COMMAND ${CMAKE_SOURCE_DIR}/bin/testing/runtest.py -CMD_ARGS --script fuzzy - --files ${CMAKE_SOURCE_DIR}/test/references/lensbox-reference.vtu - ${CMAKE_CURRENT_BINARY_DIR}/2p_box-00007.vtu - --command "${CMAKE_CURRENT_BINARY_DIR}/test_2p_incompressible_box - test_2p.input -Problem.Name 2p_box") +dune_add_test( + NAME test_2p_incompressible_box + SOURCES test_2p_fv.cc + COMPILE_DEFINITIONS TYPETAG=TwoPIncompressibleBox + COMMAND ${CMAKE_SOURCE_DIR}/bin/testing/runtest.py + CMD_ARGS --script fuzzy + --files ${CMAKE_SOURCE_DIR}/test/references/lensbox-reference.vtu + ${CMAKE_CURRENT_BINARY_DIR}/2p_box-00007.vtu + --command "${CMAKE_CURRENT_BINARY_DIR}/test_2p_incompressible_box + test_2p.input -Problem.Name 2p_box" +) ``` ## Build system + Extra: Create linked parameter file in `build-cmake` folder, separately add executable and set compile defintions for an executable: ```cmake dune_symlink_to_source_files(FILES "params.input") # using tpfa add_executable(test_2p_incompressible_tpfa EXCLUDE_FROM_ALL main.cc) -target_compile_definitions(test_2p_incompressible_tpfa PUBLIC TYPETAG=TwoPIncompressibleTpfa) +target_compile_definitions( + test_2p_incompressible_tpfa PUBLIC TYPETAG=TwoPIncompressibleTpfa +) ``` ## Build system + Instead of the box discretization use the tpfa discretization: + ```cmake -dumux_add_test(NAME test_2p_incompressible_tpfa - TARGET test_2p_incompressible_tpfa - LABELS porousmediumflow 2p - COMMAND ${CMAKE_SOURCE_DIR}/bin/testing/runtest.py - CMD_ARGS --script fuzzy - --files ${CMAKE_SOURCE_DIR}/test/references/test_2p_incompressible_cc-reference.vtu - ${CMAKE_CURRENT_BINARY_DIR}/test_2p_incompressible_tpfa-00007.vtu - --command "${CMAKE_CURRENT_BINARY_DIR}/test_2p_incompressible_tpfa params.input -Problem.Name test_2p_incompressible_tpfa") +dumux_add_test( + NAME test_2p_incompressible_tpfa + TARGET test_2p_incompressible_tpfa + LABELS porousmediumflow 2p + COMMAND ${CMAKE_SOURCE_DIR}/bin/testing/runtest.py + ... +) ``` ## Build system @@ -583,12 +653,17 @@ Handling results: * Is enabled when a multi-threading backend is found * Backend is selected by `CMAKE` during configuration and stored in `DUMUX_MULTITHREADING_BACKEND` * Possible examples are `OpenMP`, `TBB`, C++ parallel algorithms and `Kokkos` -* Can be turned of in `params.input` with: + +## Switching off multi-threading + +Simply add the following to your input file: + ```cpp [Assembly] Multithreading = false ``` -* Important for working on clusters: Number of threads can also be restricted via manipulating the environment variable `DUMUX_NUM_THREADS=2 ./executable` + +Important for working on clusters: Number of threads can also be restricted via manipulating the environment variable `DUMUX_NUM_THREADS=2 ./executable` # Exercises