From fe48378e83e389edb8f4833c23015a34739aa996 Mon Sep 17 00:00:00 2001 From: Timo Koch <timo.koch@iws.uni-stuttgart.de> Date: Tue, 15 Jun 2021 20:44:40 +0200 Subject: [PATCH] [md][boundary] Port Darcy-Stokes convergence test to new staggered --- .../freeflowporousmedium/1p_1p/CMakeLists.txt | 2 + .../1p_1p/convergence/CMakeLists.txt | 50 +++ .../1p_1p/convergence/analyticalsolutions.hh | 340 +++++++++++++++ .../1p_1p/convergence/convergencetest.py | 124 ++++++ .../1p_1p/convergence/main.cc | 320 ++++++++++++++ .../convergence/manufactured_solution.py | 93 +++++ .../1p_1p/convergence/params.input | 34 ++ .../1p_1p/convergence/problem_darcy.hh | 271 ++++++++++++ .../1p_1p/convergence/problem_freeflow.hh | 391 ++++++++++++++++++ .../1p_1p/convergence/properties.hh | 129 ++++++ .../1p_1p/convergence/spatialparams.hh | 129 ++++++ .../1p_1p/convergence/testcase.hh | 37 ++ 12 files changed, 1920 insertions(+) create mode 100644 test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/CMakeLists.txt create mode 100644 test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/analyticalsolutions.hh create mode 100755 test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/convergencetest.py create mode 100644 test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/main.cc create mode 100644 test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/manufactured_solution.py create mode 100644 test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/params.input create mode 100644 test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/problem_darcy.hh create mode 100644 test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/problem_freeflow.hh create mode 100644 test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/properties.hh create mode 100644 test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/spatialparams.hh create mode 100644 test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/testcase.hh diff --git a/test/multidomain/boundary/freeflowporousmedium/1p_1p/CMakeLists.txt b/test/multidomain/boundary/freeflowporousmedium/1p_1p/CMakeLists.txt index da2b6d53d6..617ade1705 100644 --- a/test/multidomain/boundary/freeflowporousmedium/1p_1p/CMakeLists.txt +++ b/test/multidomain/boundary/freeflowporousmedium/1p_1p/CMakeLists.txt @@ -1,3 +1,5 @@ +add_subdirectory(convergence) + add_input_file_links() add_executable(test_md_boundary_ff1p_pm1p EXCLUDE_FROM_ALL main.cc) diff --git a/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/CMakeLists.txt b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/CMakeLists.txt new file mode 100644 index 0000000000..8336a0b30d --- /dev/null +++ b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/CMakeLists.txt @@ -0,0 +1,50 @@ +add_input_file_links() +dune_symlink_to_source_files(FILES "convergencetest.py") + +add_executable(test_md_boundary_ff1p_pm1p_convtest EXCLUDE_FROM_ALL main.cc) + +dumux_add_test(NAME test_md_boundary_ff1p_pm1p_convtest_shiue1 + TARGET test_md_boundary_ff1p_pm1p_convtest + LABELS multidomain multidomain_boundary stokesdarcy + TIMEOUT 1000 + CMAKE_GUARD HAVE_UMFPACK + COMMAND ./convergencetest.py + CMD_ARGS test_md_boundary_ff1p_pm1p_convtest params.input + -Problem.TestCase ShiueExampleOne + -Darcy.Problem.BoundaryConditions Dirichlet + -FreeFlow.Problem.BoundaryConditions Dirichlet) + +dumux_add_test(NAME test_md_boundary_ff1p_pm1p_convtest_shiue2 + TARGET test_md_boundary_ff1p_pm1p_convtest + LABELS multidomain multidomain_boundary stokesdarcy + TIMEOUT 1000 + CMAKE_GUARD HAVE_UMFPACK + COMMAND ./convergencetest.py + CMD_ARGS test_md_boundary_ff1p_pm1p_convtest params.input + -Problem.TestCase ShiueExampleTwo + -Darcy.Problem.BoundaryConditions Mixed + -FreeFlow.Problem.BoundaryConditions Mixed) + +dumux_add_test(NAME test_md_boundary_ff1p_pm1p_convtest_rybak + TARGET test_md_boundary_ff1p_pm1p_convtest + LABELS multidomain multidomain_boundary stokesdarcy + TIMEOUT 1000 + CMAKE_GUARD HAVE_UMFPACK + COMMAND ./convergencetest.py + CMD_ARGS test_md_boundary_ff1p_pm1p_convtest params.input + -Problem.TestCase Rybak + -Darcy.Problem.BoundaryConditions Neumann + -FreeFlow.Problem.BoundaryConditions Stress) + +dumux_add_test(NAME test_md_boundary_ff1p_pm1p_convtest_navierstokes + TARGET test_md_boundary_ff1p_pm1p_convtest + LABELS multidomain multidomain_boundary stokesdarcy + TIMEOUT 1000 + CMAKE_GUARD HAVE_UMFPACK + COMMAND ./convergencetest.py + CMD_ARGS test_md_boundary_ff1p_pm1p_convtest params.input + -Problem.TestCase Schneider + -FreeFlow.Problem.EnableInertiaTerms true + -FreeFlow.EnableUnsymmetrizedVelocityGradientForBeaversJoseph true + -Darcy.Problem.BoundaryConditions Mixed + -FreeFlow.Problem.BoundaryConditions Mixed) diff --git a/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/analyticalsolutions.hh b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/analyticalsolutions.hh new file mode 100644 index 0000000000..6df9efadd3 --- /dev/null +++ b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/analyticalsolutions.hh @@ -0,0 +1,340 @@ +// -*- 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 + * \ingroup BoundaryTests + * \brief Analytical solutions and rhs for the different test cases + */ + +#ifndef DUMUX_CONVERGENCE_TEST_ANALYTICAL_SOLUTIONS_HH +#define DUMUX_CONVERGENCE_TEST_ANALYTICAL_SOLUTIONS_HH + +#include <cmath> +#include <dune/common/fvector.hh> +#include <dune/common/fmatrix.hh> + +namespace Dumux::Solution::DarcyStokes { + +///////////////////////////////////////////////////////////////////////////////////////////////// +// see Rybak et al., 2015: "Multirate time integration for coupled +// saturated/unsaturated porous medium and free flow systems" +///////////////////////////////////////////////////////////////////////////////////////////////// +namespace Rybak { + +// 0: velocity-x | 1: velocity-y | 2: pressure +Dune::FieldVector<double, 3> darcy(const Dune::FieldVector<double, 2>& globalPos) +{ + Dune::FieldVector<double, 3> sol(0.0); + const double x = globalPos[0]; + const double y = globalPos[1]; + + using std::exp; using std::sin; using std::cos; + sol[0] = -0.5*M_PI*y*y*cos(M_PI*x); + sol[1] = -1.0*y*sin(M_PI*x); + sol[2] = 0.5*y*y*sin(M_PI*x); + return sol; +} + +// 0: mass +Dune::FieldVector<double, 1> darcyRHS(const Dune::FieldVector<double, 2>& globalPos) +{ + const double x = globalPos[0]; + const double y = globalPos[1]; + using std::sin; + return { (0.5*M_PI*y*M_PI*y - 1)*sin(M_PI*x) }; +} + +// 0: velocity-x | 1: velocity-y | 2: pressure +Dune::FieldVector<double, 3> stokes(const Dune::FieldVector<double, 2>& globalPos) +{ + Dune::FieldVector<double, 3> sol(0.0); + const double x = globalPos[0]; + const double y = globalPos[1]; + + using std::sin; using std::cos; + sol[0] = -cos(M_PI*x)*sin(M_PI*y); + sol[1] = sin(M_PI*x)*cos(M_PI*y); + sol[2] = 0.5*y*sin(M_PI*x); + return sol; +} + +// 0: mom-x | 1: mom-y | 2: mass +Dune::FieldVector<double, 3> stokesRHS(const Dune::FieldVector<double, 2>& globalPos) +{ + const double x = globalPos[0]; + const double y = globalPos[1]; + Dune::FieldVector<double, 3> source(0.0); + using std::sin; using std::cos; + source[0] = 0.5*M_PI*y*cos(M_PI*x) - 2*M_PI*M_PI*cos(M_PI*x)*sin(M_PI*y); + source[1] = 0.5*sin(M_PI*x) + 2*M_PI*M_PI*sin(M_PI*x)*cos(M_PI*y); + return source; +} + +// sigma_ff = -sym(grad(v)) + pI +Dune::FieldMatrix<double, 2, 2> stokesStress(const Dune::FieldVector<double, 2>& globalPos) +{ + const double x = globalPos[0]; + const double y = globalPos[1]; + + using std::exp; using std::sin; using std::cos; + + Dune::FieldMatrix<double, 2, 2> stress(0.0); + stress[0][0] = (0.5*y - 2*M_PI*sin(M_PI*y))*sin(M_PI*x); + stress[1][0] = 0.0; + stress[0][1] = stress[1][0]; // symmetric + stress[1][1] = (0.5*y - 2*M_PI*sin(M_PI*y))*sin(M_PI*x); + return stress; +} + +} // end namespace Rybak + +///////////////////////////////////////////////////////////////////////////////////////////////// +// see Shiue et al., 2018: "Convergence of the MAC Scheme for the Stokes/Darcy Coupling Problem" +// Example 1 +///////////////////////////////////////////////////////////////////////////////////////////////// +namespace ShiueOne { + +// 0: velocity-x | 1: velocity-y | 2: pressure +Dune::FieldVector<double, 3> darcy(const Dune::FieldVector<double, 2>& globalPos) +{ + Dune::FieldVector<double, 3> sol(0.0); + const double x = globalPos[0]; + const double y = globalPos[1]; + + using std::exp; using std::sin; using std::cos; + sol[0] = M_PI*(-exp(1)*y + exp(y))*sin(M_PI*x); + sol[1] = (exp(1) - exp(y))*cos(M_PI*x); + sol[2] = (exp(y) - y*exp(1)) * cos(M_PI*x); + + return sol; +} + +// 0: mass +Dune::FieldVector<double, 1> darcyRHS(const Dune::FieldVector<double, 2>& globalPos) +{ + const double x = globalPos[0]; + const double y = globalPos[1]; + using std::exp; using std::sin; using std::cos; + return { cos(M_PI*x) * (M_PI*M_PI*(exp(y) - y*exp(1)) - exp(y)) }; +} + +// 0: velocity-x | 1: velocity-y | 2: pressure +Dune::FieldVector<double, 3> stokes(const Dune::FieldVector<double, 2>& globalPos) +{ + Dune::FieldVector<double, 3> sol(0.0); + const double x = globalPos[0]; + const double y = globalPos[1]; + + using std::exp; using std::sin; using std::cos; + sol[0] = -1/M_PI * exp(y) * sin(M_PI*x); + sol[1] = (exp(y) - exp(1)) * cos(M_PI*x); + sol[2] = 2*exp(y) * cos(M_PI*x); + return sol; +} + +// 0: mom-x | 1: mom-y | 2: mass +Dune::FieldVector<double, 3> stokesRHS(const Dune::FieldVector<double, 2>& globalPos) +{ + const double x = globalPos[0]; + const double y = globalPos[1]; + using std::exp; using std::sin; using std::cos; + Dune::FieldVector<double, 3> source(0.0); + source[0] = exp(y)*sin(M_PI*x) * (1/M_PI -3*M_PI); + source[1] = cos(M_PI*x) * (M_PI*M_PI*(exp(y)- exp(1)) + exp(y)); + return source; +} + +// sigma_ff = -sym(grad(v)) + pI +Dune::FieldMatrix<double, 2, 2> stokesStress(const Dune::FieldVector<double, 2>& globalPos) +{ + const double x = globalPos[0]; + const double y = globalPos[1]; + + using std::exp; using std::sin; using std::cos; + + Dune::FieldMatrix<double, 2, 2> stress(0.0); + stress[0][0] = 4.0*exp(y)*cos(M_PI*x); + stress[1][0] = (M_PI*M_PI*(exp(y) - exp(1)) + 1.0*exp(y))*sin(M_PI*x)/M_PI; + stress[0][1] = stress[1][0]; // symmetric + stress[1][1] = 0.0; + return stress; +} + +} // end namespace ShiueExOne + +///////////////////////////////////////////////////////////////////////////////////////////////// +// see Shiue et al., 2018: "Convergence of the MAC Scheme for the Stokes/Darcy Coupling Problem" +// Example 2 +///////////////////////////////////////////////////////////////////////////////////////////////// +namespace ShiueTwo { + +// 0: velocity-x | 1: velocity-y | 2: pressure +Dune::FieldVector<double, 3> darcy(const Dune::FieldVector<double, 2>& globalPos) +{ + Dune::FieldVector<double, 3> sol(0.0); + const double x = globalPos[0]; + const double y = globalPos[1]; + + sol[0] = x*(y - 1.0) + (x - 1.0)*(y - 1.0) - 2.0; + sol[1] = x*(x - 1.0) - 1.0*(y - 1.0)*(y - 1.0) - 2.0; + sol[2] = x*(1.0-x)*(y-1.0) + (y-1.0)*(y-1.0)*(y-1.0)/3.0 + 2.0*x + 2.0*y + 4.0; + + return sol; +} + +// 0: mass +Dune::FieldVector<double, 1> darcyRHS(const Dune::FieldVector<double, 2>& globalPos) +{ return { 0.0 }; } + +// 0: velocity-x | 1: velocity-y | 2: pressure +Dune::FieldVector<double, 3> stokes(const Dune::FieldVector<double, 2>& globalPos) +{ + Dune::FieldVector<double, 3> sol(0.0); + const double x = globalPos[0]; + const double y = globalPos[1]; + + sol[0] = (y-1.0)*(y-1.0) + x*(y-1.0) + 3.0*x - 1.0; + sol[1] = x*(x-1.0) - 0.5*(y-1.0)*(y-1.0) - 3.0*y + 1.0; + sol[2] = 2.0*x + y - 1.0; + return sol; +} + +// 0: mom-x | 1: mom-y | 2: mass +Dune::FieldVector<double, 3> stokesRHS(const Dune::FieldVector<double, 2>& globalPos) +{ return { 0.0, 0.0, 0.0 }; } + +// sigma_ff = -sym(grad(v)) + pI +Dune::FieldMatrix<double, 2, 2> stokesStress(const Dune::FieldVector<double, 2>& globalPos) +{ + const double x = globalPos[0]; + const double y = globalPos[1]; + + Dune::FieldMatrix<double, 2, 2> stress(0.0); + stress[0][0] = 2.0*x - y - 5.0; + stress[1][0] = -3*x - 2*y + 3.0; + stress[0][1] = stress[1][0]; // symmetric + stress[1][1] = 2.0*x + 3.0*y + 3.0; + return stress; +} + +} // end namespace ShiueTwo + +///////////////////////////////////////////////////////////////////////////////////////////////// +// see Schneider et al., 2019: "Coupling staggered-grid and MPFA finite volume methods for +// free flow/porous-medium flow problems (with c = 0)" +///////////////////////////////////////////////////////////////////////////////////////////////// +namespace Schneider { + +// 0: velocity-x | 1: velocity-y | 2: pressure +Dune::FieldVector<double, 3> darcy(const Dune::FieldVector<double, 2>& globalPos) +{ + Dune::FieldVector<double, 3> sol(0.0); + const double x = globalPos[0]; + const double y = globalPos[1]; + static constexpr double omega = M_PI; + static constexpr double c = 0.0; + using std::exp; using std::sin; using std::cos; + const double sinOmegaX = sin(omega*x); + const double cosOmegaX = cos(omega*x); + static const double expTwo = exp(2); + const double expYPlusOne = exp(y+1); + + sol[0] = c/(2*omega)*expYPlusOne*sinOmegaX*sinOmegaX + -omega*(expYPlusOne + 2 - expTwo)*cosOmegaX; + sol[1] = (0.5*c*(expYPlusOne + 2 - expTwo)*cosOmegaX + -(c*cosOmegaX + 1)*exp(y-1))*sinOmegaX; + sol[2] = (expYPlusOne + 2 - expTwo)*sinOmegaX; + + return sol; +} + +// 0: mass +Dune::FieldVector<double, 1> darcyRHS(const Dune::FieldVector<double, 2>& globalPos) +{ + const double x = globalPos[0]; + const double y = globalPos[1]; + using std::exp; using std::sin; using std::cos; + static constexpr double omega = M_PI; + static constexpr double c = 0.0; + const double cosOmegaX = cos(omega*x); + static const double expTwo = exp(2); + const double expYPlusOne = exp(y+1); + + return { sin(omega*x)*(-(c*cosOmegaX + 1)*exp(y - 1) + + 1.5*c*expYPlusOne*cosOmegaX + + omega*omega*(expYPlusOne - expTwo + 2)) }; +} + +// 0: velocity-x | 1: velocity-y | 2: pressure +Dune::FieldVector<double, 3> stokes(const Dune::FieldVector<double, 2>& globalPos) +{ + Dune::FieldVector<double, 3> sol(0.0); + const double x = globalPos[0]; + const double y = globalPos[1]; + using std::sin; + static constexpr double omega = M_PI; + const double sinOmegaX = sin(omega*x); + + sol[0] = y; + sol[1] = -y*sinOmegaX; + sol[2] = -y*y*sinOmegaX*sinOmegaX; + return sol; +} + +// 0: mom-x | 1: mom-y | 2: mass +Dune::FieldVector<double, 3> stokesRHS(const Dune::FieldVector<double, 2>& globalPos) +{ + const double x = globalPos[0]; + const double y = globalPos[1]; + using std::exp; using std::sin; using std::cos; + static constexpr double omega = M_PI; + const double sinOmegaX = sin(omega*x); + const double cosOmegaX = cos(omega*x); + + Dune::FieldVector<double, 3> source(0.0); + source[0] = -2*omega*y*y*sinOmegaX*cosOmegaX + -2*y*sinOmegaX + omega*cosOmegaX; + source[1] = -omega*y*y*cosOmegaX - omega*omega*y*sinOmegaX; + source[2] = -sinOmegaX; + return source; +} + +// sigma_ff = vvT + sym(grad(v)) + pI +Dune::FieldMatrix<double, 2, 2> stokesStress(const Dune::FieldVector<double, 2>& globalPos) +{ + using std::sin; using std::cos; + const double x = globalPos[0]; + const double y = globalPos[1]; + static constexpr double omega = M_PI; + const double sinOmegaX = sin(omega*x); + const double cosOmegaX = cos(omega*x); + + Dune::FieldMatrix<double, 2, 2> stress(0.0); + stress[0][0] = y*y * cosOmegaX*cosOmegaX; + stress[0][1] = -y*y*sinOmegaX + omega*y*cosOmegaX - 1.0; + stress[1][0] = stress[0][1]; // symmetric + stress[1][1] = 2*sinOmegaX; + return stress; +} + +} // end namespace Schneider + +} // end namespace Dumux::Solution::DarcyStokes + +#endif diff --git a/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/convergencetest.py b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/convergencetest.py new file mode 100755 index 0000000000..b52826c3c4 --- /dev/null +++ b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/convergencetest.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 + +from math import * +import subprocess +import sys + +if len(sys.argv) < 2: + sys.stderr.write('Please provide a single argument <testname> to the script\n') + sys.exit(1) + +executableName = str(sys.argv[1]) +testargs = [str(i) for i in sys.argv][2:] +testname = testargs[testargs.index('-Problem.TestCase')+1] + +# remove the old log files +subprocess.call(['rm', testname + '_freeFlow.log']) +print("Removed old log file ({})!".format(testname + '_freeFlow.log')) +subprocess.call(['rm', testname + '_darcy.log']) +print("Removed old log file ({})!".format(testname + '_darcy.log')) + +# do the runs with different refinement +for i in [0, 1, 2]: + subprocess.call(['./' + executableName] + testargs + ['-Grid.Refinement', str(i)]) + +def checkRatesFreeFlow(): + # check the rates and append them to the log file + with open(testname + '_freeFlow.log', "r+") as logfile: + + errorP, errorVx, errorVy = [], [], [] + for line in logfile: + line = line.strip("\n").strip("\[ConvergenceTest\]").split() + errorP.append(float(line[2])) + errorVx.append(float(line[5])) + errorVy.append(float(line[8])) + + resultsP, resultsVx, resultsVy = [], [], [] + logfile.truncate(0) + logfile.write("n\terrorP\t\trateP\t\terrorVx\t\trateVx\t\terrorVy\t\trateVy\n") + logfile.write("-"*50 + "\n") + for i in range(len(errorP)-1): + if isnan(errorP[i]) or isinf(errorP[i]): + continue + if not ((errorP[i] < 1e-12 or errorP[i+1] < 1e-12) and (errorVx[i] < 1e-12 or errorVx[i+1] < 1e-12) and (errorVy[i] < 1e-12 or errorVy[i+1] < 1e-12)): + rateP = (log(errorP[i])-log(errorP[i+1]))/log(2) + rateVx = (log(errorVx[i])-log(errorVx[i+1]))/log(2) + rateVy = (log(errorVy[i])-log(errorVy[i+1]))/log(2) + message = "{}\t{:0.4e}\t{:0.4e}\t{:0.4e}\t{:0.4e}\t{:0.4e}\t{:0.4e}\n".format(i, errorP[i], rateP, errorVx[i], rateVx, errorVy[i], rateVy) + logfile.write(message) + resultsP.append(rateP) + resultsVx.append(rateVx) + resultsVy.append(rateVy) + else: + logfile.write("error: exact solution!?") + i = len(errorP)-1 + message = "{}\t{:0.4e}\t\t{}\t{:0.4e}\t\t{}\t{:0.4e}\t\t{}\n".format(i, errorP[i], "", errorVx[i], "", errorVy[i], "") + logfile.write(message) + + print("\nComputed the following convergence rates for {}:\n".format(testname)) + + subprocess.call(['cat', testname + '_freeFlow.log']) + + return {"p" : resultsP, "v_x" : resultsVx, "v_y" : resultsVy} + +def checkRatesDarcy(): + # check the rates and append them to the log file + with open(testname + '_darcy.log', "r+") as logfile: + + errorP = [] + for line in logfile: + line = line.strip("\n") + line = line.strip("\[ConvergenceTest\]") + line = line.split() + errorP.append(float(line[2])) + + resultsP = [] + logfile.truncate(0) + logfile.write("n\terrorP\t\trateP\n") + logfile.write("-"*50 + "\n") + for i in range(len(errorP)-1): + if isnan(errorP[i]) or isinf(errorP[i]): + continue + if not ((errorP[i] < 1e-12 or errorP[i+1] < 1e-12)): + rateP = (log(errorP[i])-log(errorP[i+1]))/log(2) + message = "{}\t{:0.4e}\t{:0.4e}\n".format(i, errorP[i], rateP) + logfile.write(message) + resultsP.append(rateP) + else: + logfile.write("error: exact solution!?") + i = len(errorP)-1 + message = "{}\t{:0.4e}\n".format(i, errorP[i], "") + logfile.write(message) + + print("\nComputed the following convergence rates for {}:\n".format(testname)) + + subprocess.call(['cat', testname + '_darcy.log']) + + return {"p" : resultsP} + +def checkRatesFreeFlowAndDarcy(): + resultsFreeFlow = checkRatesFreeFlow() + resultsDarcy = checkRatesDarcy() + + def mean(numbers): + return float(sum(numbers)) / len(numbers) + + # check the rates, we expect rates around 2 + if mean(resultsFreeFlow["p"]) < 2.05 and mean(resultsFreeFlow["p"]) < 1.7: + sys.stderr.write("*"*70 + "\n" + "The convergence rates for pressure were not close enough to 2! Test failed.\n" + "*"*70 + "\n") + sys.exit(1) + + if mean(resultsFreeFlow["v_x"]) < 2.05 and mean(resultsFreeFlow["v_x"]) < 1.95: + sys.stderr.write("*"*70 + "\n" + "The convergence rates for x-velocity were not close enough to 2! Test failed.\n" + "*"*70 + "\n") + sys.exit(1) + + if mean(resultsFreeFlow["v_y"]) < 2.05 and mean(resultsFreeFlow["v_y"]) < 1.95: + sys.stderr.write("*"*70 + "\n" + "The convergence rates for y-velocity were not close enough to 2! Test failed.\n" + "*"*70 + "\n") + sys.exit(1) + + if mean(resultsDarcy["p"]) < 2.05 and mean(resultsDarcy["p"]) < 1.95: + sys.stderr.write("*"*70 + "\n" + "The convergence rates for pressure were not close enough to 2! Test failed.\n" + "*"*70 + "\n") + sys.exit(1) + + +checkRatesFreeFlowAndDarcy() diff --git a/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/main.cc b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/main.cc new file mode 100644 index 0000000000..8a9a0044d8 --- /dev/null +++ b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/main.cc @@ -0,0 +1,320 @@ +// -*- 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 + * \ingroup BoundaryTests + * \brief A convergence test for the coupled FreeFlow/Darcy problem (1p). + */ + +#include <config.h> + +#include <iostream> + +#include <dune/common/parallel/mpihelper.hh> +#include <dune/common/timer.hh> + +#include <dumux/common/properties.hh> +#include <dumux/common/parameters.hh> +#include <dumux/common/dumuxmessage.hh> +#include <dumux/common/partial.hh> +#include <dumux/linear/seqsolverbackend.hh> +#include <dumux/assembly/fvassembler.hh> +#include <dumux/discretization/method.hh> +#include <dumux/io/format.hh> +#include <dumux/io/vtkoutputmodule.hh> +#include <dumux/io/grid/gridmanager_yasp.hh> + +#include <dumux/multidomain/staggeredtraits.hh> +#include <dumux/multidomain/fvassembler.hh> +#include <dumux/multidomain/newtonsolver.hh> +#include <dumux/freeflow/navierstokes/velocityoutput.hh> + +#include <test/freeflow/navierstokes/analyticalsolutionvectors.hh> +#include <test/freeflow/navierstokes/errors.hh> + +#include "testcase.hh" +#include "properties.hh" + +/*! +* \brief Creates analytical solution vector for output +* Returns a tuple of the analytical solution for the pressure and velocity at cell centers +* \param problem the problem for which to evaluate the analytical solution +*/ +template<class Scalar, class Problem> +auto createDarcyAnalyticalSolution(const Problem& problem) +{ + const auto& gridGeometry = problem.gridGeometry(); + using GridView = typename std::decay_t<decltype(gridGeometry)>::GridView; + + static constexpr auto dim = GridView::dimension; + static constexpr auto dimWorld = GridView::dimensionworld; + + using VelocityVector = Dune::FieldVector<Scalar, dimWorld>; + + std::vector<Scalar> analyticalPressure; + std::vector<VelocityVector> analyticalVelocity; + + analyticalPressure.resize(gridGeometry.numDofs()); + analyticalVelocity.resize(gridGeometry.numDofs()); + + for (const auto& element : elements(gridGeometry.gridView())) + { + auto fvGeometry = localView(gridGeometry); + fvGeometry.bindElement(element); + for (auto&& scv : scvs(fvGeometry)) + { + const auto ccDofIdx = scv.dofIndex(); + const auto ccDofPosition = scv.dofPosition(); + const auto analyticalSolutionAtCc = problem.fullAnalyticalSolution(ccDofPosition); + analyticalPressure[ccDofIdx] = analyticalSolutionAtCc[dim]; + + for (int dirIdx = 0; dirIdx < dim; ++dirIdx) + analyticalVelocity[ccDofIdx][dirIdx] = analyticalSolutionAtCc[dirIdx]; + } + } + + return std::make_tuple(analyticalPressure, analyticalVelocity); +} + +template<class MassP, class MomP, class SolutionVector> +void printFreeFlowL2Error(std::shared_ptr<MomP> momentumProblem, + std::shared_ptr<MassP> massProblem, + const SolutionVector& sol) +{ + using namespace Dumux; + + NavierStokesTest::Errors errors(momentumProblem, massProblem, sol); + const int numCellCenterDofs = massProblem->gridGeometry().numDofs(); + const int numFaceDofs = momentumProblem->gridGeometry().numDofs(); + const auto absL2 = errors.l2Absolute(); + const auto relL2 = errors.l2Relative(); + + std::cout << Fmt::format("** L2 error (abs/rel) for {} cc dofs and {} face dofs (total: {}): ", + numCellCenterDofs, numFaceDofs, numCellCenterDofs + numFaceDofs) + << Fmt::format("L2(p) = {:.8e} / {:.8e}", absL2[0], relL2[0]) + << Fmt::format(", L2(vx) = {:.8e} / {:.8e}", absL2[1], relL2[1]) + << Fmt::format(", L2(vy) = {:.8e} / {:.8e}", absL2[2], relL2[2]) + << std::endl; + + // write the norm into a log file + std::ofstream logFile(massProblem->name() + ".log", std::ios::app); + logFile << "[ConvergenceTest] L2(p) = " << absL2[0] + << " L2(vx) = " << absL2[1] + << " L2(vy) = " << absL2[2] + << std::endl; +} + +template<class Problem, class SolutionVector> +void printDarcyL2Error(std::shared_ptr<Problem> problem, const SolutionVector& x) +{ + using namespace Dumux; + using Scalar = double; + + Scalar l2error = 0.0; + for (const auto& element : elements(problem->gridGeometry().gridView())) + { + auto fvGeometry = localView(problem->gridGeometry()); + fvGeometry.bindElement(element); + + for (auto&& scv : scvs(fvGeometry)) + { + const auto dofIdx = scv.dofIndex(); + const Scalar delta = x[dofIdx] - problem->fullAnalyticalSolution(scv.center())[2/*pressureIdx*/]; + l2error += scv.volume()*(delta*delta); + } + } + using std::sqrt; + l2error = sqrt(l2error); + + const auto numDofs = problem->gridGeometry().numDofs(); + std::cout << Fmt::format("** L2 error (abs) for {} cc dofs", numDofs) + << Fmt::format("L2 error = {:.8e}", l2error) + << std::endl; + + // write the norm into a log file + std::ofstream logFile(problem->name() + ".log", std::ios::app); + logFile << "[ConvergenceTest] L2(p) = " << l2error << std::endl; +} + +int main(int argc, char** argv) +{ + using namespace Dumux; + + // initialize MPI, finalize is done automatically on exit + const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); + + // print dumux start message + if (mpiHelper.rank() == 0) + DumuxMessage::print(/*firstCall=*/true); + + // parse command line arguments and input file + Parameters::init(argc, argv); + + // Define the sub problem type tags + using FreeFlowMomentumTypeTag = Properties::TTag::FreeFlowOnePMomentum; + using FreeFlowMassTypeTag = Properties::TTag::FreeFlowOnePMass; + using DarcyTypeTag = Properties::TTag::DarcyOneP; + + // try to create a grid (from the given grid file or the input file) + // for both sub-domains + using DarcyGridManager = Dumux::GridManager<GetPropType<DarcyTypeTag, Properties::Grid>>; + DarcyGridManager darcyGridManager; + darcyGridManager.init("Darcy"); // pass parameter group + + using FreeFlowGridManager = Dumux::GridManager<GetPropType<FreeFlowMomentumTypeTag, Properties::Grid>>; + FreeFlowGridManager freeFlowGridManager; + freeFlowGridManager.init("FreeFlow"); // pass parameter group + + // we compute on the leaf grid view + const auto& darcyGridView = darcyGridManager.grid().leafGridView(); + const auto& freeFlowGridView = freeFlowGridManager.grid().leafGridView(); + + // create the finite volume grid geometry + using FreeFlowMomentumGridGeometry = GetPropType<FreeFlowMomentumTypeTag, Properties::GridGeometry>; + auto freeFlowMomentumGridGeometry = std::make_shared<FreeFlowMomentumGridGeometry>(freeFlowGridView); + using FreeFlowMassGridGeometry = GetPropType<FreeFlowMassTypeTag, Properties::GridGeometry>; + auto freeFlowMassGridGeometry = std::make_shared<FreeFlowMassGridGeometry>(freeFlowGridView); + using DarcyFVGridGeometry = GetPropType<DarcyTypeTag, Properties::GridGeometry>; + auto darcyGridGeometry = std::make_shared<DarcyFVGridGeometry>(darcyGridView); + + using Traits = MultiDomainTraits<FreeFlowMomentumTypeTag, FreeFlowMassTypeTag, DarcyTypeTag>; + using CouplingManager = FreeFlowPorousMediumCouplingManager<Traits>; + auto couplingManager = std::make_shared<CouplingManager>(); + + // the indices + constexpr auto freeFlowMomentumIndex = CouplingManager::freeFlowMomentumIndex; + constexpr auto freeFlowMassIndex = CouplingManager::freeFlowMassIndex; + constexpr auto porousMediumIndex = CouplingManager::porousMediumIndex; + + // the problem (initial and boundary conditions) + const auto testCaseName = getParam<std::string>("Problem.TestCase"); + const auto testCase = [&]() + { + if (testCaseName == "ShiueExampleOne") + return DarcyStokesTestCase::ShiueExampleOne; + else if (testCaseName == "ShiueExampleTwo") + return DarcyStokesTestCase::ShiueExampleTwo; + else if (testCaseName == "Rybak") + return DarcyStokesTestCase::Rybak; + else if (testCaseName == "Schneider") + return DarcyStokesTestCase::Schneider; + else + DUNE_THROW(Dune::InvalidStateException, testCaseName + " is not a valid test case"); + }(); + + // the problem (initial and boundary conditions) + using FreeFlowMomentumProblem = GetPropType<FreeFlowMomentumTypeTag, Properties::Problem>; + auto freeFlowMomentumProblem = std::make_shared<FreeFlowMomentumProblem>(freeFlowMomentumGridGeometry, couplingManager, testCase, testCaseName); + using FreeFlowMassProblem = GetPropType<FreeFlowMassTypeTag, Properties::Problem>; + auto freeFlowMassProblem = std::make_shared<FreeFlowMassProblem>(freeFlowMassGridGeometry, couplingManager, testCase, testCaseName); + using DarcyProblem = GetPropType<DarcyTypeTag, Properties::Problem>; + auto spatialParams = std::make_shared<typename DarcyProblem::SpatialParams>(darcyGridGeometry, testCase); + auto darcyProblem = std::make_shared<DarcyProblem>(darcyGridGeometry, couplingManager, spatialParams, testCase, testCaseName); + + // the solution vector + Traits::SolutionVector sol; + sol[freeFlowMomentumIndex].resize(freeFlowMomentumGridGeometry->numDofs()); + sol[freeFlowMassIndex].resize(freeFlowMassGridGeometry->numDofs()); + sol[porousMediumIndex].resize(darcyGridGeometry->numDofs()); + + // the grid variables + using FreeFlowMomentumGridVariables = GetPropType<FreeFlowMomentumTypeTag, Properties::GridVariables>; + auto freeFlowMomentumGridVariables = std::make_shared<FreeFlowMomentumGridVariables>(freeFlowMomentumProblem, freeFlowMomentumGridGeometry); + using DarcyGridVariables = GetPropType<DarcyTypeTag, Properties::GridVariables>; + using FreeFlowMassGridVariables = GetPropType<FreeFlowMassTypeTag, Properties::GridVariables>; + auto freeFlowMassGridVariables = std::make_shared<FreeFlowMassGridVariables>(freeFlowMassProblem, freeFlowMassGridGeometry); + using DarcyGridVariables = GetPropType<DarcyTypeTag, Properties::GridVariables>; + auto darcyGridVariables = std::make_shared<DarcyGridVariables>(darcyProblem, darcyGridGeometry); + + couplingManager->init(freeFlowMomentumProblem, freeFlowMassProblem, darcyProblem, + std::make_tuple(freeFlowMomentumGridVariables, freeFlowMassGridVariables, darcyGridVariables), + sol); + + freeFlowMomentumGridVariables->init(sol[freeFlowMomentumIndex]); + freeFlowMassGridVariables->init(sol[freeFlowMassIndex]); + darcyGridVariables->init(sol[porousMediumIndex]); + + // initialize the vtk output module + VtkOutputModule freeflowVtkWriter(*freeFlowMassGridVariables, sol[freeFlowMassIndex], freeFlowMassProblem->name()); + GetPropType<FreeFlowMassTypeTag, Properties::IOFields>::initOutputModule(freeflowVtkWriter); // Add model specific output fields + freeflowVtkWriter.addVelocityOutput(std::make_shared<NavierStokesVelocityOutput<FreeFlowMassGridVariables>>()); + NavierStokesTest::AnalyticalSolutionVectors freeFlowAnalyticSol(freeFlowMomentumProblem, freeFlowMassProblem); + freeflowVtkWriter.addField(freeFlowAnalyticSol.analyticalPressureSolution(), "pressureExact"); + freeflowVtkWriter.addField(freeFlowAnalyticSol.analyticalVelocitySolution(), "velocityExact"); + freeflowVtkWriter.write(0.0); + + VtkOutputModule darcyVtkWriter(*darcyGridVariables, sol[porousMediumIndex], darcyProblem->name()); + GetPropType<DarcyTypeTag, Properties::IOFields>::initOutputModule(darcyVtkWriter); + darcyVtkWriter.addVelocityOutput(std::make_shared<GetPropType<DarcyTypeTag, Properties::VelocityOutput>>(*darcyGridVariables)); + const auto darcyAnalyticalSolution = createDarcyAnalyticalSolution<double>(*darcyProblem); + darcyVtkWriter.addField(std::get<0>(darcyAnalyticalSolution), "pressureExact"); + darcyVtkWriter.addField(std::get<1>(darcyAnalyticalSolution), "velocityExact"); + darcyVtkWriter.write(0.0); + + // the assembler for a stationary problem + using Assembler = MultiDomainFVAssembler<Traits, CouplingManager, DiffMethod::numeric>; + auto assembler = std::make_shared<Assembler>(std::make_tuple(freeFlowMomentumProblem, freeFlowMassProblem, darcyProblem), + std::make_tuple(freeFlowMomentumGridGeometry, + freeFlowMassGridGeometry, + darcyGridGeometry), + std::make_tuple(freeFlowMomentumGridVariables, + freeFlowMassGridVariables, + darcyGridVariables), + couplingManager); + + // the linear solver + using LinearSolver = UMFPackBackend; + auto linearSolver = std::make_shared<LinearSolver>(); + + // the non-linear solver + using NewtonSolver = MultiDomainNewtonSolver<Assembler, LinearSolver, CouplingManager>; + NewtonSolver nonLinearSolver(assembler, linearSolver, couplingManager); + + const auto dofsVS = freeFlowMomentumGridGeometry->numDofs(); + const auto dofsPS = freeFlowMassGridGeometry->numDofs(); + const auto dofsPD = darcyGridGeometry->numDofs(); + std::cout << "Stokes velocity dofs: " << dofsVS << std::endl; + std::cout << "Stokes pressure dofs: " << dofsPS << std::endl; + std::cout << "Darcy pressure dofs: " << dofsPD << std::endl; + std::cout << "Total number of dofs: " << dofsVS + dofsPS + dofsPD << std::endl; + + // solve the non-linear system + nonLinearSolver.solve(sol); + + // write vtk output + freeflowVtkWriter.write(1.0); + darcyVtkWriter.write(1.0); + + // print L2 errors + printFreeFlowL2Error(freeFlowMomentumProblem, freeFlowMassProblem, partial(sol, freeFlowMomentumIndex, freeFlowMassIndex)); + printDarcyL2Error(darcyProblem, sol[porousMediumIndex]); + + //////////////////////////////////////////////////////////// + // finalize, print dumux message to say goodbye + //////////////////////////////////////////////////////////// + + // print dumux end message + if (mpiHelper.rank() == 0) + { + Parameters::print(); + DumuxMessage::print(/*firstCall=*/false); + } + + return 0; +} diff --git a/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/manufactured_solution.py b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/manufactured_solution.py new file mode 100644 index 0000000000..7bc744363b --- /dev/null +++ b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/manufactured_solution.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 + +""" +This script determines the source terms needed for analytical solutions for coupled Stokes/Darcy problems. +Given an analytical solution, it evaluates the momentum and mass balance equations and outputs source terms +such that the balance equations are fulfilled. It furthermore calculates the Darcy velocity from a given pressure solution. +""" + +import sympy as sp +import numpy as np +from sympy import * + +# case = "Rybak" +#case = "Shiue_1" +case = "Schneider" + +# divergence of a matrix +def div(M): + return np.array([sp.diff(M[0,0],x) + sp.diff(M[0,1],y) , sp.diff(M[1,0],x) + sp.diff(M[1,1],y) ]) + +# gradient of a vector +def grad(v): + return np.array([[sp.diff(v[0],x), sp.diff(v[0],y)], [sp.diff(v[1],x), sp.diff(v[1],y)]]) + +# coordinates +x, y = sp.symbols("x y") + +# analytical solutions for v and p in free-flow domain (see reference given in problems) +def analyticalSolutionStokes(case): + if case == "Rybak": + vFF = np.array([-sp.cos(sp.pi*x)*sp.sin(sp.pi*y), sp.sin(sp.pi*x)*sp.cos(sp.pi*y)]) + pFF = 0.5*y*sp.sin(sp.pi*x) + elif case == "Shiue_1": + vFF = np.array([-1.0/sp.pi * sp.exp(y) * sp.sin(sp.pi*x), (sp.exp(y) - sp.exp(1)) * sp.cos(sp.pi*x)]) + pFF = 2.0*sp.exp(y) * sp.cos(sp.pi*x) + elif case == "Shiue_2": + vFF = np.array([(y-1.0)*(y-1.0) + x*(y-1.0) + 3.0*x - 1.0, x*(x-1.0) - 0.5*(y-1.0)*(y-1.0) - 3.0*y + 1.0]) + pFF = 2.0*x + y - 1.0 + else: # Schneider + vFF = np.array([y, -y*sp.sin(sp.pi*x)]) + pFF = -y*y*sp.sin(sp.pi*x)*sp.sin(sp.pi*x) + return [vFF, pFF] + +vFF, pFF = analyticalSolutionStokes(case) + +# individual terms of the Navier-Stokes eq. +vvT = np.outer(vFF, vFF) +gradV = grad(vFF) +gradVT = grad(vFF).T +pI = np.array([[pFF, 0], [0, pFF]]) + +# complete momentum flux and its divergence +if case == "Schneider": + momentumFlux = vvT - (gradV + gradVT) +pI +else: + momentumFlux = - (gradV + gradVT) +pI # only Stokes +divMomentumFlux = div(momentumFlux) + +print("\n\nStress tensor flux for case", case) +print("-- [0][0]: ", sp.simplify(momentumFlux[0][0])) +print("-- [1][1]: ", sp.simplify(momentumFlux[1][1])) +print("-- [0][1] == [1][0]: ", sp.simplify(momentumFlux[0][1])) + +print("\n\nSource terms for case", case) + +# solution for source term +print(" \nStokes:") +print("-- Source term mass:", sp.diff(vFF[0],x) + sp.diff(vFF[1], y)) +print("-- Source term momentum x:", divMomentumFlux[0]) +print("-- Source term momentum y:", divMomentumFlux[1]) + +# analytical solutions for p in Darcy domain (see reference given in problems) +def analyticalSolutionDarcy(case): + if case == "Rybak": + pD = 0.5*y*y*sp.sin(sp.pi*x) + elif case == "Shiue_1": + pD = (sp.exp(y) - y*sp.exp(1)) * sp.cos(sp.pi*x) + elif case == "Shiue_2": + pD = x*(1.0-x)*(y-1.0) + pow(y-1.0, 3)/3.0 + 2.0*x + 2.0*y + 4.0 + else: # Schneider + pD = (sp.exp(y+1) + 2.0 - sp.exp(2))*sp.sin(sp.pi*x) + return pD + +pD = analyticalSolutionDarcy(case) + +gradPdK = np.array([sp.diff(pD,x), sp.diff(pD,y)]) +vD = -gradPdK +divVd = sp.diff(vD[0],x) + sp.diff(vD[1],y) + +print("\nDarcy:") +print("-- Source term mass:", simplify(divVd)) +print("-- v x:", simplify(vD[0])) +print("-- v_y", simplify(vD[1])) diff --git a/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/params.input b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/params.input new file mode 100644 index 0000000000..6d656742ed --- /dev/null +++ b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/params.input @@ -0,0 +1,34 @@ +[Darcy.Grid] +UpperRight = 1 1 +Cells = 40 40 + +[FreeFlow.Grid] +LowerLeft = 0 1 +UpperRight = 1 2 +Cells = 40 40 + +[FreeFlow.Problem] +Name = freeFlow +EnableInertiaTerms = false + +[Darcy.Problem] +Name = darcy + +[Darcy.SpatialParams] +AlphaBeaversJoseph = 1.0 + +[Problem] +EnableGravity = false + +[Vtk] +AddVelocity = 1 + +[Component] +LiquidDensity = 1.0 +LiquidKinematicViscosity = 1.0 + +[Assembly] +NumericDifference.BaseEpsilon = 1e-4 + +[FreeFlow.Flux] +UpwindWeight = 0.5 diff --git a/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/problem_darcy.hh b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/problem_darcy.hh new file mode 100644 index 0000000000..a575666dbe --- /dev/null +++ b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/problem_darcy.hh @@ -0,0 +1,271 @@ +// -*- 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 + * \ingroup BoundaryTests + * \brief The Darcy sub-problem of coupled Stokes-Darcy convergence test + */ + +#ifndef DUMUX_DARCY_SUBPROBLEM_HH +#define DUMUX_DARCY_SUBPROBLEM_HH + +#include <dumux/common/boundarytypes.hh> +#include <dumux/common/numeqvector.hh> +#include <dumux/common/properties.hh> +#include <dumux/common/parameters.hh> + +#include <dumux/porousmediumflow/problem.hh> + +#include "testcase.hh" +#include "analyticalsolutions.hh" + +namespace Dumux { + +/*! + * \ingroup BoundaryTests + * \brief The Darcy sub-problem of coupled Stokes-Darcy convergence test + */ +template <class TypeTag> +class DarcySubProblem : public PorousMediumFlowProblem<TypeTag> +{ + using ParentType = PorousMediumFlowProblem<TypeTag>; + using Scalar = GetPropType<TypeTag, Properties::Scalar>; + using PrimaryVariables = GetPropType<TypeTag, Properties::PrimaryVariables>; + using NumEqVector = Dumux::NumEqVector<PrimaryVariables>; + using BoundaryTypes = Dumux::BoundaryTypes<GetPropType<TypeTag, Properties::ModelTraits>::numEq()>; + using VolumeVariables = GetPropType<TypeTag, Properties::VolumeVariables>; + using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>; + using GridView = typename GridGeometry::GridView; + using FVElementGeometry = typename GridGeometry::LocalView; + using SubControlVolume = typename GridGeometry::SubControlVolume; + using SubControlVolumeFace = typename GridGeometry::SubControlVolumeFace; + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + + using CouplingManager = GetPropType<TypeTag, Properties::CouplingManager>; + + enum class BC { + dirichlet, neumann, mixed + }; + +public: + //! export the Indices + using Indices = typename GetPropType<TypeTag, Properties::ModelTraits>::Indices; + + DarcySubProblem(std::shared_ptr<const GridGeometry> gridGeometry, + std::shared_ptr<CouplingManager> couplingManager, + std::shared_ptr<typename ParentType::SpatialParams> spatialParams, + const DarcyStokesTestCase testCase, + const std::string& name) + : ParentType(gridGeometry, spatialParams, "Darcy") + , couplingManager_(couplingManager) + , testCase_(testCase) + { + problemName_ = name + "_" + + getParamFromGroup<std::string>(this->paramGroup(), "Problem.Name"); + + auto bc = getParamFromGroup<std::string>(this->paramGroup(), "Problem.BoundaryConditions", "Dirichlet"); + if (bc == "Dirichlet") + boundaryConditions_ = BC::dirichlet; + else if (bc == "Neumann") + boundaryConditions_ = BC::neumann; + else if (bc == "Mixed") + boundaryConditions_ = BC::mixed; + else + DUNE_THROW(Dune::Exception, "Wrong BC type choose: Dirichlet, Neumann or Mixed"); + + std::cout << "Porous medium domain: Using " << bc << " boundary conditions" << std::endl; + } + + const std::string& name() const + { return problemName_; } + + /*! + * \name Problem parameters + */ + // \{ + + Scalar temperature() const + { return 273.15 + 10; } // 10°C + // \} + + /*! + * \name Boundary conditions + */ + // \{ + + /*! + * \brief Specifies which kind of boundary condition should be + * used for which equation on a given boundary control volume. + * + * \param element The element + * \param scvf The boundary sub control volume face + */ + BoundaryTypes boundaryTypes(const Element &element, const SubControlVolumeFace &scvf) const + { + BoundaryTypes values; + + if (couplingManager().isCoupled(CouplingManager::porousMediumIndex, CouplingManager::freeFlowMassIndex, scvf)) + values.setAllCouplingNeumann(); + else + { + if (boundaryConditions_ == BC::dirichlet) + values.setAllDirichlet(); + else if (boundaryConditions_ == BC::neumann) + values.setAllNeumann(); + else + { + if (onLeftBoundary_(scvf.center())) + values.setAllNeumann(); + else + values.setAllDirichlet(); + } + } + + return values; + } + + /*! + * \brief Evaluates the boundary conditions for a Dirichlet control volume. + * + * \param element The element for which the Dirichlet boundary condition is set + * \param scvf The boundary sub-control-volume-face + */ + PrimaryVariables dirichlet(const Element &element, const SubControlVolumeFace &scvf) const + { + const auto p = fullAnalyticalSolution(scvf.center())[2]; + return PrimaryVariables(p); + } + + /*! + * \brief Evaluates the boundary conditions for a Neumann control volume. + * + * \param element The element for which the Neumann boundary condition is set + * \param fvGeometry The fvGeometry + * \param elemVolVars The element volume variables + * \param elemFluxVarsCache Flux variables caches for all faces in stencil + * \param scvf The boundary sub control volume face + */ + template<class ElementVolumeVariables, class ElementFluxVarsCache> + NumEqVector neumann(const Element& element, + const FVElementGeometry& fvGeometry, + const ElementVolumeVariables& elemVolVars, + const ElementFluxVarsCache& elemFluxVarsCache, + const SubControlVolumeFace& scvf) const + { + NumEqVector values(0.0); + + if (couplingManager().isCoupled(CouplingManager::porousMediumIndex, CouplingManager::freeFlowMassIndex, scvf)) + values[Indices::conti0EqIdx] = couplingManager().massCouplingCondition( + CouplingManager::porousMediumIndex, CouplingManager::freeFlowMassIndex, + fvGeometry, scvf, elemVolVars + ); + + else + { + const auto sol = fullAnalyticalSolution(scvf.center()); + const auto n = scvf.unitOuterNormal(); + auto v = n; v[0] = sol[0]; v[1] = sol[1]; + values[Indices::conti0EqIdx] = v*n; + } + + return values; + } + + // \} + + /*! + * \name Volume terms + */ + // \{ + /*! + * \brief Evaluates the source term for all phases within a given + * sub control volume. + * \param globalPos The global position + */ + NumEqVector sourceAtPos(const GlobalPosition& globalPos) const + { + using namespace Solution::DarcyStokes; + switch (testCase_) + { + case DarcyStokesTestCase::ShiueExampleOne: + return ShiueOne::darcyRHS(globalPos); + case DarcyStokesTestCase::ShiueExampleTwo: + return ShiueTwo::darcyRHS(globalPos); + case DarcyStokesTestCase::Rybak: + return Rybak::darcyRHS(globalPos); + case DarcyStokesTestCase::Schneider: + return Schneider::darcyRHS(globalPos); + default: + DUNE_THROW(Dune::InvalidStateException, "Invalid test case"); + } + } + + // \} + + /*! + * \brief Evaluates the initial value for a control volume. + * \param element The element + */ + PrimaryVariables initial(const Element &element) const + { return PrimaryVariables(0.0); } + + /*! + * \brief Returns the analytical solution of the problem at a given position. + * \param globalPos The global position + * Returns vector with entries: (velocity-x | velocity-y | pressure) + */ + Dune::FieldVector<Scalar, 3> fullAnalyticalSolution(const GlobalPosition& globalPos) const + { + using namespace Solution::DarcyStokes; + switch (testCase_) + { + case DarcyStokesTestCase::ShiueExampleOne: + return ShiueOne::darcy(globalPos); + case DarcyStokesTestCase::ShiueExampleTwo: + return ShiueTwo::darcy(globalPos); + case DarcyStokesTestCase::Rybak: + return Rybak::darcy(globalPos); + case DarcyStokesTestCase::Schneider: + return Schneider::darcy(globalPos); + default: + DUNE_THROW(Dune::InvalidStateException, "Invalid test case"); + } + } + + // \} + + //! Get the coupling manager + const CouplingManager& couplingManager() const + { return *couplingManager_; } + +private: + bool onLeftBoundary_(const GlobalPosition& globalPos) const + { return globalPos[0] < this->gridGeometry().bBoxMin()[0] + eps_; } + + static constexpr Scalar eps_ = 1e-7; + std::shared_ptr<CouplingManager> couplingManager_; + std::string problemName_; + DarcyStokesTestCase testCase_; + BC boundaryConditions_; +}; + +} // end namespace Dumux + +#endif diff --git a/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/problem_freeflow.hh b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/problem_freeflow.hh new file mode 100644 index 0000000000..9ef9fd3db9 --- /dev/null +++ b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/problem_freeflow.hh @@ -0,0 +1,391 @@ +// -*- 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 + * \ingroup BoundaryTests + * \brief The free-flow sub-problem of coupled FreeFlow/Darcy convergence test + */ + +#ifndef DUMUX_FREEFLOW_SUBPROBLEM_HH +#define DUMUX_FREEFLOW_SUBPROBLEM_HH + +#include <dumux/freeflow/navierstokes/problem.hh> +#include <dumux/common/properties.hh> +#include <dumux/freeflow/navierstokes/momentum/fluxhelper.hh> +#include <dumux/freeflow/navierstokes/scalarfluxhelper.hh> +#include <dune/common/fmatrix.hh> + +#include "testcase.hh" +#include "analyticalsolutions.hh" + +namespace Dumux { + +/*! + * \ingroup BoundaryTests + * \brief The Stokes sub-problem of coupled Stokes-Darcy convergence test + */ +template <class TypeTag> +class FreeFlowSubProblem : public NavierStokesProblem<TypeTag> +{ + using ParentType = NavierStokesProblem<TypeTag>; + using Scalar = GetPropType<TypeTag, Properties::Scalar>; + using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>; + using GridView = typename GridGeometry::GridView; + using FVElementGeometry = typename GridGeometry::LocalView; + using SubControlVolumeFace = typename GridGeometry::SubControlVolumeFace; + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + using PrimaryVariables = typename ParentType::PrimaryVariables; + using NumEqVector = typename ParentType::NumEqVector; + using BoundaryTypes = typename ParentType::BoundaryTypes; + using ModelTraits = GetPropType<TypeTag, Properties::ModelTraits>; + + using CouplingManager = GetPropType<TypeTag, Properties::CouplingManager>; + + enum class BC { + dirichlet, stress, mixed + }; + +public: + //! export the Indices + using Indices = typename ModelTraits::Indices; + + FreeFlowSubProblem(std::shared_ptr<const GridGeometry> gridGeometry, + std::shared_ptr<CouplingManager> couplingManager, + const DarcyStokesTestCase testCase, + const std::string& name) + : ParentType(gridGeometry, couplingManager, "FreeFlow") + , couplingManager_(couplingManager) + , testCase_(testCase) + { + problemName_ = name + "_" + + getParamFromGroup<std::string>(this->paramGroup(), "Problem.Name"); + + auto bc = getParamFromGroup<std::string>(this->paramGroup(), "Problem.BoundaryConditions", "Dirichlet"); + if (bc == "Dirichlet") + boundaryConditions_ = BC::dirichlet; + else if (bc == "Stress") + boundaryConditions_ = BC::stress; + else if (bc == "Mixed") + boundaryConditions_ = BC::mixed; + else + DUNE_THROW(Dune::Exception, "Wrong BC type choose: Dirichlet, Stress or Mixed"); + + std::cout << "Free flow domain: Using " << bc << " boundary conditions" << std::endl; + } + + /*! + * \name Problem parameters + */ + // \{ + + const std::string& name() const + { return problemName_; } + + /*! + * \brief Returns the temperature within the domain in [K]. + * + * This problem assumes a temperature of 10 degrees Celsius. + */ + Scalar temperature() const + { return 273.15 + 10; } // 10°C + + // \} + + /*! + * \name Boundary conditions + */ + // \{ + + /*! + * \brief Specifies which kind of boundary condition should be + * used for which equation on a given boundary segment. + * + * \param element The finite element + * \param scvf The sub control volume face + */ + BoundaryTypes boundaryTypes(const Element& element, + const SubControlVolumeFace& scvf) const + { + BoundaryTypes values; + + if constexpr (ParentType::isMomentumProblem()) + { + if (couplingManager().isCoupled(CouplingManager::freeFlowMomentumIndex, CouplingManager::porousMediumIndex, scvf)) + { + values.setCouplingNeumann(Indices::momentumYBalanceIdx); + values.setCouplingNeumann(Indices::momentumXBalanceIdx); + } + else + { + if (boundaryConditions_ == BC::dirichlet) + values.setAllDirichlet(); + else if (boundaryConditions_ == BC::stress) + values.setAllNeumann(); + else + { + if (onLeftBoundary_(scvf.center())) + values.setAllNeumann(); + else + values.setAllDirichlet(); + } + } + } + else + { + if (couplingManager().isCoupled(CouplingManager::freeFlowMassIndex, CouplingManager::porousMediumIndex, scvf)) + values.setAllCouplingNeumann(); + else + values.setAllNeumann(); + } + + return values; + } + + /*! + * \brief Evaluates the boundary conditions for a Dirichlet control volume. + */ + PrimaryVariables dirichletAtPos(const GlobalPosition& globalPos) const + { return analyticalSolution(globalPos); } + + /*! + * \brief Evaluates the boundary conditions for a Neumann control volume. + * + * \param element The element for which the Neumann boundary condition is set + * \param fvGeometry The fvGeometry + * \param elemVolVars The element volume variables + * \param elemFaceVars The element face variables + * \param scvf The boundary sub control volume face + */ + template<class ElementVolumeVariables, class ElementFluxVariablesCache> + NumEqVector neumann(const Element& element, + const FVElementGeometry& fvGeometry, + const ElementVolumeVariables& elemVolVars, + const ElementFluxVariablesCache& elemFluxVarsCache, + const SubControlVolumeFace& scvf) const + { + NumEqVector values(0.0); + const auto& globalPos = scvf.ipGlobal(); + using FluxHelper = NavierStokesMomentumBoundaryFluxHelper; + + // momentum boundary conditions + if constexpr (ParentType::isMomentumProblem()) + { + // We need to take care here: If the integration point of an scvf lies both on the coupling interface and the + // domain boundary (i.e., the leftmost and rightmost points of the interface), do not evaluate the coupling or slip condition + // because they would require data from the boundary not available in this case (velocities for evaluating gradients, + // those would only be available for Dirichlet BCs). Instead, directly use the given Neumann boundary stress. + // TODO: Maybe couplingManager().isCoupled(...) could return false for these scvfs. + // + // | <-- left Neumann boundary + // | + // integration point --> o##### <-- scvf lying on coupling interface with integration point touching left Neumann boundary + // + if (couplingManager().isCoupled(CouplingManager::freeFlowMomentumIndex, CouplingManager::porousMediumIndex, scvf) + && scvf.ipGlobal()[0] > this->gridGeometry().bBoxMin()[0] + eps_ + && scvf.ipGlobal()[0] < this->gridGeometry().bBoxMax()[0] - eps_) + { + values += couplingManager().momentumCouplingCondition( + CouplingManager::freeFlowMomentumIndex, CouplingManager::porousMediumIndex, + fvGeometry, scvf, elemVolVars + ); + + values += FluxHelper::slipVelocityMomentumFlux( + *this, fvGeometry, scvf, elemVolVars, elemFluxVarsCache + ); + } + else + { + const auto stress = stressTensorAtPos(globalPos); + const auto n = scvf.unitOuterNormal(); + stress.mv(n, values); + } + } + + // mass boundary conditions + else + { + if (couplingManager().isCoupled(CouplingManager::freeFlowMassIndex, CouplingManager::porousMediumIndex, scvf)) + { + values = couplingManager().massCouplingCondition( + CouplingManager::freeFlowMassIndex, CouplingManager::porousMediumIndex, + fvGeometry, scvf, elemVolVars + ); + } + else + { + using FluxHelper = NavierStokesScalarBoundaryFluxHelper<AdvectiveFlux<ModelTraits>>; + values = FluxHelper::scalarOutflowFlux( + *this, element, fvGeometry, scvf, elemVolVars + ); + } + } + + return values; + } + + /*! + * \brief Returns true if the scvf lies on a porous slip boundary + */ + bool onSlipBoundary(const FVElementGeometry& fvGeometry, const SubControlVolumeFace& scvf) const + { + return scvf.boundary() + && couplingManager().isCoupled( + CouplingManager::freeFlowMomentumIndex, + CouplingManager::porousMediumIndex, + scvf + ); + } + + // \} + + //! Get the coupling manager + const CouplingManager& couplingManager() const + { return *couplingManager_; } + + /*! + * \name Volume terms + */ + // \{ + + /*! + * \brief Returns the sources within the domain. + * \param globalPos The global position + */ + NumEqVector sourceAtPos(const GlobalPosition &globalPos) const + { + const auto select = [&](const auto& rhs) -> NumEqVector { + if constexpr (ParentType::isMomentumProblem()) + return { rhs[0], rhs[1] }; + else + return { rhs[2] }; + }; + + using namespace Solution::DarcyStokes; + switch (testCase_) + { + case DarcyStokesTestCase::ShiueExampleOne: + return select(ShiueOne::stokesRHS(globalPos)); + case DarcyStokesTestCase::ShiueExampleTwo: + return select(ShiueTwo::stokesRHS(globalPos)); + case DarcyStokesTestCase::Rybak: + return select(Rybak::stokesRHS(globalPos)); + case DarcyStokesTestCase::Schneider: + return select(Schneider::stokesRHS(globalPos)); + default: + DUNE_THROW(Dune::InvalidStateException, "Invalid test case"); + } + } + + /*! + * \brief Evaluates the initial value for a control volume. + * \param globalPos The global position + */ + PrimaryVariables initialAtPos(const GlobalPosition& globalPos) const + { return PrimaryVariables(0.0); } + + /*! + * \brief Returns the intrinsic permeability of required as input parameter + for the Beavers-Joseph-Saffman boundary condition + */ + auto permeability(const FVElementGeometry& fvGeometry, const SubControlVolumeFace& scvf) const + { return couplingManager().darcyPermeability(fvGeometry, scvf); } + + /*! + * \brief Returns the alpha value required as input parameter for the + Beavers-Joseph-Saffman boundary condition. + */ + Scalar alphaBJ(const FVElementGeometry& fvGeometry, const SubControlVolumeFace& scvf) const + { return couplingManager().problem(CouplingManager::porousMediumIndex).spatialParams().beaversJosephCoeffAtPos(scvf.ipGlobal()); } + + /*! + * \brief Returns the analytical solution of the problem at a given position. + * \param globalPos The global position + * Returns vector with entries: (velocity-x | velocity-y | pressure) + */ + Dune::FieldVector<Scalar, 3> fullAnalyticalSolution(const GlobalPosition& globalPos) const + { + using namespace Solution::DarcyStokes; + switch (testCase_) + { + case DarcyStokesTestCase::ShiueExampleOne: + return ShiueOne::stokes(globalPos); + case DarcyStokesTestCase::ShiueExampleTwo: + return ShiueTwo::stokes(globalPos); + case DarcyStokesTestCase::Rybak: + return Rybak::stokes(globalPos); + case DarcyStokesTestCase::Schneider: + return Schneider::stokes(globalPos); + default: + DUNE_THROW(Dune::InvalidStateException, "Invalid test case"); + } + } + + /*! + * \brief Returns the analytical solution of the problem at a given position. + * \param globalPos The global position + * Returns analytical solution depending on the type of sub-problem + */ + PrimaryVariables analyticalSolution(const GlobalPosition& globalPos, Scalar time = 0.0) const + { + using namespace Solution::DarcyStokes; + const auto sol = fullAnalyticalSolution(globalPos); + if constexpr (ParentType::isMomentumProblem()) + return { sol[0], sol[1] }; + else + return { sol[2] }; + } + + // \} + +private: + /*! + * \brief Returns the stress tensor within the domain. + * \param globalPos The global position + */ + Dune::FieldMatrix<double, 2, 2> stressTensorAtPos(const GlobalPosition &globalPos) const + { + using namespace Solution::DarcyStokes; + switch (testCase_) + { + case DarcyStokesTestCase::ShiueExampleOne: + return ShiueOne::stokesStress(globalPos); + case DarcyStokesTestCase::ShiueExampleTwo: + return ShiueTwo::stokesStress(globalPos); + case DarcyStokesTestCase::Rybak: + return Rybak::stokesStress(globalPos); + case DarcyStokesTestCase::Schneider: + return Schneider::stokesStress(globalPos); + default: + DUNE_THROW(Dune::InvalidStateException, "Invalid test case"); + } + } + + bool onLeftBoundary_(const GlobalPosition& globalPos) const + { return globalPos[0] < this->gridGeometry().bBoxMin()[0] + eps_; } + + static constexpr Scalar eps_ = 1e-7; + std::string problemName_; + std::shared_ptr<CouplingManager> couplingManager_; + DarcyStokesTestCase testCase_; + BC boundaryConditions_; +}; +} // end namespace Dumux + +#endif diff --git a/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/properties.hh b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/properties.hh new file mode 100644 index 0000000000..70c62ded2f --- /dev/null +++ b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/properties.hh @@ -0,0 +1,129 @@ +// -*- 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 + * \ingroup BoundaryTests + * \brief The properties for a simple Darcy test (cell-centered finite volume method) + */ +#ifndef DUMUX_DARCYSTOKES_PROPERTIES_HH +#define DUMUX_DARCYSTOKES_PROPERTIES_HH + +#include <dune/grid/yaspgrid.hh> + +#include <dumux/material/fluidsystems/1pliquid.hh> +#include <dumux/material/components/constant.hh> + +#include <dumux/porousmediumflow/1p/model.hh> +#include <dumux/freeflow/navierstokes/mass/1p/model.hh> +#include <dumux/freeflow/navierstokes/momentum/model.hh> +#include <dumux/discretization/cctpfa.hh> +#include <dumux/discretization/fcstaggered.hh> + +#include <dumux/multidomain/boundary/freeflowporousmedium/couplingmanager.hh> +#include <dumux/multidomain/traits.hh> + +#include "spatialparams.hh" +#include "problem_darcy.hh" +#include "problem_freeflow.hh" + +namespace Dumux::Properties { + +// Create new type tags +namespace TTag { +struct DarcyOneP { using InheritsFrom = std::tuple<OneP, CCTpfaModel>; }; +} // end namespace TTag + +// Set the problem property +template<class TypeTag> +struct Problem<TypeTag, TTag::DarcyOneP> +{ using type = DarcySubProblem<TypeTag>; }; + +// the fluid system +template<class TypeTag> +struct FluidSystem<TypeTag, TTag::DarcyOneP> +{ + using Scalar = GetPropType<TypeTag, Properties::Scalar>; + using type = FluidSystems::OnePLiquid<Scalar, Components::Constant<1, Scalar> > ; +}; + +// Set the grid type +template<class TypeTag> +struct Grid<TypeTag, TTag::DarcyOneP> +{ using type = Dune::YaspGrid<2>; }; + +template<class TypeTag> +struct SpatialParams<TypeTag, TTag::DarcyOneP> +{ + using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>; + using Scalar = GetPropType<TypeTag, Properties::Scalar>; + using type = ConvergenceTestSpatialParams<GridGeometry, Scalar>; +}; + +// Create new type tags +namespace TTag { +struct FreeFlowOneP {}; +struct FreeFlowOnePMass { using InheritsFrom = std::tuple<FreeFlowOneP, NavierStokesMassOneP, CCTpfaModel>; }; +struct FreeFlowOnePMomentum { using InheritsFrom = std::tuple<FreeFlowOneP, NavierStokesMomentum, FaceCenteredStaggeredModel>; }; +} // end namespace TTag + +// the fluid system +template<class TypeTag> +struct FluidSystem<TypeTag, TTag::FreeFlowOneP> +{ + using Scalar = GetPropType<TypeTag, Properties::Scalar>; + using type = FluidSystems::OnePLiquid<Scalar, Components::Constant<1, Scalar> > ; +}; + +// Set the grid type +template<class TypeTag> +struct Grid<TypeTag, TTag::FreeFlowOneP> +{ + using Coords = Dune::EquidistantOffsetCoordinates<GetPropType<TypeTag, Properties::Scalar>, 2>; + using type = Dune::YaspGrid<2, Coords>; +}; + +// Set the problem property +template<class TypeTag> +struct Problem<TypeTag, TTag::FreeFlowOneP> +{ using type = FreeFlowSubProblem<TypeTag>; }; + +template<class TypeTag> +struct EnableGridGeometryCache<TypeTag, TTag::FreeFlowOneP> { static constexpr bool value = true; }; +template<class TypeTag> +struct EnableGridFluxVariablesCache<TypeTag, TTag::FreeFlowOneP> { static constexpr bool value = true; }; +template<class TypeTag> +struct EnableGridVolumeVariablesCache<TypeTag, TTag::FreeFlowOneP> { static constexpr bool value = true; }; + +template<class TypeTag> +struct CouplingManager<TypeTag, TTag::DarcyOneP> +{ + using Traits = MultiDomainTraits<TTag::FreeFlowOnePMomentum, TTag::FreeFlowOnePMass, TTag::DarcyOneP>; + using type = FreeFlowPorousMediumCouplingManager<Traits>; +}; + +template<class TypeTag> +struct CouplingManager<TypeTag, TTag::FreeFlowOneP> +{ + using Traits = MultiDomainTraits<TTag::FreeFlowOnePMomentum, TTag::FreeFlowOnePMass, TTag::DarcyOneP>; + using type = FreeFlowPorousMediumCouplingManager<Traits>; +}; + +} // end namespace Dumux::Properties + +#endif diff --git a/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/spatialparams.hh b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/spatialparams.hh new file mode 100644 index 0000000000..82eb797c14 --- /dev/null +++ b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/spatialparams.hh @@ -0,0 +1,129 @@ +// -*- 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 + * \ingroup BoundaryTests + * \brief The spatial parameters class for the test problem using the 1p cc model. + */ + +#ifndef DUMUX_1P_TEST_SPATIALPARAMS_HH +#define DUMUX_1P_TEST_SPATIALPARAMS_HH + +#include <dumux/material/spatialparams/fv1p.hh> +#include "testcase.hh" + +namespace Dumux { + +/*! + * \ingroup BoundaryTests + * \brief The spatial parameters class for the test problem using the + * 1p cc model. + */ +template<class GridGeometry, class Scalar> +class ConvergenceTestSpatialParams +: public FVSpatialParamsOneP<GridGeometry, Scalar, + ConvergenceTestSpatialParams<GridGeometry, Scalar>> +{ + using GridView = typename GridGeometry::GridView; + using ParentType = FVSpatialParamsOneP< + GridGeometry, Scalar, ConvergenceTestSpatialParams<GridGeometry, Scalar> + >; + + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + + static constexpr int dimWorld = GridView::dimensionworld; + using DimWorldMatrix = Dune::FieldMatrix<Scalar, dimWorld, dimWorld>; + + +public: + // export permeability type + using PermeabilityType = DimWorldMatrix; + + ConvergenceTestSpatialParams(std::shared_ptr<const GridGeometry> gridGeometry, + const DarcyStokesTestCase testCase) + : ParentType(gridGeometry) + , testCase_(testCase) + { + alphaBJ_ = getParam<Scalar>("Darcy.SpatialParams.AlphaBeaversJoseph"); + } + + /*! + * \brief Function for defining the (intrinsic) permeability \f$[m^2]\f$. + * + * \param element The element + * \param scv The sub control volume + * \param elemSol The element solution vector + * \return the intrinsic permeability + */ + template<class SubControlVolume, class ElementSolution> + PermeabilityType permeability(const Element& element, + const SubControlVolume& scv, + const ElementSolution& elemSol) const + { + PermeabilityType K(0.0); + + if (testCase_ == DarcyStokesTestCase::Schneider) + { + using std::cos; + using std::sin; + using std::exp; + + static constexpr Scalar c = 0.0; + static constexpr Scalar omega = M_PI; + + const Scalar x = scv.center()[0]; + K[0][0] = 1.0; + K[0][1] = -c/(2*omega) * sin(omega*x); + K[1][0] = K[0][1]; + K[1][1] = exp(-2)*(1 + c*cos(omega*x)); + } + else + { + const static Scalar permeability = getParam<Scalar>("Darcy.SpatialParams.Permeability", 1.0); + K[0][0] = permeability; + K[1][1] = permeability; + } + + return K; + } + + /*! \brief Defines the porosity in [-]. + * + * \param globalPos The global position + */ + Scalar porosityAtPos(const GlobalPosition& globalPos) const + { return 0.4; } + + /*! \brief Defines the Beavers-Joseph coefficient in [-]. + * + * \param globalPos The global position + */ + Scalar beaversJosephCoeffAtPos(const GlobalPosition& globalPos) const + { return alphaBJ_; } + +private: + DarcyStokesTestCase testCase_; + Scalar permeability_; + Scalar alphaBJ_; +}; + +} // end namespace Dumux + +#endif diff --git a/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/testcase.hh b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/testcase.hh new file mode 100644 index 0000000000..9496ffe6ae --- /dev/null +++ b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/testcase.hh @@ -0,0 +1,37 @@ +// -*- 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 + * \ingroup BoundaryTests + * \brief The different test cases + */ + +#ifndef DUMUX_CONVERGENCE_TEST_TESTCASE_HH +#define DUMUX_CONVERGENCE_TEST_TESTCASE_HH + +namespace Dumux { + +enum class DarcyStokesTestCase +{ + ShiueExampleOne, ShiueExampleTwo, Rybak, Schneider +}; + +} // end namespace Dumux + +#endif -- GitLab