diff --git a/dumux/discretization/cellcentered/globalvolumevariables.hh b/dumux/discretization/cellcentered/globalvolumevariables.hh index 371791f1e494ed2eaf7fe57fb2b7ddcb93f81848..b4d691d2c0f01150db1a057d6951644a0f0f7d85 100644 --- a/dumux/discretization/cellcentered/globalvolumevariables.hh +++ b/dumux/discretization/cellcentered/globalvolumevariables.hh @@ -45,6 +45,9 @@ class CCGlobalVolumeVariables<TypeTag, /*enableGlobalVolVarsCache*/true> friend CCElementVolumeVariables<TypeTag, true>; // The local jacobian needs to access and change volVars for derivative calculation friend typename GET_PROP_TYPE(TypeTag, LocalJacobian); + // as does the primary variable switch + friend class PrimaryVariableSwitch<TypeTag>; + friend typename GET_PROP_TYPE(TypeTag, PrimaryVariableSwitch); using Problem = typename GET_PROP_TYPE(TypeTag, Problem); using GridView = typename GET_PROP_TYPE(TypeTag, GridView); @@ -103,13 +106,12 @@ public: friend inline ElementVolumeVariables localView(const CCGlobalVolumeVariables& global) { return ElementVolumeVariables(global); } -private: const VolumeVariables& volVars(const IndexType scvIdx) const { return volumeVariables_[scvIdx]; } VolumeVariables& volVars(const IndexType scvIdx) { return volumeVariables_[scvIdx]; } - +private: const Problem& problem_() const { return *problemPtr_; } diff --git a/dumux/discretization/cellcentered/tpfa/darcyslaw.hh b/dumux/discretization/cellcentered/tpfa/darcyslaw.hh index cdbe9ca41bfc887977c35b165cda8443dccd792e..24499d68bcb11f0eca2a53035f99c12e2676c3be 100644 --- a/dumux/discretization/cellcentered/tpfa/darcyslaw.hh +++ b/dumux/discretization/cellcentered/tpfa/darcyslaw.hh @@ -155,7 +155,7 @@ public: const auto insideScvIdx = scvFace.insideScvIdx(); const auto& insideScv = fvGeometry.scv(insideScvIdx); const auto& insideVolVars = elemVolVars[insideScvIdx]; - const auto insideK = problem.spatialParams().intrinsicPermeability(insideScv, insideVolVars); + const auto insideK = problem.spatialParams().solDependentIntrinsicPermeability(insideScv, insideVolVars); Scalar ti = calculateOmega_(problem, scvFace, insideK, element, insideScv); if (!scvFace.boundary()) @@ -166,7 +166,7 @@ public: const auto& outsideScv = fvGeometry.scv(outsideScvIdx); const auto outsideElement = fvGeometry.globalFvGeometry().element(outsideScvIdx); const auto& outsideVolVars = elemVolVars[outsideScvIdx]; - const auto outsideK = problem.spatialParams().intrinsicPermeability(outsideScv, outsideVolVars); + const auto outsideK = problem.spatialParams().solDependentIntrinsicPermeability(outsideScv, outsideVolVars); Scalar tj = -1.0*calculateOmega_(problem, scvFace, outsideK, outsideElement, outsideScv); tij = scvFace.area()*(ti * tj)/(ti + tj); diff --git a/dumux/porousmediumflow/2pncmin/implicit/fluxvariables.hh b/dumux/porousmediumflow/2pncmin/implicit/fluxvariables.hh deleted file mode 100644 index 71c26b96cb3f8fd5978c8164ce1d3144cbdad6bc..0000000000000000000000000000000000000000 --- a/dumux/porousmediumflow/2pncmin/implicit/fluxvariables.hh +++ /dev/null @@ -1,179 +0,0 @@ -// -**- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- -// vi: set et ts=4 sw=4 sts=4: -/***************************************************************************** - * See the file COPYING for full copying permissions. * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 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 Contains the data which is required to calculate - * all fluxes of components over a face of a finite volume for - * the two-phase two-component mineralization model fully implicit model. - */ -#ifndef DUMUX_2PNCMIN_FLUX_VARIABLES_HH -#define DUMUX_2PNCMIN_FLUX_VARIABLES_HH - -#include <dumux/common/math.hh> -#include <dumux/common/spline.hh> -#include <dumux/porousmediumflow/2pnc/implicit/fluxvariables.hh> -#include "properties.hh" - -namespace Dumux -{ - -/*! - * \ingroup TwoPNCMinModel - * \ingroup ImplicitFluxVariables - * \brief Contains the data which is required to calculate - * all fluxes of components over a face of a finite volume for - * the two-phase n-component mineralization fully implicit model. - * - * This means pressure and concentration gradients, phase densities at - * the integration point, etc. - */ - -template <class TypeTag> -class TwoPNCMinFluxVariables : public TwoPNCFluxVariables<TypeTag> -{ - friend typename GET_PROP_TYPE(TypeTag, BaseFluxVariables); // be friends with base class - friend class TwoPNCFluxVariables<TypeTag>; // be friends with parent class - - typedef TwoPNCFluxVariables<TypeTag> ParentType; - typedef TwoPNCMinFluxVariables<TypeTag> ThisType; - - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; - typedef typename GridView::ctype CoordScalar; - typedef typename GridView::template Codim<0>::Entity Element; - typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - - enum { - dim = GridView::dimension, - dimWorld = GridView::dimensionworld, - numPhases = GET_PROP_VALUE(TypeTag, NumPhases), - numComponents = GET_PROP_VALUE(TypeTag, NumComponents), - }; - - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - typedef Dune::FieldMatrix<CoordScalar, dimWorld, dimWorld> DimWorldMatrix; - - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - enum { - wPhaseIdx = FluidSystem::wPhaseIdx, - nPhaseIdx = FluidSystem::nPhaseIdx, - wCompIdx = FluidSystem::wCompIdx, - }; - -protected: - /*! - * \brief Actual calculation of the normal Darcy velocities. - * \note this overloads the darcy flux variables velocity calculation - * This is only necessary because of the permeability factor. - * \todo Remove this once we have solDependent spatialParams! - * - * \param problem The problem - * \param element The finite element - * \param elemVolVars The volume variables of the current element - */ - void calculateNormalVelocity_(const Problem &problem, - const Element &element, - const ElementVolumeVariables &elemVolVars) - { - // calculate the mean intrinsic permeability - const auto& spatialParams = problem.spatialParams(); - DimWorldMatrix K(0.0); - const auto& volVarsI = elemVolVars[this->face().i]; - const auto& volVarsJ = elemVolVars[this->face().j]; - - if (GET_PROP_VALUE(TypeTag, ImplicitIsBox)) - { - auto Ki = spatialParams.intrinsicPermeability(element, this->fvGeometry_(), this->face().i); - Ki *= volVarsI.permeabilityFactor(); - - auto Kj = spatialParams.intrinsicPermeability(element, this->fvGeometry_(), this->face().j); - Kj *= volVarsJ.permeabilityFactor(); - - spatialParams.meanK(K, Ki, Kj); - } - else - { - const Element& elementI = this->fvGeometry_().neighbors[this->face().i]; - FVElementGeometry fvGeometryI; - fvGeometryI.subContVol[0].global = elementI.geometry().center(); - - const Element& elementJ = this->fvGeometry_().neighbors[this->face().j]; - FVElementGeometry fvGeometryJ; - fvGeometryJ.subContVol[0].global = elementJ.geometry().center(); - - auto Ki = spatialParams.intrinsicPermeability(elementI, fvGeometryI, 0); - Ki *= volVarsI.permeabilityFactor(); - - auto Kj = spatialParams.intrinsicPermeability(elementJ, fvGeometryJ, 0); - Kj *= volVarsJ.permeabilityFactor(); - - spatialParams.meanK(K, Ki, Kj); - } - - // loop over all phases - for (int phaseIdx = 0; phaseIdx < numPhases; phaseIdx++) - { - // calculate the flux in the normal direction of the - // current sub control volume face: - // - // v = - (K_f grad phi) * n - // with K_f = rho g / mu K - // - // Mind, that the normal has the length of it's area. - // This means that we are actually calculating - // Q = - (K grad phi) dot n /|n| * A - - - K.mv(this->potentialGrad_[phaseIdx], this->kGradP_[phaseIdx]); - this->kGradPNormal_[phaseIdx] = this->kGradP_[phaseIdx]*this->face().normal; - - // determine the upwind direction - if (this->kGradPNormal_[phaseIdx] < 0) - { - this->upstreamIdx_[phaseIdx] = this->face().i; - this->downstreamIdx_[phaseIdx] = this->face().j; - } - else - { - this->upstreamIdx_[phaseIdx] = this->face().j; - this->downstreamIdx_[phaseIdx] = this->face().i; - } - - // obtain the upwind volume variables - const auto& upVolVars = elemVolVars[ this->upstreamIdx(phaseIdx) ]; - const auto& downVolVars = elemVolVars[ this->downstreamIdx(phaseIdx) ]; - - // the minus comes from the Darcy relation which states that - // the flux is from high to low potentials. - // set the velocity - this->velocity_[phaseIdx] = this->kGradP_[phaseIdx]; - this->velocity_[phaseIdx] *= - ( this->mobilityUpwindWeight_*upVolVars.mobility(phaseIdx) - + (1.0 - this->mobilityUpwindWeight_)*downVolVars.mobility(phaseIdx)) ; - - // set the volume flux - this->volumeFlux_[phaseIdx] = this->velocity_[phaseIdx] * this->face().normal; - }// loop all phases - } -}; - -} // end namespace - -#endif diff --git a/dumux/porousmediumflow/2pncmin/implicit/localresidual.hh b/dumux/porousmediumflow/2pncmin/implicit/localresidual.hh index 1f53e0eabf48d1b83bb47cb7032824f53ec277c6..113877310a801e6d3f097a5d7636cc64ad295409 100644 --- a/dumux/porousmediumflow/2pncmin/implicit/localresidual.hh +++ b/dumux/porousmediumflow/2pncmin/implicit/localresidual.hh @@ -19,79 +19,45 @@ /*! * \file * - * \brief Element-wise calculation of the Jacobian matrix for problems + * \brief Element-wise calculation of the local residual for problems * using the two-phase n-component mineralisation box model. */ -#ifndef DUMUX_2PNCMIN_LOCAL_RESIDUAL_BASE_HH -#define DUMUX_2PNCMIN_LOCAL_RESIDUAL_BASE_HH +#ifndef DUMUX_2PNCMIN_LOCAL_RESIDUAL_HH +#define DUMUX_2PNCMIN_LOCAL_RESIDUAL_HH #include "properties.hh" -#include <dumux/porousmediumflow/2pnc/implicit/localresidual.hh> +#include <dumux/porousmediumflow/compositional/localresidual.hh> namespace Dumux { /*! * \ingroup TwoPNCMinModel * \ingroup ImplicitLocalResidual - * \brief Element-wise calculation of the Jacobian matrix for problems + * \brief Element-wise calculation of the local residual for problems * using the two-phase n-component mineralization fully implicit model. * * This class is used to fill the gaps in ImplicitLocalResidual for the two-phase n-component flow. */ template<class TypeTag> -class TwoPNCMinLocalResidual: public TwoPNCLocalResidual<TypeTag> +class TwoPNCMinLocalResidual: public CompositionalLocalResidual<TypeTag> { -protected: - typedef TwoPNCLocalResidual<TypeTag> ParentType; - typedef TwoPNCMinLocalResidual<TypeTag> ThisType; - typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; - typedef typename GET_PROP_TYPE(TypeTag, ElementSolutionVector) ElementSolutionVector; - typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; - typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - typedef typename GET_PROP_TYPE(TypeTag, LocalResidual) Implementation; - + using ParentType = CompositionalLocalResidual<TypeTag>; + using ThisType = TwoPNCMinLocalResidual<TypeTag>; + using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); + using SubControlVolume = typename GET_PROP_TYPE(TypeTag, SubControlVolume); + using VolumeVariables = typename GET_PROP_TYPE(TypeTag, VolumeVariables); + using Indices = typename GET_PROP_TYPE(TypeTag, Indices); enum { - numEq = GET_PROP_VALUE(TypeTag, NumEq), numPhases = GET_PROP_VALUE(TypeTag, NumPhases), numSPhases = GET_PROP_VALUE(TypeTag, NumSPhases), numComponents = GET_PROP_VALUE(TypeTag, NumComponents), - replaceCompEqIdx = GET_PROP_VALUE(TypeTag, ReplaceCompEqIdx), - - pressureIdx = Indices::pressureIdx, - switchIdx = Indices::switchIdx, - - wPhaseIdx = FluidSystem::wPhaseIdx, - nPhaseIdx = FluidSystem::nPhaseIdx, - - wCompIdx = FluidSystem::wCompIdx, - nCompIdx = FluidSystem::nCompIdx, - - conti0EqIdx = Indices::conti0EqIdx, - - wPhaseOnly = Indices::wPhaseOnly, - nPhaseOnly = Indices::nPhaseOnly, - bothPhases = Indices::bothPhases, - - plSg = TwoPNCFormulation::plSg, - pgSl = TwoPNCFormulation::pgSl, - formulation = GET_PROP_VALUE(TypeTag, Formulation) + conti0EqIdx = Indices::conti0EqIdx }; - typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; - typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; - typedef typename GET_PROP_TYPE(TypeTag, ElementBoundaryTypes) ElementBoundaryTypes; - typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; - typedef typename GET_PROP_TYPE(TypeTag, SpatialParams) SpatialParams; - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - public: /*! * \brief Evaluate the amount all conservation quantities @@ -101,17 +67,14 @@ public: * inside a sub control volume divided by the volume). * In contrast to the 2pnc model, here, the storage of solid phases is included too. * - * \param storage the mass of the component within the sub-control volume - * \param scvIdx The SCV (sub-control-volume) index - * \param usePrevSol Evaluate function with solution of current or previous time step + * \param scv the SCV (sub-control-volume) + * \param volVars The volume variables of the right time step */ - void computeStorage(PrimaryVariables &storage, int scvIdx, bool usePrevSol) const + PrimaryVariables computeStorage(const SubControlVolume& scv, + const VolumeVariables& volVars) const { - //call parenttype function - ParentType::computeStorage(storage, scvIdx, usePrevSol); - - const auto& elemVolVars = usePrevSol ? this->prevVolVars_() : this->curVolVars_(); - const VolumeVariables &volVars = elemVolVars[scvIdx]; + // call parenttype function + auto storage = ParentType::computeStorage(scv, volVars); // Compute storage term of all solid (precipitated) phases (excluding the non-reactive matrix) for (int phaseIdx = numPhases; phaseIdx < numPhases + numSPhases; ++phaseIdx) @@ -120,10 +83,10 @@ public: storage[eqIdx] += volVars.precipitateVolumeFraction(phaseIdx)*volVars.molarDensity(phaseIdx); } - Valgrind::CheckDefined(storage); + return storage; } }; -} // end namespace +} // end namespace Dumux #endif diff --git a/dumux/porousmediumflow/2pncmin/implicit/model.hh b/dumux/porousmediumflow/2pncmin/implicit/model.hh index 55758e6d4ac41508d2ffe2ed3aba2faf9b06e54c..d5a83a4f15e2b34a1c0c107fe0c4e56998726cd4 100644 --- a/dumux/porousmediumflow/2pncmin/implicit/model.hh +++ b/dumux/porousmediumflow/2pncmin/implicit/model.hh @@ -27,10 +27,11 @@ #include "properties.hh" #include "indices.hh" +#include "primaryvariableswitch.hh" +#include "localresidual.hh" #include <dumux/material/constants.hh> #include <dumux/porousmediumflow/2pnc/implicit/model.hh> -#include "localresidual.hh" #include <dumux/porousmediumflow/implicit/velocityoutput.hh> namespace Dumux @@ -108,22 +109,23 @@ namespace Dumux */ template<class TypeTag> -class TwoPNCMinModel: public TwoPNCModel<TypeTag> +class TwoPNCMinModel: public GET_PROP_TYPE(TypeTag, BaseModel) { - typedef TwoPNCMinModel<TypeTag> ThisType; - typedef TwoPNCModel<TypeTag> ParentType; - - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; - typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; - typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; - typedef Constants<Scalar> Constant; + // the parent class needs to access the variable switch + friend typename GET_PROP_TYPE(TypeTag, BaseModel); + + using ThisType = Dumux::TwoPNCMinModel<TypeTag>; + using ParentType = typename GET_PROP_TYPE(TypeTag, BaseModel); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry); + using VolumeVariables = typename GET_PROP_TYPE(TypeTag, VolumeVariables); + using ElementVolumeVariables = typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables); + using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + using Indices = typename GET_PROP_TYPE(TypeTag, Indices); + using Constant = Constants<Scalar>; enum { dim = GridView::dimension, @@ -154,18 +156,105 @@ class TwoPNCMinModel: public TwoPNCModel<TypeTag> formulation = GET_PROP_VALUE(TypeTag, Formulation) }; - typedef typename GridView::template Codim<dim>::Entity Vertex; - typedef typename GridView::template Codim<0>::Entity Element; - - typedef Dune::FieldVector<Scalar, dimWorld> GlobalPosition; - typedef typename GridView::ctype CoordScalar; - typedef Dune::FieldMatrix<CoordScalar, dimWorld, dimWorld> Tensor; - typedef Dune::FieldVector<Scalar, numPhases> PhasesVector; + using Vertex = typename GridView::template Codim<dim>::Entity; + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = Dune::FieldVector<Scalar, dimWorld>; + using CoordScalar = typename GridView::ctype; + using Tensor = Dune::FieldMatrix<CoordScalar, dimWorld, dimWorld>; + using PhasesVector = Dune::FieldVector<Scalar, numPhases>; enum { isBox = GET_PROP_VALUE(TypeTag, ImplicitIsBox) }; enum { dofCodim = isBox ? dim : 0 }; public: + /*! + * \brief One Newton iteration was finished. + * \param uCurrent The solution after the current Newton iteration + */ + template<typename T = TypeTag> + typename std::enable_if<GET_PROP_VALUE(T, EnableGlobalVolumeVariablesCache), void>::type + newtonEndStep() + { + // \todo resize volvars vector if grid was adapted + + // update the variable switch + switchFlag_ = priVarSwitch_().update(this->problem_(), this->curSol()); + + // update the secondary variables if global caching is enabled + // \note we only updated if phase presence changed as the volume variables + // are already updated once by the switch + if (switchFlag_) + { + for (const auto& element : elements(this->problem_().gridView())) + { + // make sure FVElementGeometry & vol vars are bound to the element + auto fvGeometry = localView(this->globalFvGeometry()); + fvGeometry.bindElement(element); + + for (auto&& scv : scvs(fvGeometry)) + { + auto dofIdxGlobal = scv.dofIndex(); + if (priVarSwitch_().wasSwitched(dofIdxGlobal)) + { + this->nonConstCurGlobalVolVars().volVars(dofIdxGlobal).update(this->curSol()[dofIdxGlobal], + this->problem_(), + element, + scv); + } + } + + } + } + } + + /*! + * \brief One Newton iteration was finished. + * \param uCurrent The solution after the current Newton iteration + */ + template<typename T = TypeTag> + typename std::enable_if<!GET_PROP_VALUE(T, EnableGlobalVolumeVariablesCache), void>::type + newtonEndStep() + { + // update the variable switch + switchFlag_ = priVarSwitch_().update(this->problem_(), this->curSol()); + } + + /*! + * \brief Called by the update() method if applying the Newton + * method was unsuccessful. + */ + void updateFailed() + { + ParentType::updateFailed(); + + switchFlag_ = false; + priVarSwitch_().resetPhasePresence(); + } + + /*! + * \brief Called by the problem if a time integration was + * successful, post processing of the solution is done and the + * result has been written to disk. + * + * This should prepare the model for the next time integration. + */ + void advanceTimeLevel() + { + ParentType::advanceTimeLevel(); + + // update the phase state + priVarSwitch_().updateOldPhasePresence(); + switchFlag_ = false; + } + + /*! + * \brief Returns true if the primary variables were switched for + * at least one dof after the last timestep. + */ + bool switched() const + { + return switchFlag_; + } /*! * \brief Append all quantities of interest which can be derived @@ -179,114 +268,112 @@ public: //additional output of the permeability and the precipitate volume fractions void addOutputVtkFields(const SolutionVector &sol, MultiWriter &writer) { - typedef Dune::BlockVector<Dune::FieldVector<Scalar, 1> > ScalarField; - typedef Dune::BlockVector<Dune::FieldVector<double, dim> > VectorField; + using ScalarField = Dune::BlockVector<Dune::FieldVector<double, 1> >; // get the number of degrees of freedom auto numDofs = this->numDofs(); // create the required scalar fields - ScalarField *Sg = writer.allocateManagedBuffer(numDofs); - ScalarField *Sl = writer.allocateManagedBuffer(numDofs); - ScalarField *pg = writer.allocateManagedBuffer (numDofs); - ScalarField *pl = writer.allocateManagedBuffer (numDofs); - ScalarField *pc = writer.allocateManagedBuffer (numDofs); - ScalarField *rhoL = writer.allocateManagedBuffer (numDofs); - ScalarField *rhoG = writer.allocateManagedBuffer (numDofs); - ScalarField *mobL = writer.allocateManagedBuffer (numDofs); - ScalarField *mobG = writer.allocateManagedBuffer (numDofs); - ScalarField *phasePresence = writer.allocateManagedBuffer (numDofs); - ScalarField *temperature = writer.allocateManagedBuffer (numDofs); - ScalarField *poro = writer.allocateManagedBuffer (numDofs); - ScalarField *permeabilityFactor = writer.allocateManagedBuffer (numDofs); - ScalarField *precipitateVolumeFraction[numSPhases]; + auto* Sg = writer.allocateManagedBuffer(numDofs); + auto* Sl = writer.allocateManagedBuffer(numDofs); + auto* pg = writer.allocateManagedBuffer (numDofs); + auto* pl = writer.allocateManagedBuffer (numDofs); + auto* pc = writer.allocateManagedBuffer (numDofs); + auto* rhoL = writer.allocateManagedBuffer (numDofs); + auto* rhoG = writer.allocateManagedBuffer (numDofs); + auto* mobL = writer.allocateManagedBuffer (numDofs); + auto* mobG = writer.allocateManagedBuffer (numDofs); + auto* phasePresence = writer.allocateManagedBuffer (numDofs); + auto* temperature = writer.allocateManagedBuffer (numDofs); + auto* poro = writer.allocateManagedBuffer (numDofs); + auto* permeabilityFactor = writer.allocateManagedBuffer (numDofs); + ScalarField* precipitateVolumeFraction[numSPhases]; for (int i = 0; i < numSPhases; ++i) precipitateVolumeFraction[i] = writer.allocateManagedBuffer(numDofs); - ScalarField *massFraction[numPhases][numComponents]; + ScalarField* massFraction[numPhases][numComponents]; for (int i = 0; i < numPhases; ++i) for (int j = 0; j < numComponents; ++j) massFraction[i][j] = writer.allocateManagedBuffer(numDofs); - ScalarField *molarity[numComponents]; + ScalarField* molarity[numComponents]; for (int j = 0; j < numComponents ; ++j) molarity[j] = writer.allocateManagedBuffer(numDofs); - ScalarField *Perm[dim]; + ScalarField* Perm[dim]; for (int j = 0; j < dim; ++j) //Permeability only in main directions xx and yy Perm[j] = writer.allocateManagedBuffer(numDofs); - VectorField *velocityN = writer.template allocateManagedBuffer<double, dim>(numDofs); - VectorField *velocityW = writer.template allocateManagedBuffer<double, dim>(numDofs); - ImplicitVelocityOutput<TypeTag> velocityOutput(this->problem_()); + // auto* velocityN = writer.template allocateManagedBuffer<double, dim>(numDofs); + // auto* velocityW = writer.template allocateManagedBuffer<double, dim>(numDofs); + // ImplicitVelocityOutput<TypeTag> velocityOutput(this->problem_()); - if (velocityOutput.enableOutput()) // check if velocity output is demanded - { - // initialize velocity fields - for (unsigned int i = 0; i < numDofs; ++i) - { - (*velocityN)[i] = Scalar(0); - (*velocityW)[i] = Scalar(0); - } - } + // if (velocityOutput.enableOutput()) // check if velocity output is demanded + // { + // // initialize velocity fields + // for (unsigned int i = 0; i < numDofs; ++i) + // { + // (*velocityN)[i] = Scalar(0); + // (*velocityW)[i] = Scalar(0); + // } + // } auto numElements = this->gridView_().size(0); - ScalarField *rank = writer.allocateManagedBuffer(numElements); + auto* rank = writer.allocateManagedBuffer(numElements); for (const auto& element : elements(this->gridView_())) { auto eIdxGlobal = this->problem_().elementMapper().index(element); (*rank)[eIdxGlobal] = this->gridView_().comm().rank(); - FVElementGeometry fvGeometry; - fvGeometry.update(this->gridView_(), element); - ElementVolumeVariables elemVolVars; - elemVolVars.update(this->problem_(), - element, - fvGeometry, - false /* oldSol? */); + auto fvGeometry = localView(this->globalFvGeometry()); + fvGeometry.bindElement(element); - for (int scvIdx = 0; scvIdx < fvGeometry.numScv; ++scvIdx) + auto elemVolVars = localView(this->curGlobalVolVars()); + elemVolVars.bindElement(element, fvGeometry, this->curSol()); + + for (auto&& scv : scvs(fvGeometry)) { - auto dofIdxGlobal = this->dofMapper().subIndex(element, scvIdx, dofCodim); - - (*Sg)[dofIdxGlobal] = elemVolVars[scvIdx].saturation(nPhaseIdx); - (*Sl)[dofIdxGlobal] = elemVolVars[scvIdx].saturation(wPhaseIdx); - (*pg)[dofIdxGlobal] = elemVolVars[scvIdx].pressure(nPhaseIdx); - (*pl)[dofIdxGlobal] = elemVolVars[scvIdx].pressure(wPhaseIdx); - (*pc)[dofIdxGlobal] = elemVolVars[scvIdx].capillaryPressure(); - (*rhoL)[dofIdxGlobal] = elemVolVars[scvIdx].density(wPhaseIdx); - (*rhoG)[dofIdxGlobal] = elemVolVars[scvIdx].density(nPhaseIdx); - (*mobL)[dofIdxGlobal] = elemVolVars[scvIdx].mobility(wPhaseIdx); - (*mobG)[dofIdxGlobal] = elemVolVars[scvIdx].mobility(nPhaseIdx); - (*poro)[dofIdxGlobal] = elemVolVars[scvIdx].porosity(); + auto dofIdxGlobal = scv.dofIndex(); + const auto& volVars = elemVolVars[scv]; + + (*Sg)[dofIdxGlobal] = volVars.saturation(nPhaseIdx); + (*Sl)[dofIdxGlobal] = volVars.saturation(wPhaseIdx); + (*pg)[dofIdxGlobal] = volVars.pressure(nPhaseIdx); + (*pl)[dofIdxGlobal] = volVars.pressure(wPhaseIdx); + (*pc)[dofIdxGlobal] = volVars.capillaryPressure(); + (*rhoL)[dofIdxGlobal] = volVars.density(wPhaseIdx); + (*rhoG)[dofIdxGlobal] = volVars.density(nPhaseIdx); + (*mobL)[dofIdxGlobal] = volVars.mobility(wPhaseIdx); + (*mobG)[dofIdxGlobal] = volVars.mobility(nPhaseIdx); + (*poro)[dofIdxGlobal] = volVars.porosity(); for (int sPhaseIdx = 0; sPhaseIdx < numSPhases; ++sPhaseIdx) - (*precipitateVolumeFraction[sPhaseIdx])[dofIdxGlobal] = elemVolVars[scvIdx].precipitateVolumeFraction(sPhaseIdx + numPhases); + (*precipitateVolumeFraction[sPhaseIdx])[dofIdxGlobal] = volVars.precipitateVolumeFraction(sPhaseIdx + numPhases); - (*temperature)[dofIdxGlobal] = elemVolVars[scvIdx].temperature(); - (*permeabilityFactor)[dofIdxGlobal] = elemVolVars[scvIdx].permeabilityFactor(); - (*phasePresence)[dofIdxGlobal] = this->staticDat_[dofIdxGlobal].phasePresence; + (*temperature)[dofIdxGlobal] = volVars.temperature(); + // (*permeabilityFactor)[dofIdxGlobal] = volVars.permeabilityFactor(); + (*phasePresence)[dofIdxGlobal] = priVarSwitch().phasePresence(dofIdxGlobal); for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) for (int compIdx = 0; compIdx < numComponents; ++compIdx) - (*massFraction[phaseIdx][compIdx])[dofIdxGlobal]= elemVolVars[scvIdx].massFraction(phaseIdx,compIdx); + (*massFraction[phaseIdx][compIdx])[dofIdxGlobal]= volVars.massFraction(phaseIdx,compIdx); for (int compIdx = 0; compIdx < numComponents; ++compIdx) - (*molarity[compIdx])[dofIdxGlobal] = (elemVolVars[scvIdx].molarity(wPhaseIdx, compIdx)); + (*molarity[compIdx])[dofIdxGlobal] = (volVars.molarity(wPhaseIdx, compIdx)); - Tensor K = this->perm_(this->problem_().spatialParams().intrinsicPermeability(element, fvGeometry, scvIdx)); + auto K = this->perm_(this->problem_().spatialParams().solDependentIntrinsicPermeability(scv, volVars)); for (int j = 0; j<dim; ++j) - (*Perm[j])[dofIdxGlobal] = K[j][j] * elemVolVars[scvIdx].permeabilityFactor(); + (*Perm[j])[dofIdxGlobal] = K[j][j]; }; - // velocity output - if(velocityOutput.enableOutput()){ - velocityOutput.calculateVelocity(*velocityW, elemVolVars, fvGeometry, element, wPhaseIdx); - velocityOutput.calculateVelocity(*velocityN, elemVolVars, fvGeometry, element, nPhaseIdx); - } + // // velocity output + // if(velocityOutput.enableOutput()){ + // velocityOutput.calculateVelocity(*velocityW, elemVolVars, fvGeometry, element, wPhaseIdx); + // velocityOutput.calculateVelocity(*velocityN, elemVolVars, fvGeometry, element, nPhaseIdx); + // } } // loop over element writer.attachDofData(*Sg, "Sg", isBox); @@ -333,180 +420,103 @@ public: writer.attachDofData(*molarity[j], oss.str(), isBox); } - if (velocityOutput.enableOutput()) // check if velocity output is demanded - { - writer.attachDofData(*velocityW, "velocityW", isBox, dim); - writer.attachDofData(*velocityN, "velocityN", isBox, dim); - } + // if (velocityOutput.enableOutput()) // check if velocity output is demanded + // { + // writer.attachDofData(*velocityW, "velocityW", isBox, dim); + // writer.attachDofData(*velocityN, "velocityN", isBox, dim); + // } writer.attachCellData(*rank, "process rank"); } /*! - * \brief Update the static data of all vertices in the grid. + * \brief Write the current solution to a restart file. * - * \param curGlobalSol The current global solution - * \param oldGlobalSol The previous global solution + * \param outStream The output stream of one entity for the restart file + * \param entity The entity, either a vertex or an element */ - void updateStaticData(SolutionVector &curGlobalSol, - const SolutionVector &oldGlobalSol) + template<class Entity> + void serializeEntity(std::ostream &outStream, const Entity &entity) { - bool wasSwitched = false; + // write primary variables + ParentType::serializeEntity(outStream, entity); - for (unsigned i = 0; i < this->staticDat_.size(); ++i) - this->staticDat_[i].visited = false; + int dofIdxGlobal = this->dofMapper().index(entity); - for (const auto& element : elements(this->gridView_())) - { - FVElementGeometry fvGeometry; - fvGeometry.update(this->gridView_(), element); - for (int scvIdx = 0; scvIdx < fvGeometry.numScv; ++scvIdx) - { - auto dofIdxGlobal = this->dofMapper().subIndex(element, scvIdx, dofCodim); - - if (this->staticDat_[dofIdxGlobal].visited) - continue; - - this->staticDat_[dofIdxGlobal].visited = true; - VolumeVariables volVars; - volVars.update(curGlobalSol[dofIdxGlobal], - this->problem_(), - element, - fvGeometry, - scvIdx, - false); - auto global = element.geometry().corner(scvIdx); - if (primaryVarSwitch_(curGlobalSol, volVars, dofIdxGlobal, global)) - wasSwitched = true; - } - } + if (!outStream.good()) + DUNE_THROW(Dune::IOError, "Could not serialize entity " << dofIdxGlobal); - // make sure that if there was a variable switch in an - // other partition we will also set the switch flag - // for our partition. - if (this->gridView_().comm().size() > 1) - wasSwitched = this->gridView_().comm().max(wasSwitched); + outStream << priVarSwitch().phasePresence(dofIdxGlobal) << " "; + } - this->setSwitched_(wasSwitched); + /*! + * \brief Reads the current solution from a restart file. + * + * \param inStream The input stream of one entity from the restart file + * \param entity The entity, either a vertex or an element + */ + template<class Entity> + void deserializeEntity(std::istream &inStream, const Entity &entity) + { + // read primary variables + ParentType::deserializeEntity(inStream, entity); + + // read phase presence + int dofIdxGlobal = this->dofMapper().index(entity); + + if (!inStream.good()) + DUNE_THROW(Dune::IOError, "Could not deserialize entity " << dofIdxGlobal); + + int phasePresence; + inStream >> phasePresence; + + priVarSwitch_().setPhasePresence(dofIdxGlobal, phasePresence); + priVarSwitch_().setOldPhasePresence(dofIdxGlobal, phasePresence); } -protected: + + const Dumux::TwoPNCMinPrimaryVariableSwitch<TypeTag>& priVarSwitch() const + { return switch_; } + +private: + + Dumux::TwoPNCMinPrimaryVariableSwitch<TypeTag>& priVarSwitch_() + { return switch_; } /*! - * \brief Set whether there was a primary variable switch after in - * the last timestep. + * \brief Applies the initial solution for all vertices of the grid. + * + * \todo the initial condition needs to be unique for + * each vertex. we should think about the API... */ - bool primaryVarSwitch_(SolutionVector &globalSol, - const VolumeVariables &volVars, - int dofIdxGlobal, - const GlobalPosition &globalPos) + void applyInitialSolution_() { - // evaluate primary variable switch - bool wouldSwitch = false; - int phasePresence = this->staticDat_[dofIdxGlobal].phasePresence; - int newPhasePresence = phasePresence; + ParentType::applyInitialSolution_(); - //check if a primary variable switch is necessary - if (phasePresence == bothPhases) - { - Scalar Smin = 0.0; //saturation threshold - if (this->staticDat_[dofIdxGlobal].wasSwitched) - Smin = -0.01; + // initialize the primary variable switch + priVarSwitch_().init(this->problem_()); + } - //if saturation of liquid phase is smaller 0 switch - if (volVars.saturation(wPhaseIdx) <= Smin) - { - wouldSwitch = true; - //liquid phase has to disappear - std::cout << "Liquid Phase disappears at vertex " << dofIdxGlobal - << ", coordinated: " << globalPos << ", Sl: " - << volVars.saturation(wPhaseIdx) << std::endl; - newPhasePresence = nPhaseOnly; - - //switch not depending on formulation - //switch "Sl" to "xgH20" - globalSol[dofIdxGlobal][switchIdx] - = volVars.moleFraction(nPhaseIdx, wCompIdx /*H2O*/); - //Here unlike 2pnc model we do not switch all components to to mole fraction in gas phase - } - //if saturation of gas phase is smaller than 0 switch - else if (volVars.saturation(nPhaseIdx) <= Smin) - { - wouldSwitch = true; - //gas phase has to disappear - std::cout << "Gas Phase disappears at vertex " << dofIdxGlobal - << ", coordinated: " << globalPos << ", Sg: " - << volVars.saturation(nPhaseIdx) << std::endl; - newPhasePresence = wPhaseOnly; - - //switch "Sl" to "xlN2" - globalSol[dofIdxGlobal][switchIdx] = volVars.moleFraction(wPhaseIdx, nCompIdx /*N2*/); - } - } - else if (phasePresence == nPhaseOnly) - { - Scalar sumxl = 0; - //Calculate sum of mole fractions (water and air) in the hypothetical liquid phase - for (int compIdx = 0; compIdx < numComponents; compIdx++) - { - sumxl += volVars.moleFraction(wPhaseIdx, compIdx); - } - Scalar xlmax = 1.0; - if (sumxl > xlmax) - wouldSwitch = true; - if (this->staticDat_[dofIdxGlobal].wasSwitched) - xlmax *=1.02; - - //if the sum of the mole fractions would be larger than - //1, wetting phase appears - if (sumxl/*sum of mole fractions*/ > xlmax/*1*/) - { - // liquid phase appears - std::cout << "Liquid Phase appears at vertex " << dofIdxGlobal - << ", coordinated: " << globalPos << ", sumxl: " - << sumxl << std::endl; - newPhasePresence = bothPhases; - if (formulation == pgSl) - globalSol[dofIdxGlobal][switchIdx] = 0.0; - else if (formulation == plSg) - globalSol[dofIdxGlobal][switchIdx] = 1.0; - //Here unlike 2pnc model we do not switch all components to to mole fraction in gas phase - } - } - else if (phasePresence == wPhaseOnly) - { - Scalar xgmax = 1; - Scalar sumxg = 0; - //Calculate sum of mole fractions in the hypothetical gas phase - for (int compIdx = 0; compIdx < numComponents; compIdx++) - { - sumxg += volVars.moleFraction(nPhaseIdx, compIdx); - } - if (sumxg > xgmax) - wouldSwitch = true; - if (this->staticDat_[dofIdxGlobal].wasSwitched) - xgmax *=1.02; - //liquid phase appears if sum is larger than one - if (sumxg > xgmax) - { - std::cout << "Gas Phase appears at vertex " << dofIdxGlobal - << ", coordinated: " << globalPos << ", sumxg: " - << sumxg << std::endl; - newPhasePresence = bothPhases; - //saturation of the liquid phase set to 0.9999 (if formulation pgSl and vice versa) - if (formulation == pgSl) - globalSol[dofIdxGlobal][switchIdx] = 0.999; - else if (formulation == plSg) - globalSol[dofIdxGlobal][switchIdx] = 0.001; + //! the class handling the primary variable switch + Dumux::TwoPNCMinPrimaryVariableSwitch<TypeTag> switch_; + bool switchFlag_; - } - } - this->staticDat_[dofIdxGlobal].phasePresence = newPhasePresence; - this->staticDat_[dofIdxGlobal].wasSwitched = wouldSwitch; - return phasePresence != newPhasePresence; + Tensor perm_(Scalar perm) const + { + Tensor K(0.0); + + for(int i=0; i<dim; i++) + K[i][i] = perm; + + return K; + } + + const Tensor& perm_(const Tensor& perm) const + { + return perm; } }; -} +} // end namespace Dumux #include "propertydefaults.hh" diff --git a/dumux/porousmediumflow/2pncmin/implicit/primaryvariableswitch.hh b/dumux/porousmediumflow/2pncmin/implicit/primaryvariableswitch.hh new file mode 100644 index 0000000000000000000000000000000000000000..0f3ab6d825fdb70a2dcdc89b846b00632503fbdc --- /dev/null +++ b/dumux/porousmediumflow/2pncmin/implicit/primaryvariableswitch.hh @@ -0,0 +1,190 @@ +// -*- 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 The primary variable switch for the 2pncmin model + */ +#ifndef DUMUX_2PNCMIN_PRIMARY_VARIABLE_SWITCH_HH +#define DUMUX_2PNCMIN_PRIMARY_VARIABLE_SWITCH_HH + +#include <dumux/porousmediumflow/compositional/primaryvariableswitch.hh> + +namespace Dumux +{ +/*! + * \ingroup TwoPNCMinModel + * \brief The primary variable switch controlling the phase presence state variable + */ +template<class TypeTag> +class TwoPNCMinPrimaryVariableSwitch : public Dumux::PrimaryVariableSwitch<TypeTag> +{ + friend typename Dumux::PrimaryVariableSwitch<TypeTag>; + using ParentType = Dumux::PrimaryVariableSwitch<TypeTag>; + + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using IndexType = typename GridView::IndexSet::IndexType; + using GlobalPosition = Dune::FieldVector<Scalar, GridView::dimensionworld>; + + using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); + using VolumeVariables = typename GET_PROP_TYPE(TypeTag, VolumeVariables); + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + using Indices = typename GET_PROP_TYPE(TypeTag, Indices); + + static const int numComponents = GET_PROP_VALUE(TypeTag, NumComponents); + static const int numMajorComponents = GET_PROP_VALUE(TypeTag, NumMajorComponents); + + enum { + pressureIdx = Indices::pressureIdx, + switchIdx = Indices::switchIdx, + + wPhaseIdx = Indices::wPhaseIdx, + nPhaseIdx = Indices::nPhaseIdx, + + wCompIdx = FluidSystem::wCompIdx, + nCompIdx = FluidSystem::nCompIdx, + + wPhaseOnly = Indices::wPhaseOnly, + nPhaseOnly = Indices::nPhaseOnly, + bothPhases = Indices::bothPhases + }; + + enum { + plSg = TwoPNCFormulation::plSg, + pgSl = TwoPNCFormulation::pgSl, + formulation = GET_PROP_VALUE(TypeTag, Formulation) + }; + +protected: + + // perform variable switch at a degree of freedom location + bool update_(PrimaryVariables& priVars, + const VolumeVariables& volVars, + IndexType dofIdxGlobal, + const GlobalPosition& globalPos) + { + // evaluate primary variable switch + bool wouldSwitch = false; + int phasePresence = this->phasePresence_[dofIdxGlobal]; + int newPhasePresence = phasePresence; + + //check if a primary variable switch is necessary + if (phasePresence == bothPhases) + { + Scalar Smin = 0.0; //saturation threshold + if (this->wasSwitched_[dofIdxGlobal]) + Smin = -0.01; + + //if saturation of liquid phase is smaller 0 switch + if (volVars.saturation(wPhaseIdx) <= Smin) + { + wouldSwitch = true; + //liquid phase has to disappear + std::cout << "Liquid Phase disappears at vertex " << dofIdxGlobal + << ", coordinated: " << globalPos << ", Sl: " + << volVars.saturation(wPhaseIdx) << std::endl; + newPhasePresence = nPhaseOnly; + + //switch not depending on formulation + //switch "Sl" to "xgH20" + priVars[switchIdx] = volVars.moleFraction(nPhaseIdx, wCompIdx /*H2O*/); + //Here unlike 2pnc model we do not switch all components to to mole fraction in gas phase + } + //if saturation of gas phase is smaller than 0 switch + else if (volVars.saturation(nPhaseIdx) <= Smin) + { + wouldSwitch = true; + //gas phase has to disappear + std::cout << "Gas Phase disappears at vertex " << dofIdxGlobal + << ", coordinated: " << globalPos << ", Sg: " + << volVars.saturation(nPhaseIdx) << std::endl; + newPhasePresence = wPhaseOnly; + + //switch "Sl" to "xlN2" + priVars[switchIdx] = volVars.moleFraction(wPhaseIdx, nCompIdx /*N2*/); + } + } + else if (phasePresence == nPhaseOnly) + { + Scalar sumxl = 0; + //Calculate sum of mole fractions (water and air) in the hypothetical liquid phase + for (int compIdx = 0; compIdx < numComponents; compIdx++) + { + sumxl += volVars.moleFraction(wPhaseIdx, compIdx); + } + Scalar xlmax = 1.0; + if (sumxl > xlmax) + wouldSwitch = true; + if (this->wasSwitched_[dofIdxGlobal]) + xlmax *=1.02; + + //if the sum of the mole fractions would be larger than + //1, wetting phase appears + if (sumxl/*sum of mole fractions*/ > xlmax/*1*/) + { + // liquid phase appears + std::cout << "Liquid Phase appears at vertex " << dofIdxGlobal + << ", coordinated: " << globalPos << ", sumxl: " + << sumxl << std::endl; + newPhasePresence = bothPhases; + if (formulation == pgSl) + priVars[switchIdx] = 0.0; + else if (formulation == plSg) + priVars[switchIdx] = 1.0; + //Here unlike 2pnc model we do not switch all components to to mole fraction in gas phase + } + } + else if (phasePresence == wPhaseOnly) + { + Scalar xgmax = 1; + Scalar sumxg = 0; + //Calculate sum of mole fractions in the hypothetical gas phase + for (int compIdx = 0; compIdx < numComponents; compIdx++) + { + sumxg += volVars.moleFraction(nPhaseIdx, compIdx); + } + if (sumxg > xgmax) + wouldSwitch = true; + if (this->wasSwitched_[dofIdxGlobal]) + xgmax *=1.02; + //liquid phase appears if sum is larger than one + if (sumxg > xgmax) + { + std::cout << "Gas Phase appears at vertex " << dofIdxGlobal + << ", coordinated: " << globalPos << ", sumxg: " + << sumxg << std::endl; + newPhasePresence = bothPhases; + //saturation of the liquid phase set to 0.9999 (if formulation pgSl and vice versa) + if (formulation == pgSl) + priVars[switchIdx] = 0.999; + else if (formulation == plSg) + priVars[switchIdx] = 0.001; + + } + } + this->phasePresence_[dofIdxGlobal] = newPhasePresence; + this->wasSwitched_[dofIdxGlobal] = wouldSwitch; + return phasePresence != newPhasePresence; + } +}; + +} // end namespace dumux + +#endif diff --git a/dumux/porousmediumflow/2pncmin/implicit/properties.hh b/dumux/porousmediumflow/2pncmin/implicit/properties.hh index 2171d0bc83d893c6433d16a41e84b7a8428bc503..d024b672cba96849357931400f8aceba7d64b05e 100644 --- a/dumux/porousmediumflow/2pncmin/implicit/properties.hh +++ b/dumux/porousmediumflow/2pncmin/implicit/properties.hh @@ -45,6 +45,11 @@ NEW_TYPE_TAG(TwoPNCMin, INHERITS_FROM(TwoPNC)); NEW_TYPE_TAG(BoxTwoPNCMin, INHERITS_FROM(BoxModel, TwoPNCMin)); NEW_TYPE_TAG(CCTwoPNCMin, INHERITS_FROM(CCModel, TwoPNCMin)); +//! The type tag for the non-isothermal two phase n component mineralisation problems +NEW_TYPE_TAG(TwoPNCMinNI, INHERITS_FROM(TwoPNCMin, NonIsothermal)); +NEW_TYPE_TAG(BoxTwoPNCMinNI, INHERITS_FROM(BoxModel, TwoPNCMinNI)); +NEW_TYPE_TAG(CCTwoPNCMinNI, INHERITS_FROM(CCModel, TwoPNCMinNI)); + ////////////////////////////////////////////////////////////////// // Property tags ////////////////////////////////////////////////////////////////// diff --git a/dumux/porousmediumflow/2pncmin/implicit/propertydefaults.hh b/dumux/porousmediumflow/2pncmin/implicit/propertydefaults.hh index b5228ad6100f5b92ec3f5a69485ef2921de76264..6f9ef2fab548587006fdac99158f3c53c0d09250 100644 --- a/dumux/porousmediumflow/2pncmin/implicit/propertydefaults.hh +++ b/dumux/porousmediumflow/2pncmin/implicit/propertydefaults.hh @@ -31,9 +31,9 @@ #include "indices.hh" #include "model.hh" #include "indices.hh" -#include "fluxvariables.hh" #include "volumevariables.hh" #include "properties.hh" +#include "primaryvariableswitch.hh" #include <dumux/porousmediumflow/2pnc/implicit/newtoncontroller.hh> #include <dumux/porousmediumflow/implicit/darcyfluxvariables.hh> @@ -119,8 +119,8 @@ SET_TYPE_PROP(TwoPNCMin, Model, TwoPNCMinModel<TypeTag>); //! the VolumeVariables property SET_TYPE_PROP(TwoPNCMin, VolumeVariables, TwoPNCMinVolumeVariables<TypeTag>); -//! the FluxVariables property -SET_TYPE_PROP(TwoPNCMin, FluxVariables, TwoPNCMinFluxVariables<TypeTag>); +//! the primary variable switch +SET_TYPE_PROP(TwoPNCMin, PrimaryVariableSwitch, TwoPNCMinPrimaryVariableSwitch<TypeTag>); //! The indices required by the isothermal 2pNcMin model SET_TYPE_PROP(TwoPNCMin, Indices, TwoPNCMinIndices <TypeTag, /*PVOffset=*/0>); @@ -136,6 +136,33 @@ SET_BOOL_PROP(TwoPNCMin, useSalinity, false); // (Nield, Bejan, Convection in porous media, 2006, p. 10) SET_SCALAR_PROP(TwoPNCMin, SpatialParamsForchCoeff, 0.55); +//! Somerton is used as default model to compute the effective thermal heat conductivity +SET_PROP(TwoPNCMinNI, ThermalConductivityModel) +{ +private: + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using Indices = typename GET_PROP_TYPE(TypeTag, Indices); +public: + using type = ThermalConductivitySomerton<Scalar, Indices>; +}; + +/*! + * \brief Set the property for the number of equations. + * For each component and each precipitated mineral/solid phase one equation has to + * be solved. + */ +SET_PROP(TwoPNCMinNI, IsothermalNumEq) +{ +private: + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + +public: + static const int value = FluidSystem::numComponents + FluidSystem::numSPhases; +}; + +//! The indices required by the isothermal 2pNcMin model +SET_TYPE_PROP(TwoPNCMinNI, IsothermalIndices, TwoPNCMinIndices <TypeTag, /*PVOffset=*/0>); + } } diff --git a/dumux/porousmediumflow/2pncmin/implicit/volumevariables.hh b/dumux/porousmediumflow/2pncmin/implicit/volumevariables.hh index e775435e054bbf4d34b6b088d151d734cde7a686..f681c635fbe0b23f4fe7b70a3de140f335836825 100644 --- a/dumux/porousmediumflow/2pncmin/implicit/volumevariables.hh +++ b/dumux/porousmediumflow/2pncmin/implicit/volumevariables.hh @@ -25,9 +25,6 @@ #ifndef DUMUX_2PNCMIN_VOLUME_VARIABLES_HH #define DUMUX_2PNCMIN_VOLUME_VARIABLES_HH -#include <vector> -#include <iostream> - #include <dumux/common/math.hh> #include <dumux/implicit/model.hh> #include <dumux/material/fluidstates/compositional.hh> @@ -50,19 +47,22 @@ namespace Dumux template <class TypeTag> class TwoPNCMinVolumeVariables : public TwoPNCVolumeVariables<TypeTag> { - typedef TwoPNCVolumeVariables<TypeTag> ParentType; - typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) Implementation; - - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; - typedef typename GET_PROP_TYPE(TypeTag, MaterialLawParams) MaterialLawParams; - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + // base type is used for energy related quantites + using BaseType = ImplicitVolumeVariables<TypeTag>; + + using ParentType = TwoPNCVolumeVariables<TypeTag>; + using Implementation = typename GET_PROP_TYPE(TypeTag, VolumeVariables); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using Grid = typename GET_PROP_TYPE(TypeTag, Grid); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry); + using SubControlVolume = typename GET_PROP_TYPE(TypeTag, SubControlVolume); + using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + using MaterialLaw = typename GET_PROP_TYPE(TypeTag, MaterialLaw); + using MaterialLawParams = typename GET_PROP_TYPE(TypeTag, MaterialLawParams); + using Indices = typename GET_PROP_TYPE(TypeTag, Indices); enum { @@ -99,17 +99,18 @@ class TwoPNCMinVolumeVariables : public TwoPNCVolumeVariables<TypeTag> useSalinity = GET_PROP_VALUE(TypeTag, useSalinity) }; - typedef typename GridView::template Codim<0>::Entity Element; - typedef Dune::FieldVector<Scalar, dimWorld> GlobalPosition; - typedef typename Grid::ctype CoordScalar; - typedef Dumux::Miscible2pNCComposition<Scalar, FluidSystem> Miscible2pNCComposition; - typedef Dumux::ComputeFromReferencePhase2pNCMin<Scalar, FluidSystem> ComputeFromReferencePhase2pNCMin; + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = Dune::FieldVector<Scalar, dimWorld>; + using CoordScalar = typename Grid::ctype; + using Miscible2pNCComposition = Dumux::Miscible2pNCComposition<Scalar, FluidSystem>; + using ComputeFromReferencePhase2pNCMin = Dumux::ComputeFromReferencePhase2pNCMin<Scalar, FluidSystem>; enum { isBox = GET_PROP_VALUE(TypeTag, ImplicitIsBox) }; enum { dofCodim = isBox ? dim : 0 }; + public: - typedef typename GET_PROP_TYPE(TypeTag, FluidState) FluidState; + using FluidState = typename GET_PROP_TYPE(TypeTag, FluidState); /*! * \copydoc ImplicitVolumeVariables::update @@ -117,22 +118,15 @@ public: void update(const PrimaryVariables &priVars, const Problem &problem, const Element &element, - const FVElementGeometry &fvGeometry, - int scvIdx, - bool isOldSol) + const SubControlVolume& scv) { - ParentType::update(priVars, problem, element, fvGeometry, scvIdx, isOldSol); - completeFluidState(priVars, problem, element, fvGeometry, scvIdx, this->fluidState_, isOldSol); + // Update parent type (also completes the fluid state) + ParentType::update(priVars, problem, element, scv); ///////////// // calculate the remaining quantities ///////////// - // porosity evaluation - initialPorosity_ = problem.spatialParams().porosity(element, fvGeometry, scvIdx); - minimumPorosity_ = problem.spatialParams().porosityMin(element, fvGeometry, scvIdx); - - sumPrecipitates_ = 0.0; for(int sPhaseIdx = 0; sPhaseIdx < numSPhases; ++sPhaseIdx) { @@ -151,73 +145,39 @@ public: // porosity value, as the porous media media properties change related to salt precipitation will not be // accounted otherwise. - // this->porosity_ = initialPorosity_ - sumPrecipitates_; - - this->porosity_ = std::max(minimumPorosity_, std::max(0.0, initialPorosity_ - sumPrecipitates_)); + // overwrite porosity + // porosity evaluation + auto initialPorosity = problem.spatialParams().porosity(scv); + auto minimumPorosity = problem.spatialParams().porosityMin(scv); + this->porosity_ = std::max(minimumPorosity, std::max(0.0, initialPorosity - sumPrecipitates_)); salinity_= 0.0; moleFractionSalinity_ = 0.0; for (int compIdx = numMajorComponents; compIdx< numComponents; compIdx++) //sum of the mass fraction of the components { - if(this->fluidState_.moleFraction(wPhaseIdx, compIdx)> 0) + if(this->fluidState_.moleFraction(wPhaseIdx, compIdx) > 0) { salinity_+= this->fluidState_.massFraction(wPhaseIdx, compIdx); moleFractionSalinity_ += this->fluidState_.moleFraction(wPhaseIdx, compIdx); } } - - // TODO/FIXME: Different relations for the porosoty-permeability changes are given here. We have to fins a way - // so that one can select the relation form the input file. - - // kozeny-Carman relation - permeabilityFactor_ = std::pow(((1-initialPorosity_)/(1-this->porosity_)), 2) - * std::pow((this->porosity_/initialPorosity_), 3); - - // Verma-Pruess relation - // permeabilityFactor_ = 100 * std::pow(((this->porosity_/initialPorosity_)-0.9),2); - - // Modified Fair-Hatch relation with final porosity set to 0.2 and E1=1 - // permeabilityFactor_ = std::pow((this->porosity_/initialPorosity_),3) - // * std::pow((std::pow((1 - initialPorosity_),2/3))+(std::pow((0.2 - initialPorosity_),2/3)),2) - // / std::pow((std::pow((1 -this->porosity_),2/3))+(std::pow((0.2 -this->porosity_),2/3)),2); - - //Timur relation with residual water saturation set to 0.001 - // permeabilityFactor_ = 0.136 * (std::pow(this->porosity_,4.4)) / (2000 * (std::pow(0.001,2))); - - //Timur relation1 with residual water saturation set to 0.001 - // permeabilityFactor_ = 0.136 * (std::pow(this->porosity_,4.4)) / (200000 * (std::pow(0.001,2))); - - // Bern. relation - // permeabilityFactor_ = std::pow((this->porosity_/initialPorosity_),8); - - //Tixier relation with residual water saturation set to 0.001 - // permeabilityFactor_ = (std::pow((250 * (std::pow(this->porosity_,3)) / 0.001),2)) / initialPermeability_; - - //Coates relation with residual water saturation set to 0.001 - // permeabilityFactor_ = (std::pow((100 * (std::pow(this->porosity_,2)) * (1-0.001) / 0.001,2))) / initialPermeability_ ; - - // energy related quantities not contained in the fluid state - //asImp_().updateEnergy_(priVars, problem,element, fvGeometry, scvIdx, isOldSol); } - /*! - * \copydoc ImplicitModel::completeFluidState - * \param isOldSol Specifies whether this is the previous solution or the current one - */ + /*! + * \copydoc ImplicitModel::completeFluidState + * \param isOldSol Specifies whether this is the previous solution or the current one + */ static void completeFluidState(const PrimaryVariables& priVars, const Problem& problem, const Element& element, - const FVElementGeometry& fvGeometry, - int scvIdx, - FluidState& fluidState, - bool isOldSol = false) + const SubControlVolume& scv, + FluidState& fluidState) { - Scalar t = Implementation::temperature_(priVars, problem, element,fvGeometry, scvIdx); + Scalar t = BaseType::temperature(priVars, problem, element, scv); fluidState.setTemperature(t); - int dofIdxGlobal = problem.model().dofMapper().subIndex(element, scvIdx, dofCodim); - int phasePresence = problem.model().phasePresence(dofIdxGlobal, isOldSol); + auto phasePresence = problem.model().priVarSwitch().phasePresence(scv.dofIndex()); ///////////// // set the saturations @@ -242,7 +202,10 @@ public: DUNE_THROW(Dune::InvalidStateException, "Formulation: " << formulation << " is invalid."); } else + { DUNE_THROW(Dune::InvalidStateException, "phasePresence: " << phasePresence << " is invalid."); + } + fluidState.setSaturation(nPhaseIdx, Sg); fluidState.setSaturation(wPhaseIdx, 1.0 - Sg); @@ -251,19 +214,32 @@ public: ///////////// // calculate capillary pressure - const MaterialLawParams &materialParams = problem.spatialParams().materialLawParams(element, fvGeometry, scvIdx); - Scalar pc = MaterialLaw::pc(materialParams, 1 - Sg); + const auto& materialParams = problem.spatialParams().materialLawParams(element, scv); + auto pc = MaterialLaw::pc(materialParams, 1 - Sg); // extract the pressures - if (formulation == plSg) { + if (formulation == plSg) + { fluidState.setPressure(wPhaseIdx, priVars[pressureIdx]); + if (priVars[pressureIdx] + pc < 0.0) + DUNE_THROW(Dumux::NumericalProblem, "Capillary pressure is too low"); fluidState.setPressure(nPhaseIdx, priVars[pressureIdx] + pc); } - else if (formulation == pgSl) { + else if (formulation == pgSl) + { fluidState.setPressure(nPhaseIdx, priVars[pressureIdx]); + // Here we check for (p_g - pc) in order to ensure that (p_l > 0) + if (priVars[pressureIdx] - pc < 0.0) + { + std::cout<< " The gas pressure is p_g = "<< priVars[pressureIdx]<<", the capillary pressure p_c = "<< pc << std::endl; + DUNE_THROW(Dumux::NumericalProblem, "Capillary pressure is too high"); + } fluidState.setPressure(wPhaseIdx, priVars[pressureIdx] - pc); } - else DUNE_THROW(Dune::InvalidStateException, "Formulation: " << formulation << " is invalid."); + else + { + DUNE_THROW(Dune::InvalidStateException, "Formulation: " << formulation << " is invalid."); + } ///////////// // calculate the phase compositions @@ -287,10 +263,10 @@ public: } Miscible2pNCComposition::solve(fluidState, - paramCache, - wPhaseIdx, //known phaseIdx - /*setViscosity=*/true, - /* =*/false); + paramCache, + wPhaseIdx, //known phaseIdx + /*setViscosity=*/true, + /*setInternalEnergy=*/false); } else if (phasePresence == nPhaseOnly) { @@ -301,24 +277,23 @@ public: for (int compIdx=0; compIdx<numComponents; ++compIdx) { fugCoeffL[compIdx] = FluidSystem::fugacityCoefficient(fluidState, - paramCache, - wPhaseIdx, - compIdx); + paramCache, + wPhaseIdx, + compIdx); fugCoeffG[compIdx] = FluidSystem::fugacityCoefficient(fluidState, - paramCache, - nPhaseIdx, - compIdx); + paramCache, + nPhaseIdx, + compIdx); } for (int compIdx=numMajorComponents; compIdx<numComponents; ++compIdx) moleFrac[compIdx] = (priVars[compIdx]*fugCoeffL[compIdx]*fluidState.pressure(wPhaseIdx)) - /(fugCoeffG[compIdx]*fluidState.pressure(nPhaseIdx)); + /(fugCoeffG[compIdx]*fluidState.pressure(nPhaseIdx)); moleFrac[wCompIdx] = priVars[switchIdx]; Scalar sumMoleFracNotGas = 0; for (int compIdx=numMajorComponents; compIdx<numComponents; ++compIdx) - { sumMoleFracNotGas+=moleFrac[compIdx]; - } + sumMoleFracNotGas += moleFrac[wCompIdx]; moleFrac[nCompIdx] = 1 - sumMoleFracNotGas; @@ -338,10 +313,9 @@ public: /*setViscosity=*/true, /*setEnthalpy=*/false); - } + } else if (phasePresence == wPhaseOnly) { - // only the liquid phase is present, i.e. liquid phase // composition is stored explicitly. // extract _mass_ fractions in the gas phase @@ -379,7 +353,12 @@ public: paramCache.updateAll(fluidState); for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { - Scalar h = Implementation::enthalpy_(fluidState, paramCache, phaseIdx); + Scalar rho = FluidSystem::density(fluidState, paramCache, phaseIdx); + Scalar mu = FluidSystem::viscosity(fluidState, paramCache, phaseIdx); + Scalar h = BaseType::enthalpy(fluidState, paramCache, phaseIdx); + + fluidState.setDensity(phaseIdx, rho); + fluidState.setViscosity(phaseIdx, mu); fluidState.setEnthalpy(phaseIdx, h); } } @@ -396,38 +375,24 @@ public: * \brief Returns the inital porosity of the * pure, precipitate-free porous medium */ - Scalar initialPorosity() const - { return initialPorosity_;} - - /*! - * \brief Returns the inital permeability of the - * pure, precipitate-free porous medium - */ - Scalar initialPermeability() const - { return initialPermeability_;} - - /*! - * \brief Returns the factor for the reduction of the initial permeability - * due precipitates in the porous medium - */ - Scalar permeabilityFactor() const - { return permeabilityFactor_; } - - /*! - * \brief Returns the mole fraction of the salinity in the liquid phase - */ - Scalar moleFracSalinity() const - { - return moleFractionSalinity_; - } - - /*! - * \brief Returns the salinity (mass fraction) in the liquid phase - */ - Scalar salinity() const - { - return salinity_; - } + // Scalar initialPorosity() const + // { return initialPorosity_;} + + // /*! + // * \brief Returns the mole fraction of the salinity in the liquid phase + // */ + // Scalar moleFracSalinity() const + // { + // return moleFractionSalinity_; + // } + + // /*! + // * \brief Returns the salinity (mass fraction) in the liquid phase + // */ + // Scalar salinity() const + // { + // return salinity_; + // } /*! * \brief Returns the density of the phase for all fluid and solid phases @@ -470,56 +435,18 @@ public: * phase is equal to the phaseIdx */ Scalar molality(int phaseIdx, int compIdx) const // [moles/Kg] - { return this->fluidState_.moleFraction(phaseIdx, compIdx) - /(fluidState_.moleFraction(phaseIdx, phaseIdx) - * FluidSystem::molarMass(phaseIdx));} - -protected: - friend class TwoPNCVolumeVariables<TypeTag>; - static Scalar temperature_(const PrimaryVariables &priVars, - const Problem& problem, - const Element &element, - const FVElementGeometry &fvGeometry, - int scvIdx) { - return problem.temperatureAtPos(fvGeometry.subContVol[scvIdx].global); + return this->fluidState_.moleFraction(phaseIdx, compIdx) + /(this->fluidState_.moleFraction(phaseIdx, phaseIdx) + * FluidSystem::molarMass(phaseIdx)); } - template<class ParameterCache> - static Scalar enthalpy_(const FluidState& fluidState, - const ParameterCache& paramCache, - int phaseIdx) - { - return 0; - } - - /*! - * \brief Update all quantities for a given control volume. - * - * \param priVars The solution primary variables - * \param problem The problem - * \param element The element - * \param fvGeometry Evaluate function with solution of current or previous time step - * \param scvIdx The local index of the SCV (sub-control volume) - * \param isOldSol Evaluate function with solution of current or previous time step - */ - void updateEnergy_(const PrimaryVariables &priVars, - const Problem &problem, - const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx, - bool isOldSol) - {}; +protected: Scalar precipitateVolumeFraction_[numSPhases]; - Scalar permeabilityFactor_; - Scalar initialPorosity_; - Scalar initialPermeability_; - Scalar minimumPorosity_; Scalar sumPrecipitates_; Scalar salinity_; Scalar moleFractionSalinity_; - FluidState fluidState_; private: Implementation &asImp_() diff --git a/test/porousmediumflow/2pncmin/implicit/dissolutionproblem.hh b/test/porousmediumflow/2pncmin/implicit/dissolutionproblem.hh index 2c3bb297725c19db78bc2b5b3619aa9b32d44ce0..c08f541cc8c7bb36c6e80ff52a129e23a0d38973 100644 --- a/test/porousmediumflow/2pncmin/implicit/dissolutionproblem.hh +++ b/test/porousmediumflow/2pncmin/implicit/dissolutionproblem.hh @@ -24,6 +24,7 @@ #ifndef DUMUX_DISSOLUTION_PROBLEM_HH #define DUMUX_DISSOLUTION_PROBLEM_HH +#include <dumux/implicit/cellcentered/tpfa/properties.hh> #include <dumux/porousmediumflow/2pncmin/implicit/model.hh> #include <dumux/porousmediumflow/implicit/problem.hh> #include <dumux/material/fluidsystems/brineair.hh> @@ -40,7 +41,7 @@ namespace Properties { NEW_TYPE_TAG(DissolutionProblem, INHERITS_FROM(TwoPNCMin, DissolutionSpatialparams)); NEW_TYPE_TAG(DissolutionBoxProblem, INHERITS_FROM(BoxModel, DissolutionProblem)); -NEW_TYPE_TAG(DissolutionCCProblem, INHERITS_FROM(CCModel, DissolutionProblem)); +NEW_TYPE_TAG(DissolutionCCProblem, INHERITS_FROM(CCTpfaModel, DissolutionProblem)); // Set the grid type SET_TYPE_PROP(DissolutionProblem, Grid, Dune::YaspGrid<2>); @@ -51,8 +52,8 @@ SET_TYPE_PROP(DissolutionProblem, Problem, DissolutionProblem<TypeTag>); // Set fluid configuration SET_PROP(DissolutionProblem, FluidSystem) { - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef FluidSystems::BrineAir<Scalar, H2O<Scalar>, true/*useComplexrelations=*/> type; + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using type = FluidSystems::BrineAir<Scalar, H2O<Scalar>, true/*useComplexrelations=*/>; }; // Set the spatial parameters @@ -62,7 +63,7 @@ SET_TYPE_PROP(DissolutionProblem, SpatialParams, DissolutionSpatialparams<TypeTa SET_BOOL_PROP(DissolutionProblem, ProblemEnableGravity, true); //Set properties here to override the default property settings in the model. -SET_INT_PROP(DissolutionProblem, ReplaceCompEqIdx, 1); +SET_INT_PROP(DissolutionProblem, ReplaceCompEqIdx, 1); //! Replace gas balance by total mass balance SET_INT_PROP(DissolutionProblem, Formulation, TwoPNCFormulation::pgSl); } @@ -87,12 +88,12 @@ SET_INT_PROP(DissolutionProblem, Formulation, TwoPNCFormulation::pgSl); template <class TypeTag> class DissolutionProblem : public ImplicitPorousMediaProblem<TypeTag> { - typedef ImplicitPorousMediaProblem<TypeTag> ParentType; - typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + using ParentType = ImplicitPorousMediaProblem<TypeTag>; + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + using VolumeVariables = typename GET_PROP_TYPE(TypeTag, VolumeVariables); + using Indices = typename GET_PROP_TYPE(TypeTag, Indices); enum { @@ -128,20 +129,20 @@ class DissolutionProblem : public ImplicitPorousMediaProblem<TypeTag> }; - 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 GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; - - 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 Dune::FieldVector<Scalar, dimWorld> GlobalPosition; + using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables); + using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes); + using TimeManager = typename GET_PROP_TYPE(TypeTag, TimeManager); + using ElementVolumeVariables = typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables); + using Element = typename GridView::template Codim<0>::Entity; + using Vertex = typename GridView::template Codim<dim>::Entity; + using Intersection = typename GridView::Intersection; + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry); + using SubControlVolume = typename GET_PROP_TYPE(TypeTag, SubControlVolume); + using GlobalPosition = Dune::FieldVector<Scalar, dimWorld>; public: DissolutionProblem(TimeManager &timeManager, const GridView &gridView) - : ParentType(timeManager, gridView) + : ParentType(timeManager, gridView) { outerSalinity_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Problem, OuterSalinity); @@ -165,9 +166,6 @@ public: temperatureLow_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, FluidSystem, TemperatureLow); temperatureHigh_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, FluidSystem, TemperatureHigh); name_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, std::string, Problem, Name); - freqMassOutput_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, int, Output, FreqMassOutput); - storageLastTimestep_ = 0.0; - lastMassOutputTime_ = 0.0; outfile.open("evaporation.out"); outfile << "time; evaporationRate" << std::endl; @@ -203,7 +201,7 @@ public: * * This is used as a prefix for files generated by the simulation. */ - const std::string name() const + const std::string& name() const { return name_; } /*! @@ -223,9 +221,11 @@ public: * \brief Specifies which kind of boundary condition should be * used for which equation on a given boundary segment. */ - void boundaryTypesAtPos(BoundaryTypes &bcTypes, const GlobalPosition &globalPos) const + BoundaryTypes boundaryTypesAtPos(const GlobalPosition &globalPos) const { - const Scalar rmax = this->bBoxMax()[0]; // outerRadius_; + BoundaryTypes bcTypes; + + const Scalar rmax = this->bBoxMax()[0]; const Scalar rmin = this->bBoxMin()[0]; // default to Neumann @@ -238,70 +238,80 @@ public: // Constant pressure at well (Dirichlet condition) if(globalPos[0] < rmin + eps_) bcTypes.setAllDirichlet(); + + return bcTypes; } /*! * \brief Evaluate the boundary conditions for a dirichlet * boundary segment. - * - * For this method, the \a values parameter stores primary variables. */ - void dirichletAtPos(PrimaryVariables &values, const GlobalPosition &globalPos) const + PrimaryVariables dirichletAtPos(const GlobalPosition &globalPos) const { + PrimaryVariables priVars(0.0); + const Scalar rmax = this->bBoxMax()[0]; const Scalar rmin = this->bBoxMin()[0]; if(globalPos[0] > rmax - eps_) { - values[pressureIdx] = outerPressure_ ; // Outer boundary pressure bar - values[switchIdx] = outerLiqSaturation_; // Saturation outer boundary - values[xlNaClIdx] = massTomoleFrac_(outerSalinity_);// mole fraction salt - values[precipNaClIdx] = 0.0;// precipitated salt + priVars[pressureIdx] = outerPressure_ ; // Outer boundary pressure bar + priVars[switchIdx] = outerLiqSaturation_; // Saturation outer boundary + priVars[xlNaClIdx] = massToMoleFrac_(outerSalinity_);// mole fraction salt + priVars[precipNaClIdx] = 0.0;// precipitated salt } if(globalPos[0] < rmin + eps_) { - values[pressureIdx] = innerPressure_ ; // Inner boundary pressure bar - values[switchIdx] = innerLiqSaturation_; // Saturation inner boundary - values[xlNaClIdx] = massTomoleFrac_(innerSalinity_);// mole fraction salt - values[precipNaClIdx] = 0.0;// precipitated salt + priVars[pressureIdx] = innerPressure_ ; // Inner boundary pressure bar + priVars[switchIdx] = innerLiqSaturation_; // Saturation inner boundary + priVars[xlNaClIdx] = massToMoleFrac_(innerSalinity_);// mole fraction salt + priVars[precipNaClIdx] = 0.0;// precipitated salt } + + return priVars; } /*! * \brief Evaluate the boundary conditions for a neumann * boundary segment. * - * For this method, the \a values parameter stores the mass flux + * For this method, the \a priVars parameter stores the mass flux * in normal direction of each component. Negative values mean * influx. */ - void neumann(PrimaryVariables &values, - const Element &element, - const FVElementGeometry &fvGeometry, - const Intersection &is, - int scvIdx, - int boundaryFaceIdx) const + PrimaryVariables neumannAtPos(const GlobalPosition& globalPos) const { - values = 0.0; + return PrimaryVariables(0.0); } /*! * \brief Evaluate the initial value for a control volume. * + * \param values The initial values for the primary variables + * \param element The finite element + * \param fvGeometry The finite-volume geometry + * \param scvIdx The local subcontrolvolume index + * * For this method, the \a values parameter stores primary * variables. */ - void initialAtPos(PrimaryVariables &values, const GlobalPosition &globalPos) const + PrimaryVariables initial(const SubControlVolume &scv) const { - values[pressureIdx] = reservoirPressure_; - values[switchIdx] = initLiqSaturation_; // Sl primary variable - values[xlNaClIdx] = massTomoleFrac_(outerSalinity_); // mole fraction + PrimaryVariables priVars(0.0); + + const auto& globalPos = scv.dofPosition(); + + priVars[pressureIdx] = reservoirPressure_; + priVars[switchIdx] = initLiqSaturation_; // Sl primary variable + priVars[xlNaClIdx] = massToMoleFrac_(outerSalinity_); // mole fraction if(globalPos[0] > 5.0 - eps_ && globalPos[0] < 20.0 - eps_) - values[precipNaClIdx] = initPrecipitatedSalt2_; // [kg/m^3] + priVars[precipNaClIdx] = initPrecipitatedSalt2_; // [kg/m^3] else - values[precipNaClIdx] = initPrecipitatedSalt1_; // [kg/m^3] + priVars[precipNaClIdx] = initPrecipitatedSalt1_; // [kg/m^3] + + return priVars; } /*! @@ -313,58 +323,67 @@ public: * \brief Evaluate the source term for all phases within a given * sub-control-volume. * - * For this method, the \a values parameter stores the rate mass - * of a component is generated or annihilate per volume - * unit. Positive values mean that mass is created, negative ones - * mean that it vanishes. + * This is the method for the case where the source term is + * potentially solution dependent and requires some quantities that + * are specific to the fully-implicit method. + * + * \param values The source and sink values for the conservation equations in units of + * \f$ [ \textnormal{unit of conserved quantity} / (m^3 \cdot s )] \f$ + * \param element The finite element + * \param fvGeometry The finite-volume geometry + * \param elemVolVars All volume variables for the element + * \param scv The subcontrolvolume + * + * For this method, the \a values parameter stores the conserved quantity rate + * generated or annihilate per volume unit. Positive values mean + * that the conserved quantity is created, negative ones mean that it vanishes. + * E.g. for the mass balance that would be a mass rate in \f$ [ kg / (m^3 \cdot s)] \f$. */ - void solDependentSource(PrimaryVariables &source, - const Element &element, - const FVElementGeometry &fvGeometry, - int scvIdx, - const ElementVolumeVariables &elemVolVars) const + PrimaryVariables source(const Element &element, + const FVElementGeometry& fvGeometry, + const ElementVolumeVariables& elemVolVars, + const SubControlVolume &scv) const { - source = 0; - const auto& volVars = elemVolVars[scvIdx]; + PrimaryVariables source(0.0); + + const auto& volVars = elemVolVars[scv]; + Scalar moleFracNaCl_lPhase = volVars.moleFraction(wPhaseIdx, NaClIdx); Scalar moleFracNaCl_gPhase = volVars.moleFraction(nPhaseIdx, NaClIdx); - Scalar massFracNaCl_Max_lPhase = this->spatialParams().SolubilityLimit(); - Scalar moleFracNaCl_Max_lPhase = massTomoleFrac_(massFracNaCl_Max_lPhase); + Scalar massFracNaCl_Max_lPhase = this->spatialParams().solubilityLimit(); + Scalar moleFracNaCl_Max_lPhase = massToMoleFrac_(massFracNaCl_Max_lPhase); Scalar moleFracNaCl_Max_gPhase = moleFracNaCl_Max_lPhase / volVars.pressure(nPhaseIdx); - Scalar saltPorosity = this->spatialParams().porosityMin(element, fvGeometry, scvIdx); + Scalar saltPorosity = this->spatialParams().porosityMin(scv); // liquid phase Scalar precipSalt = volVars.porosity() * volVars.molarDensity(wPhaseIdx) * volVars.saturation(wPhaseIdx) - * std::abs(moleFracNaCl_lPhase - moleFracNaCl_Max_lPhase); - - if (moleFracNaCl_lPhase < moleFracNaCl_Max_lPhase) - precipSalt *= -1; + * (moleFracNaCl_lPhase - moleFracNaCl_Max_lPhase); // gas phase - if (moleFracNaCl_gPhase > moleFracNaCl_Max_gPhase) - precipSalt += volVars.porosity() * volVars.molarDensity(nPhaseIdx) - * volVars.saturation(nPhaseIdx) - * std::abs(moleFracNaCl_gPhase - moleFracNaCl_Max_gPhase); + precipSalt += volVars.porosity() * volVars.molarDensity(nPhaseIdx) + * volVars.saturation(nPhaseIdx) + * (moleFracNaCl_gPhase - moleFracNaCl_Max_gPhase); // make sure we don't disolve more salt than previously precipitated if (precipSalt*this->timeManager().timeStepSize() + volVars.precipitateVolumeFraction(sPhaseIdx)* volVars.molarDensity(sPhaseIdx)< 0) - precipSalt = - volVars.precipitateVolumeFraction(sPhaseIdx)* volVars.molarDensity(sPhaseIdx)/this->timeManager().timeStepSize(); + precipSalt = -volVars.precipitateVolumeFraction(sPhaseIdx)* volVars.molarDensity(sPhaseIdx)/this->timeManager().timeStepSize(); - if (volVars.precipitateVolumeFraction(sPhaseIdx) >= volVars.initialPorosity() - saltPorosity && precipSalt > 0) + if (volVars.precipitateVolumeFraction(sPhaseIdx) >= this->spatialParams().porosity(scv) - saltPorosity && precipSalt > 0) precipSalt = 0; source[conti0EqIdx + NaClIdx] += -precipSalt; source[precipNaClEqIdx] += precipSalt; - Valgrind::CheckDefined(source); + return source; } + /*! * \brief Return the initial phase state inside a control volume. + * + * \param scv The sub control volume */ - int initialPhasePresence(const Element& element, - const FVElementGeometry& fvGeometry, - int scvIdx) const + int initialPhasePresence(const SubControlVolume& scv) const { return bothPhases; } @@ -376,7 +395,7 @@ private: * * \param XlNaCl the XlNaCl [kg NaCl / kg solution] */ - static Scalar massTomoleFrac_(Scalar XlNaCl) + static Scalar massToMoleFrac_(Scalar XlNaCl) { const Scalar Mw = 18.015e-3; /* molecular weight of water [kg/mol] */ const Scalar Ms = 58.44e-3; /* molecular weight of NaCl [kg/mol] */ @@ -389,9 +408,6 @@ private: int nTemperature_; int nPressure_; - int freqMassOutput_; - PrimaryVariables storageLastTimestep_; - Scalar lastMassOutputTime_; std::string name_; Scalar pressureLow_, pressureHigh_; @@ -412,6 +428,7 @@ private: std::ofstream outfile; }; -} //end namespace + +} //end namespace Dumux #endif diff --git a/test/porousmediumflow/2pncmin/implicit/dissolutionspatialparams.hh b/test/porousmediumflow/2pncmin/implicit/dissolutionspatialparams.hh index e0e993dea4aae8870f520a319b9fdcb4b0c2866c..6f7683be3392f307a5f0cc9f7ac8b5cbf204786b 100644 --- a/test/porousmediumflow/2pncmin/implicit/dissolutionspatialparams.hh +++ b/test/porousmediumflow/2pncmin/implicit/dissolutionspatialparams.hh @@ -44,12 +44,13 @@ SET_PROP(DissolutionSpatialparams, MaterialLaw) { private: // define the material law which is parameterized by effective saturations - typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); public: // define the material law parameterized by absolute saturations - typedef EffToAbsLaw<RegularizedBrooksCorey<Scalar> > type; + using type = EffToAbsLaw<RegularizedBrooksCorey<Scalar>>; }; -} + +} // end namespace Properties /** * \brief Definition of the spatial parameters for the brine-co2 problem @@ -58,36 +59,39 @@ public: template<class TypeTag> class DissolutionSpatialparams : 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 GET_PROP_TYPE(TypeTag, MaterialLawParams) MaterialLawParams; - typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; - typedef typename GridView::ctype CoordScalar; + using ParentType = ImplicitSpatialParams<TypeTag>; + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using MaterialLawParams = typename GET_PROP_TYPE(TypeTag, MaterialLawParams); + using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); + using VolumeVariables = typename GET_PROP_TYPE(TypeTag, VolumeVariables); + using CoordScalar = typename GridView::ctype; enum { dim=GridView::dimension, dimWorld=GridView::dimensionworld, }; - typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + using Indices = typename GET_PROP_TYPE(TypeTag, Indices); enum { wPhaseIdx = FluidSystem::wPhaseIdx, nPhaseIdx = FluidSystem::nPhaseIdx, }; - typedef Dune::FieldVector<CoordScalar, dimWorld> GlobalPosition; - typedef Dune::FieldMatrix<CoordScalar, dimWorld, dimWorld> Tensor; - typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; - typedef typename GridView::template Codim<0>::Entity Element; + using GlobalPosition = Dune::FieldVector<CoordScalar, dimWorld>; + using Tensor = Dune::FieldMatrix<CoordScalar, dimWorld, dimWorld>; + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry); + using SubControlVolume = typename GET_PROP_TYPE(TypeTag, SubControlVolume); + using Element = typename GridView::template Codim<0>::Entity; public: - DissolutionSpatialparams(const GridView &gridView) - : ParentType(gridView), K_(0.0) + DissolutionSpatialparams(const Problem& problem, const GridView &gridView) + : ParentType(problem, gridView), initialPermeability_(0.0) { // set main diagonal entries of the permeability tensor to a value // setting to one value means: isotropic, homogeneous for (int i = 0; i < dim; i++) - K_[i][i] = 2.23e-14; + initialPermeability_[i][i] = 2.23e-14; // residual saturations materialParams_.setSwr(0.2); @@ -105,15 +109,22 @@ public: * \param fvGeometry The finite-volume geometry in the box scheme * \param scvIdx The local vertex index * - * Alternatively, the function intrinsicPermeabilityAtPos(const GlobalPosition& globalPos) - * could be defined, where globalPos is the vector including the global coordinates - * of the finite volume. + * Solution dependent permeability function */ - const Tensor& intrinsicPermeability(const Element &element, - const FVElementGeometry &fvGeometry, - const int scvIdx) const + Tensor solDependentIntrinsicPermeability(const SubControlVolume& scv, + const VolumeVariables& volVars) const { - return K_; + // Kozeny-Carman relation + auto initialPorosity = porosity(scv); + auto factor = std::pow((1-initialPorosity)/(1-volVars.porosity()), 2) + * std::pow(volVars.porosity()/initialPorosity, 3); + + auto perm = initialPermeability_; + + for (int i = 0; i < dim; i++) + perm[i][i] *= factor; + + return perm; } /*! @@ -124,12 +135,10 @@ public: * \param scvIdx The local index of the sub-control volume where * the porosity needs to be defined */ - Scalar porosityMin(const Element &element, - const FVElementGeometry &fvGeometry, - int scvIdx) const - { + Scalar porosityMin(const SubControlVolume &scv) const + { return 1e-5; - } + } /*! * \brief Define the minimum porosity \f$[-]\f$ after clogging caused by mineralization @@ -139,30 +148,24 @@ public: * \param scvIdx The local index of the sub-control volume where * the porosity needs to be defined */ - Scalar porosity(const Element &element, - const FVElementGeometry &fvGeometry, - int scvIdx) const - { + Scalar porosity(const SubControlVolume &scv) const + { return 0.11; - } + } - Scalar solidity(const Element &element, - const FVElementGeometry &fvGeometry, - int scvIdx) const + Scalar solidity(const SubControlVolume &scv) const { - return 1.0 - porosity(element, fvGeometry, scvIdx); + return 1.0 - porosity(scv); } - Scalar SolubilityLimit() const + Scalar solubilityLimit() const { return 0.26; } - Scalar theta(const Element &element, - const FVElementGeometry &fvGeometry, - int scvIdx) const + Scalar theta(const SubControlVolume &scv) const { return 10.0; } @@ -170,17 +173,16 @@ public: // return the brooks-corey context depending on the position const MaterialLawParams& materialLawParams(const Element &element, - const FVElementGeometry &fvGeometry, - int scvIdx) const + const SubControlVolume &scv) const { return materialParams_; } private: - Tensor K_; + Tensor initialPermeability_; MaterialLawParams materialParams_; }; -} +} // end namespace Dumux #endif