From 0e4cd9efbe5000f6cebc47aa0f8f4d1742fc8c03 Mon Sep 17 00:00:00 2001 From: Klaus Mosthaf <klmos@env.dtu.dk> Date: Fri, 24 Jan 2014 14:41:43 +0000 Subject: [PATCH] added 2cstokes2p2c test (first coupled ff-pm example); adapted makefiles and configure, added stokesnccouplinglocalresidual git-svn-id: svn://svn.iws.uni-stuttgart.de/DUMUX/dumux/trunk@12349 2fb0f335-1f38-0410-981e-8018bf24f1b0 --- configure.ac | 2 + .../stokesnccouplinglocalresidual.hh | 429 ++++++++++ test/Makefile.am | 1 + .../2cstokes2p2c/2cstokes2p2cproblem.hh | 738 ++++++++++++++++++ .../2cstokes2p2c/2cstokes2p2cspatialparams.hh | 489 ++++++++++++ .../2cstokes2p2c/2p2csubproblem.hh | 459 +++++++++++ test/multidomain/2cstokes2p2c/Makefile.am | 5 + .../2cstokes2p2c/grids/interfacedomain.dgf | 15 + .../2cstokes2p2c/grids/test_2cstokes2p2c.dgf | 17 + .../2cstokes2p2c/stokes2csubproblem.hh | 598 ++++++++++++++ .../2cstokes2p2c/test_2cstokes2p2c.cc | 252 ++++++ .../2cstokes2p2c/test_2cstokes2p2c.input | 170 ++++ test/multidomain/Makefile.am | 7 + 13 files changed, 3182 insertions(+) create mode 100644 dumux/multidomain/couplinglocalresiduals/stokesnccouplinglocalresidual.hh create mode 100644 test/multidomain/2cstokes2p2c/2cstokes2p2cproblem.hh create mode 100644 test/multidomain/2cstokes2p2c/2cstokes2p2cspatialparams.hh create mode 100644 test/multidomain/2cstokes2p2c/2p2csubproblem.hh create mode 100644 test/multidomain/2cstokes2p2c/Makefile.am create mode 100644 test/multidomain/2cstokes2p2c/grids/interfacedomain.dgf create mode 100644 test/multidomain/2cstokes2p2c/grids/test_2cstokes2p2c.dgf create mode 100644 test/multidomain/2cstokes2p2c/stokes2csubproblem.hh create mode 100644 test/multidomain/2cstokes2p2c/test_2cstokes2p2c.cc create mode 100644 test/multidomain/2cstokes2p2c/test_2cstokes2p2c.input create mode 100644 test/multidomain/Makefile.am diff --git a/configure.ac b/configure.ac index 85f211277d..39272660ee 100644 --- a/configure.ac +++ b/configure.ac @@ -128,6 +128,8 @@ AC_CONFIG_FILES([dumux.pc test/material/ncpflash/Makefile test/material/pengrobinson/Makefile test/material/tabulation/Makefile + test/multidomain/Makefile + test/multidomain/2cstokes2p2c/Makefile test/references/Makefile tutorial/Makefile ]) diff --git a/dumux/multidomain/couplinglocalresiduals/stokesnccouplinglocalresidual.hh b/dumux/multidomain/couplinglocalresiduals/stokesnccouplinglocalresidual.hh new file mode 100644 index 0000000000..27de52eef2 --- /dev/null +++ b/dumux/multidomain/couplinglocalresiduals/stokesnccouplinglocalresidual.hh @@ -0,0 +1,429 @@ +/***************************************************************************** + * Copyright (C) 2009-2012 by Katherina Baber, Klaus Mosthaf * + * Copyright (C) 2008-2009 by Bernd Flemisch, Andreas Lauser * + * Institute for Modelling Hydraulic and Environmental Systems * + * University of Stuttgart, Germany * + * email: <givenname>.<name>@iws.uni-stuttgart.de * + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Element-wise calculation of the Jacobian matrix for problems + * using the stokes box model. + */ + +#ifndef DUMUX_STOKESNC_COUPLING_LOCAL_RESIDUAL_HH +#define DUMUX_STOKESNC_COUPLING_LOCAL_RESIDUAL_HH + +#include <dumux/freeflow/stokesnc/stokesncmodel.hh> + +namespace Dumux +{ +/*! + * \ingroup BoxStokesModel + * \brief Element-wise calculation of the Jacobian matrix for problems + * using the Stokes box model. + * + * This class is also used for the stokes transport + * model, which means that it uses static polymorphism. + */ +template<class TypeTag> +class StokesncCouplingLocalResidual : public StokesncLocalResidual<TypeTag> +{ +protected: + typedef StokesncCouplingLocalResidual<TypeTag> ThisType; + typedef StokesncLocalResidual<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, LocalResidual) Implementation; + + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + + + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld, + numEq = GET_PROP_VALUE(TypeTag, NumEq), + numComponents = FluidSystem::numComponents + }; + enum { + //indices of the equations + massBalanceIdx = Indices::massBalanceIdx, //!< Index of the mass balance + + momentumXIdx = Indices::momentumXIdx, //!< Index of the x-component of the momentum balance + momentumYIdx = Indices::momentumYIdx, //!< Index of the y-component of the momentum balance + momentumZIdx = Indices::momentumZIdx, //!< Index of the z-component of the momentum balance + lastMomentumIdx = Indices::lastMomentumIdx, //!< Index of the last component of the momentum balance + transportEqIdx = Indices::transportEqIdx//!< Index of the transport equation + }; + enum { + //indices of phase and transported component + phaseIdx = Indices::phaseIdx, + transportCompIdx = Indices::transportCompIdx + }; + enum { + dimXIdx = Indices::dimXIdx, //!< Index for the first component of a vector + dimYIdx = Indices::dimYIdx, //!< Index for the second component of a vector + dimZIdx = Indices::dimZIdx //!< Index for the third component of a vector + }; + + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + typedef typename GridView::template Codim<dim>::Entity Vertex; + typedef typename GridView::template Codim<dim>::EntityPointer VertexPointer; + typedef typename GridView::ctype CoordScalar; + typedef Dune::GenericReferenceElements<Scalar, dim> ReferenceElements; + typedef Dune::GenericReferenceElement<Scalar, dim> ReferenceElement; + + typedef Dune::FieldVector<CoordScalar, dimWorld> GlobalPosition; + typedef Dune::FieldVector<Scalar, dim> DimVector; + + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, DofMapper) DofMapper; + typedef typename GET_PROP_TYPE(TypeTag, ElementSolutionVector) ElementSolutionVector; + + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + + typedef typename GridView::Intersection Intersection; + typedef typename GridView::IntersectionIterator IntersectionIterator; + typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, ElementBoundaryTypes) ElementBoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + + static const bool useMoles = GET_PROP_VALUE(TypeTag, UseMoles); + +public: + // the stokes model needs a modified treatment of the BCs + void evalBoundary_() + { + assert(this->residual_.size() == this->fvGeometry_().numScv); + const ReferenceElement &refElement = ReferenceElements::general(this->element_().geometry().type()); + + // loop over vertices of the element + for (int idx = 0; idx < this->fvGeometry_().numScv; idx++) + { + // consider only SCVs on the boundary + if (this->fvGeometry_().subContVol[idx].inner) + continue; + + // important at corners of the grid + DimVector momentumResidual(0.0); + DimVector averagedNormal(0.0); + int numberOfOuterFaces = 0; + // evaluate boundary conditions for the intersections of + // the current element + const BoundaryTypes &bcTypes = this->bcTypes_(idx); + IntersectionIterator isIt = this->gridView_().ibegin(this->element_()); + const IntersectionIterator &endIt = this->gridView_().iend(this->element_()); + for (; isIt != endIt; ++isIt) + { + // handle only intersections on the boundary + if (!isIt->boundary()) + continue; + + // assemble the boundary for all vertices of the current face + const int faceIdx = isIt->indexInInside(); + const int numFaceVertices = refElement.size(faceIdx, 1, dim); + + // loop over the single vertices on the current face + for (int faceVertIdx = 0; faceVertIdx < numFaceVertices; ++faceVertIdx) + { + // only evaluate, if we consider the same face vertex as in the outer + // loop over the element vertices + if (refElement.subEntity(faceIdx, 1, faceVertIdx, dim) + != idx) + continue; + + const int boundaryFaceIdx = this->fvGeometry_().boundaryFaceIndex(faceIdx, faceVertIdx); + const FluxVariables boundaryVars(this->problem_(), + this->element_(), + this->fvGeometry_(), + boundaryFaceIdx, + this->curVolVars_(), + true); + + // the computed residual of the momentum equations is stored + // into momentumResidual for the replacement of the mass balance + // in case of Dirichlet conditions for the momentum balance; + // the fluxes at the boundary are added in the second step + if (this->momentumBalanceDirichlet_(bcTypes)) + { + DimVector muGradVelNormal(0.); + const DimVector &boundaryFaceNormal = + boundaryVars.face().normal; + + boundaryVars.velocityGrad().umv(boundaryFaceNormal, muGradVelNormal); + muGradVelNormal *= boundaryVars.dynamicViscosity(); + + for (int i=0; i < this->residual_.size(); i++) + Valgrind::CheckDefined(this->residual_[i]); + for (int dimIdx=0; dimIdx < dim; ++dimIdx) + momentumResidual[dimIdx] = this->residual_[idx][momentumXIdx+dimIdx]; + + //Sign is right!!!: boundary flux: -mu grad v n + //but to compensate outernormal -> residual - (-mu grad v n) + momentumResidual += muGradVelNormal; + averagedNormal += boundaryFaceNormal; + } + + // evaluate fluxes at a single boundary segment + asImp_()->evalNeumannSegment_(isIt, idx, boundaryFaceIdx, boundaryVars); + asImp_()->evalOutflowSegment_(isIt, idx, boundaryFaceIdx, boundaryVars); + + //for the corner points, the boundary flux across the vertical non-coupling boundary faces + //has to be calculated to fulfill the mass balance + //convert suddomain intersection into multidomain intersection and check whether it is an outer boundary + if(!GridView::Grid::multiDomainIntersection(*isIt).neighbor() + && this->boundaryHasMortarCoupling_(this->bcTypes_(idx))) + { + const GlobalPosition& globalPos = this->fvGeometry_().subContVol[idx].global; + //problem specific function, in problem orientation of interface is known + if(this->problem_().isInterfaceCornerPoint(globalPos)) + { + PrimaryVariables priVars(0.0); +// DimVector faceCoord = this->fvGeometry_().boundaryFace[boundaryFaceIdx].ipGlobal; +// std::cout<<faceCoord<<std::endl; + + const int numVertices = refElement.size(dim); + bool evalBoundaryFlux = false; + for(int equationIdx = 0; equationIdx < numEq; ++equationIdx) + { + for(int i= 0; i < numVertices; i++) + { + //if vertex is on boundary and not the coupling vertex: check whether an outflow condition is set + if(this->model_().onBoundary(this->element_(), i) && i!=idx) + if (!this->bcTypes_(i).isOutflow(equationIdx)) + evalBoundaryFlux = true; + } + + //calculate the actual boundary fluxes and add to residual (only for momentum and transport equation, mass balance already has outflow) + asImp_()->computeFlux(priVars, boundaryFaceIdx, true/*on boundary*/); + if(evalBoundaryFlux) + this->residual_[idx][equationIdx] += priVars[equationIdx]; + } + } + } + // Beavers-Joseph condition at the coupling boundary/interface + if(boundaryHasCoupling_(bcTypes) || boundaryHasMortarCoupling_(bcTypes)) + { + evalBeaversJoseph_(isIt, idx, boundaryFaceIdx, boundaryVars); + asImp_()->evalCouplingVertex_(isIt, idx, boundaryFaceIdx, boundaryVars); + } + + // count the number of outer faces to determine, if we are on + // a corner point and if an interpolation should be done + numberOfOuterFaces++; + } // end loop over face vertices + } // end loop over intersections + + // replace defect at the corner points of the grid + // by the interpolation of the primary variables + if(!bcTypes.isDirichlet(massBalanceIdx)) + { + if (this->momentumBalanceDirichlet_(bcTypes)) + this->replaceMassbalanceResidual_(momentumResidual, averagedNormal, idx); + else // de-stabilize (remove alpha*grad p - alpha div f + // from computeFlux on the boundary) + this->removeStabilizationAtBoundary_(idx); + } + if (numberOfOuterFaces == 2) + this->interpolateCornerPoints_(bcTypes, idx); + } // end loop over element vertices + + // evaluate the dirichlet conditions of the element + if (this->bcTypes_().hasDirichlet()) + asImp_()->evalDirichlet_(); + } + + void evalBoundaryPDELab_() + { + // loop over vertices of the element + for (int idx = 0; idx < this->fvGeometry_().numScv; idx++) + { + // consider only SCVs on the boundary + if (this->fvGeometry_().subContVol[idx].inner) + continue; + + this->removeStabilizationAtBoundary_(idx); + } // end loop over vertices + } + +protected: + /*! + * \brief Evaluate one part of the Dirichlet-like coupling conditions for a single + * sub-control volume face; rest is done in the local coupling operator + */ + void evalCouplingVertex_(const IntersectionIterator &isIt, + const int scvIdx, + const int boundaryFaceIdx, + const FluxVariables &boundaryVars) + { + const VolumeVariables &volVars = this->curVolVars_()[scvIdx]; + const BoundaryTypes &bcTypes = this->bcTypes_(scvIdx); + + // TODO: beaversJosephCoeff is used to determine, if we are on a coupling face + // this is important at the corners. However, a better way should be found. + + // check if BJ-coeff is not zero to decide, if coupling condition + // for the momentum balance (Dirichlet vor v.n) has to be set; + // may be important at corners + const Scalar beaversJosephCoeff = this->problem_().beaversJosephCoeff(this->element_(), + this->fvGeometry_(), + *isIt, + scvIdx, + boundaryFaceIdx); + // set velocity normal to the interface + if (bcTypes.isCouplingInflow(momentumYIdx) && beaversJosephCoeff) + this->residual_[scvIdx][momentumYIdx] = + volVars.velocity() * + boundaryVars.face().normal / + boundaryVars.face().normal.two_norm(); + Valgrind::CheckDefined(this->residual_[scvIdx][momentumYIdx]); + + // add pressure correction - required for pressure coupling, + // if p.n comes from the pm + if ((bcTypes.isCouplingOutflow(momentumYIdx) && beaversJosephCoeff) || bcTypes.isMortarCoupling(momentumYIdx)) + { + DimVector pressureCorrection(isIt->centerUnitOuterNormal()); + pressureCorrection *= volVars.pressure(); // TODO: 3D + this->residual_[scvIdx][momentumYIdx] += pressureCorrection[momentumYIdx]* + boundaryVars.face().area; + } + + // set mole or mass fraction for the transported components + for (int compIdx = 0; compIdx < numComponents; compIdx++) + { + int eqIdx = dim + compIdx; + if (eqIdx != massBalanceIdx) { + if (bcTypes.isCouplingOutflow(eqIdx)) + { + if(useMoles) + this->residual_[scvIdx][eqIdx] = volVars.fluidState().moleFraction(phaseIdx, compIdx); + else + this->residual_[scvIdx][eqIdx] = volVars.fluidState().massFraction(phaseIdx, compIdx); + Valgrind::CheckDefined(this->residual_[scvIdx][eqIdx]); + } + } + } + } + + void evalBeaversJoseph_(const IntersectionIterator &isIt, + const int scvIdx, + const int boundaryFaceIdx, + const FluxVariables &boundaryVars) //TODO: required + { + const BoundaryTypes &bcTypes = this->bcTypes_(scvIdx); + + Scalar beaversJosephCoeff = this->problem_().beaversJosephCoeff(this->element_(), + this->fvGeometry_(), + *isIt, + scvIdx, + boundaryFaceIdx); + + // only enter here, if we are on a coupling boundary (see top) + // and the BJ coefficient is not zero + if (beaversJosephCoeff) + { + const Scalar Kxx = this->problem_().permeability(this->element_(), + this->fvGeometry_(), + scvIdx); + beaversJosephCoeff /= sqrt(Kxx); + const DimVector& elementUnitNormal = isIt->centerUnitOuterNormal(); + + // implementation as NEUMANN condition ///////////////////////////////////////////// + // (v.n)n + if (bcTypes.isCouplingOutflow(momentumXIdx)) + { + const Scalar normalComp = boundaryVars.velocity()*elementUnitNormal; + DimVector normalV = elementUnitNormal; + normalV *= normalComp; // v*n*n + + // v_tau = v - (v.n)n + const DimVector tangentialV = boundaryVars.velocity() - normalV; + const Scalar boundaryFaceArea = boundaryVars.face().area; + + for (int dimIdx=0; dimIdx < dim; ++dimIdx) + this->residual_[scvIdx][dimIdx] += beaversJosephCoeff * + boundaryFaceArea * + tangentialV[dimIdx] * + boundaryVars.dynamicViscosity(); + + //////////////////////////////////////////////////////////////////////////////////// + //normal component has only to be set if no coupling conditions are defined + //set NEUMANN flux (set equal to pressure in problem) +// PrimaryVariables priVars(0.0); +// this->problem_().neumann(priVars, this->element_(), this->fvGeometry_(), +// *isIt, scvIdx, boundaryFaceIdx); +// for (int dimIdx=0; dimIdx < dim; ++dimIdx) +// this->residual_[scvIdx][dimIdx] += priVars[dimIdx]* +// boundaryFaceArea; + } + if (bcTypes.isCouplingInflow(momentumXIdx)) //TODO: 3D + { + /////////////////////////////////////////////////////////////////////////////////////////// + //IMPLEMENTATION AS DIRICHLET CONDITION + //tangential componment: vx = sqrt K /alpha * (grad v n(unity))t + DimVector tangentialVelGrad(0); + boundaryVars.velocityGrad().umv(elementUnitNormal, tangentialVelGrad); + tangentialVelGrad /= -beaversJosephCoeff; // was - before + this->residual_[scvIdx][momentumXIdx] = + tangentialVelGrad[momentumXIdx] - this->curPriVars_(scvIdx)[momentumXIdx]; + + //////////////////////////////////////////////////////////////////////////////////// + //for testing Beavers and Joseph without adjacent porous medium set vy to 0 +// Scalar normalVel(0); +// this->residual_[scvIdx][momentumYIdx] = this->curPrimaryVars_(scvIdx)[momentumYIdx] - normalVel; + //////////////////////////////////////////////////////////////////////////////////// + } + } + } + + // return true, if at least one equation on the boundary has a coupling condition + bool boundaryHasCoupling_(const BoundaryTypes& bcTypes) const + { + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) + if (bcTypes.isCouplingInflow(eqIdx) || bcTypes.isCouplingOutflow(eqIdx)) + return true; + return false; + } + + // return true, if at least one equation on the boundary has a mortar coupling condition + bool boundaryHasMortarCoupling_(const BoundaryTypes& bcTypes) const + { + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) + if (bcTypes.isMortarCoupling(eqIdx)) + return true; + return false; + } + + +private: + Implementation *asImp_() + { return static_cast<Implementation *>(this); } + const Implementation *asImp_() const + { return static_cast<const Implementation *>(this); } + +}; + +} + +#endif diff --git a/test/Makefile.am b/test/Makefile.am index 06b1fd9513..58568807e9 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -4,6 +4,7 @@ SUBDIRS = common \ geomechanics \ implicit \ material \ + multidomain \ references EXTRA_DIST = CMakeLists.txt diff --git a/test/multidomain/2cstokes2p2c/2cstokes2p2cproblem.hh b/test/multidomain/2cstokes2p2c/2cstokes2p2cproblem.hh new file mode 100644 index 0000000000..728995cd9b --- /dev/null +++ b/test/multidomain/2cstokes2p2c/2cstokes2p2cproblem.hh @@ -0,0 +1,738 @@ +// -*- 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 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +#ifndef DUMUX_2CSTOKES2P2CPROBLEM_HH +#define DUMUX_2CSTOKES2P2CPROBLEM_HH + +#include <dune/grid/common/gridinfo.hh> +#include <dune/grid/multidomaingrid.hh> +#include <dune/grid/io/file/dgfparser.hh> + +#include <dumux/multidomain/common/multidomainproblem.hh> +#include <dumux/multidomain/2cstokes2p2c/2cstokes2p2clocaloperator.hh> +#include <dumux/multidomain/2cstokes2p2c/2cstokes2p2cnewtoncontroller.hh> +#include <dumux/material/fluidsystems/h2oairfluidsystem.hh> +#include <dumux/linear/pardisobackend.hh> + +#include "2cstokes2p2cspatialparams.hh" +#include "stokes2csubproblem.hh" +#include "2p2csubproblem.hh" + +namespace Dumux +{ +template <class TypeTag> +class TwoCStokesTwoPTwoCProblem; + +namespace Properties +{ +NEW_TYPE_TAG(TwoCStokesTwoPTwoCProblem, INHERITS_FROM(MultiDomain)); + +// Set the grid type +SET_PROP(TwoCStokesTwoPTwoCProblem, Grid) +{ + public: +#ifdef HAVE_UG + typedef typename Dune::UGGrid<2> type; +#elif HAVE_ALUGRID + typedef typename Dune::ALUGrid<2, 2, Dune::cube, Dune::nonconforming> type; +#endif +}; + +// Specify the multidomain gridview +SET_PROP(TwoCStokesTwoPTwoCProblem, GridView) +{ + typedef typename GET_PROP_TYPE(TypeTag, MultiDomainGrid) MDGrid; + public: + typedef typename MDGrid::LeafGridView type; +}; + +// Set the global problem +SET_TYPE_PROP(TwoCStokesTwoPTwoCProblem, Problem, Dumux::TwoCStokesTwoPTwoCProblem<TypeTag>); + +// Set the two sub-problems of the global problem +SET_TYPE_PROP(TwoCStokesTwoPTwoCProblem, SubDomain1TypeTag, TTAG(Stokes2cSubProblem)); +SET_TYPE_PROP(TwoCStokesTwoPTwoCProblem, SubDomain2TypeTag, TTAG(TwoPTwoCSubProblem)); + +// Set the global problem in the context of the two sub-problems +SET_TYPE_PROP(Stokes2cSubProblem, MultiDomainTypeTag, TTAG(TwoCStokesTwoPTwoCProblem)); +SET_TYPE_PROP(TwoPTwoCSubProblem, MultiDomainTypeTag, TTAG(TwoCStokesTwoPTwoCProblem)); + +// Set the other sub-problem for each of the two sub-problems +SET_TYPE_PROP(Stokes2cSubProblem, OtherSubDomainTypeTag, TTAG(TwoPTwoCSubProblem)); +SET_TYPE_PROP(TwoPTwoCSubProblem, OtherSubDomainTypeTag, TTAG(Stokes2cSubProblem)); + +SET_PROP(Stokes2cSubProblem, SpatialParams) +{ typedef Dumux::TwoCStokesTwoPTwoCSpatialParams<TypeTag> type; }; +SET_PROP(TwoPTwoCSubProblem, SpatialParams) +{ typedef Dumux::TwoCStokesTwoPTwoCSpatialParams<TypeTag> type; }; + +// Set the fluid system +SET_PROP(TwoCStokesTwoPTwoCProblem, FluidSystem) +{ + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dumux::FluidSystems::H2OAir<Scalar, + Dumux::H2O<Scalar>, + /*useComplexrelations=*/false> type; +}; + +SET_TYPE_PROP(TwoCStokesTwoPTwoCProblem, JacobianAssembler, Dumux::MultiDomainAssembler<TypeTag>); + +// Set the local coupling operator +SET_TYPE_PROP(TwoCStokesTwoPTwoCProblem, MultiDomainCouplingLocalOperator, Dumux::TwoCStokesTwoPTwoCLocalOperator<TypeTag>); + +SET_PROP(TwoCStokesTwoPTwoCProblem, SolutionVector) +{ + private: + typedef typename GET_PROP_TYPE(TypeTag, MultiDomainGridOperator) MDGridOperator; + public: + typedef typename MDGridOperator::Traits::Domain type; +}; + +// newton controller +SET_TYPE_PROP(TwoCStokesTwoPTwoCProblem, NewtonController, Dumux::TwoCStokesTwoPTwoCNewtonController<TypeTag>); + +// if you do not have PARDISO, the SuperLU solver is used: +#ifdef HAVE_PARDISO +SET_TYPE_PROP(TwoCStokesTwoPTwoCProblem, LinearSolver, PardisoBackend<TypeTag>); +#else +SET_TYPE_PROP(TwoCStokesTwoPTwoCProblem, LinearSolver, SuperLUBackend<TypeTag>); +#endif // HAVE_PARDISO + +// set this to one here (must fit to the structure of the coupled matrix which has block length 1) +SET_INT_PROP(TwoCStokesTwoPTwoCProblem, NumEq, 1); + +// Write the solutions of individual newton iterations? +SET_BOOL_PROP(TwoCStokesTwoPTwoCProblem, NewtonWriteConvergence, false); +} + +/*! + * \brief The problem class for the coupling of an isothermal two-component Stokes + * and an isothermal two-phase two-component Darcy model. + * + * The problem class for the coupling of an isothermal two-component Stokes (stokes2c) + * and an isothermal two-phase two-component Darcy model (2p2c). + * It uses the 2p2cCoupling model and the Stokes2ccoupling model and provides + * the problem specifications for common parameters of the two submodels. + * The initial and boundary conditions of the submodels are specified in the two subproblems, + * 2p2csubproblem.hh and stokes2csubproblem.hh, which are accessible via the coupled problem. + */ +template <class TypeTag = TTAG(TwoCStokesTwoPTwoCProblem) > +class TwoCStokesTwoPTwoCProblem : public MultiDomainProblem<TypeTag> +{ + template<int dim> + struct VertexLayout + { + bool contains(Dune::GeometryType geomtype) + { return geomtype.dim() == 0; } + }; + + typedef TwoCStokesTwoPTwoCProblem<TypeTag> ThisType; + typedef MultiDomainProblem<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, NewtonController) NewtonController; + typedef typename GET_PROP_TYPE(TypeTag, TimeManager) TimeManager; + + typedef typename GET_PROP_TYPE(TypeTag, SubDomain1TypeTag) Stokes2cTypeTag; + typedef typename GET_PROP_TYPE(TypeTag, SubDomain2TypeTag) TwoPTwoCTypeTag; + + typedef typename GET_PROP_TYPE(Stokes2cTypeTag, Problem) Stokes2cSubProblem; + typedef typename GET_PROP_TYPE(TwoPTwoCTypeTag, Problem) TwoPTwoCSubProblem; + + typedef typename GET_PROP_TYPE(Stokes2cTypeTag, ElementSolutionVector) ElementSolutionVector1; + typedef typename GET_PROP_TYPE(TwoPTwoCTypeTag, ElementSolutionVector) ElementSolutionVector2; + + typedef typename GET_PROP_TYPE(Stokes2cTypeTag, ElementVolumeVariables) ElementVolumeVariables1; + typedef typename GET_PROP_TYPE(TwoPTwoCTypeTag, ElementVolumeVariables) ElementVolumeVariables2; + + typedef typename GET_PROP_TYPE(Stokes2cTypeTag, FluxVariables) BoundaryVariables1; + + typedef typename GET_PROP_TYPE(Stokes2cTypeTag, FVElementGeometry) FVElementGeometry1; + typedef typename GET_PROP_TYPE(TwoPTwoCTypeTag, FVElementGeometry) FVElementGeometry2; + + typedef typename GET_PROP_TYPE(TypeTag, MultiDomainGrid) MDGrid; + typedef typename MDGrid::LeafGridView MDGridView; + enum { dim = MDGridView::dimension }; + + typedef typename MDGrid::Traits::template Codim<0>::EntityPointer MDElementPointer; + typedef typename MDGrid::Traits::template Codim<0>::Entity MDElement; + typedef typename MDGrid::SubDomainGrid SDGrid; + typedef typename SDGrid::template Codim<0>::EntityPointer SDElementPointer; + + typedef typename GET_PROP_TYPE(Stokes2cTypeTag, GridView) Stokes2cGridView; + typedef typename GET_PROP_TYPE(TwoPTwoCTypeTag, GridView) TwoPTwoCGridView; + typedef typename Stokes2cGridView::template Codim<0>::Entity SDElement1; + typedef typename TwoPTwoCGridView::template Codim<0>::Entity SDElement2; + + typedef typename MDGrid::template Codim<0>::LeafIterator ElementIterator; + typedef typename MDGrid::LeafSubDomainInterfaceIterator SDInterfaceIterator; + + typedef Dune::FieldVector<Scalar, dim> GlobalPosition; + typedef Dune::FieldVector<Scalar, dim> FieldVector; + + typedef typename GET_PROP_TYPE(Stokes2cTypeTag, Indices) Stokes2cIndices; + typedef typename GET_PROP_TYPE(TwoPTwoCTypeTag, Indices) TwoPTwoCIndices; + + typedef typename GET_PROP_TYPE(Stokes2cTypeTag, FluidSystem) FluidSystem; + + enum { numEq1 = GET_PROP_VALUE(Stokes2cTypeTag, NumEq) }; + enum { // indices in the Stokes domain + momentumXIdx1 = Stokes2cIndices::momentumXIdx, //!< Index of the x-component of the momentum balance + momentumYIdx1 = Stokes2cIndices::momentumYIdx, //!< Index of the y-component of the momentum balance + momentumZIdx1 = Stokes2cIndices::momentumZIdx, //!< Index of the z-component of the momentum balance + lastMomentumIdx1 = Stokes2cIndices::lastMomentumIdx, //!< Index of the last component of the momentum balance + massBalanceIdx1 = Stokes2cIndices::massBalanceIdx, //!< Index of the mass balance + transportEqIdx1 = Stokes2cIndices::transportEqIdx, //!< Index of the transport equation + }; + + enum { numEq2 = GET_PROP_VALUE(TwoPTwoCTypeTag, NumEq) }; + enum { // indices in the Darcy domain + massBalanceIdx2 = TwoPTwoCIndices::pressureIdx, + switchIdx2 = TwoPTwoCIndices::switchIdx, + contiTotalMassIdx2 = TwoPTwoCIndices::contiNEqIdx, + contiWEqIdx2 = TwoPTwoCIndices::contiWEqIdx, + + wCompIdx2 = TwoPTwoCIndices::wCompIdx, + nCompIdx2 = TwoPTwoCIndices::nCompIdx, + + wPhaseIdx2 = TwoPTwoCIndices::wPhaseIdx, + nPhaseIdx2 = TwoPTwoCIndices::nPhaseIdx + + }; + enum { phaseIdx = GET_PROP_VALUE(Stokes2cTypeTag, PhaseIdx) }; + enum { transportCompIdx1 = Stokes2cIndices::transportCompIdx }; + + +public: + /*! + * \brief docme + * + * \param hostGrid docme + * \param timeManager The time manager + * + */ + TwoCStokesTwoPTwoCProblem(MDGrid &mdGrid, + TimeManager &timeManager) + : ParentType(mdGrid, timeManager) + { + try + { + // define location of the interface + interface_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Grid, InterfacePos); + noDarcyX_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Problem, NoDarcyX); + episodeLength_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, TimeManager, EpisodeLength); + initializationTime_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, TimeManager, InitTime); + + // define output options + freqRestart_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, int, Output, FreqRestart); + freqOutput_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, int, Output, FreqOutput); + freqFluxOutput_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, int, Output, FreqFluxOutput); + freqVaporFluxOutput_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, int, Output, FreqVaporFluxOutput); + } + catch (Dumux::ParameterException &e) { + std::cerr << e << ". Abort!\n"; + exit(1) ; + } + catch (...) { + std::cerr << "Unknown exception thrown!\n"; + exit(1); + } + + stokes2c_ = this->sdID1(); + twoPtwoC_ = this->sdID2(); + + initializeGrid(); + + // initialize the tables of the fluid system + FluidSystem::init(/*tempMin=*/273.15, /*tempMax=*/373.15, /*numTemp=*/200, + /*pMin=*/1e4, /*pMax=*/2e5, /*numP=*/200); + + if (initializationTime_ > 0.0) + this->timeManager().startNextEpisode(initializationTime_); + else + this->timeManager().startNextEpisode(episodeLength_); + } + + ~TwoCStokesTwoPTwoCProblem() + { + fluxFile_.close(); + }; + + //! \copydoc Dumux::CoupledProblem::init() + void init() + { + ParentType::init(); + + // plot the Pc-Sw curves, if requested + this->sdProblem2().spatialParams().plotMaterialLaw(); + + std::cout << "Writing flux data at interface\n"; + if (this->timeManager().time() == 0) + { + fluxFile_.open("fluxes.out"); + fluxFile_ << "time; flux1; advFlux1; diffFlux1; totalFlux1; flux2; wPhaseFlux2; nPhaseFlux2\n"; + counter_ = 1; + } + else + fluxFile_.open("fluxes.out", std::ios_base::app); + } + + /*! + * \brief docme + */ + void initializeGrid() + { + MDGrid& mdGrid = this->mdGrid(); + mdGrid.startSubDomainMarking(); + + // subdivide grid in two subdomains + ElementIterator eendit = mdGrid.template leafend<0>(); + for (ElementIterator elementIt = mdGrid.template leafbegin<0>(); + elementIt != eendit; ++elementIt) + { + // this is required for parallelization + // checks if element is within a partition + if (elementIt->partitionType() != Dune::InteriorEntity) + continue; + + GlobalPosition globalPos = elementIt->geometry().center(); + + if (globalPos[1] > interface_) + mdGrid.addToSubDomain(stokes2c_,*elementIt); + else + if(globalPos[0] > noDarcyX_) + mdGrid.addToSubDomain(twoPtwoC_,*elementIt); + } + mdGrid.preUpdateSubDomains(); + mdGrid.updateSubDomains(); + mdGrid.postUpdateSubDomains(); + + gridinfo(this->sdGrid1()); + gridinfo(this->sdGrid2()); + + this->sdProblem1().spatialParams().loadIntrinsicPermeability(this->sdGridView1()); + this->sdProblem2().spatialParams().loadIntrinsicPermeability(this->sdGridView2()); + } + + //! \copydoc Dumux::CoupledProblem::postTimeStep() + void postTimeStep() + { + // call the postTimeStep function of the subproblems + this->sdProblem1().postTimeStep(); + this->sdProblem2().postTimeStep(); + + if (shouldWriteFluxFile() || shouldWriteVaporFlux()) + { + counter_ = this->sdProblem1().currentVTKFileNumber() + 1; + + calculateFirstInterfaceFluxes(); + calculateSecondInterfaceFluxes(); + } + } + + //! \copydoc Dumux::CoupledProblem::episodeEnd() + void episodeEnd() + { this->timeManager().startNextEpisode(episodeLength_); } + + /* + * \brief Calculates fluxes and coupling terms at the interface for the Stokes model. + * Flux output files are created and the summarized flux is written to a file. + */ + void calculateFirstInterfaceFluxes() + { + const MDGrid& mdGrid = this->mdGrid(); + ElementVolumeVariables1 elemVolVarsPrev1, elemVolVarsCur1; + Scalar sumVaporFluxes = 0.; + Scalar advectiveVaporFlux = 0.; + Scalar diffusiveVaporFlux = 0.; + + // count number of elements to determine number of interface nodes + int numElements = 0; + const SDInterfaceIterator endIfIt = mdGrid.leafSubDomainInterfaceEnd(stokes2c_, twoPtwoC_); + for (SDInterfaceIterator ifIt = + mdGrid.leafSubDomainInterfaceBegin(stokes2c_, twoPtwoC_); ifIt != endIfIt; ++ifIt) + numElements++; + + const int numInterfaceVertices = numElements + 1; + std::vector<InterfaceFluxes<numEq1> > outputVector(numInterfaceVertices); // vector for the output of the fluxes + FVElementGeometry1 fvGeometry1; + int interfaceVertIdx = -1; + + // loop over faces on the interface + for (SDInterfaceIterator ifIt = + mdGrid.leafSubDomainInterfaceBegin(stokes2c_, twoPtwoC_); ifIt != endIfIt; ++ifIt) + { + const int firstFaceIdx = ifIt->indexInFirstCell(); + const MDElementPointer mdElementPointer1 = ifIt->firstCell(); // ATTENTION!!! + const MDElement& mdElement1 = *mdElementPointer1; // Entity pointer has to be copied before. + const SDElementPointer sdElementPointer1 = this->sdElementPointer1(mdElement1); + const SDElement1& sdElement1 = *sdElementPointer1; + fvGeometry1.update(this->sdGridView1(), sdElement1); + + const Dune::GenericReferenceElement<typename MDGrid::ctype,dim>& referenceElement1 = + Dune::GenericReferenceElements<typename MDGrid::ctype,dim>::general(mdElement1.type()); + const int numVerticesOfFace = referenceElement1.size(firstFaceIdx, 1, dim); + + // evaluate residual of the sub model without boundary conditions (stabilization is removed) + // the element volume variables are updated here + this->localResidual1().evalNoBoundary(sdElement1, fvGeometry1, + elemVolVarsPrev1, elemVolVarsCur1); + + for (int nodeInFace = 0; nodeInFace < numVerticesOfFace; nodeInFace++) + { + const int vertInElem1 = referenceElement1.subEntity(firstFaceIdx, 1, nodeInFace, dim); + const FieldVector& vertexGlobal = mdElement1.geometry().corner(vertInElem1); + const unsigned firstGlobalIdx = this->mdVertexMapper().map(stokes2c_, mdElement1, vertInElem1, dim); + const ElementSolutionVector1& firstVertexDefect = this->localResidual1().residual(); + + // loop over all interface vertices to check if vertex id is already in stack + bool existing = false; + for (int interfaceVertex=0; interfaceVertex < numInterfaceVertices; ++interfaceVertex) + { + if (firstGlobalIdx == outputVector[interfaceVertex].globalIdx) + { + existing = true; + interfaceVertIdx = interfaceVertex; + break; + } + } + + if (!existing) + interfaceVertIdx++; + + if (shouldWriteFluxFile()) // compute only if required + { + outputVector[interfaceVertIdx].interfaceVertex = interfaceVertIdx; + outputVector[interfaceVertIdx].globalIdx = firstGlobalIdx; + outputVector[interfaceVertIdx].xCoord = vertexGlobal[0]; + outputVector[interfaceVertIdx].yCoord = vertexGlobal[1]; + outputVector[interfaceVertIdx].count += 1; + for (int eqIdx=0; eqIdx < numEq1; ++eqIdx) + outputVector[interfaceVertIdx].defect[eqIdx] += + firstVertexDefect[vertInElem1][eqIdx]; + } + + // compute summarized fluxes for output + if (shouldWriteVaporFlux()) + { + int boundaryFaceIdx = + fvGeometry1.boundaryFaceIndex(firstFaceIdx, nodeInFace); + + const BoundaryVariables1 boundaryVars1(this->sdProblem1(), + sdElement1, + fvGeometry1, + boundaryFaceIdx, + elemVolVarsCur1, + /*onBoundary=*/true); + + advectiveVaporFlux += computeAdvectiveVaporFluxes1(elemVolVarsCur1, boundaryVars1, vertInElem1); + diffusiveVaporFlux += computeDiffusiveVaporFluxes1(elemVolVarsCur1, boundaryVars1, vertInElem1); + sumVaporFluxes += firstVertexDefect[vertInElem1][transportEqIdx1]; + } + } + } // end loop over element faces on interface + + if (shouldWriteFluxFile()) + { + std::cout << "Writing flux file\n"; + char outputname[20]; + sprintf(outputname, "%s%05d%s","fluxes1_", counter_,".out"); + std::ofstream outfile(outputname, std::ios_base::out); + outfile << "Xcoord1 " + << "totalFlux1 " + << "componentFlux1 " + << "count1 " + << std::endl; + for (int interfaceVertIdx=0; interfaceVertIdx < numInterfaceVertices; interfaceVertIdx++) + { + if (outputVector[interfaceVertIdx].count > 2) + std::cerr << "too often at one node!!"; + + if (outputVector[interfaceVertIdx].count==2) + outfile << outputVector[interfaceVertIdx].xCoord << " " + << outputVector[interfaceVertIdx].defect[massBalanceIdx1] << " " // total mass flux + << outputVector[interfaceVertIdx].defect[transportEqIdx1] << " " // total flux of component + << outputVector[interfaceVertIdx].count << " " + << std::endl; + } + outfile.close(); + } + if (shouldWriteVaporFlux()) + fluxFile_ << this->timeManager().time() + this->timeManager().timeStepSize() << "; " + << sumVaporFluxes << "; " << advectiveVaporFlux << "; " << diffusiveVaporFlux << "; " + << advectiveVaporFlux-diffusiveVaporFlux << "; "; + } + + /* + * \brief Calculates fluxes and coupling terms at the interface for the Darcy model. + * Flux output files are created and the summarized flux is written to a file. + */ + void calculateSecondInterfaceFluxes() + { + const MDGrid& mdGrid = this->mdGrid(); + ElementVolumeVariables2 elemVolVarsPrev2, elemVolVarsCur2; + + Scalar sumVaporFluxes = 0.; + Scalar sumWaterFluxInGasPhase = 0.; + + // count number of elements to determine number of interface nodes + int numElements = 0; + const SDInterfaceIterator endIfIt = mdGrid.leafSubDomainInterfaceEnd(stokes2c_, twoPtwoC_); + for (SDInterfaceIterator ifIt = + mdGrid.leafSubDomainInterfaceBegin(stokes2c_, twoPtwoC_); ifIt != endIfIt; ++ifIt) + numElements++; + + const int numInterfaceVertices = numElements + 1; + std::vector<InterfaceFluxes<numEq2> > outputVector(numInterfaceVertices); // vector for the output of the fluxes + FVElementGeometry2 fvGeometry2; + int interfaceVertIdx = -1; + + // loop over the element faces on the interface + for (SDInterfaceIterator ifIt = + mdGrid.leafSubDomainInterfaceBegin(stokes2c_, twoPtwoC_); ifIt != endIfIt; ++ifIt) + { + const int secondFaceIdx = ifIt->indexInSecondCell(); + const MDElementPointer mdElementPointer2 = ifIt->secondCell(); // ATTENTION!!! + const MDElement& mdElement2 = *mdElementPointer2; // Entity pointer has to be copied before. + const SDElementPointer sdElementPointer2 = this->sdElementPointer2(mdElement2); + const SDElement2& sdElement2 = *sdElementPointer2; + fvGeometry2.update(this->sdGridView2(), sdElement2); + + const Dune::GenericReferenceElement<typename MDGrid::ctype,dim>& referenceElement2 = + Dune::GenericReferenceElements<typename MDGrid::ctype,dim>::general(mdElement2.type()); + const int numVerticesOfFace = referenceElement2.size(secondFaceIdx, 1, dim); + + // evaluate residual of the sub model without boundary conditions + this->localResidual2().evalNoBoundary(sdElement2, fvGeometry2, + elemVolVarsPrev2, elemVolVarsCur2); + // evaluate the vapor fluxes within each phase + this->localResidual2().evalPhaseFluxes(); + + for (int nodeInFace = 0; nodeInFace < numVerticesOfFace; nodeInFace++) + { + const int vertInElem2 = referenceElement2.subEntity(secondFaceIdx, 1, nodeInFace, dim); + const FieldVector& vertexGlobal = mdElement2.geometry().corner(vertInElem2); + const unsigned secondGlobalIdx = this->mdVertexMapper().map(twoPtwoC_,mdElement2,vertInElem2,dim); + const ElementSolutionVector2& secondVertexDefect = this->localResidual2().residual(); + + bool existing = false; + // loop over all interface vertices to check if vertex id is already in stack + for (int interfaceVertex=0; interfaceVertex < numInterfaceVertices; ++interfaceVertex) + { + if (secondGlobalIdx == outputVector[interfaceVertex].globalIdx) + { + existing = true; + interfaceVertIdx = interfaceVertex; + break; + } + } + + if (!existing) + interfaceVertIdx++; + + if (shouldWriteFluxFile()) + { + outputVector[interfaceVertIdx].interfaceVertex = interfaceVertIdx; + outputVector[interfaceVertIdx].globalIdx = secondGlobalIdx; + outputVector[interfaceVertIdx].xCoord = vertexGlobal[0]; + outputVector[interfaceVertIdx].yCoord = vertexGlobal[1]; + for (int eqIdx=0; eqIdx < numEq2; ++eqIdx) + outputVector[interfaceVertIdx].defect[eqIdx] += secondVertexDefect[vertInElem2][eqIdx]; + outputVector[interfaceVertIdx].count += 1; + } + if (shouldWriteVaporFlux()) + { + if (!existing) // add phase storage only once per vertex + sumWaterFluxInGasPhase += + this->localResidual2().evalPhaseStorage(vertInElem2); + + sumVaporFluxes += secondVertexDefect[vertInElem2][contiWEqIdx2]; + sumWaterFluxInGasPhase += + this->localResidual2().elementFluxes(vertInElem2); + } + } + } + + if (shouldWriteFluxFile()) + { + char outputname[20]; + sprintf(outputname, "%s%05d%s","fluxes2_", counter_,".out"); + std::ofstream outfile(outputname, std::ios_base::out); + outfile << "Xcoord2 " + << "totalFlux2 " + << "componentFlux2 " + << std::endl; + + for (int interfaceVertIdx=0; interfaceVertIdx < numInterfaceVertices; interfaceVertIdx++) + { + if (outputVector[interfaceVertIdx].count > 2) + std::cerr << "too often at one node!!"; + + if (outputVector[interfaceVertIdx].count==2) + outfile << outputVector[interfaceVertIdx].xCoord << " " + << outputVector[interfaceVertIdx].defect[contiTotalMassIdx2] << " " // total mass flux + << outputVector[interfaceVertIdx].defect[contiWEqIdx2] << " " // total flux of component + << std::endl; + } + outfile.close(); + } + if (shouldWriteVaporFlux()){ + Scalar sumWaterFluxInLiquidPhase = sumVaporFluxes - sumWaterFluxInGasPhase; + fluxFile_ << sumVaporFluxes << "; " + << sumWaterFluxInLiquidPhase << "; " + << sumWaterFluxInGasPhase << std::endl; + } + } + + /*! + * \brief docme + * + * \param elemVolVars1 docme + * \param boundaryVars1 docme + * \param vertInElem1 docme + * + */ + Scalar computeAdvectiveVaporFluxes1(const ElementVolumeVariables1& elemVolVars1, + const BoundaryVariables1& boundaryVars1, + int vertInElem1) + { + Scalar advFlux = elemVolVars1[vertInElem1].density() * + elemVolVars1[vertInElem1].fluidState().massFraction(phaseIdx, transportCompIdx1) * + boundaryVars1.normalVelocity(); + return advFlux; + } + + /*! + * \brief docme + * + * \param elemVolVars1 docme + * \param boundaryVars1 docme + * \param vertInElem1 docme + * + */ + Scalar computeDiffusiveVaporFluxes1(const ElementVolumeVariables1& elemVolVars1, + const BoundaryVariables1& boundaryVars1, + int vertInElem1) + { + Scalar diffFlux = boundaryVars1.moleFractionGrad(transportCompIdx1) * + boundaryVars1.face().normal * + boundaryVars1.diffusionCoeff(transportCompIdx1) * + boundaryVars1.molarDensity() * + FluidSystem::molarMass(transportCompIdx1); + return diffFlux; + } + + //! \copydoc Dumux::CoupledProblem::shouldWriteRestartFile() + bool shouldWriteRestartFile() const + { + if ((this->timeManager().timeStepIndex() > 0 && + (this->timeManager().timeStepIndex() % freqRestart_ == 0)) + // also write a restart file at the end of each episode + || this->timeManager().episodeWillBeOver()) + return true; + return false; + } + + + //! \copydoc Dumux::CoupledProblem::shouldWriteOutput() + bool shouldWriteOutput() const + { + if (this->timeManager().timeStepIndex() % freqOutput_ == 0 + || this->timeManager().episodeWillBeOver()) + return true; + return false; + } + + /*! + * \brief Returns true if a file with the fluxes across the + * free-flow -- porous-medium interface should be written. + */ + bool shouldWriteFluxFile() const + { + if (this->timeManager().timeStepIndex() % freqFluxOutput_ == 0 + || this->timeManager().episodeWillBeOver()) + return true; + return false; + } + + /*! + * \brief Returns true if the summarized vapor fluxes + * across the free-flow -- porous-medium interface, + * representing the evaporation rate (related to the + * interface area), should be written. + */ + bool shouldWriteVaporFlux() const + { + if (this->timeManager().timeStepIndex() % freqVaporFluxOutput_ == 0 + || this->timeManager().episodeWillBeOver()) + return true; + return false; + + } + + Stokes2cSubProblem& stokes2cProblem() + { return this->sdProblem1(); } + const Stokes2cSubProblem& stokes2cProblem() const + { return this->sdProblem1(); } + + TwoPTwoCSubProblem& twoPtwoCProblem() + { return this->sdProblem2(); } + const TwoPTwoCSubProblem& twoPtwoCProblem() const + { return this->sdProblem2(); } + +private: + typename MDGrid::SubDomainType stokes2c_; + typename MDGrid::SubDomainType twoPtwoC_; + + unsigned counter_; + unsigned freqRestart_; + unsigned freqOutput_; + unsigned freqFluxOutput_; + unsigned freqVaporFluxOutput_; + + Scalar interface_; + Scalar noDarcyX_; + Scalar episodeLength_; + Scalar initializationTime_; + + template <int numEq> + struct InterfaceFluxes + { + unsigned count; + unsigned interfaceVertex; + unsigned globalIdx; + Scalar xCoord; + Scalar yCoord; + Dune::FieldVector<Scalar, numEq> defect; + + InterfaceFluxes() + { + count = 0; + interfaceVertex = 0; + globalIdx = 0; + xCoord = 0.0; + yCoord = 0.0; + defect = 0.0; + } + }; + std::ofstream fluxFile_; +}; + +} //end namespace + +#endif diff --git a/test/multidomain/2cstokes2p2c/2cstokes2p2cspatialparams.hh b/test/multidomain/2cstokes2p2c/2cstokes2p2cspatialparams.hh new file mode 100644 index 0000000000..2707b898e9 --- /dev/null +++ b/test/multidomain/2cstokes2p2c/2cstokes2p2cspatialparams.hh @@ -0,0 +1,489 @@ +// -*- 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 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +#ifndef DUMUX_TWOCSTOKES_2P2C_SPATIALPARAMS_HH +#define DUMUX_TWOCSTOKES_2P2C_SPATIALPARAMS_HH + +#include <dune/grid/io/file/vtk/common.hh> + +//#include <dumux/material/spatialparameters/gstatrandompermeability.hh> +#include <dumux/material/spatialparams/implicitspatialparams.hh> +#include <dumux/material/fluidmatrixinteractions/2p/linearmaterial.hh> +#include <dumux/material/fluidmatrixinteractions/2p/regularizedvangenuchten.hh> +//#include <dumux/material/fluidmatrixinteractions/2p/linearizedregvangenuchtenparams.hh> +//#include <dumux/material/fluidmatrixinteractions/2p/regularizedbrookscorey.hh> +#include <dumux/material/fluidmatrixinteractions/2p/efftoabslaw.hh> +//#include <dumux/io/plotfluidmatrixlaw.hh> + +namespace Dumux +{ + +//forward declaration +template<class TypeTag> +class TwoCStokesTwoPTwoCSpatialParams; + +namespace Properties +{ +// The spatial parameters TypeTag +NEW_TYPE_TAG(TwoCStokesTwoPTwoCSpatialParams); + +// Set the spatial parameters +SET_TYPE_PROP(TwoCStokesTwoPTwoCSpatialParams, SpatialParams, + TwoCStokesTwoPTwoCSpatialParams<TypeTag>); + +// Set the material Law +SET_PROP(TwoCStokesTwoPTwoCSpatialParams, MaterialLaw) +{ +private: + // define the material law which is parameterized by effective + // saturations + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; +// typedef RegularizedBrooksCorey<Scalar> EffMaterialLaw; +// typedef RegularizedVanGenuchten<Scalar, LinearizedRegVanGenuchtenParams<Scalar, TypeTag> > EffMaterialLaw; + typedef RegularizedVanGenuchten<Scalar> EffMaterialLaw; +public: + // define the material law parameterized by absolute saturations + typedef EffToAbsLaw<EffMaterialLaw> type; +}; +} + + +/** \todo Please doc me! */ +template<class TypeTag> +class TwoCStokesTwoPTwoCSpatialParams : public ImplicitSpatialParams<TypeTag> +{ + typedef ImplicitSpatialParams<TypeTag> ParentType; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GridView::ctype CoordScalar; + + enum { + dim=GridView::dimension, + dimWorld=GridView::dimensionworld + }; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + enum { + wPhaseIdx = FluidSystem::wPhaseIdx, + nPhaseIdx = FluidSystem::nPhaseIdx + }; + + typedef Dune::FieldVector<CoordScalar,dim> LocalPosition; + typedef Dune::FieldVector<CoordScalar,dimWorld> GlobalPosition; + typedef Dune::FieldVector<CoordScalar,dimWorld> DimVector; + + typedef typename GridView::IndexSet IndexSet; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + typedef typename GridView::template Codim<0>::Entity Element; + + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + + typedef std::vector<Scalar> PermeabilityType; + typedef typename GET_PROP(TypeTag, ParameterTree) ParameterTree; + typedef typename GridView::template Codim<dim>::Iterator VertexIterator; + +public: + typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; + typedef typename MaterialLaw::Params MaterialLawParams; + typedef std::vector<MaterialLawParams> MaterialLawParamsVector; + + /*! + * \brief Spatial parameters for the coupled 2cstokes and 2p2c test + * + * \param gridView The GridView which is used by the problem + */ + TwoCStokesTwoPTwoCSpatialParams(const GridView& gridView) + : ParentType(gridView), +// permeability_(gridView.size(dim), 0.0), + indexSet_(gridView.indexSet()) + { + try + { + soilType_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, int, SpatialParams, SoilType); + xMaterialInterface_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams, MaterialInterfaceX); + + // porosities + coarsePorosity_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Coarse, Porosity1); + mediumPorosity_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Medium, Porosity2); + finePorosity_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Fine, Porosity3); + + // intrinsic permeabilities + coarsePermeability_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Coarse, Permeability1); + mediumPermeability_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Medium, Permeability2); + finePermeability_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Fine, Permeability3); + + // thermal conductivity of the solid material + coarseLambdaSolid_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Coarse, LambdaSolid1); + mediumLambdaSolid_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Medium, LambdaSolid2); + fineLambdaSolid_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Fine, LambdaSolid3); + + if (soilType_ != 0) + { + // residual saturations + coarseParams_.setSwr(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Coarse, Swr1)); + coarseParams_.setSnr(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Coarse, Snr1)); + mediumParams_.setSwr(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Medium, Swr2)); + mediumParams_.setSnr(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Medium, Snr2)); + fineParams_.setSwr(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Fine, Swr3)); + fineParams_.setSnr(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Fine, Snr3)); + + // parameters for the vanGenuchten law + coarseParams_.setVgAlpha(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Coarse, VgAlpha1)); + coarseParams_.setVgn(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Coarse, VgN1)); + mediumParams_.setVgAlpha(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Medium, VgAlpha2)); + mediumParams_.setVgn(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Medium, VgN2)); + fineParams_.setVgAlpha(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Fine, VgAlpha3)); + fineParams_.setVgn(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Fine, VgN3)); + } + } + catch (Dumux::ParameterException &e) { + std::cerr << e << ". Abort!\n"; + exit(1) ; + } + catch (...) { + std::cerr << "Unknown exception thrown!\n"; + exit(1); + } + } + + ~TwoCStokesTwoPTwoCSpatialParams() + {} + + /*! + * \brief The intrinsic permeability of the porous medium. + * + * \param element The current finite element + * \param fvGeometry The current finite volume geometry of the element + * \param scvIdx The index sub-control volume face where the + * intrinsic velocity ought to be calculated. + */ + const Scalar intrinsicPermeability(const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) const + { +// // heterogeneous parameter field computed with GSTAT +// if (soilType_ == 0) +// return permeability_[indexSet_.index( +// *(element.template subEntity<dim> (scvIdx)))]; +// // soil can be chosen by setting the parameter soiltype accordingly +// else +// { + const GlobalPosition &globalPos = element.geometry().corner(scvIdx); + + if (checkSoilType(globalPos) == 1) + return coarsePermeability_; + if (checkSoilType(globalPos) == 3) + return finePermeability_; + else + return mediumPermeability_; +// } + } + + //! \copydoc Dumux::ImplicitSpatialParamsOneP::porosity() + Scalar porosity(const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) const + { + const GlobalPosition &globalPos = element.geometry().corner(scvIdx); + + if (checkSoilType(globalPos) == 1) + return coarsePorosity_; + if (checkSoilType(globalPos) == 3) + return finePorosity_; + else + return mediumPorosity_; + } + + + //! \copydoc Dumux::ImplicitSpatialParams::materialLawParams() + const MaterialLawParams& materialLawParams(const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) const + { +// if (soilType_ == 0) +// return materialLawParams_[indexSet_.index( +// *(element.template subEntity<dim> (scvIdx)))]; +// else +// { + const GlobalPosition &globalPos = element.geometry().corner(scvIdx); + if (checkSoilType(globalPos)==1) + return coarseParams_; + if (checkSoilType(globalPos)==3) + return fineParams_; + else + return mediumParams_; +// } + } + + + /*! + * \brief Returns the heat capacity \f$[J/m^3 K]\f$ of the rock matrix. + * + * This is only required for non-isothermal models. + * + * \param element The finite element + * \param fvGeometry The finite volume geometry + * \param scvIdx The local index of the sub-control volume where + * the heat capacity needs to be defined + */ + Scalar heatCapacity(const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) const + { + return + 790 // specific heat capacity of granite [J / (kg K)] + * 2700 // density of granite [kg/m^3] + * (1 - porosity(element, fvGeometry, scvIdx)); + } + + /*! + * \brief docme + * + * \param element The finite element + * \param fvGeometry The finite-volume geometry + * \param scvIdx The local subcontrolvolume index + * + */ + Scalar thermalConductivitySolid(const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) const + { + const GlobalPosition &pos = element.geometry().corner(scvIdx); + + if (checkSoilType(pos) == 1) + return coarseLambdaSolid_; + if (checkSoilType(pos) == 3) + return fineLambdaSolid_; + else + return mediumLambdaSolid_; + } + + /*! + * \brief docme + * + * \param global Pos The global position + * + */ + const unsigned checkSoilType(const GlobalPosition &globalPos) const + { + // one soil, which can be chosen as runtime parameter: + // 1: coarse + // 2: medium + // 3: fine + return soilType_; + } + + /*! + * \brief This method allows the generation of a statistical field for the soil parameters + * using GStat + * + * \param gridView The GridView which is used by the problem + * + */ + void loadIntrinsicPermeability(const GridView& gridView) + { +// // only load random field, if soilType is set to 0 +// if (soilType_ != 0) +// return; +// +// const unsigned size = gridView.size(dim); +// permeability_.resize(size, 0.0); +// materialLawParams_.resize(size); +// +// bool create = true; +// if (ParameterTree::tree().hasKey("SpatialParams.GenerateNewPermeability")) +// create = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, bool , SpatialParams, GenerateNewPermeability); +// +// std::string gStatControlFileName("gstatControl_2D.txt"); +// if (ParameterTree::tree().hasKey("SpatialParams.GStatControlFileName")) +// gStatControlFileName = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, std::string, SpatialParams, +// GStatControlFileName); +// +// +// std::string gStatInputFileName("gstatInput.txt"); +// if (ParameterTree::tree().hasKey("SpatialParams.GStatInputFileName")) +// gStatInputFileName = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, std::string, SpatialParams, +// GStatInputFileName); +// +// std::string permeabilityFileName("permeab.dat"); +// if (ParameterTree::tree().hasKey("SpatialParams.PermeabilityInputFileName")) +// permeabilityFileName = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, std::string, SpatialParams, +// PermeabilityInputFileName); +// +// // create random permeability object +// GstatRandomPermeability<GridView, Scalar> randomPermeability(gridView, +// create, +// permeabilityFileName.c_str(), +// gStatControlFileName.c_str(), +// gStatInputFileName.c_str()); +// +// Scalar totalVolume = 0; +// Scalar meanPermeability = 0; +// +//// for (unsigned n=0; n<numVertices; ++n) +//// { +//// //TODO the following can be done in a loop +//// } +// // iterate over elements +// ElementIterator eItEnd = gridView.template end<0> (); +// for (ElementIterator eIt = gridView.template begin<0> (); eIt +// != eItEnd; ++eIt) +// { +// Scalar perm = randomPermeability.K(*eIt)[0][0]; +// permeability_[indexSet_.index(*(eIt->template subEntity<dim> (3)))] +// = perm; +// +// Scalar volume = eIt->geometry().volume(); +// totalVolume += volume; +// +// meanPermeability += volume / perm; +// } +// +// for (ElementIterator eIt = gridView.template begin<0> (); eIt +// != eItEnd; ++eIt) +// { +// Scalar perm = randomPermeability.K(*eIt)[0][0]; +// permeability_[indexSet_.index(*(eIt->template subEntity<dim> (1)))] +// = perm; +// +// Scalar volume = eIt->geometry().volume(); +// totalVolume += volume; +// +// meanPermeability += volume / perm; +// } +// +// for (ElementIterator eIt = gridView.template begin<0> (); eIt +// != eItEnd; ++eIt) +// { +// Scalar perm = randomPermeability.K(*eIt)[0][0]; +// permeability_[indexSet_.index(*(eIt->template subEntity<dim> (2)))] +// = perm; +// +// Scalar volume = eIt->geometry().volume(); +// totalVolume += volume; +// +// meanPermeability += volume / perm; +// } +// +// for (ElementIterator eIt = gridView.template begin<0> (); eIt +// != eItEnd; ++eIt) +// { +// Scalar perm = randomPermeability.K(*eIt)[0][0]; +// permeability_[indexSet_.index(*(eIt->template subEntity<dim> (0)))] +// = perm; +// +// Scalar volume = eIt->geometry().volume(); +// totalVolume += volume; +// +// meanPermeability += volume / perm; +// } +// +// meanPermeability /= totalVolume; +// meanPermeability = 1.0 / meanPermeability; +// +// //Iterate over elements +// VertexIterator vItEnd = gridView.template end<dim> (); +// for (VertexIterator vIt = gridView.template begin<dim> (); vIt +// != vItEnd; ++vIt) +// { +// int globalIdx = indexSet_.index(*vIt); +// +// Scalar elementEntryPressure = 1./GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Medium, VgAlpha2); +// elementEntryPressure *= sqrt(meanPermeability / permeability_[globalIdx]); +// +// materialLawParams_[globalIdx].setVgAlpha(1./elementEntryPressure); +// materialLawParams_[globalIdx].setVgn(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Medium, VgN2)); +// materialLawParams_[globalIdx].setSwr(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Medium, Swr2)); +// materialLawParams_[globalIdx].setSnr(GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Medium, Snr2)); +// } +// +// // if (create) +// // { +// Dune::VTKWriter<GridView> vtkwriter(gridView); +// vtkwriter.addVertexData(permeability_, "absolute permeability"); +// PermeabilityType logPerm(size); +// for (unsigned i = 0; i < size; i++) +// logPerm[i] = log10(permeability_[i]); +// vtkwriter.addVertexData(logPerm, "logarithm of permeability"); +// vtkwriter.write("permeability", Dune::VTK::OutputType::ascii); + } + + // + /*! + * \brief This is called from the coupled problem + * and creates a gnuplot output of the Pc-Sw curve -- currently commented + */ + void plotMaterialLaw() + { +// if (soilType_ == 0) +// { +// std::cout << "Material law plot not possible for heterogeneous media!\n"; +// return; +// } +// if (GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, bool, SpatialParams.Coarse, PlotMaterialLaw1)) +// { +// PlotFluidMatrixLaw<MaterialLaw> coarseFluidMatrixLaw_; +// coarseFluidMatrixLaw_.plotpC(coarseParams_, +// GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Coarse, Swr1), +// 1.0 - GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Coarse, Snr1)); +// } +// if (GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, bool, SpatialParams.Medium, PlotMaterialLaw2)) +// { +// PlotFluidMatrixLaw<MaterialLaw> mediumFluidMatrixLaw_; +// mediumFluidMatrixLaw_.plotpC(mediumParams_, +// GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Medium, Swr2), +// 1.0 - GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Medium, Snr2)); +// } +// if (GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, bool, SpatialParams.Fine, PlotMaterialLaw3)) +// { +// PlotFluidMatrixLaw<MaterialLaw> fineFluidMatrixLaw_; +// fineFluidMatrixLaw_.plotpC(fineParams_, +// GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Fine, Swr3), +// 1.0 - GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams.Fine, Snr3)); +// } + } + +private: + unsigned soilType_; + + Scalar coarsePermeability_; + Scalar mediumPermeability_; + Scalar finePermeability_; + + Scalar coarsePorosity_; + Scalar mediumPorosity_; + Scalar finePorosity_; + + Scalar coarseLambdaSolid_; + Scalar mediumLambdaSolid_; + Scalar fineLambdaSolid_; + + Scalar xMaterialInterface_; +// MaterialLawParamsVector materialLawParams_; +// PermeabilityType permeability_; + const IndexSet& indexSet_; + + MaterialLawParams coarseParams_; + MaterialLawParams mediumParams_; + MaterialLawParams fineParams_; +}; + +} // end namespace + +#endif // DUMUX_TWOCSTOKES_2P2C_SPATIALPARAMS_HH diff --git a/test/multidomain/2cstokes2p2c/2p2csubproblem.hh b/test/multidomain/2cstokes2p2c/2p2csubproblem.hh new file mode 100644 index 0000000000..de6edfcaff --- /dev/null +++ b/test/multidomain/2cstokes2p2c/2p2csubproblem.hh @@ -0,0 +1,459 @@ +// -*- 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 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Porous-medium subproblem with coupling at the top boundary. + */ +#ifndef DUMUX_2P2C_SUBPROBLEM_HH +#define DUMUX_2P2C_SUBPROBLEM_HH + +#include <dumux/implicit/common/implicitporousmediaproblem.hh> +#include <dumux/multidomain/couplinglocalresiduals/2p2ccouplinglocalresidual.hh> +#include <dumux/multidomain/common/subdomainpropertydefaults.hh> +#include <dumux/multidomain/common/multidomainlocaloperator.hh> + +#include "2cstokes2p2cproblem.hh" + +namespace Dumux +{ +template <class TypeTag> +class TwoPTwoCSubProblem; + +namespace Properties +{ +NEW_TYPE_TAG(TwoPTwoCSubProblem, + INHERITS_FROM(BoxTwoPTwoC, SubDomain, TwoCStokesTwoPTwoCSpatialParams)); + +// Set the problem property +SET_TYPE_PROP(TwoPTwoCSubProblem, Problem, TwoPTwoCSubProblem<TTAG(TwoPTwoCSubProblem)>); + +// Set the local residual extended for the coupling +SET_TYPE_PROP(TwoPTwoCSubProblem, LocalResidual, TwoPTwoCCouplingLocalResidual<TypeTag>); + +// choose pn and Sw as primary variables +SET_INT_PROP(TwoPTwoCSubProblem, Formulation, TwoPTwoCFormulation::pnsw); + + +// the gas component balance (air) is replaced by the total mass balance +SET_PROP(TwoPTwoCSubProblem, ReplaceCompEqIdx) +{ + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + static const int value = Indices::contiNEqIdx; +}; + +// set the constraints for multidomain / pdelab // not required here +SET_TYPE_PROP(TwoPTwoCSubProblem, Constraints, + Dune::PDELab::NonoverlappingConformingDirichletConstraints); + +// set the grid operator +SET_PROP(TwoPTwoCSubProblem, GridOperator) +{ + private: + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, ConstraintsTrafo) ConstraintsTrafo; + typedef typename GET_PROP_TYPE(TypeTag, GridFunctionSpace) GridFunctionSpace; + typedef Dumux::PDELab::MultiDomainLocalOperator<TypeTag> LocalOperator; + + enum{numEq = GET_PROP_VALUE(TypeTag, NumEq)}; + + public: + typedef Dune::PDELab::GridOperator<GridFunctionSpace, + GridFunctionSpace, LocalOperator, + Dune::PDELab::ISTLBCRSMatrixBackend<numEq, numEq>, + Scalar, Scalar, Scalar, + ConstraintsTrafo, + ConstraintsTrafo, + true> type; +}; + +// the fluidsystem is set in the coupled problem +SET_PROP(TwoPTwoCSubProblem, FluidSystem) +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, MultiDomainTypeTag) CoupledTypeTag; + typedef typename GET_PROP_TYPE(CoupledTypeTag, FluidSystem) FluidSystem; +public: + typedef FluidSystem type; +}; + + +// use formulation based on mass fractions +SET_BOOL_PROP(TwoPTwoCSubProblem, UseMoles, false); + +// enable/disable velocity output +SET_BOOL_PROP(TwoPTwoCSubProblem, VtkAddVelocity, true); + +// Enable gravity +SET_BOOL_PROP(TwoPTwoCSubProblem, ProblemEnableGravity, true); +} + +template <class TypeTag = TTAG(TwoPTwoCSubProblem) > +class TwoPTwoCSubProblem : public ImplicitPorousMediaProblem<TypeTag> +{ + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GridView::Grid Grid; + + typedef TwoPTwoCSubProblem<TypeTag> ThisType; + typedef ImplicitPorousMediaProblem<TypeTag> ParentType; + + // copy some indices for convenience + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + + // the type tag of the coupled problem + typedef typename GET_PROP_TYPE(TypeTag, MultiDomainTypeTag) CoupledTypeTag; + + enum { numEq = GET_PROP_VALUE(TypeTag, NumEq)}; + enum { // the equation indices + contiTotalMassIdx = Indices::contiNEqIdx, + contiWEqIdx = Indices::contiWEqIdx, + }; + enum { // the indices of the primary variables + pressureIdx = Indices::pressureIdx, + switchIdx = Indices::switchIdx, + }; + enum { // the indices for the phase presence + wPhaseOnly = Indices::wPhaseOnly, + nPhaseOnly = Indices::nPhaseOnly, + bothPhases = Indices::bothPhases + }; + enum { // grid and world dimension + dim = GridView::dimension, + dimWorld = GridView::dimensionworld + }; + + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, TimeManager) TimeManager; + + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<dim>::Entity Vertex; + typedef typename GridView::Intersection Intersection; + + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + + typedef Dune::FieldVector<Scalar, dimWorld> GlobalPosition; + + /*! + * \brief docme + * + * \param timeManager The TimeManager which is used by the simulation + * \param gridView The simulation's idea about physical space + * + */ +public: + TwoPTwoCSubProblem(TimeManager &timeManager, const GridView gridView) + : ParentType(timeManager, gridView) + { + try + { + Scalar noDarcyX = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Problem, NoDarcyX); + Scalar xMin = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Grid, XMin); + + bboxMin_[0] = std::max(xMin,noDarcyX); + bboxMax_[0] = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Grid, XMax); + + bboxMin_[1] = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Grid, YMin); + bboxMax_[1] = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Grid, InterfacePos); + + runUpDistanceX_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Problem, RunUpDistanceX); // first part of the interface without coupling + xMaterialInterface_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams, MaterialInterfaceX); + initializationTime_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, TimeManager, InitTime); + + refTemperature_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, PorousMedium, RefTemperaturePM); + refTemperatureFF_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, FreeFlow, RefTemperature); + refPressure_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, PorousMedium, RefPressurePM); + initialSw1_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, PorousMedium, InitialSw1); + initialSw2_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, PorousMedium, InitialSw2); + + freqMassOutput_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, int, Output, FreqMassOutput); + + storageLastTimestep_ = Scalar(0); + lastMassOutputTime_ = Scalar(0); + + outfile.open("evaporation.out"); + outfile << "time; evaporationRate; massH2O" << std::endl; + } + catch (Dumux::ParameterException &e) { + std::cerr << e << ". Abort!\n"; + exit(1) ; + } + catch (...) { + std::cerr << "Unknown exception thrown!\n"; + exit(1); + } + } + + ~TwoPTwoCSubProblem() + { + outfile.close(); + } + + /*! + * \name Problem parameters + */ + // \{ + + //! \copydoc Dumux::ImplicitProblem::name() + const std::string &name() const + { return GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, std::string, Vtk, NamePM); } + + //! \copydoc Dumux::ImplicitProblem::init() + void init() + { + // set the initial condition of the model + ParentType::init(); + + this->model().globalStorage(storageLastTimestep_); + + const int blModel = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, int, FreeFlow, UseBoundaryLayerModel); + std::cout << "Using boundary layer model " << blModel << std::endl; + } + + //! \copydoc Dumux::ImplicitPorousMediaProblem::temperatureAtPos() + Scalar temperatureAtPos(const GlobalPosition &globalPos) const + { + return refTemperature_; + }; + + // \} + + //! \copydoc Dumux::ImplicitProblem::boundaryTypes() + void boundaryTypes(BoundaryTypes &values, const Vertex &vertex) const + { + const GlobalPosition globalPos = vertex.geometry().center(); + Scalar time = this->timeManager().time(); + + values.setAllNeumann(); + + if (onUpperBoundary_(globalPos)) + { + if (time > initializationTime_) + { + if (globalPos[0] > runUpDistanceX_ - eps_) + values.setAllCouplingInflow(); + else + values.setAllNeumann(); + } + else + { +// values.setAllDirichlet(); + values.setAllNeumann(); + } + } + } + + //! \copydoc Dumux::ImplicitProblem::dirichlet() + void dirichlet(PrimaryVariables &values, const Vertex &vertex) const + { + const GlobalPosition globalPos = vertex.geometry().center(); + + initial_(values, globalPos); + } + + //! \copydoc Dumux::ImplicitProblem::neumann() + void neumann(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvGeometry, + const Intersection &is, + const int scvIdx, + const int boundaryFaceIdx) const + { + values = Scalar(0); + } + + // \} + + /*! + * \name Volume terms + */ + // \{ + + //! \copydoc Dumux::ImplicitProblem::source() + void source(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) const + { + values = Scalar(0); + } + + //! \copydoc Dumux::ImplicitProblem::initial() + void initial(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) const + { + const GlobalPosition &globalPos = element.geometry().corner(scvIdx); + + values = Scalar(0); + + initial_(values, globalPos); + } + + /*! + * \brief Return the initial phase state inside a control volume. + * + * \param vert The vertex + * \param globalIdx The index of the global vertex + * \param globalPos The global position + */ + int initialPhasePresence(const Vertex &vert, + const int &globalIdx, + const GlobalPosition &globalPos) const + { + return bothPhases; + } + + //! \copydoc Dumux::ImplicitProblem::postTimeStep() + void postTimeStep() + { + // Calculate masses + PrimaryVariables storage; + + this->model().globalStorage(storage); + const Scalar time = this->timeManager().time() + this->timeManager().timeStepSize(); + + // Write mass balance information for rank 0 + if (this->gridView().comm().rank() == 0) + { + if (this->timeManager().timeStepIndex() % freqMassOutput_ == 0 + || this->timeManager().episodeWillBeOver()) + { + PrimaryVariables evaporationRate(0.); + evaporationRate = storageLastTimestep_ - storage; + + assert(time - lastMassOutputTime_ != 0); + evaporationRate /= (time - lastMassOutputTime_); + // 2d: interface length has to be accounted for + // in order to obtain kg/m²s + evaporationRate /= (bboxMax_[0]-bboxMin_[0]); + + std::cout << "TotalMass: " << storage[contiTotalMassIdx] + << " MassH2O: " << storage[contiWEqIdx] + << " EvaporationRate: " << evaporationRate[contiWEqIdx] + << " Time: " << time <<std::endl; + if (this->timeManager().time() != 0.) + outfile << time << + "; " << evaporationRate[contiTotalMassIdx] << + "; " << evaporationRate[contiWEqIdx] << + std::endl; + + storageLastTimestep_ = storage; + lastMassOutputTime_ = time; + } + } + } + + + /*! + * \brief Determine if we are on a corner of the grid + * + * \param globalPos The global position + * + */ + bool isCornerPoint(const GlobalPosition &globalPos) + { + return ((onLeftBoundary_(globalPos) && onLowerBoundary_(globalPos)) || + (onLeftBoundary_(globalPos) && onUpperBoundary_(globalPos)) || + (onRightBoundary_(globalPos) && onLowerBoundary_(globalPos)) || + (onRightBoundary_(globalPos) && onUpperBoundary_(globalPos))); + } + + // required in case of mortar coupling + // otherwise it should return false + /*! + * \brief docme + * + * \param globalPos The global position + * + */ + bool isInterfaceCornerPoint(const GlobalPosition &globalPos) const + { return false; } + + // \} +private: + // internal method for the initial condition (reused for the + // dirichlet conditions!) + + void initial_(PrimaryVariables &values, + const GlobalPosition &globalPos) const + { + values[pressureIdx] = refPressure_ + + 1000.*this->gravity()[1]*(globalPos[1]-bboxMax_[1]); + if (globalPos[0] < xMaterialInterface_) + values[switchIdx] = initialSw1_; + else + values[switchIdx] = initialSw2_; + +// if (onUpperBoundary_(globalPos) && onLeftBoundary_(globalPos)) +// { + // values[switchIdx] = 0.99e-4; +// } + + // values[pressureIdx] = 1e5*(2.0 - globalPos[0]); + // if (onLeftBoundary_(globalPos)) + // values[switchIdx] = 0.9e-4; + // else + // values[switchIdx] = 1e-4; + } + + bool onLeftBoundary_(const GlobalPosition &globalPos) const + { return globalPos[0] < bboxMin_[0] + eps_; } + + bool onRightBoundary_(const GlobalPosition &globalPos) const + { return globalPos[0] > bboxMax_[0] - eps_; } + + bool onLowerBoundary_(const GlobalPosition &globalPos) const + { return globalPos[1] < bboxMin_[1] + eps_; } + + bool onUpperBoundary_(const GlobalPosition &globalPos) const + { return globalPos[1] > bboxMax_[1] - eps_; } + + bool onBoundary_(const GlobalPosition &globalPos) const + { + return (onLeftBoundary_(globalPos) || onRightBoundary_(globalPos) + || onLowerBoundary_(globalPos) || onUpperBoundary_(globalPos)); + } + + static constexpr Scalar eps_ = 1e-8; + GlobalPosition bboxMin_; + GlobalPosition bboxMax_; + Scalar xMaterialInterface_; + + int freqMassOutput_; + + PrimaryVariables storageLastTimestep_; + Scalar lastMassOutputTime_; + + Scalar refTemperature_; + Scalar refTemperatureFF_; + Scalar refPressure_; + Scalar initialSw1_; + Scalar initialSw2_; + + Scalar runUpDistanceX_; + Scalar initializationTime_; + std::ofstream outfile; +}; +} //end namespace + +#endif // DUMUX_2P2C_SUBPROBLEM_HH diff --git a/test/multidomain/2cstokes2p2c/Makefile.am b/test/multidomain/2cstokes2p2c/Makefile.am new file mode 100644 index 0000000000..ac442bfc93 --- /dev/null +++ b/test/multidomain/2cstokes2p2c/Makefile.am @@ -0,0 +1,5 @@ +# tests where program to build and program to run are equal +check_PROGRAMS = test_2cstokes2p2c +test_2cstokes2p2c_SOURCES = test_2cstokes2p2c.cc + +include $(top_srcdir)/am/global-rules diff --git a/test/multidomain/2cstokes2p2c/grids/interfacedomain.dgf b/test/multidomain/2cstokes2p2c/grids/interfacedomain.dgf new file mode 100644 index 0000000000..b0290172cb --- /dev/null +++ b/test/multidomain/2cstokes2p2c/grids/interfacedomain.dgf @@ -0,0 +1,15 @@ +DGF +Interval +0 0 % first corner +0.25 0.5 % second corner +1 1 % 1 cells in x and 1 in y direction +# + +Cube +0 1 2 3 +# + +BOUNDARYDOMAIN +default 1 % all boundaries have id 1 +#BOUNDARYDOMAIN +# unitcube.dgf diff --git a/test/multidomain/2cstokes2p2c/grids/test_2cstokes2p2c.dgf b/test/multidomain/2cstokes2p2c/grids/test_2cstokes2p2c.dgf new file mode 100644 index 0000000000..3085c34981 --- /dev/null +++ b/test/multidomain/2cstokes2p2c/grids/test_2cstokes2p2c.dgf @@ -0,0 +1,17 @@ +DGF +Interval +0 0 % first corner +0.25 0.5 % second corner +32 64 % cells in x and y direction +# +GridParameter +overlap 0 +periodic +closure green +copies none +heapsize 500 + +BOUNDARYDOMAIN +default 1 % all boundaries have id 1 +#BOUNDARYDOMAIN +# unitcube.dgf diff --git a/test/multidomain/2cstokes2p2c/stokes2csubproblem.hh b/test/multidomain/2cstokes2p2c/stokes2csubproblem.hh new file mode 100644 index 0000000000..f523d7537f --- /dev/null +++ b/test/multidomain/2cstokes2p2c/stokes2csubproblem.hh @@ -0,0 +1,598 @@ +// -*- 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 2 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 Stokes2cProblems + * \brief Definition of an isothermal compositional Stokes problem + */ +#ifndef DUMUX_STOKES2C_SUBPROBLEM_HH +#define DUMUX_STOKES2C_SUBPROBLEM_HH + +#include <dumux/freeflow/stokesnc/stokesncmodel.hh> +#include <dumux/multidomain/couplinglocalresiduals/stokesnccouplinglocalresidual.hh> +#include <dumux/multidomain/common/subdomainpropertydefaults.hh> + +namespace Dumux +{ + +template <class TypeTag> +class Stokes2cSubProblem; + +////////// +// Specify the properties for the Stokes problem +////////// +namespace Properties +{ +NEW_TYPE_TAG(Stokes2cSubProblem, + INHERITS_FROM(BoxStokesnc, SubDomain, TwoCStokesTwoPTwoCSpatialParams)); + +// Set the problem property +SET_TYPE_PROP(Stokes2cSubProblem, Problem, Dumux::Stokes2cSubProblem<TypeTag>); + +/*! + * \brief Set the property for the material parameters by extracting + * it from the material law. + */ +SET_PROP(Stokes2cSubProblem, MaterialLawParams) +{ + private: + typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; + public: + typedef typename MaterialLaw::Params type; +}; + +//! Use the Stokes2cCouplingLocalResidual for the computation of the local residual in the Stokes domain +SET_TYPE_PROP(Stokes2cSubProblem, + LocalResidual, + StokesncCouplingLocalResidual<TypeTag>); + +SET_TYPE_PROP(Stokes2cSubProblem, Constraints, + Dune::PDELab::NonoverlappingConformingDirichletConstraints); + +SET_PROP(Stokes2cSubProblem, GridOperator) +{ + private: + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, ConstraintsTrafo) ConstraintsTrafo; + typedef typename GET_PROP_TYPE(TypeTag, GridFunctionSpace) GridFunctionSpace; + typedef Dumux::PDELab::MultiDomainLocalOperator<TypeTag> LocalOperator; + + enum{numEq = GET_PROP_VALUE(TypeTag, NumEq)}; + public: + typedef Dune::PDELab::GridOperator<GridFunctionSpace, + GridFunctionSpace, LocalOperator, + Dune::PDELab::ISTLBCRSMatrixBackend<numEq, numEq>, + Scalar, Scalar, Scalar, + ConstraintsTrafo, ConstraintsTrafo, + true> type; +}; + +SET_PROP(Stokes2cSubProblem, FluidSystem) +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, MultiDomainTypeTag) CoupledTypeTag; + typedef typename GET_PROP_TYPE(CoupledTypeTag, FluidSystem) FluidSystem; +public: + typedef FluidSystem type; +}; + +//! Set Scalar to type long double for higher accuracy +SET_TYPE_PROP(BoxStokes, Scalar, double); +//SET_TYPE_PROP(BoxStokes, Scalar, long double); + +// use formulation based on mass fractions +SET_BOOL_PROP(Stokes2cSubProblem, UseMoles, false); + +// Disable gravity +SET_BOOL_PROP(Stokes2cSubProblem, ProblemEnableGravity, false); + +// switch inertia term on or off +SET_BOOL_PROP(Stokes2cSubProblem, EnableNavierStokes, false); +} + +/*! + * \ingroup Stokes2cBoxProblems + * \brief Stokes2c problem with air flowing + * from the left to the right. + * + * The stokes subdomain is sized 1m times 1m. The boundary conditions for the momentum balances + * are all set to Dirichlet. The mass balance receives + * outflow bcs, which are replaced in the localresidual by the sum + * of the two momentum balances. In the middle of the right boundary, + * one vertex receives Dirichlet bcs, to set the pressure level. + * + * This sub problem uses the \ref Stokes2cModel. It is part of the 2cstokes2p2c model and + * is combined with the 2p2csubproblem for the Darcy domain. + * + */ +template <class TypeTag> +class Stokes2cSubProblem : public StokesProblem<TypeTag> +{ + typedef Stokes2cSubProblem<TypeTag> ThisType; + typedef StokesProblem<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, TimeManager) TimeManager; + + // soil parameters for beavers & joseph + typedef typename GET_PROP_TYPE(TypeTag, SpatialParams) SpatialParams; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum { + + // Number of equations and grid dimension + numEq = GET_PROP_VALUE(TypeTag, NumEq), + dim = GridView::dimension, + + // copy some indices for convenience + massBalanceIdx = Indices::massBalanceIdx, + + momentumXIdx = Indices::momentumXIdx, //!< Index of the x-component of the momentum balance + momentumYIdx = Indices::momentumYIdx, //!< Index of the y-component of the momentum balance + momentumZIdx = Indices::momentumZIdx, //!< Index of the z-component of the momentum balance + + transportEqIdx = Indices::transportEqIdx //!< Index of the transport equation (massfraction) + }; + enum { // primary variable indices + pressureIdx = Indices::pressureIdx, + velocityXIdx = Indices::velocityXIdx, + velocityYIdx = Indices::velocityYIdx, + velocityZIdx = Indices::velocityZIdx, + massOrMoleFracIdx = Indices::massOrMoleFracIdx + }; + enum { phaseIdx = Indices::phaseIdx }; + enum { numComponents = Indices::numComponents }; +// enum { numComponents = GET_PROP_VALUE(TypeTag, NumComponents) }; + enum { + transportCompIdx = Indices::transportCompIdx, //!< water component index + phaseCompIdx = Indices::phaseCompIdx //!< air component index + }; + + + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; + + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<dim>::Entity Vertex; + typedef typename GridView::ctype CoordScalar; + typedef typename GridView::Intersection Intersection; + + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, FluidState) FluidState; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldVector<CoordScalar, dim> GlobalPosition; + + +public: + /*! + * \brief docme + * + * \param timeManager The time manager + * \param gridView The grid view + * + */ + Stokes2cSubProblem(TimeManager &timeManager, const GridView gridView) + : ParentType(timeManager, gridView), + spatialParams_(gridView) + { + try + { + bboxMin_[0] = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Grid, XMin); + bboxMax_[0] = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Grid, XMax); + bboxMin_[1] = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Grid, InterfacePos); + bboxMax_[1] = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Grid, YMax); + + refTemperature_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, FreeFlow, RefTemperature); + refPressure_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, FreeFlow, RefPressure); + refMassfrac_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, FreeFlow, RefMassfrac); + vxMax_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, FreeFlow, VxMax); + bjSlipVel_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, FreeFlow, BeaversJosephSlipVel); + sinusVelVar_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, FreeFlow, SinusVelVar); + + xMaterialInterface_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams, MaterialInterfaceX); + runUpDistanceX_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Problem, RunUpDistanceX); // first part of the interface without coupling + initializationTime_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, TimeManager, InitTime); + + alphaBJ_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, SpatialParams, AlphaBJ); + } + catch (Dumux::ParameterException &e) { + std::cerr << e << ". Abort!\n"; + exit(1) ; + } + catch (...) { + std::cerr << "Unknown exception thrown!\n"; + exit(1); + } + } + + /*! + * \name Problem parameters + */ + // \{ + + //! \copydoc Dumux::ImplicitProblem::name() + const std::string &name() const + { return GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, std::string, Vtk, NameFF); } + + /*! + * \brief Returns the temperature within the domain. + * + * This problem assumes a temperature of 10 degrees Celsius. + */ + Scalar temperature() const + { + return refTemperature_; + }; + + // \} + + /*! + * \name Boundary conditions + */ + // \{ + + //! \copydoc Dumux::ImplicitProblem::boundaryTypes() + void boundaryTypes(BoundaryTypes &values, const Vertex &vertex) const + { + const GlobalPosition &globalPos = + vertex.geometry().center(); + const Scalar time = this->timeManager().time(); + + values.setAllDirichlet(); + + if (onUpperBoundary_(globalPos)) + values.setNeumann(transportEqIdx); + + // Left inflow boundaries should be Neumann, otherwise the + // evaporative fluxes are much more grid dependent + if (onLeftBoundary_(globalPos)) + { + values.setNeumann(transportEqIdx); + + if (onUpperBoundary_(globalPos)) // corner point + values.setAllDirichlet(); + } + + + if (onRightBoundary_(globalPos)) + { + values.setAllOutflow(); + + if (onUpperBoundary_(globalPos)) // corner point + values.setAllDirichlet(); + } + + if (onLowerBoundary_(globalPos)) + { + values.setAllDirichlet(); + values.setNeumann(transportEqIdx); + + if (globalPos[0] > runUpDistanceX_-eps_ && time > initializationTime_) + { + values.setAllCouplingOutflow(); +// values.setCouplingInflow(energyEqIdx); + } + } + + // the mass balance has to be of type outflow + // it does not get a coupling condition, since pn is a condition for stokes + values.setOutflow(massBalanceIdx); + // // set all corner points to dirichlet + // if ((onLeftBoundary_(globalPos) || onRightBoundary_(globalPos)) && + // (onUpperBoundary_(globalPos) || onLowerBoundary_(globalPos))) + // { + // values.setAllDirichlet(); + //// values.setCouplingOutflow(momentumXIdx); + // } + + // set pressure at one point, do NOT specify this + // if the Darcy domain has a Dirichlet condition for pressure + if (onRightBoundary_(globalPos)) + { + if (time > initializationTime_) + values.setDirichlet(pressureIdx, massBalanceIdx); + else + if (!onLowerBoundary_(globalPos) && !onUpperBoundary_(globalPos)) + values.setDirichlet(pressureIdx, massBalanceIdx); + } + } + + //! \copydoc Dumux::ImplicitProblem::dirichlet() + void dirichlet(PrimaryVariables &values, const Vertex &vertex) const + { + + const GlobalPosition globalPos = vertex.geometry().center(); +// initial_(values, globalPos); + + FluidState fluidState; + updateFluidStateForBC_(fluidState); + + const Scalar density = + FluidSystem::density(fluidState, phaseIdx); + + values[velocityXIdx] = xVelocity_(globalPos); + values[velocityYIdx] = 0.0; + values[pressureIdx] = refPressure_ + + density*this->gravity()[1]*(globalPos[1] - bboxMin_[1]); + values[massOrMoleFracIdx] = refMassfrac_; + } + + //! \copydoc Dumux::ImplicitProblem::neumann() + void neumann(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvGeometry, + const Intersection &is, + const int scvIdx, + const int boundaryFaceIdx) const + { + const GlobalPosition &globalPos = + fvGeometry.boundaryFace[boundaryFaceIdx].ipGlobal; + + values = 0.; + + FluidState fluidState; + updateFluidStateForBC_(fluidState); + + const Scalar density = + FluidSystem::density(fluidState, phaseIdx); + const Scalar xVelocity = xVelocity_(globalPos); + + if (onLeftBoundary_(globalPos) + && globalPos[1] > bboxMin_[1] && globalPos[1] < bboxMax_[1]) + { + values[transportEqIdx] = -xVelocity*density*refMassfrac_; + +// (globalPos[1] - bboxMin_[1])*(bboxMax_[1] - globalPos[1]) +// / (0.25*height_()*height_())); + } + } + + /*! + * \brief Evaluate the Beavers-Joseph coefficient + * at the center of a given intersection + * + * \param element The finite element + * \param fvGeometry The finite-volume geometry + * \param is The intersection between element and boundary + * \param scvIdx The local subcontrolvolume index + * \param boundaryFaceIdx The index of the boundary face + * + * \return Beavers-Joseph coefficient + */ + Scalar beaversJosephCoeff(const Element &element, + const FVElementGeometry &fvGeometry, + const Intersection &is, + const int scvIdx, + const int boundaryFaceIdx) const + { + const GlobalPosition &globalPos = + fvGeometry.boundaryFace[boundaryFaceIdx].ipGlobal; + + if (onLowerBoundary_(globalPos)) + return alphaBJ_; + else + return 0.0; + } + + /*! + * \brief Evaluate the intrinsic permeability + * at the corner of a given element + * + * \param element The finite element + * \param fvGeometry The finite-volume geometry + * \param scvIdx The local subcontrolvolume index + * + * + * \return permeability in x-direction + */ + Scalar permeability(const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) const + { + return spatialParams_.intrinsicPermeability(element, + fvGeometry, + scvIdx); + } + // \} + + /*! + * \name Volume terms + */ + // \{ + + //! \copydoc Dumux::ImplicitProblem::source() + void source(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) const + { + // ATTENTION: The source term of the mass balance has to be chosen as + // div (q_momentum) in the problem file + values = Scalar(0); + } + + //! \copydoc Dumux::ImplicitProblem::initial() + void initial(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) const + { + const GlobalPosition &globalPos + = element.geometry().corner(scvIdx); + + initial_(values, globalPos); + } + // \} + + /*! + * \brief Determine if we are on a corner of the grid + * + * \param globalPos The global position + * + */ + bool isCornerPoint(const GlobalPosition &globalPos) + { + if ((onLeftBoundary_(globalPos) && onLowerBoundary_(globalPos)) || + (onLeftBoundary_(globalPos) && onUpperBoundary_(globalPos)) || + (onRightBoundary_(globalPos) && onLowerBoundary_(globalPos)) || + (onRightBoundary_(globalPos) && onUpperBoundary_(globalPos))) + return true; + else + return false; + } + + // required in case of mortar coupling + // otherwise it should return false + /*! + * \brief docme + * + * \param global Pos The global position + * + */ + bool isInterfaceCornerPoint(const GlobalPosition &globalPos) const + { return false; } + + /*! + * \brief Returns the spatial parameters object. + */ + SpatialParams &spatialParams() + { return spatialParams_; } + const SpatialParams &spatialParams() const + { return spatialParams_; } + + /*! + * \brief Returns the reference pressure. + */ + const Scalar refPressure() const + { return refPressure_; } + + const Scalar refTemperature() const + { return refTemperature_; } + + const Scalar refMassfrac() const + { return refMassfrac_; } +private: + // internal method for the initial condition (reused for the + // dirichlet conditions!) + void initial_(PrimaryVariables &values, + const GlobalPosition &globalPos) const + { + FluidState fluidState; + updateFluidStateForBC_(fluidState); + + const Scalar density = + FluidSystem::density(fluidState, phaseIdx); + + values[velocityXIdx] = xVelocity_(globalPos); + values[velocityYIdx] = 0.; + + values[pressureIdx] = refPressure_ + + density*this->gravity()[1]*(globalPos[1] - bboxMin_[1]); + values[massOrMoleFracIdx] = refMassfrac_; + } + + const Scalar xVelocity_(const GlobalPosition &globalPos) const + { + const Scalar vmax = vxMax_ + hourlyVariation_(sinusVelVar_); + // const Scalar relativeHeight = (globalPos[1]-bboxMin_[1])/height_(); + // linear profile +// return vmax*relativeHeight + bjSlipVel_; // BJ slip velocity is added as sqrt(Kxx) + // parabolic profile + return 4*vmax*(globalPos[1] - bboxMin_[1])*(bboxMax_[1] - globalPos[1]) + / (height_()*height_()) + bjSlipVel_; + // logarithmic profile +// return 0.1*vmax*log((relativeHeight+1e-3)/1e-3) + bjSlipVel_; + } + + void updateFluidStateForBC_(FluidState& fluidState) const + { + fluidState.setTemperature(refTemperature()); + fluidState.setPressure(phaseIdx, refPressure_); + + Scalar massFraction[numComponents]; + massFraction[transportCompIdx] = refMassfrac(); + massFraction[phaseCompIdx] = 1 - massFraction[transportCompIdx]; + + // calculate average molar mass of the gas phase + Scalar M1 = FluidSystem::molarMass(transportCompIdx); + Scalar M2 = FluidSystem::molarMass(phaseCompIdx); + Scalar X2 = massFraction[phaseCompIdx]; + Scalar massToMoleDenominator = M2 + X2*(M1 - M2); + + fluidState.setMoleFraction(phaseIdx, transportCompIdx, massFraction[transportCompIdx]*M2/massToMoleDenominator); + fluidState.setMoleFraction(phaseIdx, phaseCompIdx, massFraction[phaseCompIdx]*M1/massToMoleDenominator); + } + + const Scalar diurnalVariation_(const Scalar value) const + { + const Scalar time = this->timeManager().time(); + return sin(2*M_PI*time/86400) * value; + } + + const Scalar hourlyVariation_(const Scalar value) const + { + const Scalar time = this->timeManager().time(); + return sin(2*M_PI*time/3600) * value; + } + + bool onLeftBoundary_(const GlobalPosition &globalPos) const + { return globalPos[0] < bboxMin_[0] + eps_; } + + bool onRightBoundary_(const GlobalPosition &globalPos) const + { return globalPos[0] > bboxMax_[0] - eps_; } + + bool onLowerBoundary_(const GlobalPosition &globalPos) const + { return globalPos[1] < bboxMin_[1] + eps_; } + + bool onUpperBoundary_(const GlobalPosition &globalPos) const + { return globalPos[1] > bboxMax_[1] - eps_; } + + bool onBoundary_(const GlobalPosition &globalPos) const + { + return (onLeftBoundary_(globalPos) || onRightBoundary_(globalPos) + || onLowerBoundary_(globalPos) || onUpperBoundary_(globalPos)); + } + + const Scalar height_() const + { return bboxMax_[1] - bboxMin_[1]; } + + // spatial parameters + SpatialParams spatialParams_; + + static constexpr Scalar eps_ = 1e-8; + GlobalPosition bboxMin_; + GlobalPosition bboxMax_; + + Scalar refPressure_; + Scalar refTemperature_; + Scalar refMassfrac_; + + Scalar vxMax_; + Scalar bjSlipVel_; + Scalar sinusVelVar_; + Scalar alphaBJ_; + + Scalar xMaterialInterface_; + Scalar runUpDistanceX_; + Scalar initializationTime_; +}; +} //end namespace + +#endif // DUMUX_STOKES2C_SUBPROBLEM_HH diff --git a/test/multidomain/2cstokes2p2c/test_2cstokes2p2c.cc b/test/multidomain/2cstokes2p2c/test_2cstokes2p2c.cc new file mode 100644 index 0000000000..03bb706839 --- /dev/null +++ b/test/multidomain/2cstokes2p2c/test_2cstokes2p2c.cc @@ -0,0 +1,252 @@ +// -*- 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 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief test for the coupled 2c-Stokes and 2p2c-Darcy model + */ + +#include "config.h" +#include <iostream> + +#include <dune/common/version.hh> +#if DUNE_VERSION_NEWER(DUNE_COMMON, 2, 3) +#include <dune/common/parallel/mpihelper.hh> +#else +#include <dune/common/mpihelper.hh> +#endif + +#include <dune/common/parametertreeparser.hh> +#include <dumux/io/interfacemeshcreator.hh> + +#include "2cstokes2p2cproblem.hh" + +/*! + * \brief Print a usage string for simulations. + * + * \param progname The name of the executable + */ +void printUsage(const char *progName) +{ + std::cout << "usage: " << progName + << " [--restart restartTime] -parameterFile test_2cstokes2p2c.input\n"; + exit(1); +} + +template <class TypeTag> +int start_(int argc, + char **argv) +{ + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid; + typedef typename GET_PROP_TYPE(TypeTag, MultiDomainGrid) MDGrid; + typedef typename GET_PROP_TYPE(TypeTag, GridCreator) GridCreator; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, TimeManager) TimeManager; + + typedef Dune::GridPtr<Grid> GridPointer; + + //////////////////////////////////////////////////////////// + // Load the input parameters + //////////////////////////////////////////////////////////// + + typedef typename GET_PROP(TypeTag, ParameterTree) ParameterTree; + Dune::ParameterTreeParser::readOptions(argc, argv, ParameterTree::tree()); + + if (ParameterTree::tree().hasKey("parameterFile") || argc==1) + { + // read input file, but do not overwrite options specified + // on the command line, since the latter have precedence. + std::string inputFileName ; + if(argc==1) // if there are no arguments given (and there is a file ./<programname>.input) we use it as input file + { + std::cout<< "\nNo parameter file given. \n" + << "Defaulting to '" + << argv[0] + << ".input' for input file.\n"; + inputFileName = argv[0]; + inputFileName += ".input"; + } + else + inputFileName = GET_RUNTIME_PARAM(TypeTag, std::string, parameterFile); // otherwise we read from the command line + + std::ifstream parameterFile; + + // check whether the parameter file exists. + parameterFile.open(inputFileName.c_str()); + if (not parameterFile.is_open()){ + std::cout<< "\n\t -> Could not open file" + << inputFileName + << ". <- \n\n\n\n"; + printUsage(argv[0]); + return 1; + } + parameterFile.close(); + + Dune::ParameterTreeParser::readINITree(inputFileName, + ParameterTree::tree(), + /*overwrite=*/false); + } + + // initialize MPI, finalize is done automatically on exit + static Dune::MPIHelper& mpiHelper = Dune::MPIHelper::instance(argc, argv); + + // define the problem dimensions + const int dim=2; + + // deal with the restart stuff + int argIdx = 1; + bool restart = false; + double tStart = 0.0; + if (argc > 1 && std::string("--restart") == argv[argIdx]) + { + restart = true; + ++argIdx; + + std::istringstream(argv[argIdx++]) >> tStart; + } + + std::string dgfFileName; + Scalar dt, tEnd; + Dune::FieldVector<int, dim> nElements; + Scalar interfacePos, gradingFactor; + int gridRefinement; + bool useInterfaceMeshCreator; + + try + { + dgfFileName = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, std::string, Grid, File); + dt = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, TimeManager, DtInitial); + tEnd = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, TimeManager, TEnd); + nElements[0] = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, int, Grid, CellsX); + if (dim>1) nElements[1] = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, int, Grid, CellsY); + if (dim==3) nElements[2] = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, int, Grid, CellsZ); + interfacePos = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Grid, InterfacePos); + gradingFactor = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Grid, Grading); + gridRefinement = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Grid, Refinement); + useInterfaceMeshCreator = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, bool, Grid, UseInterfaceMeshCreator); + } + catch (Dumux::ParameterException &e) { + std::cerr << e << ". Abort!\n"; + exit(1) ; + } + catch (...) { + std::cerr << "Unknown exception thrown!\n"; + exit(1); + } + std::cout << "Starting with timestep size = " << dt << "s, simulation end = " << tEnd << "s\n"; + + if (useInterfaceMeshCreator) + { + Dumux::InterfaceMeshCreator<Grid> interfaceMeshCreator; + GridCreator::gridPtr() = interfaceMeshCreator.create(dgfFileName, nElements, interfacePos, gradingFactor); + } + else + { + // try to create a grid (from the given grid file) + try { GridCreator::makeGrid(); } + catch (...) { + std::string usageMessage = "\n\t -> Creation of the grid failed! <- \n\n\n\n"; +// usageMessage += usageTextBlock(); +// usage(argv[0], usageMessage); + throw; + } + } + + if (gridRefinement) + GridCreator::grid().globalRefine(gridRefinement); + + if (mpiHelper.size() > 1) { + if (!Dune::Capabilities::isParallel<Grid>::v) { + std::cerr << "WARNING: THE PROGRAM IS STARTED USING MPI, BUT THE GRID IMPLEMENTATION\n" + << " YOU HAVE CHOSEN IS NOT PARALLEL!\n"; + } + GridCreator::loadBalance(); + } + + // Instantiate the time manager + TimeManager timeManager; + + // instantiate coupled problem + Dune::shared_ptr<MDGrid> mdGrid_ = Dune::make_shared<MDGrid> (GridCreator::grid()); + + // instantiate coupled problem + Problem problem(*mdGrid_, + timeManager); + + Dumux::Parameters::print<TypeTag>(); + + // run the simulation + timeManager.init(problem, + tStart, // initial time + dt, // initial time step + tEnd, // final time + restart); + + // print all properties + Dumux::Properties::print<TypeTag>(); + + timeManager.run(); + + return 0; +} + +/*! + * \ingroup Start + * + * \brief Provides a main function which reads in parameters from the + * command line and a parameter file. + * + * In this function only the differentiation between debugger + * or not is made. + * + * \tparam TypeTag The type tag of the problem which needs to be solved + * + * \param argc The number of command line arguments of the program + * \param argv The contents of the command line arguments of the program + * \param usage Callback function for printing the usage message + */ +template <class TypeTag> +int start(int argc, + char **argv) +{ + try { + return start_<TypeTag>(argc, argv); + } + catch (Dumux::ParameterException &e) + { + std::cerr << e << ". Abort!\n"; + printUsage(argv[0]); + return 1; + } + catch (Dune::Exception &e) { + std::cerr << "Dune reported error: " << e << std::endl; + return 2; + } + catch (...) { + std::cerr << "Unknown exception thrown!\n"; + return 3; + } +} + +int main(int argc, char** argv) +{ + typedef TTAG(TwoCStokesTwoPTwoCProblem) ProblemTypeTag; + return start<ProblemTypeTag>(argc, argv); +} diff --git a/test/multidomain/2cstokes2p2c/test_2cstokes2p2c.input b/test/multidomain/2cstokes2p2c/test_2cstokes2p2c.input new file mode 100644 index 0000000000..819ae289ff --- /dev/null +++ b/test/multidomain/2cstokes2p2c/test_2cstokes2p2c.input @@ -0,0 +1,170 @@ +############################################################# +#Configuration file for test_2cstokes2p2c +############################################################# + +############################################################# +#General conditions: Problem, TimeManager, Vtk, Grid +############################################################# + +############################################################# +[Problem] +############################################################# +#First part of the interface, where it is not coupled; +#set to zero or negative if not desired +RunUpDistanceX = 0.0 # 0.06, has to be larger than NoDarcyX !? +NoDarcyX = 0.0 # 0.05 + +############################################################# +[TimeManager] +############################################################# +#Initial and maximum time-step size +DtInitial = 5e-1 +MaxTimeStepSize = 180 + +#Initialization time without coupling +#set to zero or negative if not desired +InitTime = 864 + +#Simulation end +TEnd= 1.3e6 + +#Define the length of an episode (for output) +EpisodeLength = 43200 + +############################################################# +[Vtk] +############################################################# +#Names for VTK output +NameFF = stokes2c +NamePM = 2p2c +AddVelocity = 1 + +############################################################# +[Grid] +############################################################# +UseInterfaceMeshCreator = true + +#Name of the dgf file (grid) +File = grids/interfacedomain.dgf +Refinement = 0 + +#Number of elements in x-, y-, z-direction +CellsX = 30 +CellsY = 62 +CellsZ = 1 +#Grading and refinement of the mesh +Grading = 1.13 + +#Extend of entire domain +XMin = 0.0 +XMax = 0.25 +YMin = 0.0 +YMax = 0.5 + +#Vertical position of coupling interface +InterfacePos = 0.25 + +############################################################# +[Output] +############################################################# +#Frequency of restart file, flux and VTK output +FreqRestart = 1000 # how often restart files are written out +FreqOutput = 50 # frequency of VTK output +FreqMassOutput = 2 # frequency of mass and evaporation rate output (Darcy) +FreqFluxOutput = 1000 # frequency of detailed flux output +FreqVaporFluxOutput = 2 # frequency of summarized flux output + +############################################################# +[Stokes] +############################################################# +StabilizationAlpha = -1.0 + +############################################################# +[FreeFlow] +############################################################# +RefPressure = 1e5 +RefTemperature = 298.15 +RefMassfrac = 0.008 +VxMax = 3.5 +BeaversJosephSlipVel = 0.00134 +SinusVelVar = 0.0 +ExponentMTC = 0.0 # 1./6., Mass transfer coefficient for S^MTC +UseBoundaryLayerModel = 9 # integer, 0 for no boundary layer model, 1 for Blasius, 2 for turbulent BL, 3 for constant thickness +BoundaryLayerOffset = 0.0 # For BL model like Blasius, determines a virtual run-up distance for the flow +ConstThickness = 0.0018 # for a constant BL thickness, use BL model 3 +MassTransferModel = 2 # 0 for none, 1 for power law, 2 for Schluender model + +############################################################# +[PorousMedium] +############################################################# +RefPressurePM = 1e5 +RefTemperaturePM = 298.15 +InitialSw1 = 0.98 +InitialSw2 = 0.98 +CharPoreDiameter = 1e-4 + +############################################################# +[SpatialParams] +############################################################# +MaterialInterfaceX = 100.0 +AlphaBJ = 1.0 + +# for homogeneous setups (0 for heterogeneous): +SoilType = 2 +RegularizationThreshold = 1e-2 + +# GStat stuff, only required for SoilType=0 +GenerateNewPermeability = true +GStatControlFileName = gstatControl_2D.txt +GStatInputFileName = gstatInput.txt +PermeabilityInputFileName = permeab.dat + +### SoilType = 1 ### +[SpatialParams.Coarse] +Permeability1 = 7.0e-10 +Porosity1 = 0.44 +Swr1 = 0.005 +Snr1 = 0.01 +VgAlpha1 = 8.74e-4 +VgN1 = 3.35 +PlotMaterialLaw1 = false +LambdaSolid1 = 5.3 + +### SoilType = 2 ### +[SpatialParams.Medium] +Permeability2 = 2.65e-10 +Porosity2 = 0.41 +Swr2 = 0.005 +Snr2 = 0.01 +VgAlpha2 = 6.371e-4 +VgN2 = 6.9 +PlotMaterialLaw2 = false +LambdaSolid2 = 5.3 + +### SoilType = 3 ### +[SpatialParams.Fine] +Permeability3 = 1.06e-10 +Porosity3 = 0.334 +Swr3 = 0.028 +Snr3 = 0.01 +VgAlpha3 = 5.81e-4 +VgN3 = 17.8 +PlotMaterialLaw3 = false +LambdaSolid3 = 5.3 + +############################################################# +[Newton] +############################################################# +RelTolerance = 1e-5 +TargetSteps = 8 +MaxSteps = 12 +WriteConvergence = false +MaxTimeStepDivisions = 20 + +############################################################# +[LinearSolver] +############################################################# +ResidualReduction = 1e-10 +Verbosity = 0 +MaxIterations = 200 + diff --git a/test/multidomain/Makefile.am b/test/multidomain/Makefile.am new file mode 100644 index 0000000000..08f06370a2 --- /dev/null +++ b/test/multidomain/Makefile.am @@ -0,0 +1,7 @@ +SUBDIRS = \ + 2cstokes2p2c + +EXTRA_DIST = CMakeLists.txt + +include $(top_srcdir)/am/global-rules + -- GitLab