From 3b7ffc22180a19ff600fd1eca49f53c1273024a7 Mon Sep 17 00:00:00 2001 From: Andreas Lauser <and@poware.org> Date: Wed, 2 Mar 2011 16:42:52 +0000 Subject: [PATCH] make the modified 2p2c(ni) models run and rename them to new_2p2c(ni) also, restore the 2p2c(ni) models to their previous state.in order to compile the new_2p2c(ni) tests, you have to set symbolic links to the devel repository for the following the direcories dumux/material/fluidstates dumux/material/new_fluidsystems dumux/material/eos dumux/material/constraintsolvers git-svn-id: svn://svn.iws.uni-stuttgart.de/DUMUX/dumux/trunk@5335 2fb0f335-1f38-0410-981e-8018bf24f1b0 --- configure.ac | 4 + dumux/boxmodels/2p2c/2p2cfluidstate.hh | 2 - dumux/boxmodels/2p2c/2p2cfluxvariables.hh | 8 +- dumux/boxmodels/2p2c/2p2clocalresidual.hh | 13 +- dumux/boxmodels/2p2c/2p2cmodel.hh | 24 +- dumux/boxmodels/2p2c/2p2cnewtoncontroller.hh | 2 +- dumux/boxmodels/2p2c/2p2cvolumevariables.hh | 314 ++------ dumux/boxmodels/2p2cni/2p2cnilocalresidual.hh | 10 +- .../boxmodels/2p2cni/2p2cnivolumevariables.hh | 93 ++- dumux/boxmodels/Makefile.am | 2 +- .../new_2p2c/2p2cboundaryvariables.hh | 252 ++++++ dumux/boxmodels/new_2p2c/2p2cfluidstate.hh | 371 +++++++++ dumux/boxmodels/new_2p2c/2p2cfluxvariables.hh | 382 +++++++++ dumux/boxmodels/new_2p2c/2p2cindices.hh | 128 +++ dumux/boxmodels/new_2p2c/2p2clocalresidual.hh | 342 ++++++++ dumux/boxmodels/new_2p2c/2p2cmodel.hh | 760 ++++++++++++++++++ .../new_2p2c/2p2cnewtoncontroller.hh | 181 +++++ dumux/boxmodels/new_2p2c/2p2cproblem.hh | 129 +++ dumux/boxmodels/new_2p2c/2p2cproperties.hh | 68 ++ .../new_2p2c/2p2cpropertydefaults.hh | 165 ++++ .../boxmodels/new_2p2c/2p2cvolumevariables.hh | 456 +++++++++++ dumux/boxmodels/new_2p2c/Makefile.am | 4 + .../new_2p2cni/2p2cniboundaryvariables.hh | 272 +++++++ .../new_2p2cni/2p2cnifluxvariables.hh | 131 +++ dumux/boxmodels/new_2p2cni/2p2cniindices.hh | 57 ++ .../new_2p2cni/2p2cnilocalresidual.hh | 183 +++++ dumux/boxmodels/new_2p2cni/2p2cnimodel.hh | 108 +++ dumux/boxmodels/new_2p2cni/2p2cniproblem.hh | 80 ++ .../boxmodels/new_2p2cni/2p2cniproperties.hh | 54 ++ .../new_2p2cni/2p2cnipropertydefaults.hh | 85 ++ .../new_2p2cni/2p2cnivolumevariables.hh | 129 +++ dumux/boxmodels/new_2p2cni/Makefile.am | 4 + test/boxmodels/2p2cni/waterairproblem.hh | 11 +- test/boxmodels/Makefile.am | 4 +- test/boxmodels/new_2p2c/CMakeLists.txt | 16 + test/boxmodels/new_2p2c/Makefile.am | 8 + test/boxmodels/new_2p2c/grids/test_2p2c.dgf | 10 + test/boxmodels/new_2p2c/injectionproblem.hh | 379 +++++++++ .../new_2p2c/injectionspatialparameters.hh | 268 ++++++ test/boxmodels/new_2p2c/test_2p2c.cc | 34 + test/boxmodels/new_2p2cni/CMakeLists.txt | 16 + test/boxmodels/new_2p2cni/Makefile.am | 8 + .../new_2p2cni/grids/test_2p2cni.dgf | 15 + test/boxmodels/new_2p2cni/test_2p2cni.cc | 37 + test/boxmodels/new_2p2cni/waterairproblem.hh | 397 +++++++++ .../new_2p2cni/waterairspatialparameters.hh | 269 +++++++ 46 files changed, 5951 insertions(+), 334 deletions(-) create mode 100644 dumux/boxmodels/new_2p2c/2p2cboundaryvariables.hh create mode 100644 dumux/boxmodels/new_2p2c/2p2cfluidstate.hh create mode 100644 dumux/boxmodels/new_2p2c/2p2cfluxvariables.hh create mode 100644 dumux/boxmodels/new_2p2c/2p2cindices.hh create mode 100644 dumux/boxmodels/new_2p2c/2p2clocalresidual.hh create mode 100644 dumux/boxmodels/new_2p2c/2p2cmodel.hh create mode 100644 dumux/boxmodels/new_2p2c/2p2cnewtoncontroller.hh create mode 100644 dumux/boxmodels/new_2p2c/2p2cproblem.hh create mode 100644 dumux/boxmodels/new_2p2c/2p2cproperties.hh create mode 100644 dumux/boxmodels/new_2p2c/2p2cpropertydefaults.hh create mode 100644 dumux/boxmodels/new_2p2c/2p2cvolumevariables.hh create mode 100644 dumux/boxmodels/new_2p2c/Makefile.am create mode 100644 dumux/boxmodels/new_2p2cni/2p2cniboundaryvariables.hh create mode 100644 dumux/boxmodels/new_2p2cni/2p2cnifluxvariables.hh create mode 100644 dumux/boxmodels/new_2p2cni/2p2cniindices.hh create mode 100644 dumux/boxmodels/new_2p2cni/2p2cnilocalresidual.hh create mode 100644 dumux/boxmodels/new_2p2cni/2p2cnimodel.hh create mode 100644 dumux/boxmodels/new_2p2cni/2p2cniproblem.hh create mode 100644 dumux/boxmodels/new_2p2cni/2p2cniproperties.hh create mode 100644 dumux/boxmodels/new_2p2cni/2p2cnipropertydefaults.hh create mode 100644 dumux/boxmodels/new_2p2cni/2p2cnivolumevariables.hh create mode 100644 dumux/boxmodels/new_2p2cni/Makefile.am create mode 100644 test/boxmodels/new_2p2c/CMakeLists.txt create mode 100644 test/boxmodels/new_2p2c/Makefile.am create mode 100644 test/boxmodels/new_2p2c/grids/test_2p2c.dgf create mode 100644 test/boxmodels/new_2p2c/injectionproblem.hh create mode 100644 test/boxmodels/new_2p2c/injectionspatialparameters.hh create mode 100644 test/boxmodels/new_2p2c/test_2p2c.cc create mode 100644 test/boxmodels/new_2p2cni/CMakeLists.txt create mode 100644 test/boxmodels/new_2p2cni/Makefile.am create mode 100644 test/boxmodels/new_2p2cni/grids/test_2p2cni.dgf create mode 100644 test/boxmodels/new_2p2cni/test_2p2cni.cc create mode 100644 test/boxmodels/new_2p2cni/waterairproblem.hh create mode 100644 test/boxmodels/new_2p2cni/waterairspatialparameters.hh diff --git a/configure.ac b/configure.ac index f449b1fdc3..c73c682c54 100644 --- a/configure.ac +++ b/configure.ac @@ -25,6 +25,8 @@ AC_CONFIG_FILES([dumux.pc dumux/boxmodels/2p/Makefile dumux/boxmodels/2p2c/Makefile dumux/boxmodels/2p2cni/Makefile + dumux/boxmodels/new_2p2c/Makefile + dumux/boxmodels/new_2p2cni/Makefile dumux/boxmodels/2pni/Makefile dumux/boxmodels/common/Makefile dumux/boxmodels/richards/Makefile @@ -62,6 +64,8 @@ AC_CONFIG_FILES([dumux.pc test/boxmodels/1p2c/Makefile test/boxmodels/2p2c/Makefile test/boxmodels/2p2cni/Makefile + test/boxmodels/new_2p2c/Makefile + test/boxmodels/new_2p2cni/Makefile test/boxmodels/richards/Makefile test/common/Makefile test/common/pardiso/Makefile diff --git a/dumux/boxmodels/2p2c/2p2cfluidstate.hh b/dumux/boxmodels/2p2c/2p2cfluidstate.hh index 1233d106dc..5e689939b7 100644 --- a/dumux/boxmodels/2p2c/2p2cfluidstate.hh +++ b/dumux/boxmodels/2p2c/2p2cfluidstate.hh @@ -26,7 +26,6 @@ * \brief Calculates the phase state from the primary variables in the * 2p2c model. */ -#if 0 #ifndef DUMUX_2P2C_PHASE_STATE_HH #define DUMUX_2P2C_PHASE_STATE_HH @@ -368,4 +367,3 @@ public: } // end namepace #endif -#endif diff --git a/dumux/boxmodels/2p2c/2p2cfluxvariables.hh b/dumux/boxmodels/2p2c/2p2cfluxvariables.hh index ccebd24833..828268f48e 100644 --- a/dumux/boxmodels/2p2c/2p2cfluxvariables.hh +++ b/dumux/boxmodels/2p2c/2p2cfluxvariables.hh @@ -243,7 +243,6 @@ private: const Element &element, const ElementVolumeVariables &elemDat) { -#if 0 const VolumeVariables &vDat_i = elemDat[face().i]; const VolumeVariables &vDat_j = elemDat[face().j]; @@ -260,6 +259,7 @@ private: // calculate tortuosity at the nodes i and j needed // for porous media diffusion coefficient + Scalar tau_i = 1.0/(vDat_i.porosity() * vDat_i.porosity()) * pow(vDat_i.porosity() * vDat_i.saturation(phaseIdx), 7.0/3); @@ -271,8 +271,8 @@ private: // -> harmonic mean porousDiffCoeff_[phaseIdx] = harmonicMean(vDat_i.porosity() * vDat_i.saturation(phaseIdx) * tau_i * vDat_i.diffCoeff(phaseIdx), vDat_j.porosity() * vDat_j.saturation(phaseIdx) * tau_j * vDat_j.diffCoeff(phaseIdx)); + } -#endif } public: @@ -312,13 +312,11 @@ public: int downstreamIdx(int phaseIdx) const { return downstreamIdx_[phaseIdx]; } -#if 0 /*! * \brief The binary diffusion coefficient for each fluid phase. */ Scalar porousDiffCoeff(int phaseIdx) const { return porousDiffCoeff_[phaseIdx]; }; -#endif /*! * \brief Return density \f$\mathrm{[kg/m^3]}\f$ of a phase at the integration @@ -371,10 +369,8 @@ protected: // local index of the downwind vertex for each phase int downstreamIdx_[numPhases]; -/* // the diffusion coefficient for the porous medium Scalar porousDiffCoeff_[numPhases]; -*/ }; } // end namepace diff --git a/dumux/boxmodels/2p2c/2p2clocalresidual.hh b/dumux/boxmodels/2p2c/2p2clocalresidual.hh index 73d7c658e9..937e8c635b 100644 --- a/dumux/boxmodels/2p2c/2p2clocalresidual.hh +++ b/dumux/boxmodels/2p2c/2p2clocalresidual.hh @@ -74,6 +74,8 @@ protected: typedef typename GET_PROP_TYPE(TypeTag, PTAG(PrimaryVariables)) PrimaryVariables; typedef typename GET_PROP_TYPE(TypeTag, PTAG(BoundaryTypes)) BoundaryTypes; + typedef TwoPTwoCFluidState<TypeTag> FluidState; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(TwoPTwoCIndices)) Indices; enum @@ -205,9 +207,7 @@ public: flux = 0; asImp_()->computeAdvectiveFlux(flux, vars); - Valgrind::CheckDefined(flux); asImp_()->computeDiffusiveFlux(flux, vars); - Valgrind::CheckDefined(flux); } /*! @@ -248,13 +248,6 @@ public: - mobilityUpwindAlpha) * (dn.density(phaseIdx) * dn.mobility(phaseIdx) * dn.fluidState().massFrac( phaseIdx, compIdx)); - Valgrind::CheckDefined(vars.KmvpNormal(phaseIdx)); - Valgrind::CheckDefined(up.density(phaseIdx)); - Valgrind::CheckDefined(up.mobility(phaseIdx)); - Valgrind::CheckDefined(up.fluidState().massFrac(phaseIdx, compIdx)); - Valgrind::CheckDefined(dn.density(phaseIdx)); - Valgrind::CheckDefined(dn.mobility(phaseIdx)); - Valgrind::CheckDefined(dn.fluidState().massFrac(phaseIdx, compIdx)); } } } @@ -268,7 +261,6 @@ public: */ void computeDiffusiveFlux(PrimaryVariables &flux, const FluxVariables &vars) const { -#if 0 // add diffusive flux of gas component in liquid phase Scalar tmp = - vars.porousDiffCoeff(lPhaseIdx) * @@ -284,7 +276,6 @@ public: (vars.molarConcGrad(gPhaseIdx) * vars.face().normal); flux[contiLEqIdx] += tmp * FluidSystem::molarMass(lCompIdx); flux[contiGEqIdx] -= tmp * FluidSystem::molarMass(gCompIdx); -#endif } /*! diff --git a/dumux/boxmodels/2p2c/2p2cmodel.hh b/dumux/boxmodels/2p2c/2p2cmodel.hh index c59755e211..899ee39df6 100644 --- a/dumux/boxmodels/2p2c/2p2cmodel.hh +++ b/dumux/boxmodels/2p2c/2p2cmodel.hh @@ -143,6 +143,8 @@ class TwoPTwoCModel: public BoxModel<TypeTag> formulation = GET_PROP_VALUE(TypeTag, PTAG(Formulation)) }; + typedef TwoPTwoCFluidState<TypeTag> FluidState; + typedef typename GridView::template Codim<dim>::Entity Vertex; typedef typename GridView::template Codim<0>::Entity Element; typedef typename GridView::template Codim<0>::Iterator ElementIterator; @@ -220,6 +222,9 @@ public: setSwitched_(false); resetPhasePresence_(); + /*this->localJacobian().updateStaticData(this->curSolFunction(), + this->prevSolFunction()); + */ }; /*! @@ -370,7 +375,7 @@ public: { (*massFrac[phaseIdx][compIdx])[globalIdx] = volVars.fluidState().massFrac(phaseIdx, - compIdx); + compIdx); Valgrind::CheckDefined( (*massFrac[phaseIdx][compIdx])[globalIdx][0]); @@ -638,8 +643,7 @@ protected: // perform variable switch at a vertex; Returns true if a // variable switch was performed. bool primaryVarSwitch_(SolutionVector &globalSol, - const VolumeVariables &volVars, - int globalIdx, + const VolumeVariables &volVars, int globalIdx, const GlobalPosition &globalPos) { // evaluate primary variable switch @@ -665,16 +669,14 @@ protected: if (xll + xlg > xlMax) { // liquid phase appears - /* - std::cout << "Liquid phase appears at vertex " << globalIdx + std::cout << "liquid phase appears at vertex " << globalIdx << ", coordinates: " << globalPos << ", xll + xlg: " << xll + xlg << std::endl; - */ newPhasePresence = bothPhases; if (formulation == pgSl) - globalSol[globalIdx][switchIdx] = 0.001; + globalSol[globalIdx][switchIdx] = 0.0; else if (formulation == plSg) - globalSol[globalIdx][switchIdx] = 0.999; + globalSol[globalIdx][switchIdx] = 1.0; }; } else if (phasePresence == lPhaseOnly) @@ -695,11 +697,9 @@ protected: if (xgl + xgg > xgMax) { // gas phase appears - /* std::cout << "gas phase appears at vertex " << globalIdx << ", coordinates: " << globalPos << ", x_gl + x_gg: " << xgl + xgg << std::endl; - */ newPhasePresence = bothPhases; if (formulation == pgSl) globalSol[globalIdx][switchIdx] = 0.999; @@ -717,11 +717,9 @@ protected: { wouldSwitch = true; // gas phase disappears - /* std::cout << "Gas phase disappears at vertex " << globalIdx << ", coordinates: " << globalPos << ", Sg: " << volVars.saturation(gPhaseIdx) << std::endl; - */ newPhasePresence = lPhaseOnly; globalSol[globalIdx][switchIdx] @@ -731,11 +729,9 @@ protected: { wouldSwitch = true; // liquid phase disappears - /* std::cout << "Liquid phase disappears at vertex " << globalIdx << ", coordinates: " << globalPos << ", Sl: " << volVars.saturation(lPhaseIdx) << std::endl; - */ newPhasePresence = gPhaseOnly; globalSol[globalIdx][switchIdx] diff --git a/dumux/boxmodels/2p2c/2p2cnewtoncontroller.hh b/dumux/boxmodels/2p2c/2p2cnewtoncontroller.hh index 1b4ad9376f..0001582c22 100644 --- a/dumux/boxmodels/2p2c/2p2cnewtoncontroller.hh +++ b/dumux/boxmodels/2p2c/2p2cnewtoncontroller.hh @@ -68,7 +68,7 @@ public: TwoPTwoCNewtonController() { this->setRelTolerance(1e-7); - this->setTargetSteps(10); + this->setTargetSteps(9); this->setMaxSteps(18); }; diff --git a/dumux/boxmodels/2p2c/2p2cvolumevariables.hh b/dumux/boxmodels/2p2c/2p2cvolumevariables.hh index 04734d91a2..004226f89a 100644 --- a/dumux/boxmodels/2p2c/2p2cvolumevariables.hh +++ b/dumux/boxmodels/2p2c/2p2cvolumevariables.hh @@ -33,15 +33,11 @@ #include <dumux/common/math.hh> #include <dune/common/collectivecommunication.hh> - -#include <dumux/material/fluidstates/equilibriumfluidstate.hh> -#include <dumux/material/constraintsolvers/compositionfromfugacities.hh> - #include <vector> #include <iostream> #include "2p2cproperties.hh" -#include "2p2cindices.hh" +#include "2p2cfluidstate.hh" namespace Dumux { @@ -73,34 +69,17 @@ class TwoPTwoCVolumeVariables : public BoxVolumeVariables<TypeTag> numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)), formulation = GET_PROP_VALUE(TypeTag, PTAG(Formulation)), - // component indices lCompIdx = Indices::lCompIdx, gCompIdx = Indices::gCompIdx, - // phase indices lPhaseIdx = Indices::lPhaseIdx, - gPhaseIdx = Indices::gPhaseIdx, - - // primary variable indices - pressureIdx = Indices::pressureIdx, - switchIdx = Indices::switchIdx, - - // phase states - lPhaseOnly = Indices::lPhaseOnly, - gPhaseOnly = Indices::gPhaseOnly, - bothPhases = Indices::bothPhases, - - // formulations - plSg = TwoPTwoCFormulation::plSg, - pgSl = TwoPTwoCFormulation::pgSl, + gPhaseIdx = Indices::gPhaseIdx }; typedef typename GridView::template Codim<0>::Entity Element; + typedef TwoPTwoCFluidState<TypeTag> FluidState; public: - //! The return type of the fluidState() method - typedef Dumux::EquilibriumFluidState<Scalar, FluidSystem> FluidState; - /*! * \brief Update all quantities for a given control volume. * @@ -125,227 +104,56 @@ public: scvIdx, isOldSol); - typename FluidSystem::MutableParameters mutParams; - typename FluidSystem::MutableParameters::FluidState &fs - = mutParams.fluidState(); - - int globalVertIdx = problem.model().vertexMapper().map(element, scvIdx, dim); - int phasePresence = problem.model().phasePresence(globalVertIdx, isOldSol); - - ///////////// - // set the phase saturations - ///////////// - if (phasePresence == gPhaseOnly) { - fs.setSaturation(lPhaseIdx, 0.0); - fs.setSaturation(gPhaseIdx, 1.0); - } - else if (phasePresence == lPhaseOnly) { - fs.setSaturation(lPhaseIdx, 1.0); - fs.setSaturation(gPhaseIdx, 0.0); - } - else if (phasePresence == bothPhases) { - Scalar Sg; - if (formulation == plSg) - Sg = priVars[switchIdx]; - else if (formulation == pgSl) - Sg = 1.0 - priVars[switchIdx]; - - fs.setSaturation(lPhaseIdx, 1 - Sg); - fs.setSaturation(gPhaseIdx, Sg); - } - - ///////////// - // set the phase temperatures - ///////////// - // update the temperature part of the energy module - Scalar T = asImp_().getTemperature(priVars, - element, - elemGeom, - scvIdx, - problem); - Valgrind::CheckDefined(T); - for (int i = 0; i < numPhases; ++i) - fs.setTemperature(i, T); - - ///////////// - // set the phase pressures - ///////////// + asImp().updateTemperature_(priVars, + element, + elemGeom, + scvIdx, + problem); // capillary pressure parameters const MaterialLawParams &materialParams = problem.spatialParameters().materialLawParams(element, elemGeom, scvIdx); - Scalar pC = MaterialLaw::pC(materialParams, fs.saturation(lPhaseIdx)); - if (formulation == plSg) { - fs.setPressure(lPhaseIdx, priVars[pressureIdx]); - fs.setPressure(gPhaseIdx, priVars[pressureIdx] + pC); - } - else if (formulation == pgSl){ - fs.setPressure(lPhaseIdx, priVars[pressureIdx] - pC); - fs.setPressure(gPhaseIdx, priVars[pressureIdx]); - } - Valgrind::CheckDefined(fs.pressure(lPhaseIdx)); - Valgrind::CheckDefined(fs.pressure(gPhaseIdx)); - - // update the mutable parameters for the pure components - mutParams.updatePure(lPhaseIdx); - mutParams.updatePure(gPhaseIdx); - - ///////////// - // set the phase compositions. - ///////////// - if (phasePresence == gPhaseOnly) { - // mass fractions - Scalar Xg1 = priVars[switchIdx]; - Scalar Xg2 = 1 - Xg1; - - // molar masses - Scalar M1 = FluidSystem::molarMass(lCompIdx); - Scalar M2 = FluidSystem::molarMass(gCompIdx); - - // convert mass to mole fractions - Scalar meanM = M1*M2/(M2 + Xg2*(M1 - M2)); - fs.setMoleFrac(gPhaseIdx, lCompIdx, Xg1 * M1/meanM); - fs.setMoleFrac(gPhaseIdx, gCompIdx, Xg2 * M2/meanM); - mutParams.updateMix(gPhaseIdx); - - // calculate component fugacities in gas phase - Scalar fug1 = FluidSystem::computeFugacity(mutParams, gPhaseIdx, lCompIdx); - Scalar fug2 = FluidSystem::computeFugacity(mutParams, gPhaseIdx, gCompIdx); - fs.setFugacity(gPhaseIdx, lCompIdx, fug1); - fs.setFugacity(gPhaseIdx, gCompIdx, fug2); - - // use same fugacities in liquid phase - fs.setFugacity(lPhaseIdx, lCompIdx, fug1); - fs.setFugacity(lPhaseIdx, gCompIdx, fug2); - - // initial guess of liquid composition - fs.setMoleFrac(lPhaseIdx, lCompIdx, 0.98); - fs.setMoleFrac(lPhaseIdx, gCompIdx, 0.02); - - // calculate liquid composition from fugacities - typedef Dumux::CompositionFromFugacities<Scalar, FluidSystem> CompFromFug; - CompFromFug::run(mutParams, lPhaseIdx); - - Valgrind::CheckDefined(fs.moleFrac(gPhaseIdx, lCompIdx)); - Valgrind::CheckDefined(fs.moleFrac(gPhaseIdx, gCompIdx)); - Valgrind::CheckDefined(fs.moleFrac(lPhaseIdx, lCompIdx)); - Valgrind::CheckDefined(fs.moleFrac(lPhaseIdx, gCompIdx)); - - // calculate molar volume of gas phase - Scalar Vmg = FluidSystem::computeMolarVolume(mutParams, gPhaseIdx); - fs.setMolarVolume(gPhaseIdx, Vmg); - } - else if (phasePresence == lPhaseOnly) { - // mass fractions - Scalar Xl2 = priVars[switchIdx]; - Scalar Xl1 = 1 - Xl2; - - // molar masses - Scalar M1 = FluidSystem::molarMass(lCompIdx); - Scalar M2 = FluidSystem::molarMass(gCompIdx); - - // convert mass to mole fractions - Scalar meanM = M1*M2/(M2 + Xl2*(M1 - M2)); - fs.setMoleFrac(lPhaseIdx, lCompIdx, Xl1 * M1/meanM); - fs.setMoleFrac(lPhaseIdx, gCompIdx, Xl2 * M2/meanM); - mutParams.updateMix(lPhaseIdx); + + int globalVertIdx = problem.model().dofMapper().map(element, scvIdx, dim); + int phasePresence = problem.model().phasePresence(globalVertIdx, isOldSol); + + // calculate phase state + fluidState_.update(priVars, materialParams, temperature(), phasePresence); + Valgrind::CheckDefined(fluidState_); + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + // Mobilities + const Scalar mu = + FluidSystem::phaseViscosity(phaseIdx, + fluidState().temperature(), + fluidState().phasePressure(lPhaseIdx), + fluidState()); + Scalar kr; + if (phaseIdx == lPhaseIdx) + kr = MaterialLaw::krw(materialParams, saturation(lPhaseIdx)); + else // ATTENTION: krn requires the liquid saturation + // as parameter! + kr = MaterialLaw::krn(materialParams, saturation(lPhaseIdx)); + mobility_[phaseIdx] = kr / mu; + Valgrind::CheckDefined(mobility_[phaseIdx]); - // calculate component fugacities in liquid phase - Scalar fug1 = FluidSystem::computeFugacity(mutParams, lPhaseIdx, lCompIdx); - Scalar fug2 = FluidSystem::computeFugacity(mutParams, lPhaseIdx, gCompIdx); - fs.setFugacity(lPhaseIdx, lCompIdx, fug1); - fs.setFugacity(lPhaseIdx, gCompIdx, fug2); - - // use same fugacities in gas phase - fs.setFugacity(gPhaseIdx, lCompIdx, fug1); - fs.setFugacity(gPhaseIdx, gCompIdx, fug2); - - // initial guess of gas composition - fs.setMoleFrac(gPhaseIdx, lCompIdx, 0.05); - fs.setMoleFrac(gPhaseIdx, gCompIdx, 0.95); - - // calculate liquid composition from fugacities - typedef Dumux::CompositionFromFugacities<Scalar, FluidSystem> CompFromFug; - CompFromFug::run(mutParams, gPhaseIdx); - Valgrind::CheckDefined(fs.moleFrac(gPhaseIdx, lCompIdx)); - Valgrind::CheckDefined(fs.moleFrac(gPhaseIdx, gCompIdx)); - Valgrind::CheckDefined(fs.moleFrac(lPhaseIdx, lCompIdx)); - Valgrind::CheckDefined(fs.moleFrac(lPhaseIdx, gCompIdx)); - - // calculate molar volume of liquid phase - Scalar Vml = FluidSystem::computeMolarVolume(mutParams, lPhaseIdx); - fs.setMolarVolume(lPhaseIdx, Vml); - } - else if (phasePresence == bothPhases) { - // HACK: assume both phases to be an ideal mixture, - // i.e. the fugacity coefficents do not depend on the - // composition - Scalar phi_l1 = FluidSystem::computeFugacityCoeff(mutParams, lPhaseIdx, lCompIdx); - Scalar phi_l2 = FluidSystem::computeFugacityCoeff(mutParams, lPhaseIdx, gCompIdx); - Scalar phi_g1 = FluidSystem::computeFugacityCoeff(mutParams, gPhaseIdx, lCompIdx); - Scalar phi_g2 = FluidSystem::computeFugacityCoeff(mutParams, gPhaseIdx, gCompIdx); - Scalar pl = fs.pressure(lPhaseIdx); - Scalar pg = fs.pressure(gPhaseIdx); - Valgrind::CheckDefined(phi_l1); - Valgrind::CheckDefined(phi_l2); - Valgrind::CheckDefined(phi_g1); - Valgrind::CheckDefined(phi_g2); - - Scalar xg2 = (phi_g2/phi_l1 - pl/pg) / (phi_g1/phi_l1 - phi_g2/phi_l2); - Scalar xg1 = 1 - xg2; - Scalar xl2 = (xg2*pg*phi_g2)/(pl*phi_l2); - Scalar xl1 = 1 - xl2; - - fs.setMoleFrac(lPhaseIdx, lCompIdx, xl1); - fs.setMoleFrac(lPhaseIdx, gCompIdx, xl2); - fs.setMoleFrac(gPhaseIdx, lCompIdx, xg1); - fs.setMoleFrac(gPhaseIdx, gCompIdx, xg2); - - mutParams.updateMix(lPhaseIdx); - mutParams.updateMix(gPhaseIdx); - - Scalar Vml = FluidSystem::computeMolarVolume(mutParams, lPhaseIdx); - Scalar Vmg = FluidSystem::computeMolarVolume(mutParams, gPhaseIdx); - fs.setMolarVolume(lPhaseIdx, Vml); - fs.setMolarVolume(gPhaseIdx, Vmg); + // binary diffusion coefficents + diffCoeff_[phaseIdx] = + FluidSystem::diffCoeff(phaseIdx, + lCompIdx, + gCompIdx, + fluidState_.temperature(), + fluidState_.phasePressure(phaseIdx), + fluidState_); + Valgrind::CheckDefined(diffCoeff_[phaseIdx]); } - - // Mobilities - Scalar muL = FluidSystem::computeViscosity(mutParams, lPhaseIdx); - Scalar krL = MaterialLaw::krw(materialParams, fs.saturation(lPhaseIdx)); - mobility_[lPhaseIdx] = krL / muL; - Valgrind::CheckDefined(mobility_[lPhaseIdx]); - - // ATTENTION: krn requires the liquid saturation as parameter! - Scalar muG = FluidSystem::computeViscosity(mutParams, gPhaseIdx); - Scalar krG = MaterialLaw::krn(materialParams, fs.saturation(lPhaseIdx)); - mobility_[gPhaseIdx] = krG / muG; - Valgrind::CheckDefined(mobility_[gPhaseIdx]); - -#if 0 - // binary diffusion coefficents - diffCoeff_[phaseIdx] = - FluidSystem::diffCoeff(phaseIdx, - lCompIdx, - gCompIdx, - fluidState_.temperature(), - fluidState_.phasePressure(phaseIdx), - fluidState_); - Valgrind::CheckDefined(diffCoeff_[phaseIdx]); -#endif - // porosity porosity_ = problem.spatialParameters().porosity(element, elemGeom, scvIdx); Valgrind::CheckDefined(porosity_); - - asImp_().updateEnergy(mutParams, priVars, element, elemGeom, scvIdx, problem); - - // assign the equilibrium fluid state from the generic one - fluidState_.assign(fs); - } + } /*! * \brief Returns the phase state for the control-volume. @@ -378,7 +186,7 @@ public: * \param phaseIdx The phase index */ Scalar molarDensity(int phaseIdx) const - { return fluidState_.molarDensity(phaseIdx); } + { return fluidState_.density(phaseIdx) / fluidState_.averageMolarMass(phaseIdx); } /*! * \brief Returns the effective pressure of a given phase within @@ -387,7 +195,7 @@ public: * \param phaseIdx The phase index */ Scalar pressure(int phaseIdx) const - { return fluidState_.pressure(phaseIdx); } + { return fluidState_.phasePressure(phaseIdx); } /*! * \brief Returns temperature inside the sub-control volume. @@ -397,7 +205,7 @@ public: * identical. */ Scalar temperature() const - { return fluidState_.temperature(); } + { return temperature_; } /*! * \brief Returns the effective mobility of a given phase within @@ -414,7 +222,7 @@ public: * \brief Returns the effective capillary pressure within the control volume. */ Scalar capillaryPressure() const - { return fluidState_.pressure(gPhaseIdx) - fluidState_.pressure(lPhaseIdx); } + { return fluidState_.capillaryPressure(); } /*! * \brief Returns the average porosity within the control volume. @@ -422,43 +230,35 @@ public: Scalar porosity() const { return porosity_; } -#if 0 /*! * \brief Returns the binary diffusion coefficients for a phase */ Scalar diffCoeff(int phaseIdx) const { return diffCoeff_[phaseIdx]; } -#endif + protected: - Scalar getTemperature(const PrimaryVariables &priVars, - const Element &element, - const FVElementGeometry &elemGeom, - int scvIdx, - const Problem &problem) + + void updateTemperature_(const PrimaryVariables &priVars, + const Element &element, + const FVElementGeometry &elemGeom, + int scvIdx, + const Problem &problem) { - return problem.temperature(element, elemGeom, scvIdx); + temperature_ = problem.temperature(element, elemGeom, scvIdx); } - - template <class MutableParameters> - void updateEnergy(MutableParameters &mutParams, - const PrimaryVariables &priVars, - const Element &element, - const FVElementGeometry &elemGeom, - int scvIdx, - const Problem &problem) - {}; + Scalar temperature_; //!< Temperature within the control volume Scalar porosity_; //!< Effective porosity within the control volume Scalar mobility_[numPhases]; //!< Effective mobility within the control volume -// Scalar diffCoeff_[numPhases]; //!< Binary diffusion coefficients for the phases + Scalar diffCoeff_[numPhases]; //!< Binary diffusion coefficients for the phases FluidState fluidState_; private: - Implementation &asImp_() + Implementation &asImp() { return *static_cast<Implementation*>(this); } - const Implementation &asImp_() const + const Implementation &asImp() const { return *static_cast<const Implementation*>(this); } }; diff --git a/dumux/boxmodels/2p2cni/2p2cnilocalresidual.hh b/dumux/boxmodels/2p2cni/2p2cnilocalresidual.hh index 50d1d0b698..e9abac3a8a 100644 --- a/dumux/boxmodels/2p2cni/2p2cnilocalresidual.hh +++ b/dumux/boxmodels/2p2cni/2p2cnilocalresidual.hh @@ -112,11 +112,13 @@ public: // compute the energy storage result[energyEqIdx] = vertDat.porosity()*(vertDat.density(lPhaseIdx) * - vertDat.fluidState().internalEnergy(lPhaseIdx) * + vertDat.internalEnergy(lPhaseIdx) * + //vertDat.enthalpy(lPhaseIdx) * vertDat.saturation(lPhaseIdx) + vertDat.density(gPhaseIdx) * - vertDat.fluidState().internalEnergy(gPhaseIdx) * + vertDat.internalEnergy(gPhaseIdx) * + //vertDat.enthalpy(gPhaseIdx) * vertDat.saturation(gPhaseIdx)) + vertDat.temperature()*vertDat.heatCapacity(); @@ -150,12 +152,12 @@ public: mobilityUpwindAlpha * // upstream vertex ( up.density(phase) * up.mobility(phase) * - up.fluidState().enthalpy(phase)) + up.enthalpy(phase)) + (1-mobilityUpwindAlpha) * // downstream vertex ( dn.density(phase) * dn.mobility(phase) * - dn.fluidState().enthalpy(phase)) ); + dn.enthalpy(phase)) ); } } diff --git a/dumux/boxmodels/2p2cni/2p2cnivolumevariables.hh b/dumux/boxmodels/2p2cni/2p2cnivolumevariables.hh index 2a12e12e13..a2187dc62e 100644 --- a/dumux/boxmodels/2p2cni/2p2cnivolumevariables.hh +++ b/dumux/boxmodels/2p2cni/2p2cnivolumevariables.hh @@ -70,19 +70,6 @@ class TwoPTwoCNIVolumeVariables : public TwoPTwoCVolumeVariables<TypeTag> //! \endcond public: - /*! - * \brief Update the temperature of the sub-control volume. - */ - Scalar getTemperature(const PrimaryVariables &sol, - const Element &element, - const FVElementGeometry &elemGeom, - int scvIdx, - const Problem &problem) const - { - // retrieve temperature from solution vector - return sol[temperatureIdx]; - } - /*! * \brief Update all quantities for a given control volume. * @@ -93,26 +80,55 @@ public: * \param vertIdx The local index of the SCV (sub-control volume) * \param isOldSol Evaluate function with solution of current or previous time step */ - template <class MutableParams> - void updateEnergy(MutableParams &mutParams, - const PrimaryVariables &sol, - const Element &element, - const FVElementGeometry &elemGeom, - int scvIdx, - const Problem &problem) + void update(const PrimaryVariables &sol, + const Problem &problem, + const Element &element, + const FVElementGeometry &elemGeom, + int vertIdx, + bool isOldSol) { - heatCapacity_ = - problem.spatialParameters().heatCapacity(element, elemGeom, scvIdx); - Valgrind::CheckDefined(heatCapacity_); + // vertex update data for the mass balance + ParentType::update(sol, + problem, + element, + elemGeom, + vertIdx, + isOldSol); // the internal energies and the enthalpies - typename MutableParams::FluidState &fs = mutParams.fluidState(); for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { - Scalar h = - FluidSystem::computeEnthalpy(mutParams, phaseIdx); - fs.setEnthalpy(phaseIdx, h); + enthalpy_[phaseIdx] = + FluidSystem::phaseEnthalpy(phaseIdx, + this->fluidState().temperature(), + this->fluidState().phasePressure(phaseIdx), + this->fluidState()); + internalEnergy_[phaseIdx] = + FluidSystem::phaseInternalEnergy(phaseIdx, + this->fluidState().temperature(), + this->fluidState().phasePressure(phaseIdx), + this->fluidState()); } - } + Valgrind::CheckDefined(internalEnergy_); + Valgrind::CheckDefined(enthalpy_); + }; + + /*! + * \brief Returns the total internal energy of a phase in the + * sub-control volume. + * + * \param phaseIdx The phase index + */ + Scalar internalEnergy(int phaseIdx) const + { return internalEnergy_[phaseIdx]; }; + + /*! + * \brief Returns the total enthalpy of a phase in the sub-control + * volume. + * + * \param phaseIdx The phase index + */ + Scalar enthalpy(int phaseIdx) const + { return enthalpy_[phaseIdx]; }; /*! * \brief Returns the total heat capacity \f$\mathrm{[J/(K*m^3]}\f$ of the rock matrix in @@ -122,6 +138,27 @@ public: { return heatCapacity_; }; protected: + // this method gets called by the parent class. since this method + // is protected, we are friends with our parent.. + friend class TwoPTwoCVolumeVariables<TypeTag>; + void updateTemperature_(const PrimaryVariables &sol, + const Element &element, + const FVElementGeometry &elemGeom, + int scvIdx, + const Problem &problem) + { + // retrieve temperature from solution vector + this->temperature_ = sol[temperatureIdx]; + + heatCapacity_ = + problem.spatialParameters().heatCapacity(element, elemGeom, scvIdx); + + Valgrind::CheckDefined(this->temperature_); + Valgrind::CheckDefined(heatCapacity_); + } + + Scalar internalEnergy_[numPhases]; + Scalar enthalpy_[numPhases]; Scalar heatCapacity_; }; diff --git a/dumux/boxmodels/Makefile.am b/dumux/boxmodels/Makefile.am index 0fd56c7e68..9d175728d1 100644 --- a/dumux/boxmodels/Makefile.am +++ b/dumux/boxmodels/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = common 1p 1p2c 2p 2p2c 2p2cni 2pni richards +SUBDIRS = common 1p 1p2c 2p 2p2c 2p2cni new_2p2c new_2p2cni 2pni richards boxmodelsdir = $(includedir)/dumux/boxmodels diff --git a/dumux/boxmodels/new_2p2c/2p2cboundaryvariables.hh b/dumux/boxmodels/new_2p2c/2p2cboundaryvariables.hh new file mode 100644 index 0000000000..58e4cb7d5c --- /dev/null +++ b/dumux/boxmodels/new_2p2c/2p2cboundaryvariables.hh @@ -0,0 +1,252 @@ +// $Id$ +/***************************************************************************** + * Copyright (C) 2010 by Katherina Baber, Klaus Mosthaf * + * Copyright (C) 2008-2009 by Bernd Flemisch, Andreas Lauser * + * Institute of Hydraulic Engineering * + * 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 This file contains the data which is required to calculate + * all fluxes of the fluid phase over the boundary of a finite volume. + * + * This means pressure and temperature gradients, phase densities at + * the integration point of the boundary, etc. + */ +#ifndef DUMUX_2P2C_BOUNDARY_VARIABLES_HH +#define DUMUX_2P2C_BOUNDARY_VARIABLES_HH + +#include <dumux/common/math.hh> + +namespace Dumux +{ + +/*! + * \ingroup TwoPTwoCModel + * \brief This template class contains the data which is required to + * calculate the fluxes of the fluid phases over the boundary of a + * finite volume for the 2p2c model. + * + * This means pressure and velocity gradients, phase density and viscosity at + * the integration point of the boundary, etc. + */ +template <class TypeTag> +class TwoPTwoCBoundaryVariables +{ + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Problem)) Problem; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(VolumeVariables)) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(ElementVolumeVariables)) ElementVolumeVariables; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(TwoPTwoCIndices)) Indices; + + enum { + dim = GridView::dimension, + }; + + typedef typename GridView::template Codim<0>::Entity Element; + typedef Dune::FieldVector<Scalar, dim> VelocityVector; + typedef Dune::FieldVector<Scalar, dim> ScalarGradient; + typedef Dune::FieldMatrix<Scalar, dim, dim> VectorGradient; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + typedef typename FVElementGeometry::SubControlVolume SCV; + typedef typename FVElementGeometry::BoundaryFace BoundaryFace; + + enum { + lPhaseIdx = Indices::lPhaseIdx, + gPhaseIdx = Indices::gPhaseIdx, + + lCompIdx = Indices::lCompIdx, + gCompIdx = Indices::gCompIdx, + + numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)) + }; + +public: + TwoPTwoCBoundaryVariables(const Problem &problem, + const Element &element, + const FVElementGeometry &elemGeom, + int boundaryFaceIdx, + const ElementVolumeVariables &elemDat, + int scvIdx) + : fvElemGeom_(elemGeom), scvIdx_(scvIdx) + { + boundaryFace_ = &fvElemGeom_.boundaryFace[boundaryFaceIdx]; + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + densityAtIP_[phaseIdx] = Scalar(0); + pressureAtIP_[phaseIdx] = Scalar(0); + molarDensityAtIP_[phaseIdx] = Scalar(0); + potentialGrad_[phaseIdx] = Scalar(0); + concentrationGrad_[phaseIdx] = Scalar(0); + molarConcGrad_[phaseIdx] = Scalar(0); + } + + calculateBoundaryValues_(problem, element, elemDat); + }; + + Scalar KmvpNormal(int phaseIdx) const + { return KmvpNormal_[phaseIdx]; } + + /*! + * \brief The binary diffusion coefficient for each fluid phase. + */ + Scalar porousDiffCoeff(int phaseIdx) const + { return porousDiffCoeff_[phaseIdx]; }; + + /*! + * \brief Return density \f$\mathrm{[kg/m^3]}\f$ of a phase at the integration + * point. + */ + Scalar densityAtIP(int phaseIdx) const + { return densityAtIP_[phaseIdx]; } + + /*! + * \brief Return molar density \f$\mathrm{[mol/m^3]}\f$ of a phase at the integration + * point. + */ + Scalar molarDensityAtIP(int phaseIdx) const + { return molarDensityAtIP_[phaseIdx]; } + + /*! + * \brief The concentration gradient of a component in a phase. + */ + const ScalarGradient &concentrationGrad(int phaseIdx) const + { return concentrationGrad_[phaseIdx]; }; + + /*! + * \brief The molar concentration gradient of a component in a phase. + */ + const ScalarGradient &molarConcGrad(int phaseIdx) const + { return molarConcGrad_[phaseIdx]; }; + + const FVElementGeometry &fvElemGeom() const + { return fvElemGeom_; } + + const BoundaryFace& boundaryFace() const + { return *boundaryFace_; } + +protected: + void calculateBoundaryValues_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemDat) + { + ScalarGradient tmp(0.0); + + // calculate gradients and secondary variables at IPs of the boundary + for (int idx = 0; + idx < fvElemGeom_.numVertices; + idx++) // loop over adjacent vertices + { + // FE gradient at vertex idx + const ScalarGradient& feGrad = boundaryFace_->grad[idx]; + + // compute sum of pressure gradients for each phase + for (int phaseIdx = 0; phaseIdx < numPhases; phaseIdx++) + { + // the pressure gradient + tmp = feGrad; + tmp *= elemDat[idx].pressure(phaseIdx); + potentialGrad_[phaseIdx] += tmp; + } + + // the concentration gradient of the non-wetting + // component in the wetting phase + tmp = feGrad; + tmp *= elemDat[idx].fluidState().massFrac(lPhaseIdx, gCompIdx); + concentrationGrad_[lPhaseIdx] += tmp; + + tmp = feGrad; + tmp *= elemDat[idx].fluidState().moleFrac(lPhaseIdx, gCompIdx); + molarConcGrad_[lPhaseIdx] += tmp; + + // the concentration gradient of the wetting component + // in the non-wetting phase + tmp = feGrad; + tmp *= elemDat[idx].fluidState().massFrac(gPhaseIdx, lCompIdx); + concentrationGrad_[gPhaseIdx] += tmp; + + tmp = feGrad; + tmp *= elemDat[idx].fluidState().moleFrac(gPhaseIdx, lCompIdx); + molarConcGrad_[gPhaseIdx] += tmp; + + for (int phaseIdx=0; phaseIdx < numPhases; phaseIdx++) + { + pressureAtIP_[phaseIdx] += elemDat[idx].pressure(phaseIdx)*boundaryFace_->shapeValue[idx]; + densityAtIP_[phaseIdx] += elemDat[idx].density(phaseIdx)*boundaryFace_->shapeValue[idx]; + molarDensityAtIP_[phaseIdx] += elemDat[idx].molarDensity(phaseIdx)*boundaryFace_->shapeValue[idx]; + } + } + + for (int phaseIdx=0; phaseIdx < numPhases; phaseIdx++) + { + tmp = problem.gravity(); + tmp *= densityAtIP_[phaseIdx]; + + potentialGrad_[phaseIdx] -= tmp; + + Scalar k = problem.spatialParameters().intrinsicPermeability(element, fvElemGeom_, scvIdx_); + VectorGradient K(0); + K[0][0] = K[1][1] = k; + ScalarGradient Kmvp; + K.mv(potentialGrad_[phaseIdx], Kmvp); + KmvpNormal_[phaseIdx] = - (Kmvp*boundaryFace_->normal); + + const VolumeVariables &vertDat = elemDat[scvIdx_]; + + if (vertDat.saturation(phaseIdx) <= 0) + porousDiffCoeff_[phaseIdx] = 0.0; + else + { + Scalar tau = 1.0/(vertDat.porosity()*vertDat.porosity())* + pow(vertDat.porosity()*vertDat.saturation(phaseIdx), 7.0/3); + + porousDiffCoeff_[phaseIdx] = vertDat.porosity()*vertDat.saturation(phaseIdx)*tau*vertDat.diffCoeff(phaseIdx); + } + } + } + + const FVElementGeometry &fvElemGeom_; + const BoundaryFace *boundaryFace_; + + // gradients + ScalarGradient potentialGrad_[numPhases]; + ScalarGradient concentrationGrad_[numPhases]; + ScalarGradient molarConcGrad_[numPhases]; + + // density of each face at the integration point + Scalar pressureAtIP_[numPhases]; + Scalar densityAtIP_[numPhases]; + Scalar molarDensityAtIP_[numPhases]; + + // intrinsic permeability times pressure potential gradient + // projected on the face normal + Scalar KmvpNormal_[numPhases]; + + // the diffusion coefficient for the porous medium + Scalar porousDiffCoeff_[numPhases]; + + int scvIdx_; +}; + +} // end namespace + +#endif diff --git a/dumux/boxmodels/new_2p2c/2p2cfluidstate.hh b/dumux/boxmodels/new_2p2c/2p2cfluidstate.hh new file mode 100644 index 0000000000..1233d106dc --- /dev/null +++ b/dumux/boxmodels/new_2p2c/2p2cfluidstate.hh @@ -0,0 +1,371 @@ +// $Id$ +/***************************************************************************** + * Copyright (C) 2010 by Andreas Lauser * + * Copyright (C) 2008-2009 by Klaus Mosthaf * + * Copyright (C) 2008-2009 by Bernd Flemisch * + * Institute of Hydraulic Engineering * + * 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 Calculates the phase state from the primary variables in the + * 2p2c model. + */ +#if 0 +#ifndef DUMUX_2P2C_PHASE_STATE_HH +#define DUMUX_2P2C_PHASE_STATE_HH + +#include "2p2cproperties.hh" +#include "2p2cindices.hh" + +#include <dumux/material/fluidstate.hh> + +namespace Dumux +{ +/*! + * \ingroup TwoPTwoCModel + * \brief Calculates the phase state from the primary variables in the + * 2p2c model. + */ +template <class TypeTag> +class TwoPTwoCFluidState : public FluidState<typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)), + TwoPTwoCFluidState<TypeTag> > +{ + typedef TwoPTwoCFluidState<TypeTag> ThisType; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(PrimaryVariables)) PrimaryVariables; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(TwoPTwoCIndices)) Indices; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MaterialLaw)) MaterialLaw; + typedef typename MaterialLaw::Params MaterialLawParams; + + enum { + lPhaseIdx = Indices::lPhaseIdx, + gPhaseIdx = Indices::gPhaseIdx, + + lCompIdx = Indices::lCompIdx, + gCompIdx = Indices::gCompIdx, + + switchIdx = Indices::switchIdx, + pressureIdx = Indices::pressureIdx, + }; + + // present phases + enum { + lPhaseOnly = Indices::lPhaseOnly, + gPhaseOnly = Indices::gPhaseOnly, + bothPhases = Indices::bothPhases + }; + + // formulations + enum { + formulation = GET_PROP_VALUE(TypeTag, PTAG(Formulation)), + plSg = TwoPTwoCFormulation::plSg, + pgSl = TwoPTwoCFormulation::pgSl + }; + +public: + enum { numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)) }; + enum { numSolvents = 1 }; + + /*! + * \brief Update the phase state from the primary variables. + * + * \param primaryVars The primary variables + * \param pcParams The parameters for the material law + * \param temperature The temperature + * \param phasePresence Stands either for nonwetting phase, wetting phase or both phases + */ + void update(const PrimaryVariables &primaryVars, + const MaterialLawParams &pcParams, + Scalar temperature, + int phasePresence) + { + Valgrind::CheckDefined(primaryVars); + + Valgrind::SetUndefined(concentration_); + Valgrind::SetUndefined(phaseConcentration_); + Valgrind::SetUndefined(avgMolarMass_); + Valgrind::SetUndefined(phasePressure_); + Valgrind::SetUndefined(temperature_); + Valgrind::SetUndefined(Sg_); + + temperature_ = temperature; + Valgrind::CheckDefined(temperature_); + + // extract non-wetting phase pressure + if (phasePresence == gPhaseOnly) + Sg_ = 1.0; + else if (phasePresence == lPhaseOnly) { + Sg_ = 0.0; + } + else if (phasePresence == bothPhases) { + if (formulation == plSg) + Sg_ = primaryVars[switchIdx]; + else if (formulation == pgSl) + Sg_ = 1.0 - primaryVars[switchIdx]; + else DUNE_THROW(Dune::InvalidStateException, "Formulation: " << formulation << " is invalid."); + } + else DUNE_THROW(Dune::InvalidStateException, "phasePresence: " << phasePresence << " is invalid."); + Valgrind::CheckDefined(Sg_); + + // calculate capillary pressure + Scalar pC = MaterialLaw::pC(pcParams, 1 - Sg_); + + // extract the pressures + if (formulation == plSg) { + phasePressure_[lPhaseIdx] = primaryVars[pressureIdx]; + phasePressure_[gPhaseIdx] = phasePressure_[lPhaseIdx] + pC; + } + else if (formulation == pgSl) { + phasePressure_[gPhaseIdx] = primaryVars[pressureIdx]; + phasePressure_[lPhaseIdx] = phasePressure_[gPhaseIdx] - pC; + } + else DUNE_THROW(Dune::InvalidStateException, "Formulation: " << formulation << " is invalid."); + Valgrind::CheckDefined(phasePressure_); + + // now comes the tricky part: calculate phase composition + if (phasePresence == bothPhases) { + // both phases are present, phase composition results from + // the gas <-> liquid equilibrium. + FluidSystem::computeEquilibrium(*this, -1); + } + else if (phasePresence == gPhaseOnly) { + // only the gas phase is present, gas phase composition is + // stored explicitly. + + // extract _mass_ (!) fractions in the gas phase, misuse + // the concentration_ array for temporary storage + concentration_[gPhaseIdx][lCompIdx] = primaryVars[switchIdx]; + concentration_[gPhaseIdx][gCompIdx] = 1 - concentration_[gPhaseIdx][lCompIdx]; + + // calculate average molar mass of the gas phase + Scalar M1 = FluidSystem::molarMass(lCompIdx); + Scalar M2 = FluidSystem::molarMass(gCompIdx); + Scalar X2 = concentration_[gPhaseIdx][gCompIdx]; // mass fraction of solvent in gas + avgMolarMass_[gPhaseIdx] = M1*M2/(M2 + X2*(M1 - M2)); + + // convert mass to mole fractions + concentration_[gPhaseIdx][lCompIdx] *= avgMolarMass_[gPhaseIdx]/M1; + concentration_[gPhaseIdx][gCompIdx] *= avgMolarMass_[gPhaseIdx]/M2; + + // convert to real concentrations + phaseConcentration_[gPhaseIdx] = 1.0; + Scalar rhog = FluidSystem::phaseDensity(gPhaseIdx, + temperature_, + phasePressure_[gPhaseIdx], + *this); + phaseConcentration_[gPhaseIdx] = rhog / avgMolarMass_[gPhaseIdx]; + concentration_[gPhaseIdx][lCompIdx] *= phaseConcentration_[gPhaseIdx]; + concentration_[gPhaseIdx][gCompIdx] *= phaseConcentration_[gPhaseIdx]; + + // tell the fluid system to calculate the composition of + // the remaining phases + FluidSystem::computeEquilibrium(*this, gPhaseIdx); + } + else if (phasePresence == lPhaseOnly) { + // only the gas phase is present, liquid phase composition is + // stored explicitly. + + // extract _mass_ (!) fractions in the liquid phase, misuse + // the concentration_ array for temporary storage + concentration_[lPhaseIdx][gCompIdx] = primaryVars[switchIdx]; + concentration_[lPhaseIdx][lCompIdx] = 1 - concentration_[lPhaseIdx][gCompIdx]; + + Scalar M1 = FluidSystem::molarMass(lCompIdx); + Scalar M2 = FluidSystem::molarMass(gCompIdx); + Scalar X2 = concentration_[lPhaseIdx][gCompIdx]; // mass fraction of solvent in gas + avgMolarMass_[lPhaseIdx] = M1*M2/(M2 + X2*(M1 - M2)); + + // convert mass to mole fractions + concentration_[lPhaseIdx][lCompIdx] *= avgMolarMass_[lPhaseIdx]/M1; + concentration_[lPhaseIdx][gCompIdx] *= avgMolarMass_[lPhaseIdx]/M2; + + // convert to real concentrations + phaseConcentration_[lPhaseIdx] = 1.0; + Scalar rhol = FluidSystem::phaseDensity(lPhaseIdx, + temperature_, + phasePressure_[lPhaseIdx], + *this); + phaseConcentration_[lPhaseIdx] = rhol / avgMolarMass_[lPhaseIdx]; + concentration_[lPhaseIdx][lCompIdx] *= phaseConcentration_[lPhaseIdx]; + concentration_[lPhaseIdx][gCompIdx] *= phaseConcentration_[lPhaseIdx]; + + // tell the fluid system to calculate the composition of + // the remaining phases + FluidSystem::computeEquilibrium(*this, lPhaseIdx); + } + + Valgrind::CheckDefined(concentration_); + Valgrind::CheckDefined(phaseConcentration_); + Valgrind::CheckDefined(avgMolarMass_); + Valgrind::CheckDefined(phasePressure_); + Valgrind::CheckDefined(temperature_); + Valgrind::CheckDefined(Sg_); + } + +public: + /*! + * \brief Retrieves the phase composition and pressure from a + * phase composition class. + * + * \param phaseIdx The phase index + * \param compo The index of the component + * + * This method is called by the fluid system's + * computeEquilibrium() + */ + template <class PhaseCompo> + void assignPhase(int phaseIdx, const PhaseCompo &compo) + { + avgMolarMass_[phaseIdx] = compo.meanMolarMass(); + phasePressure_[phaseIdx] = compo.pressure(); + + phaseConcentration_[phaseIdx] = compo.phaseConcentration(); + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + concentration_[phaseIdx][compIdx] = compo.concentration(compIdx); + }; + + /*! + * \brief Returns the saturation of a phase. + * + * \param phaseIdx The phase index + */ + Scalar saturation(int phaseIdx) const + { + if (phaseIdx == lPhaseIdx) + return Scalar(1.0) - Sg_; + else + return Sg_; + }; + + /*! + * \brief Returns the mole fraction of a component in a fluid phase. + * + * \param phaseIdx The phase index + * \param compIdx The index of the component + */ + Scalar moleFrac(int phaseIdx, int compIdx) const + { + return + concentration_[phaseIdx][compIdx]/ + phaseConcentration_[phaseIdx]; + } + + /*! + * \brief Returns the molar density of a phase \f$\mathrm{[mol/m^3]}\f$. + * + * \param phaseIdx The phase index + * + * This is equivalent to the sum of all molar component concentrations. + */ + Scalar phaseConcentration(int phaseIdx) const + { return phaseConcentration_[phaseIdx]; }; + + /*! + * \brief Returns the molar concentration of a component in a phase \f$\mathrm{[mol/m^3]}\f$. + * + * \param phaseIdx The phase index + * \param compIdx The index of the component + * + */ + Scalar concentration(int phaseIdx, int compIdx) const + { return concentration_[phaseIdx][compIdx]; }; + + /*! + * \brief Returns the mass fraction of a component in a phase. + * + * \param phaseIdx The phase index + * \param compIdx The index of the component + * + */ + Scalar massFrac(int phaseIdx, int compIdx) const + { + return + moleFrac(phaseIdx, compIdx) * + FluidSystem::molarMass(compIdx)/avgMolarMass_[phaseIdx]; + } + + /*! + * \brief Returns the mass density of a phase \f$\mathrm{[kg/m^3]}\f$. + * + * \param phaseIdx The phase index + */ + Scalar density(int phaseIdx) const + { return phaseConcentration_[phaseIdx]*avgMolarMass_[phaseIdx]; } + + /*! + * \brief Returns mean molar mass of a phase \f$\mathrm{[kg/mol]}\f$. + * + * \param phaseIdx The phase index + * + * This is equivalent to the sum of all component molar masses + * weighted by their respective mole fraction. + */ + Scalar averageMolarMass(int phaseIdx) const + { return avgMolarMass_[phaseIdx]; }; + + /*! + * \brief Returns the pressure of a fluid phase \f$\mathrm{[Pa]}\f$. + * + * \param phaseIdx The phase index + */ + Scalar phasePressure(int phaseIdx) const + { return phasePressure_[phaseIdx]; } + + /*! + * \brief Return the fugacity of a component \f$\mathrm{[Pa]}\f$. + * + * \param compIdx The index of the component + */ + Scalar fugacity(int compIdx) const + { return moleFrac(gPhaseIdx, compIdx)*phasePressure(gPhaseIdx); }; + + /*! + * \brief Returns the capillary pressure \f$\mathrm{[Pa]}\f$ + */ + Scalar capillaryPressure() const + { return phasePressure_[gPhaseIdx] - phasePressure_[lPhaseIdx]; } + + /*! + * \brief Returns the temperature of the fluids \f$\mathrm{[K]}\f$. + * + * Note that we assume thermodynamic equilibrium, so all fluids + * and the rock matrix exhibit the same temperature. + */ + Scalar temperature() const + { return temperature_; }; + +public: + Scalar concentration_[numPhases][numComponents]; + Scalar phaseConcentration_[numPhases]; + Scalar avgMolarMass_[numPhases]; + Scalar phasePressure_[numPhases]; + Scalar temperature_; + Scalar Sg_; +}; + +} // end namepace + +#endif +#endif diff --git a/dumux/boxmodels/new_2p2c/2p2cfluxvariables.hh b/dumux/boxmodels/new_2p2c/2p2cfluxvariables.hh new file mode 100644 index 0000000000..ccebd24833 --- /dev/null +++ b/dumux/boxmodels/new_2p2c/2p2cfluxvariables.hh @@ -0,0 +1,382 @@ +// $Id$ +/***************************************************************************** + * Copyright (C) 2008-2009 by Klaus Mosthaf * + * Copyright (C) 2008-2009 by Andreas Lauser * + * Copyright (C) 2008 by Bernd Flemisch * + * Institute of Hydraulic Engineering * + * 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 This file 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 model. + */ +/*! + * \ingroup TwoPTwoCModel + */ +#ifndef DUMUX_2P2C_FLUX_VARIABLES_HH +#define DUMUX_2P2C_FLUX_VARIABLES_HH + +#include <dumux/common/math.hh> +#include <dumux/common/spline.hh> + +namespace Dumux +{ + +/*! + * \brief This template class 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 model. + * + * This means pressure and concentration gradients, phase densities at + * the integration point, etc. + */ +template <class TypeTag> +class TwoPTwoCFluxVariables +{ + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Problem)) Problem; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(VolumeVariables)) VolumeVariables; + + typedef typename GridView::ctype CoordScalar; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(ElementVolumeVariables)) ElementVolumeVariables; + + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld, + numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)) + }; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(SpatialParameters)) SpatialParameters; + typedef typename FVElementGeometry::SubControlVolume SCV; + typedef typename FVElementGeometry::SubControlVolumeFace SCVFace; + + typedef Dune::FieldVector<CoordScalar, dimWorld> Vector; + typedef Dune::FieldMatrix<CoordScalar, dimWorld, dimWorld> Tensor; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(TwoPTwoCIndices)) Indices; + enum { + lPhaseIdx = Indices::lPhaseIdx, + gPhaseIdx = Indices::gPhaseIdx, + + lCompIdx = Indices::lCompIdx, + gCompIdx = Indices::gCompIdx, + }; + +public: + /* + * \brief The constructor + * + * \param problem The problem + * \param element The finite element + * \param elemGeom The finite-volume geometry in the box scheme + * \param faceIdx The local index of the SCV (sub-control-volume) face + * \param elemDat The volume variables of the current element + */ + TwoPTwoCFluxVariables(const Problem &problem, + const Element &element, + const FVElementGeometry &elemGeom, + int faceIdx, + const ElementVolumeVariables &elemDat) + : fvElemGeom_(elemGeom) + { + scvfIdx_ = faceIdx; + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + densityAtIP_[phaseIdx] = Scalar(0); + molarDensityAtIP_[phaseIdx] = Scalar(0); + potentialGrad_[phaseIdx] = Scalar(0); + concentrationGrad_[phaseIdx] = Scalar(0); + molarConcGrad_[phaseIdx] = Scalar(0); + } + + calculateGradients_(problem, element, elemDat); + calculateVelocities_(problem, element, elemDat); + calculateDiffCoeffPM_(problem, element, elemDat); + }; + +private: + void calculateGradients_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemDat) + { + // calculate gradients + Vector tmp(0.0); + for (int idx = 0; + idx < fvElemGeom_.numVertices; + idx++) // loop over adjacent vertices + { + // FE gradient at vertex idx + const Vector &feGrad = face().grad[idx]; + + // compute sum of pressure gradients for each phase + for (int phaseIdx = 0; phaseIdx < numPhases; phaseIdx++) + { + // the pressure gradient + tmp = feGrad; + tmp *= elemDat[idx].pressure(phaseIdx); + potentialGrad_[phaseIdx] += tmp; + } + + // the concentration gradient of the non-wetting + // component in the wetting phase + tmp = feGrad; + tmp *= elemDat[idx].fluidState().massFrac(lPhaseIdx, gCompIdx); + concentrationGrad_[lPhaseIdx] += tmp; + + tmp = feGrad; + tmp *= elemDat[idx].fluidState().moleFrac(lPhaseIdx, gCompIdx); + molarConcGrad_[lPhaseIdx] += tmp; + +// // the concentration gradient of the wetting component +// // in the non-wetting phase + tmp = feGrad; + tmp *= elemDat[idx].fluidState().massFrac(gPhaseIdx, lCompIdx); + concentrationGrad_[gPhaseIdx] += tmp; + + tmp = feGrad; + tmp *= elemDat[idx].fluidState().moleFrac(gPhaseIdx, lCompIdx); + molarConcGrad_[gPhaseIdx] += tmp; + } + + // correct the pressure gradients by the hydrostatic + // pressure due to gravity + for (int phaseIdx=0; phaseIdx < numPhases; phaseIdx++) + { + int i = face().i; + int j = face().j; + Scalar fI = rhoFactor_(phaseIdx, i, elemDat); + Scalar fJ = rhoFactor_(phaseIdx, j, elemDat); + if (fI + fJ <= 0) + fI = fJ = 0.5; // doesn't matter because no phase is + // present in both cells! + densityAtIP_[phaseIdx] = + (fI*elemDat[i].density(phaseIdx) + + fJ*elemDat[j].density(phaseIdx)) + / + (fI + fJ); + // phase density + molarDensityAtIP_[phaseIdx] + = + (fI*elemDat[i].molarDensity(phaseIdx) + + fJ*elemDat[j].molarDensity(phaseIdx)) + / + (fI + fJ); + + tmp = problem.gravity(); + tmp *= densityAtIP_[phaseIdx]; + + potentialGrad_[phaseIdx] -= tmp; + } + } + + Scalar rhoFactor_(int phaseIdx, int scvIdx, const ElementVolumeVariables &vDat) + { + static const Scalar eps = 1e-2; + const Scalar sat = vDat[scvIdx].density(phaseIdx); + if (sat > eps) + return 0.5; + if (sat <= 0) + return 0; + + static const Dumux::Spline<Scalar> sp(0, eps, // x0, x1 + 0, 0.5, // y0, y1 + 0, 0); // m0, m1 + return sp.eval(sat); + } + + void calculateVelocities_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemDat) + { + const SpatialParameters &spatialParams = problem.spatialParameters(); + // multiply the pressure potential with the intrinsic + // permeability + Tensor K; + for (int phaseIdx=0; phaseIdx < numPhases; phaseIdx++) + { + spatialParams.meanK(K, + spatialParams.intrinsicPermeability(element, + fvElemGeom_, + face().i), + spatialParams.intrinsicPermeability(element, + fvElemGeom_, + face().j)); + K.mv(potentialGrad_[phaseIdx], Kmvp_[phaseIdx]); + KmvpNormal_[phaseIdx] = - (Kmvp_[phaseIdx] * face().normal); + } + + // set the upstream and downstream vertices + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + { + upstreamIdx_[phaseIdx] = face().i; + downstreamIdx_[phaseIdx] = face().j; + + if (KmvpNormal_[phaseIdx] < 0) { + std::swap(upstreamIdx_[phaseIdx], + downstreamIdx_[phaseIdx]); + } + } + } + + void calculateDiffCoeffPM_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemDat) + { +#if 0 + const VolumeVariables &vDat_i = elemDat[face().i]; + const VolumeVariables &vDat_j = elemDat[face().j]; + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + { + // make sure to only calculate diffusion coefficents + // for phases which exist in both finite volumes + if (vDat_i.saturation(phaseIdx) <= 0 || + vDat_j.saturation(phaseIdx) <= 0) + { + porousDiffCoeff_[phaseIdx] = 0.0; + continue; + } + + // calculate tortuosity at the nodes i and j needed + // for porous media diffusion coefficient + Scalar tau_i = + 1.0/(vDat_i.porosity() * vDat_i.porosity()) * + pow(vDat_i.porosity() * vDat_i.saturation(phaseIdx), 7.0/3); + Scalar tau_j = + 1.0/(vDat_j.porosity() * vDat_j.porosity()) * + pow(vDat_j.porosity() * vDat_j.saturation(phaseIdx), 7.0/3); + // Diffusion coefficient in the porous medium + + // -> harmonic mean + porousDiffCoeff_[phaseIdx] = harmonicMean(vDat_i.porosity() * vDat_i.saturation(phaseIdx) * tau_i * vDat_i.diffCoeff(phaseIdx), + vDat_j.porosity() * vDat_j.saturation(phaseIdx) * tau_j * vDat_j.diffCoeff(phaseIdx)); + } +#endif + } + +public: + /*! + * \brief Return the pressure potential multiplied with the + * intrinsic permeability which goes from vertex i to + * vertex j. + * + * Note that the length of the face's normal is the area of the + * phase, so this is not the actual velocity by the integral of + * the velocity over the face's area. Also note that the phase + * mobility is not yet included here since this would require a + * decision on the upwinding approach (which is done in the + * actual model). + */ + Scalar KmvpNormal(int phaseIdx) const + { return KmvpNormal_[phaseIdx]; } + + /*! + * \brief Return the pressure potential multiplied with the + * intrinsic permeability as vector (for velocity output) + */ + Vector Kmvp(int phaseIdx) const + { return Kmvp_[phaseIdx]; } + + /*! + * \brief Return the local index of the upstream control volume + * for a given phase. + */ + int upstreamIdx(int phaseIdx) const + { return upstreamIdx_[phaseIdx]; } + + /*! + * \brief Return the local index of the downstream control volume + * for a given phase. + */ + int downstreamIdx(int phaseIdx) const + { return downstreamIdx_[phaseIdx]; } + +#if 0 + /*! + * \brief The binary diffusion coefficient for each fluid phase. + */ + Scalar porousDiffCoeff(int phaseIdx) const + { return porousDiffCoeff_[phaseIdx]; }; +#endif + + /*! + * \brief Return density \f$\mathrm{[kg/m^3]}\f$ of a phase at the integration + * point. + */ + Scalar densityAtIP(int phaseIdx) const + { return densityAtIP_[phaseIdx]; } + + /*! + * \brief Return molar density \f$\mathrm{[mol/m^3]}\f$ of a phase at the integration + * point. + */ + Scalar molarDensityAtIP(int phaseIdx) const + { return molarDensityAtIP_[phaseIdx]; } + + /*! + * \brief The concentration gradient of a component in a phase. + */ + const Vector &concentrationGrad(int phaseIdx) const + { return concentrationGrad_[phaseIdx]; }; + + /*! + * \brief The molar concentration gradient of a component in a phase. + */ + const Vector &molarConcGrad(int phaseIdx) const + { return molarConcGrad_[phaseIdx]; }; + + const SCVFace &face() const + { return fvElemGeom_.subContVolFace[scvfIdx_]; } + +protected: + const FVElementGeometry &fvElemGeom_; + int scvfIdx_; + + // gradients + Vector potentialGrad_[numPhases]; + Vector concentrationGrad_[numPhases]; + Vector molarConcGrad_[numPhases]; + + // density of each face at the integration point + Scalar densityAtIP_[numPhases], molarDensityAtIP_[numPhases]; + + // intrinsic permeability times pressure potential gradient + Vector Kmvp_[numPhases]; + // projected on the face normal + Scalar KmvpNormal_[numPhases]; + + // local index of the upwind vertex for each phase + int upstreamIdx_[numPhases]; + // local index of the downwind vertex for each phase + int downstreamIdx_[numPhases]; + +/* + // the diffusion coefficient for the porous medium + Scalar porousDiffCoeff_[numPhases]; +*/ +}; + +} // end namepace + +#endif diff --git a/dumux/boxmodels/new_2p2c/2p2cindices.hh b/dumux/boxmodels/new_2p2c/2p2cindices.hh new file mode 100644 index 0000000000..8862e7f9e8 --- /dev/null +++ b/dumux/boxmodels/new_2p2c/2p2cindices.hh @@ -0,0 +1,128 @@ +// $Id: 2p2cproperties.hh 3784 2010-06-24 13:43:57Z bernd $ +/***************************************************************************** + * Copyright (C) 2008 by Klaus Mosthaf, Andreas Lauser, Bernd Flemisch * + * Institute of Hydraulic Engineering * + * 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 Defines the indices required for the 2p2c BOX model. + */ +#ifndef DUMUX_2P2C_INDICES_HH +#define DUMUX_2P2C_INDICES_HH + +namespace Dumux +{ +/*! + * \ingroup TwoPTwoCModel + */ +// \{ + +/*! + * \brief Enumerates the formulations which the 2p2c model accepts. + */ +struct TwoPTwoCFormulation +{ + enum { + plSg, + pgSl + }; +}; + +/*! + * \brief The indices for the isothermal TwoPTwoC model. + * + * \tparam formulation The formulation, either pwSn or pnSw. + * \tparam PVOffset The first index in a primary variable vector. + */ +template <class TypeTag, + int formulation = TwoPTwoCFormulation::plSg, + int PVOffset = 0> +class TwoPTwoCIndices +{ + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + +public: + // Phase indices + static const int lPhaseIdx = FluidSystem::lPhaseIdx; //!< Index of the liquid phase + static const int gPhaseIdx = FluidSystem::gPhaseIdx; //!< Index of the gas phase + + // Component indices + static const int lCompIdx = 0; //!< Index of the liquid's primary component + static const int gCompIdx = 1; //!< Index of the gas' primary component + + // present phases (-> 'pseudo' primary variable) + static const int lPhaseOnly = 1; //!< Only the non-wetting phase is present + static const int gPhaseOnly = 0; //!< Only the wetting phase is present + static const int bothPhases = 2; //!< Both phases are present + + // Primary variable indices + static const int pressureIdx = PVOffset + 0; //!< Index for wetting/non-wetting phase pressure (depending on formulation) in a solution vector + static const int switchIdx = PVOffset + 1; //!< Index of the either the saturation or the mass fraction of the non-wetting/wetting phase + + static const int plIdx = pressureIdx; //!< Index for liquid phase pressure in a solution vector + static const int SgOrXIdx = switchIdx; //!< Index of the either the saturation of the gas phase or the mass fraction secondary component in the only phase + + // equation indices + static const int contiLEqIdx = PVOffset + lCompIdx; //!< Index of the mass conservation equation for the liquid's primary component + static const int contiGEqIdx = PVOffset + gCompIdx; //!< Index of the mass conservation equation for the gas' primary component +}; + +/*! + * \brief The indices for the isothermal TwoPTwoC model in the pg-Sl + * formulation. + * + * \tparam PVOffset The first index in a primary variable vector. + */ +template <class TypeTag, int PVOffset> +class TwoPTwoCIndices<TypeTag, TwoPTwoCFormulation::pgSl, PVOffset> +{ + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + +public: + // Phase indices + static const int lPhaseIdx = FluidSystem::lPhaseIdx; //!< Index of the liquid phase + static const int gPhaseIdx = FluidSystem::gPhaseIdx; //!< Index of the gas phase + + // Component indices + static const int lCompIdx = 0; //!< Index of the liquid's primary component + static const int gCompIdx = 1; //!< Index of the gas' primary component + + // present phases (-> 'pseudo' primary variable) + static const int lPhaseOnly = 1; //!< Only the non-wetting phase is present + static const int gPhaseOnly = 2; //!< Only the wetting phase is present + static const int bothPhases = 3; //!< Both phases are present + + // Primary variable indices + static const int pressureIdx = PVOffset + 0; //!< Index for wetting/non-wetting phase pressure (depending on formulation) in a solution vector + static const int switchIdx = PVOffset + 1; //!< Index of the either the saturation or the mass fraction of the non-wetting/wetting phase + + static const int pgIdx = pressureIdx; //!< Index for gas phase pressure in a solution vector + static const int SlOrXIdx = switchIdx; //!< Index of the either the saturation of the liquid phase or the mass fraction secondary component in the only phase + + // Equation indices + static const int contiLEqIdx = PVOffset + lCompIdx; //!< Index of the mass conservation equation for the liquid's primary component + static const int contiGEqIdx = PVOffset + gCompIdx; //!< Index of the mass conservation equation for the gas' primary component +}; + +// \} + +} + +#endif diff --git a/dumux/boxmodels/new_2p2c/2p2clocalresidual.hh b/dumux/boxmodels/new_2p2c/2p2clocalresidual.hh new file mode 100644 index 0000000000..73d7c658e9 --- /dev/null +++ b/dumux/boxmodels/new_2p2c/2p2clocalresidual.hh @@ -0,0 +1,342 @@ +// $Id: 2p2clocalresidual.hh 3795 2010-06-25 16:08:04Z melanie $ +/***************************************************************************** + * Copyright (C) 2008-2009 by Klaus Mosthaf * + * Copyright (C) 2008-2009 by Bernd Flemisch * + * Copyright (C) 2009-2010 by Andreas Lauser * + * Institute of Hydraulic Engineering * + * 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 two-phase two-component box model. + */ + +#ifndef DUMUX_NEW_2P2C_LOCAL_RESIDUAL_BASE_HH +#define DUMUX_NEW_2P2C_LOCAL_RESIDUAL_BASE_HH + +#include <dumux/boxmodels/common/boxmodel.hh> +#include <dumux/common/math.hh> + +#include "2p2cproperties.hh" + +#include "2p2cvolumevariables.hh" + +#include "2p2cfluxvariables.hh" + +#include "2p2cnewtoncontroller.hh" + +#include <iostream> +#include <vector> + +//#define VELOCITY_OUTPUT 1 // uncomment this line if an output of the velocity is needed + +namespace Dumux +{ +/*! + * \ingroup TwoPTwoCModel + * \brief Element-wise calculation of the Jacobian matrix for problems + * using the two-phase two-component box model. + * + * This class is used to fill the gaps in BoxLocalResidual for the 2P-2C flow. + */ +template<class TypeTag> +class TwoPTwoCLocalResidual: public BoxLocalResidual<TypeTag> +{ +protected: + typedef TwoPTwoCLocalResidual<TypeTag> ThisType; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(LocalResidual)) Implementation; + typedef BoxLocalResidual<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Problem)) Problem; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(SolutionVector)) SolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(ElementSolutionVector)) ElementSolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(PrimaryVariables)) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(BoundaryTypes)) BoundaryTypes; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(TwoPTwoCIndices)) Indices; + + enum + { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld, + + numEq = GET_PROP_VALUE(TypeTag, PTAG(NumEq)), + numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)), + numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)), + + pressureIdx = Indices::pressureIdx, + switchIdx = Indices::switchIdx, + + contiLEqIdx = Indices::contiLEqIdx, + contiGEqIdx = Indices::contiGEqIdx, + + lPhaseIdx = Indices::lPhaseIdx, + gPhaseIdx = Indices::gPhaseIdx, + + lCompIdx = Indices::lCompIdx, + gCompIdx = Indices::gCompIdx, + + lPhaseOnly = Indices::lPhaseOnly, + gPhaseOnly = Indices::gPhaseOnly, + bothPhases = Indices::bothPhases, + + plSg = TwoPTwoCFormulation::plSg, + pgSl = TwoPTwoCFormulation::pgSl, + formulation = GET_PROP_VALUE(TypeTag, PTAG(Formulation)) + }; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(VolumeVariables)) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(ElementVolumeVariables)) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(ElementBoundaryTypes)) ElementBoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluxVariables)) FluxVariables; + + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::ctype CoordScalar; + + typedef Dune::FieldVector<Scalar, numPhases> PhasesVector; + typedef Dune::FieldVector<Scalar, dim> LocalPosition; + typedef Dune::FieldVector<Scalar, dimWorld> GlobalPosition; + typedef Dune::FieldMatrix<Scalar, dimWorld, dimWorld> Tensor; + + static const Scalar mobilityUpwindAlpha = + GET_PROP_VALUE(TypeTag, PTAG(MobilityUpwindAlpha)); + +public: + /*! + * \brief Evaluate the storage term of the current solution in a + * single phase. + * + * \param element The element + * \param phaseIdx The index of the fluid phase + */ + void evalPhaseStorage(const Element &element, int phaseIdx) + { + FVElementGeometry fvGeom; + fvGeom.update(this->gridView_(), element); + ElementBoundaryTypes bcTypes; + bcTypes.update(this->problem_(), element, fvGeom); + ElementVolumeVariables volVars; + volVars.update(this->problem_(), element, fvGeom, false); + + this->residual_.resize(fvGeom.numVertices); + this->residual_ = 0; + + this->elemPtr_ = &element; + this->fvElemGeomPtr_ = &fvGeom; + this->bcTypesPtr_ = &bcTypes; + this->prevVolVarsPtr_ = 0; + this->curVolVarsPtr_ = &volVars; + evalPhaseStorage_(phaseIdx); + } + + /*! + * \brief Evaluate the amount all conservation quantities + * (e.g. phase mass) within a sub-control volume. + * + * The result should be averaged over the volume (e.g. phase mass + * inside a sub control volume divided by the volume) + * + * \param result 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 + */ + void computeStorage(PrimaryVariables &result, int scvIdx, bool usePrevSol) const + { + // if flag usePrevSol is set, the solution from the previous + // time step is used, otherwise the current solution is + // used. The secondary variables are used accordingly. This + // is required to compute the derivative of the storage term + // using the implicit euler method. + const ElementVolumeVariables &elemVolVars = usePrevSol ? this->prevVolVars_() + : this->curVolVars_(); + const VolumeVariables &volVars = elemVolVars[scvIdx]; + + // compute storage term of all components within all phases + result = 0; + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + { + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + { + int eqIdx = (compIdx == lCompIdx) ? contiLEqIdx : contiGEqIdx; + result[eqIdx] += volVars.density(phaseIdx) + * volVars.saturation(phaseIdx) + * volVars.fluidState().massFrac(phaseIdx, compIdx); + } + } + result *= volVars.porosity(); + } + + /*! + * \brief Evaluates the total flux of all conservation quantities + * over a face of a sub-control volume. + * + * \param flux The flux over the SCV (sub-control-volume) face for each component + * \param faceIdx The index of the SCV face + */ + void computeFlux(PrimaryVariables &flux, int faceIdx) const + { + FluxVariables vars(this->problem_(), + this->elem_(), + this->fvElemGeom_(), + faceIdx, + this->curVolVars_()); + + flux = 0; + asImp_()->computeAdvectiveFlux(flux, vars); + Valgrind::CheckDefined(flux); + asImp_()->computeDiffusiveFlux(flux, vars); + Valgrind::CheckDefined(flux); + } + + /*! + * \brief Evaluates the advective mass flux of all components over + * a face of a subcontrol volume. + * + * \param flux The advective flux over the sub-control-volume face for each component + * \param vars The flux variables at the current SCV + */ + void computeAdvectiveFlux(PrimaryVariables &flux, const FluxVariables &vars) const + { + //////// + // advective fluxes of all components in all phases + //////// + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + { + // data attached to upstream and the downstream vertices + // of the current phase + const VolumeVariables &up = + this->curVolVars_(vars.upstreamIdx(phaseIdx)); + const VolumeVariables &dn = + this->curVolVars_(vars.downstreamIdx(phaseIdx)); + + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + { + int eqIdx = (compIdx == lCompIdx) ? contiLEqIdx : contiGEqIdx; + // add advective flux of current component in current + // phase + if (mobilityUpwindAlpha > 0.0) + // upstream vertex + flux[eqIdx] += vars.KmvpNormal(phaseIdx) + * mobilityUpwindAlpha * (up.density(phaseIdx) + * up.mobility(phaseIdx) * up.fluidState().massFrac( + phaseIdx, compIdx)); + if (mobilityUpwindAlpha < 1.0) + // downstream vertex + flux[eqIdx] += vars.KmvpNormal(phaseIdx) * (1 + - mobilityUpwindAlpha) * (dn.density(phaseIdx) + * dn.mobility(phaseIdx) * dn.fluidState().massFrac( + phaseIdx, compIdx)); + Valgrind::CheckDefined(vars.KmvpNormal(phaseIdx)); + Valgrind::CheckDefined(up.density(phaseIdx)); + Valgrind::CheckDefined(up.mobility(phaseIdx)); + Valgrind::CheckDefined(up.fluidState().massFrac(phaseIdx, compIdx)); + Valgrind::CheckDefined(dn.density(phaseIdx)); + Valgrind::CheckDefined(dn.mobility(phaseIdx)); + Valgrind::CheckDefined(dn.fluidState().massFrac(phaseIdx, compIdx)); + } + } + } + + /*! + * \brief Adds the diffusive mass flux of all components over + * a face of a subcontrol volume. + * + * \param flux The diffusive flux over the sub-control-volume face for each component + * \param vars The flux variables at the current SCV + */ + void computeDiffusiveFlux(PrimaryVariables &flux, const FluxVariables &vars) const + { +#if 0 + // add diffusive flux of gas component in liquid phase + Scalar tmp = + - vars.porousDiffCoeff(lPhaseIdx) * + vars.molarDensityAtIP(lPhaseIdx) * + (vars.molarConcGrad(lPhaseIdx) * vars.face().normal); + flux[contiGEqIdx] += tmp * FluidSystem::molarMass(gCompIdx); + flux[contiLEqIdx] -= tmp * FluidSystem::molarMass(lCompIdx); + + // add diffusive flux of liquid component in gas phase + tmp = + - vars.porousDiffCoeff(gPhaseIdx) * + vars.molarDensityAtIP(gPhaseIdx) * + (vars.molarConcGrad(gPhaseIdx) * vars.face().normal); + flux[contiLEqIdx] += tmp * FluidSystem::molarMass(lCompIdx); + flux[contiGEqIdx] -= tmp * FluidSystem::molarMass(gCompIdx); +#endif + } + + /*! + * \brief Calculate the source term of the equation + * + * \param q The source/sink in the SCV for each component + * \param localVertexIdx The index of the SCV + */ + void computeSource(PrimaryVariables &q, int localVertexIdx) + { + this->problem_().source(q, + this->elem_(), + this->fvElemGeom_(), + localVertexIdx); + } + +protected: + void evalPhaseStorage_(int phaseIdx) + { + // evaluate the storage terms of a single phase + for (int i=0; i < this->fvElemGeom_().numVertices; i++) { + PrimaryVariables &result = this->residual_[i]; + const ElementVolumeVariables &elemVolVars = this->curVolVars_(); + const VolumeVariables &volVars = elemVolVars[i]; + + // compute storage term of all components within all phases + result = 0; + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + { + int eqIdx = (compIdx == lCompIdx) ? contiLEqIdx : contiGEqIdx; + result[eqIdx] += volVars.density(phaseIdx) + * volVars.saturation(phaseIdx) + * volVars.fluidState().massFrac(phaseIdx, compIdx); + } + + result *= volVars.porosity(); + result *= this->fvElemGeom_().subContVol[i].volume; + } + } + + + Implementation *asImp_() + { + return static_cast<Implementation *> (this); + } + const Implementation *asImp_() const + { + return static_cast<const Implementation *> (this); + } + +}; + +} // end namepace + +#endif diff --git a/dumux/boxmodels/new_2p2c/2p2cmodel.hh b/dumux/boxmodels/new_2p2c/2p2cmodel.hh new file mode 100644 index 0000000000..c59755e211 --- /dev/null +++ b/dumux/boxmodels/new_2p2c/2p2cmodel.hh @@ -0,0 +1,760 @@ +// $Id$ +/***************************************************************************** + * Copyright (C) 2008 by Klaus Mosthaf, Andreas Lauser, Bernd Flemisch * + * Institute of Hydraulic Engineering * + * 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 Adaption of the BOX scheme to the two-phase two-component flow model. +*/ + +#ifndef DUMUX_2P2C_MODEL_HH +#define DUMUX_2P2C_MODEL_HH + +#include "2p2cproperties.hh" +#include "2p2clocalresidual.hh" +#include "2p2cproblem.hh" + +namespace Dumux +{ +/*! + * \ingroup BoxModels + * \defgroup TwoPTwoCModel Two-phase two-component box model + */ + +/*! + * \ingroup TwoPTwoCModel + * \brief Adaption of the BOX scheme to the two-phase two-component flow model. + * + * This model implements two-phase two-component flow of two compressible and + * partially miscible fluids \f$\alpha \in \{ w, n \}\f$ composed of the two components + * \f$\kappa \in \{ w, a \}\f$. The standard multiphase Darcy + * approach is used as the equation for the conservation of momentum: + * \f[ + v_\alpha = - \frac{k_{r\alpha}}{\mu_\alpha} \mbox{\bf K} + \left(\text{grad}\, p_\alpha - \varrho_{\alpha} \mbox{\bf g} \right) + * \f] + * + * By inserting this into the equations for the conservation of the + * components, one gets one transport equation for each component + * \f{eqnarray} + && \phi \frac{\partial (\sum_\alpha \varrho_\alpha X_\alpha^\kappa S_\alpha )} + {\partial t} + - \sum_\alpha \text{div} \left\{ \varrho_\alpha X_\alpha^\kappa + \frac{k_{r\alpha}}{\mu_\alpha} \mbox{\bf K} + (\text{grad}\, p_\alpha - \varrho_{\alpha} \mbox{\bf g}) \right\} + \nonumber \\ \nonumber \\ + &-& \sum_\alpha \text{div} \left\{{\bf D_{\alpha, pm}^\kappa} \varrho_{\alpha} \text{grad}\, X^\kappa_{\alpha} \right\} + - \sum_\alpha q_\alpha^\kappa = 0 \qquad \kappa \in \{w, a\} \, , + \alpha \in \{w, g\} + \f} + * + * This is discretized using a fully-coupled vertex + * centered finite volume (box) scheme as spatial and + * the implicit Euler method as temporal discretization. + * + * By using constitutive relations for the capillary pressure \f$p_c = + * p_n - p_w\f$ and relative permeability \f$k_{r\alpha}\f$ and taking + * advantage of the fact that \f$S_w + S_n = 1\f$ and \f$X^\kappa_w + X^\kappa_n = 1\f$, the number of + * unknowns can be reduced to two. + * The used primary variables are, like in the two-phase model, either \f$p_w\f$ and \f$S_n\f$ + * or \f$p_n\f$ and \f$S_w\f$. The formulation which ought to be used can be + * specified by setting the <tt>Formulation</tt> property to either + * TwoPTwoCIndices::pWsN or TwoPTwoCIndices::pNsW. By + * default, the model uses \f$p_w\f$ and \f$S_n\f$. + * Moreover, the second primary variable depends on the phase state, since a + * primary variable switch is included. The phase state is stored for all nodes + * of the system. Following cases can be distinguished: + * <ul> + * <li> Both phases are present: The saturation is used (either \f$S_n\f$ or \f$S_w\f$, dependent on the chosen <tt>Formulation</tt>), + * as long as \f$ 0 < S_\alpha < 1\f$</li>. + * <li> Only wetting phase is present: The mass fraction of, e.g., air in the wetting phase \f$X^a_w\f$ is used, + * as long as the maximum mass fraction is not exceeded (\f$X^a_w<X^a_{w,max}\f$)</li> + * <li> Only non-wetting phase is present: The mass fraction of, e.g., water in the non-wetting phase, \f$X^w_n\f$, is used, + * as long as the maximum mass fraction is not exceeded (\f$X^w_n<X^w_{n,max}\f$)</li> + * </ul> + */ + +template<class TypeTag> +class TwoPTwoCModel: public BoxModel<TypeTag> +{ + typedef TwoPTwoCModel<TypeTag> ThisType; + typedef BoxModel<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Problem)) Problem; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(PrimaryVariables)) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(VolumeVariables)) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(ElementVolumeVariables)) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(ElementBoundaryTypes)) ElementBoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluxVariables)) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(VertexMapper)) VertexMapper; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(ElementMapper)) ElementMapper; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(SolutionVector)) SolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(TwoPTwoCIndices)) Indices; + + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld, + + numEq = GET_PROP_VALUE(TypeTag, PTAG(NumEq)), + numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)), + numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)), + + pressureIdx = Indices::pressureIdx, + switchIdx = Indices::switchIdx, + + contiLEqIdx = Indices::contiLEqIdx, + contiGEqIdx = Indices::contiGEqIdx, + + lPhaseIdx = Indices::lPhaseIdx, + gPhaseIdx = Indices::gPhaseIdx, + + lCompIdx = Indices::lCompIdx, + gCompIdx = Indices::gCompIdx, + + lPhaseOnly = Indices::lPhaseOnly, + gPhaseOnly = Indices::gPhaseOnly, + bothPhases = Indices::bothPhases, + + plSg = TwoPTwoCFormulation::plSg, + pgSl = TwoPTwoCFormulation::pgSl, + formulation = GET_PROP_VALUE(TypeTag, PTAG(Formulation)) + }; + + typedef typename GridView::template Codim<dim>::Entity Vertex; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + typedef typename GridView::template Codim<dim>::Iterator VertexIterator; + + typedef Dune::FieldVector<Scalar, dim> LocalPosition; + typedef Dune::FieldVector<Scalar, dimWorld> GlobalPosition; + + static const Scalar mobilityUpwindAlpha = + GET_PROP_VALUE(TypeTag, PTAG(MobilityUpwindAlpha)); + +public: + /*! + * \brief Initialize the static data with the initial solution. + * + * \param problem The problem to be solved + */ + void init(Problem &problem) + { + ParentType::init(problem); + + staticVertexDat_.resize(this->gridView_().size(dim)); + + setSwitched_(false); + + VertexIterator it = this->gridView_().template begin<dim> (); + const VertexIterator &endit = this->gridView_().template end<dim> (); + for (; it != endit; ++it) + { + int globalIdx = this->dofMapper().map(*it); + const GlobalPosition &globalPos = it->geometry().corner(0); + + // initialize phase presence + staticVertexDat_[globalIdx].phasePresence + = this->problem_().initialPhasePresence(*it, globalIdx, + globalPos); + staticVertexDat_[globalIdx].wasSwitched = false; + + staticVertexDat_[globalIdx].oldPhasePresence + = staticVertexDat_[globalIdx].phasePresence; + } + } + + /*! + * \brief Compute the total storage inside one phase of all + * conservation quantities. + * + * \param dest Contains the storage of each component for one phase + * \param phaseIdx The phase index + */ + void globalPhaseStorage(PrimaryVariables &dest, int phaseIdx) + { + dest = 0; + + ElementIterator elemIt = this->gridView_().template begin<0>(); + const ElementIterator elemEndIt = this->gridView_().template end<0>(); + for (; elemIt != elemEndIt; ++elemIt) { + this->localResidual().evalPhaseStorage(*elemIt, phaseIdx); + + for (int i = 0; i < elemIt->template count<dim>(); ++i) + dest += this->localResidual().residual(i); + }; + + if (this->gridView_().comm().size() > 1) + dest = this->gridView_().comm().sum(dest); + } + + /*! + * \brief Called by the update() method if applying the newton + * method was unsuccessful. + */ + void updateFailed() + { + ParentType::updateFailed(); + + setSwitched_(false); + resetPhasePresence_(); + }; + + /*! + * \brief Returns the relative weight of a primary variable for + * calculating relative errors. + * + * \param globalVertexIdx The global vertex index + * \param pvIdx The primary variable index + */ + Scalar primaryVarWeight(int globalVertexIdx, int pvIdx) const + { + if (Indices::pressureIdx == pvIdx) + return std::min(1.0/this->prevSol()[globalVertexIdx][pvIdx], 1.0); + return 1; + } + + /*! + * \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 + updateOldPhasePresence_(); + setSwitched_(false); + } + + /*! + * \brief Return true if the primary variables were switched for + * at least one vertex after the last timestep. + */ + bool switched() const + { + return switchFlag_; + } + + /*! + * \brief Returns the phase presence of the current or the old solution of a vertex. + * + * \param globalVertexIdx The global vertex index + * \param oldSol Evaluate function with solution of current or previous time step + */ + int phasePresence(int globalVertexIdx, bool oldSol) const + { + return oldSol ? staticVertexDat_[globalVertexIdx].oldPhasePresence + : staticVertexDat_[globalVertexIdx].phasePresence; + } + + /*! + * \brief Append all quantities of interest which can be derived + * from the solution of the current time step to the VTK + * writer. + * + * \param sol The solution vector + * \param writer The writer for multi-file VTK datasets + */ + template<class MultiWriter> + void addOutputVtkFields(const SolutionVector &sol, + MultiWriter &writer) + { + typedef Dune::BlockVector<Dune::FieldVector<Scalar, 1> > ScalarField; + + // create the required scalar fields + unsigned numVertices = this->problem_().gridView().size(dim); + + ScalarField *Sg = writer.template createField<Scalar, 1> (numVertices); + ScalarField *Sl = writer.template createField<Scalar, 1> (numVertices); + ScalarField *pg = writer.template createField<Scalar, 1> (numVertices); + ScalarField *pl = writer.template createField<Scalar, 1> (numVertices); + ScalarField *pc = writer.template createField<Scalar, 1> (numVertices); + ScalarField *rhoL = + writer.template createField<Scalar, 1> (numVertices); + ScalarField *rhoG = + writer.template createField<Scalar, 1> (numVertices); + ScalarField *mobL = + writer.template createField<Scalar, 1> (numVertices); + ScalarField *mobG = + writer.template createField<Scalar, 1> (numVertices); + ScalarField *phasePresence = writer.template createField<Scalar, 1> ( + numVertices); + ScalarField *massFrac[numPhases][numComponents]; + for (int i = 0; i < numPhases; ++i) + for (int j = 0; j < numComponents; ++j) + massFrac[i][j] = writer.template createField<Scalar, 1>(numVertices); + ScalarField *temperature = writer.template createField<Scalar, 1>(numVertices); + ScalarField *poro = writer.template createField<Scalar, 1>(numVertices); + +#ifdef VELOCITY_OUTPUT // check if velocity output is demanded + ScalarField *velocityX = writer.template createField<Scalar, 1>(numVertices); + ScalarField *velocityY = writer.template createField<Scalar, 1>(numVertices); + ScalarField *velocityZ = writer.template createField<Scalar, 1>(numVertices); +// Scalar maxV=0.; // variable to store the maximum face velocity + + // initialize velocity fields + Scalar boxSurface[numVertices]; + for (int i = 0; i < numVertices; ++i) + { + (*velocityX)[i] = 0; + if (dim > 1) + (*velocityY)[i] = 0; + if (dim > 2) + (*velocityZ)[i] = 0; + boxSurface[i] = 0.0; // initialize the boundary surface of the fv-boxes + } +#endif + + unsigned numElements = this->gridView_().size(0); + ScalarField *rank = + writer.template createField<Scalar, 1> (numElements); + + FVElementGeometry fvElemGeom; + VolumeVariables volVars; + + ElementIterator elemIt = this->gridView_().template begin<0>(); + ElementIterator elemEndIt = this->gridView_().template end<0>(); + for (; elemIt != elemEndIt; ++elemIt) + { + int idx = this->problem_().elementMapper().map(*elemIt); + (*rank)[idx] = this->gridView_().comm().rank(); + fvElemGeom.update(this->gridView_(), *elemIt); + + int numVerts = elemIt->template count<dim> (); + for (int i = 0; i < numVerts; ++i) + { + int globalIdx = this->vertexMapper().map(*elemIt, i, dim); + volVars.update(sol[globalIdx], + this->problem_(), + *elemIt, + fvElemGeom, + i, + false); + (*Sg)[globalIdx] = volVars.saturation(gPhaseIdx); + (*Sl)[globalIdx] = volVars.saturation(lPhaseIdx); + (*pg)[globalIdx] = volVars.pressure(gPhaseIdx); + (*pl)[globalIdx] = volVars.pressure(lPhaseIdx); + (*pc)[globalIdx] = volVars.capillaryPressure(); + (*rhoL)[globalIdx] = volVars.fluidState().density(lPhaseIdx); + (*rhoG)[globalIdx] = volVars.fluidState().density(gPhaseIdx); + (*mobL)[globalIdx] = volVars.mobility(lPhaseIdx); + (*mobG)[globalIdx] = volVars.mobility(gPhaseIdx); + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + { + (*massFrac[phaseIdx][compIdx])[globalIdx] + = volVars.fluidState().massFrac(phaseIdx, + compIdx); + + Valgrind::CheckDefined( + (*massFrac[phaseIdx][compIdx])[globalIdx][0]); + } + (*poro)[globalIdx] = volVars.porosity(); + (*temperature)[globalIdx] = volVars.temperature(); + (*phasePresence)[globalIdx] + = staticVertexDat_[globalIdx].phasePresence; + }; + +#ifdef VELOCITY_OUTPUT // check if velocity output is demanded + // In the box method, the velocity is evaluated on the FE-Grid. However, to get an + // average apparent velocity at the vertex, all contributing velocities have to be interpolated. + GlobalPosition velocity(0.); + ElementVolumeVariables elemVolVars; + + elemVolVars.update(this->problem_(), + *elemIt, + fvElemGeom, + false /* isOldSol? */); + + // loop over the phases + for (int faceIdx = 0; faceIdx< fvElemGeom.numEdges; faceIdx++) + { + //prepare the flux calculations (set up and prepare geometry, FE gradients) + FluxVariables fluxDat(this->problem_(), + *elemIt, + fvElemGeom, + faceIdx, + elemVolVars); + + // choose phase of interest. Alternatively, a loop over all phases would be possible. + int phaseIdx = gPhaseIdx; + + // get darcy velocity + velocity = fluxDat.Kmvp(phaseIdx); // mind the sign: vDarcy = kf grad p + + // up+downstream mobility + const VolumeVariables &up = elemVolVars[fluxDat.upstreamIdx(phaseIdx)]; + const VolumeVariables &down = elemVolVars[fluxDat.downstreamIdx(phaseIdx)]; + Scalar scvfArea = fluxDat.face().normal.two_norm(); //get surface area to weight velocity at the IP with the surface area + velocity *= (mobilityUpwindAlpha*up.mobility(phaseIdx) + (1-mobilityUpwindAlpha)*down.mobility(phaseIdx))* scvfArea; + + int vertIIdx = this->problem_().vertexMapper().map(*elemIt, + fluxDat.face().i, + dim); + int vertJIdx = this->problem_().vertexMapper().map(*elemIt, + fluxDat.face().j, + dim); + // add surface area for weighting purposes + boxSurface[vertIIdx] += scvfArea; + boxSurface[vertJIdx] += scvfArea; + + // Add velocity to upstream and downstream vertex. + // Beware: velocity has to be substracted because of the (wrong) sign of vDarcy + (*velocityX)[vertIIdx] -= velocity[0]; + (*velocityX)[vertJIdx] -= velocity[0]; + if (dim >= 2) + { + (*velocityY)[vertIIdx] -= velocity[1]; + (*velocityY)[vertJIdx] -= velocity[1]; + } + if (dim == 3) + { + (*velocityZ)[vertIIdx] -= velocity[2]; + (*velocityZ)[vertJIdx] -= velocity[2]; + } + } +#endif + } + +#ifdef VELOCITY_OUTPUT // check if velocity output is demanded + // normalize the velocities at the vertices + for (int i = 0; i < numVertices; ++i) + { + (*velocityX)[i] /= boxSurface[i]; + if (dim >= 2) + (*velocityY)[i] /= boxSurface[i]; + if (dim == 3) + (*velocityZ)[i] /= boxSurface[i]; + } +#endif + + writer.addVertexData(Sg, "Sg"); + writer.addVertexData(Sl, "Sl"); + writer.addVertexData(pg, "pg"); + writer.addVertexData(pl, "pl"); + writer.addVertexData(pc, "pc"); + writer.addVertexData(rhoL, "rhoL"); + writer.addVertexData(rhoG, "rhoG"); + writer.addVertexData(mobL, "mobL"); + writer.addVertexData(mobG, "mobG"); + for (int i = 0; i < numPhases; ++i) + { + for (int j = 0; j < numComponents; ++j) + { + std::string name = (boost::format("X_%s%s") + % ((i == lPhaseIdx) ? "l" : "g") + % FluidSystem::componentName(j)).str(); + writer.addVertexData(massFrac[i][j], name.c_str()); + } + } + writer.addVertexData(poro, "porosity"); + writer.addVertexData(temperature, "temperature"); + writer.addVertexData(phasePresence, "phase presence"); + +#ifdef VELOCITY_OUTPUT // check if velocity output is demanded + writer.addVertexData(velocityX, "Vx"); + if (dim >= 2) + writer.addVertexData(velocityY, "Vy"); + if (dim == 3) + writer.addVertexData(velocityZ, "Vz"); +#endif + writer.addCellData(rank, "process rank"); + } + + /*! + * \brief Write the current solution to a restart file. + * + * \param outStream The output stream of one vertex for the restart file + * \param vert The vertex + */ + void serializeEntity(std::ostream &outStream, const Vertex &vert) + { + // write primary variables + ParentType::serializeEntity(outStream, vert); + + int vertIdx = this->dofMapper().map(vert); + if (!outStream.good()) + DUNE_THROW(Dune::IOError, "Could not serialize vertex " << vertIdx); + + outStream << staticVertexDat_[vertIdx].phasePresence << " "; + } + + /*! + * \brief Reads the current solution for a vertex from a restart + * file. + * + * \param inStream The input stream of one vertex from the restart file + * \param vert The vertex + */ + void deserializeEntity(std::istream &inStream, const Vertex &vert) + { + // read primary variables + ParentType::deserializeEntity(inStream, vert); + + // read phase presence + int vertIdx = this->dofMapper().map(vert); + if (!inStream.good()) + DUNE_THROW(Dune::IOError, + "Could not deserialize vertex " << vertIdx); + + inStream >> staticVertexDat_[vertIdx].phasePresence; + staticVertexDat_[vertIdx].oldPhasePresence + = staticVertexDat_[vertIdx].phasePresence; + + } + + /*! + * \brief Update the static data of all vertices in the grid. + * + * \param curGlobalSol The current global solution + * \param oldGlobalSol The previous global solution + */ + void updateStaticData(SolutionVector &curGlobalSol, + const SolutionVector &oldGlobalSol) + { + bool wasSwitched = false; + + for (unsigned i = 0; i < staticVertexDat_.size(); ++i) + staticVertexDat_[i].visited = false; + + FVElementGeometry fvElemGeom; + static VolumeVariables volVars; + ElementIterator it = this->gridView_().template begin<0> (); + const ElementIterator &endit = this->gridView_().template end<0> (); + for (; it != endit; ++it) + { + fvElemGeom.update(this->gridView_(), *it); + for (int i = 0; i < fvElemGeom.numVertices; ++i) + { + int globalIdx = this->vertexMapper().map(*it, i, dim); + + if (staticVertexDat_[globalIdx].visited) + continue; + + staticVertexDat_[globalIdx].visited = true; + volVars.update(curGlobalSol[globalIdx], + this->problem_(), + *it, + fvElemGeom, + i, + false); + const GlobalPosition &global = it->geometry().corner(i); + if (primaryVarSwitch_(curGlobalSol, + volVars, + globalIdx, + global)) + wasSwitched = true; + } + } + + // 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); + + setSwitched_(wasSwitched); + } + +protected: + /*! + * \brief Data which is attached to each vertex and is not only + * stored locally. + */ + struct StaticVars + { + int phasePresence; + bool wasSwitched; + + int oldPhasePresence; + bool visited; + }; + + /*! + * \brief Reset the current phase presence of all vertices to the old one. + * + * This is done after an update failed. + */ + void resetPhasePresence_() + { + int numVertices = this->gridView_().size(dim); + for (int i = 0; i < numVertices; ++i) + { + staticVertexDat_[i].phasePresence + = staticVertexDat_[i].oldPhasePresence; + staticVertexDat_[i].wasSwitched = false; + } + } + + /*! + * \brief Set the old phase of all verts state to the current one. + */ + void updateOldPhasePresence_() + { + int numVertices = this->gridView_().size(dim); + for (int i = 0; i < numVertices; ++i) + { + staticVertexDat_[i].oldPhasePresence + = staticVertexDat_[i].phasePresence; + staticVertexDat_[i].wasSwitched = false; + } + } + + /*! + * \brief Set whether there was a primary variable switch after in + * the last timestep. + */ + void setSwitched_(bool yesno) + { + switchFlag_ = yesno; + } + + // perform variable switch at a vertex; Returns true if a + // variable switch was performed. + bool primaryVarSwitch_(SolutionVector &globalSol, + const VolumeVariables &volVars, + int globalIdx, + const GlobalPosition &globalPos) + { + // evaluate primary variable switch + bool wouldSwitch = false; + int phasePresence = staticVertexDat_[globalIdx].phasePresence; + int newPhasePresence = phasePresence; + + // check if a primary var switch is necessary + if (phasePresence == gPhaseOnly) + { + // calculate mole fraction in the hypothetic liquid phase + Scalar xll = volVars.fluidState().moleFrac(lPhaseIdx, lCompIdx); + Scalar xlg = volVars.fluidState().moleFrac(lPhaseIdx, gCompIdx); + + Scalar xlMax = 1.0; + if (xll + xlg > xlMax) + wouldSwitch = true; + if (staticVertexDat_[globalIdx].wasSwitched) + xlMax *= 1.02; + + // if the sum of the mole fractions would be larger than + // 100%, liquid phase appears + if (xll + xlg > xlMax) + { + // liquid phase appears + /* + std::cout << "Liquid phase appears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", xll + xlg: " + << xll + xlg << std::endl; + */ + newPhasePresence = bothPhases; + if (formulation == pgSl) + globalSol[globalIdx][switchIdx] = 0.001; + else if (formulation == plSg) + globalSol[globalIdx][switchIdx] = 0.999; + }; + } + else if (phasePresence == lPhaseOnly) + { + // calculate fractions of the partial pressures in the + // hypothetic gas phase + Scalar xgl = volVars.fluidState().moleFrac(gPhaseIdx, lCompIdx); + Scalar xgg = volVars.fluidState().moleFrac(gPhaseIdx, gCompIdx); + + Scalar xgMax = 1.0; + if (xgl + xgg > xgMax) + wouldSwitch = true; + if (staticVertexDat_[globalIdx].wasSwitched) + xgMax *= 1.02; + + // if the sum of the mole fractions would be larger than + // 100%, gas phase appears + if (xgl + xgg > xgMax) + { + // gas phase appears + /* + std::cout << "gas phase appears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", x_gl + x_gg: " + << xgl + xgg << std::endl; + */ + newPhasePresence = bothPhases; + if (formulation == pgSl) + globalSol[globalIdx][switchIdx] = 0.999; + else if (formulation == plSg) + globalSol[globalIdx][switchIdx] = 0.001; + } + } + else if (phasePresence == bothPhases) + { + Scalar Smin = 0.0; + if (staticVertexDat_[globalIdx].wasSwitched) + Smin = -0.01; + + if (volVars.saturation(gPhaseIdx) <= Smin) + { + wouldSwitch = true; + // gas phase disappears + /* + std::cout << "Gas phase disappears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", Sg: " + << volVars.saturation(gPhaseIdx) << std::endl; + */ + newPhasePresence = lPhaseOnly; + + globalSol[globalIdx][switchIdx] + = volVars.fluidState().massFrac(lPhaseIdx, gCompIdx); + } + else if (volVars.saturation(lPhaseIdx) <= Smin) + { + wouldSwitch = true; + // liquid phase disappears + /* + std::cout << "Liquid phase disappears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", Sl: " + << volVars.saturation(lPhaseIdx) << std::endl; + */ + newPhasePresence = gPhaseOnly; + + globalSol[globalIdx][switchIdx] + = volVars.fluidState().massFrac(gPhaseIdx, lCompIdx); + } + } + + staticVertexDat_[globalIdx].phasePresence = newPhasePresence; + staticVertexDat_[globalIdx].wasSwitched = wouldSwitch; + return phasePresence != newPhasePresence; + } + + // parameters given in constructor + std::vector<StaticVars> staticVertexDat_; + bool switchFlag_; +}; + +} + +#include "2p2cpropertydefaults.hh" + +#endif diff --git a/dumux/boxmodels/new_2p2c/2p2cnewtoncontroller.hh b/dumux/boxmodels/new_2p2c/2p2cnewtoncontroller.hh new file mode 100644 index 0000000000..1b4ad9376f --- /dev/null +++ b/dumux/boxmodels/new_2p2c/2p2cnewtoncontroller.hh @@ -0,0 +1,181 @@ +// $Id$ +/***************************************************************************** + * Copyright (C) 2008 by Bernd Flemisch, Andreas Lauser * + * Institute of Hydraulic Engineering * + * 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 A 2p2c specific controller for the newton solver. + * + * This controller 'knows' what a 'physically meaningful' solution is + * which allows the newton method to abort quicker if the solution is + * way out of bounds. + */ +#ifndef DUMUX_2P2C_NEWTON_CONTROLLER_HH +#define DUMUX_2P2C_NEWTON_CONTROLLER_HH + +#include "2p2cproperties.hh" + +#include <dumux/nonlinear/newtoncontroller.hh> + +namespace Dumux { +/*! + * \ingroup TwoPTwoCModel + * \brief A 2p2c specific controller for the newton solver. + * + * This controller 'knows' what a 'physically meaningful' solution is + * which allows the newton method to abort quicker if the solution is + * way out of bounds. + */ +template <class TypeTag> +class TwoPTwoCNewtonController : public NewtonController<TypeTag> +{ + typedef NewtonController<TypeTag> ParentType; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(NewtonController)) Implementation; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Model)) Model; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(NewtonMethod)) NewtonMethod; + + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(SolutionVector)) SolutionVector; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(TwoPTwoCIndices)) Indices; + + enum { + pressureIdx = Indices::pressureIdx, + switchIdx = Indices::switchIdx + }; + + enum { enablePartialReassemble = GET_PROP_VALUE(TypeTag, PTAG(EnablePartialReassemble)) }; + +public: + TwoPTwoCNewtonController() + { + this->setRelTolerance(1e-7); + this->setTargetSteps(10); + this->setMaxSteps(18); + }; + + + /*! + * \brief + * Suggest a new time step size based either on the number of newton + * iterations required or on the variable switch + * + * \param u The current global solution vector + * \param uLastIter The previous global solution vector + * + */ + void newtonEndStep(SolutionVector &uCurrentIter, + const SolutionVector &uLastIter) + { + // call the method of the base class + this->method().model().updateStaticData(uCurrentIter, uLastIter); + ParentType::newtonEndStep(uCurrentIter, uLastIter); + } + + /*! + * \brief Update the current solution function with a delta vector. + * + * The error estimates required for the newtonConverged() and + * newtonProceed() methods should be updated here. + * + * Different update strategies, such as line search and chopped + * updates can be implemented. The default behaviour is just to + * subtract deltaU from uLastIter. + * + * \param uCurrentIter The solution after the current Newton iteration + * \param uLastIter The solution after the last Newton iteration + * \param deltaU The delta as calculated from solving the linear + * system of equations. This parameter also stores + * the updated solution. + */ + void newtonUpdate(SolutionVector &uCurrentIter, + const SolutionVector &uLastIter, + const SolutionVector &deltaU) + { + this->writeConvergence_(uLastIter, deltaU); + + this->newtonUpdateRelError(uLastIter, deltaU); + + // compute the vertex and element colors for partial + // reassembly + if (enablePartialReassemble) { + Scalar reassembleTol = Dumux::geometricMean(this->error_, 0.1*this->tolerance_); + reassembleTol = std::max(reassembleTol, 0.1*this->tolerance_); + this->model_().jacobianAssembler().updateDiscrepancy(uLastIter, deltaU); + this->model_().jacobianAssembler().computeColors(reassembleTol); + } + + if (GET_PROP_VALUE(TypeTag, PTAG(NewtonUseLineSearch))) + lineSearchUpdate_(uCurrentIter, uLastIter, deltaU); + else { + uCurrentIter = uLastIter; + uCurrentIter -= deltaU; + } + } + + + /*! + * \brief + * Returns true if the current solution can be considered to + * be accurate enough + */ + bool newtonConverged() + { + if (this->method().model().switched()) + return false; + + return ParentType::newtonConverged(); + }; + +private: + void lineSearchUpdate_(SolutionVector &uCurrentIter, + const SolutionVector &uLastIter, + const SolutionVector &deltaU) + { + Scalar lambda = 1.0; + Scalar globDef; + + // calculate the residual of the current solution + SolutionVector tmp(uLastIter); + Scalar oldGlobDef = this->method().model().globalResidual(tmp, uLastIter); + + while (true) { + uCurrentIter = deltaU; + uCurrentIter *= -lambda; + uCurrentIter += uLastIter; + + // calculate the residual of the current solution + globDef = this->method().model().globalResidual(tmp, uCurrentIter); + + if (globDef < oldGlobDef || lambda <= 1.0/8) { + this->endIterMsg() << ", defect " << oldGlobDef << "->" << globDef << "@lambda=" << lambda; + return; + } + + // try with a smaller update + lambda /= 2; + } + }; + +}; +} + +#endif diff --git a/dumux/boxmodels/new_2p2c/2p2cproblem.hh b/dumux/boxmodels/new_2p2c/2p2cproblem.hh new file mode 100644 index 0000000000..53c91d2820 --- /dev/null +++ b/dumux/boxmodels/new_2p2c/2p2cproblem.hh @@ -0,0 +1,129 @@ +// $Id$ +/***************************************************************************** + * Copyright (C) 2009 by Andreas Lauser * + * Institute of Hydraulic Engineering * + * 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 Base class for all problems which use the two-phase, + * two-component box model + */ +#ifndef DUMUX_2P2C_PROBLEM_HH +#define DUMUX_2P2C_PROBLEM_HH + +#include <dumux/boxmodels/common/boxproblem.hh> + +namespace Dumux +{ +/*! + * \ingroup TwoPTwoCModel + * \brief Base class for all problems which use the two-phase, two-component box model + * + */ +template<class TypeTag> +class TwoPTwoCProblem : public BoxProblem<TypeTag> +{ + typedef BoxProblem<TypeTag> ParentType; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Model)) Implementation; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + typedef typename GridView::Grid Grid; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(TimeManager)) TimeManager; + + // material properties + typedef typename GET_PROP_TYPE(TypeTag, PTAG(SpatialParameters)) SpatialParameters; + + enum { + dim = Grid::dimension, + dimWorld = Grid::dimensionworld + }; + + typedef Dune::FieldVector<Scalar, dimWorld> GlobalPosition; + +public: + /*! + * \brief The constructor + * + * \param timeManager The time manager + * \param gridView The grid view + */ + TwoPTwoCProblem(TimeManager &timeManager, const GridView &gridView) + : ParentType(timeManager, gridView), + gravity_(0), + spatialParams_(gridView) + { + if (GET_PROP_VALUE(TypeTag, PTAG(EnableGravity))) + gravity_[dim-1] = -9.81; + } + + /*! + * \name Problem parameters + */ + // \{ + + /*! + * \brief Returns the temperature within the domain. + * + * This method MUST be overwritten by the actual problem. + */ + Scalar temperature() const + { DUNE_THROW(Dune::NotImplemented, "The Problem must implement a temperature() method for isothermal problems!"); }; + + /*! + * \brief Returns the acceleration due to gravity. + * + * If the <tt>EnableGravity</tt> property is true, this means + * \f$\boldsymbol{g} = ( 0,\dots,\ -9.81)^T \f$, else \f$\boldsymbol{g} = ( 0,\dots, 0)^T \f$ + */ + const GlobalPosition &gravity() const + { return gravity_; } + + /*! + * \brief Returns the spatial parameters object. + */ + SpatialParameters &spatialParameters() + { return spatialParams_; } + + /*! + * \brief Returns the spatial parameters object. + */ + const SpatialParameters &spatialParameters() const + { return spatialParams_; } + + // \} + +private: + //! Returns the implementation of the problem (i.e. static polymorphism) + Implementation *asImp_() + { return static_cast<Implementation *>(this); } + + //! \copydoc asImp_() + const Implementation *asImp_() const + { return static_cast<const Implementation *>(this); } + + GlobalPosition gravity_; + + // spatial parameters + SpatialParameters spatialParams_; +}; + +} + +#endif diff --git a/dumux/boxmodels/new_2p2c/2p2cproperties.hh b/dumux/boxmodels/new_2p2c/2p2cproperties.hh new file mode 100644 index 0000000000..5204442601 --- /dev/null +++ b/dumux/boxmodels/new_2p2c/2p2cproperties.hh @@ -0,0 +1,68 @@ +// $Id$ +/***************************************************************************** + * Copyright (C) 2008-2010 by Andreas Lauser * + * Copyright (C) 2008-2009 by Melanie Darcis * + * Copyright (C) 2008-2009 by Klaus Mosthaf * + * Copyright (C) 2008-2009 by Bernd Flemisch * + * Institute of Hydraulic Engineering * + * 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/>. * + *****************************************************************************/ +/*! + * \ingroup TwoPTwoCModel + */ +/*! + * \file + * + * \brief Defines the properties required for the 2p2c BOX model. + */ +#ifndef DUMUX_2P2C_PROPERTIES_HH +#define DUMUX_2P2C_PROPERTIES_HH + +#include <dumux/boxmodels/common/boxproperties.hh> + +namespace Dumux +{ + +namespace Properties +{ +////////////////////////////////////////////////////////////////// +// Type tags +////////////////////////////////////////////////////////////////// + +//! The type tag for the isothermal single phase problems +NEW_TYPE_TAG(BoxTwoPTwoC, INHERITS_FROM(BoxModel)); + +////////////////////////////////////////////////////////////////// +// Property tags +////////////////////////////////////////////////////////////////// + +NEW_PROP_TAG(NumPhases); //!< Number of fluid phases in the system +NEW_PROP_TAG(NumComponents); //!< Number of fluid components in the system +NEW_PROP_TAG(TwoPTwoCIndices); //!< Enumerations for the 2p2c models +NEW_PROP_TAG(Formulation); //!< The formulation of the model +NEW_PROP_TAG(SpatialParameters); //!< The type of the spatial parameters +NEW_PROP_TAG(FluidSystem); //!< Type of the multi-component relations + +NEW_PROP_TAG(MaterialLaw); //!< The material law which ought to be used (extracted from the spatial parameters) +NEW_PROP_TAG(MaterialLawParams); //!< The parameters of the material law (extracted from the spatial parameters) + +NEW_PROP_TAG(EnableGravity); //!< Returns whether gravity is considered in the problem +NEW_PROP_TAG(MobilityUpwindAlpha); //!< The value of the upwind parameter for the mobility +} +} + +#endif diff --git a/dumux/boxmodels/new_2p2c/2p2cpropertydefaults.hh b/dumux/boxmodels/new_2p2c/2p2cpropertydefaults.hh new file mode 100644 index 0000000000..a499883722 --- /dev/null +++ b/dumux/boxmodels/new_2p2c/2p2cpropertydefaults.hh @@ -0,0 +1,165 @@ +// $Id: 2p2cproperties.hh 3784 2010-06-24 13:43:57Z bernd $ +/***************************************************************************** + * Copyright (C) 2008 by Klaus Mosthaf, Andreas Lauser, Bernd Flemisch * + * Institute of Hydraulic Engineering * + * 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/>. * + *****************************************************************************/ +/*! + * \ingroup TwoPTwoCModel + */ +/*! + * \file + * + * \brief Defines default values for most properties required by the + * 2p2c box model. + */ +#ifndef DUMUX_2P2C_PROPERTY_DEFAULTS_HH +#define DUMUX_2P2C_PROPERTY_DEFAULTS_HH + +#include "2p2cindices.hh" + +#include "2p2cmodel.hh" +#include "2p2cproblem.hh" +#include "2p2cindices.hh" +#include "2p2cfluxvariables.hh" +#include "2p2cvolumevariables.hh" +#include "2p2cfluidstate.hh" +#include "2p2cproperties.hh" +#include "2p2cnewtoncontroller.hh" +#include "2p2cboundaryvariables.hh" + +namespace Dumux +{ + +namespace Properties { +////////////////////////////////////////////////////////////////// +// Property values +////////////////////////////////////////////////////////////////// + +/*! + * \brief Set the property for the number of components. + * + * We just forward the number from the fluid system and use an static + * assert to make sure it is 2. + */ +SET_PROP(BoxTwoPTwoC, NumComponents) +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + +public: + static const int value = FluidSystem::numComponents; + + static_assert(value == 2, + "Only fluid systems with 2 components are supported by the 2p-2c model!"); +}; + +/*! + * \brief Set the property for the number of fluid phases. + * + * We just forward the number from the fluid system and use an static + * assert to make sure it is 2. + */ +SET_PROP(BoxTwoPTwoC, NumPhases) +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + +public: + static const int value = FluidSystem::numPhases; + static_assert(value == 2, + "Only fluid systems with 2 phases are supported by the 2p-2c model!"); +}; + +SET_INT_PROP(BoxTwoPTwoC, NumEq, 2); //!< set the number of equations to 2 + +//! Set the default formulation to pl-Sg +SET_INT_PROP(BoxTwoPTwoC, + Formulation, + TwoPTwoCFormulation::plSg); + +/*! + * \brief Set the property for the material law by retrieving it from + * the spatial parameters. + */ +SET_PROP(BoxTwoPTwoC, MaterialLaw) +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, PTAG(SpatialParameters)) SpatialParameters; + +public: + typedef typename SpatialParameters::MaterialLaw type; +}; + +/*! + * \brief Set the property for the material parameters by extracting + * it from the material law. + */ +SET_PROP(BoxTwoPTwoC, MaterialLawParams) +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MaterialLaw)) MaterialLaw; + +public: + typedef typename MaterialLaw::Params type; +}; + +//! Use the 2p2c local jacobian operator for the 2p2c model +SET_TYPE_PROP(BoxTwoPTwoC, + LocalResidual, + TwoPTwoCLocalResidual<TypeTag>); + +//! Use the 2p2c specific newton controller for the 2p2c model +SET_TYPE_PROP(BoxTwoPTwoC, NewtonController, TwoPTwoCNewtonController<TypeTag>); + +//! the Model property +SET_TYPE_PROP(BoxTwoPTwoC, Model, TwoPTwoCModel<TypeTag>); + +//! the VolumeVariables property +SET_TYPE_PROP(BoxTwoPTwoC, VolumeVariables, TwoPTwoCVolumeVariables<TypeTag>); + +//! the FluxVariables property +SET_TYPE_PROP(BoxTwoPTwoC, FluxVariables, TwoPTwoCFluxVariables<TypeTag>); + +//! the BoundaryVariables property +SET_TYPE_PROP(BoxTwoPTwoC, BoundaryVariables, TwoPTwoCBoundaryVariables<TypeTag>); + +//! the upwind factor for the mobility. +SET_SCALAR_PROP(BoxTwoPTwoC, MobilityUpwindAlpha, 1.0); + +//! The indices required by the isothermal 2p2c model +SET_PROP(BoxTwoPTwoC, + TwoPTwoCIndices) +{ private: + enum { Formulation = GET_PROP_VALUE(TypeTag, PTAG(Formulation)) }; +public: + typedef TwoPTwoCIndices<TypeTag, Formulation, 0> type; +}; + +// enable jacobian matrix recycling by default +SET_BOOL_PROP(BoxTwoPTwoC, EnableJacobianRecycling, true); +// enable partial reassembling by default +SET_BOOL_PROP(BoxTwoPTwoC, EnablePartialReassemble, true); +// enable time-step ramp up by default +SET_BOOL_PROP(BoxTwoPTwoC, EnableTimeStepRampUp, false); + +// +} + +} + +#endif diff --git a/dumux/boxmodels/new_2p2c/2p2cvolumevariables.hh b/dumux/boxmodels/new_2p2c/2p2cvolumevariables.hh new file mode 100644 index 0000000000..239e5ad450 --- /dev/null +++ b/dumux/boxmodels/new_2p2c/2p2cvolumevariables.hh @@ -0,0 +1,456 @@ +// $Id$ +/***************************************************************************** + * Copyright (C) 2008,2009 by Klaus Mosthaf, * + * Andreas Lauser, * + * Bernd Flemisch * + * Institute of Hydraulic Engineering * + * 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 Contains the quantities which are constant within a + * finite volume in the two-phase, two-component model. + */ +#ifndef DUMUX_2P2C_VOLUME_VARIABLES_HH +#define DUMUX_2P2C_VOLUME_VARIABLES_HH + +#include <dumux/boxmodels/common/boxmodel.hh> +#include <dumux/common/math.hh> + +#include <dune/common/collectivecommunication.hh> + +#include <dumux/material/fluidstates/equilibriumfluidstate.hh> +#include <dumux/material/constraintsolvers/compositionfromfugacities.hh> + +#include <vector> +#include <iostream> + +#include "2p2cproperties.hh" +#include "2p2cindices.hh" + +namespace Dumux +{ + +/*! + * \ingroup TwoPTwoCModel + * \brief Contains the quantities which are are constant within a + * finite volume in the two-phase, two-component model. + */ +template <class TypeTag> +class TwoPTwoCVolumeVariables : public BoxVolumeVariables<TypeTag> +{ + typedef BoxVolumeVariables<TypeTag> ParentType; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(VolumeVariables)) Implementation; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Problem)) Problem; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(PrimaryVariables)) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MaterialLaw)) MaterialLaw; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MaterialLawParams)) MaterialLawParams; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(TwoPTwoCIndices)) Indices; + enum { + dim = GridView::dimension, + + numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)), + formulation = GET_PROP_VALUE(TypeTag, PTAG(Formulation)), + + // component indices + lCompIdx = Indices::lCompIdx, + gCompIdx = Indices::gCompIdx, + + // phase indices + lPhaseIdx = Indices::lPhaseIdx, + gPhaseIdx = Indices::gPhaseIdx, + + // primary variable indices + pressureIdx = Indices::pressureIdx, + switchIdx = Indices::switchIdx, + + // phase states + lPhaseOnly = Indices::lPhaseOnly, + gPhaseOnly = Indices::gPhaseOnly, + bothPhases = Indices::bothPhases, + + // formulations + plSg = TwoPTwoCFormulation::plSg, + pgSl = TwoPTwoCFormulation::pgSl, + }; + + typedef typename GridView::template Codim<0>::Entity Element; + +public: + //! The return type of the fluidState() method + typedef Dumux::EquilibriumFluidState<Scalar, FluidSystem> FluidState; + + /*! + * \brief Update all quantities for a given control volume. + * + * \param priVars The primary variables + * \param problem The problem + * \param element The element + * \param elemGeom The finite-volume geometry in the box scheme + * \param scvIdx The local index of the SCV (sub-control volume) + * \param isOldSol Evaluate function with solution of current or previous time step + */ + void update(const PrimaryVariables &priVars, + const Problem &problem, + const Element &element, + const FVElementGeometry &elemGeom, + int scvIdx, + bool isOldSol) + { + ParentType::update(priVars, + problem, + element, + elemGeom, + scvIdx, + isOldSol); + + typename FluidSystem::MutableParameters mutParams; + + int globalVertIdx = problem.model().vertexMapper().map(element, scvIdx, dim); + int phasePresence = problem.model().phasePresence(globalVertIdx, isOldSol); + + ///////////// + // set the phase saturations + ///////////// + if (phasePresence == gPhaseOnly) { + mutParams.setSaturation(lPhaseIdx, 0.0); + mutParams.setSaturation(gPhaseIdx, 1.0); + } + else if (phasePresence == lPhaseOnly) { + mutParams.setSaturation(lPhaseIdx, 1.0); + mutParams.setSaturation(gPhaseIdx, 0.0); + } + else if (phasePresence == bothPhases) { + Scalar Sg; + if (formulation == plSg) + Sg = priVars[switchIdx]; + else if (formulation == pgSl) + Sg = 1.0 - priVars[switchIdx]; + + mutParams.setSaturation(lPhaseIdx, 1 - Sg); + mutParams.setSaturation(gPhaseIdx, Sg); + } + + ///////////// + // set the phase temperatures + ///////////// + // update the temperature part of the energy module + Scalar T = asImp_().getTemperature(priVars, + element, + elemGeom, + scvIdx, + problem); + Valgrind::CheckDefined(T); + for (int i = 0; i < numPhases; ++i) + mutParams.setTemperature(i, T); + + ///////////// + // set the phase pressures + ///////////// + + // capillary pressure parameters + const MaterialLawParams &materialParams = + problem.spatialParameters().materialLawParams(element, elemGeom, scvIdx); + Scalar pC = MaterialLaw::pC(materialParams, mutParams.saturation(lPhaseIdx)); + if (formulation == plSg) { + mutParams.setPressure(lPhaseIdx, priVars[pressureIdx]); + mutParams.setPressure(gPhaseIdx, priVars[pressureIdx] + pC); + } + else if (formulation == pgSl){ + mutParams.setPressure(lPhaseIdx, priVars[pressureIdx] - pC); + mutParams.setPressure(gPhaseIdx, priVars[pressureIdx]); + } + Valgrind::CheckDefined(mutParams.pressure(lPhaseIdx)); + Valgrind::CheckDefined(mutParams.pressure(gPhaseIdx)); + + ///////////// + // set the phase compositions. + ///////////// + if (phasePresence == gPhaseOnly) { + // mass fractions + Scalar Xg1 = priVars[switchIdx]; + Scalar Xg2 = 1 - Xg1; + + // molar masses + Scalar M1 = FluidSystem::molarMass(lCompIdx); + Scalar M2 = FluidSystem::molarMass(gCompIdx); + + // convert mass to mole fractions + Scalar meanM = M1*M2/(M2 + Xg2*(M1 - M2)); + mutParams.setMoleFrac(gPhaseIdx, lCompIdx, Xg1 * M1/meanM); + mutParams.setMoleFrac(gPhaseIdx, gCompIdx, Xg2 * M2/meanM); + + // calculate component fugacities in gas phase + Scalar fug1 = FluidSystem::computeFugacity(mutParams, gPhaseIdx, lCompIdx); + Scalar fug2 = FluidSystem::computeFugacity(mutParams, gPhaseIdx, gCompIdx); + mutParams.setFugacity(gPhaseIdx, lCompIdx, fug1); + mutParams.setFugacity(gPhaseIdx, gCompIdx, fug2); + + // use same fugacities in liquid phase + mutParams.setFugacity(lPhaseIdx, lCompIdx, fug1); + mutParams.setFugacity(lPhaseIdx, gCompIdx, fug2); + + // initial guess of liquid composition + mutParams.setMoleFrac(lPhaseIdx, lCompIdx, 0.98); + mutParams.setMoleFrac(lPhaseIdx, gCompIdx, 0.02); + + // calculate liquid composition from fugacities + typedef Dumux::CompositionFromFugacities<Scalar, FluidSystem> CompFromFug; + CompFromFug::solve(mutParams, lPhaseIdx); + + Valgrind::CheckDefined(mutParams.moleFrac(gPhaseIdx, lCompIdx)); + Valgrind::CheckDefined(mutParams.moleFrac(gPhaseIdx, gCompIdx)); + Valgrind::CheckDefined(mutParams.moleFrac(lPhaseIdx, lCompIdx)); + Valgrind::CheckDefined(mutParams.moleFrac(lPhaseIdx, gCompIdx)); + + // calculate molar volume of gas phase + Scalar Vmg = FluidSystem::computeMolarVolume(mutParams, gPhaseIdx); + mutParams.setMolarVolume(gPhaseIdx, Vmg); + } + else if (phasePresence == lPhaseOnly) { + // mass fractions + Scalar Xl2 = priVars[switchIdx]; + Scalar Xl1 = 1 - Xl2; + + // molar masses + Scalar M1 = FluidSystem::molarMass(lCompIdx); + Scalar M2 = FluidSystem::molarMass(gCompIdx); + + // convert mass to mole fractions + Scalar meanM = M1*M2/(M2 + Xl2*(M1 - M2)); + mutParams.setMoleFrac(lPhaseIdx, lCompIdx, Xl1 * M1/meanM); + mutParams.setMoleFrac(lPhaseIdx, gCompIdx, Xl2 * M2/meanM); + + // calculate component fugacities in liquid phase + Scalar fug1 = FluidSystem::computeFugacity(mutParams, lPhaseIdx, lCompIdx); + Scalar fug2 = FluidSystem::computeFugacity(mutParams, lPhaseIdx, gCompIdx); + mutParams.setFugacity(lPhaseIdx, lCompIdx, fug1); + mutParams.setFugacity(lPhaseIdx, gCompIdx, fug2); + + // use same fugacities in gas phase + mutParams.setFugacity(gPhaseIdx, lCompIdx, fug1); + mutParams.setFugacity(gPhaseIdx, gCompIdx, fug2); + + // initial guess of gas composition + mutParams.setMoleFrac(gPhaseIdx, lCompIdx, 0.05); + mutParams.setMoleFrac(gPhaseIdx, gCompIdx, 0.95); + + // calculate liquid composition from fugacities + typedef Dumux::CompositionFromFugacities<Scalar, FluidSystem> CompFromFug; + CompFromFug::solve(mutParams, gPhaseIdx); + Valgrind::CheckDefined(mutParams.moleFrac(gPhaseIdx, lCompIdx)); + Valgrind::CheckDefined(mutParams.moleFrac(gPhaseIdx, gCompIdx)); + Valgrind::CheckDefined(mutParams.moleFrac(lPhaseIdx, lCompIdx)); + Valgrind::CheckDefined(mutParams.moleFrac(lPhaseIdx, gCompIdx)); + + // calculate molar volume of liquid phase + Scalar Vml = FluidSystem::computeMolarVolume(mutParams, lPhaseIdx); + mutParams.setMolarVolume(lPhaseIdx, Vml); + } + else if (phasePresence == bothPhases) { + // HACK: assume both phases to be an ideal mixture, + // i.e. the fugacity coefficents do not depend on the + // composition + Scalar phi_l1 = FluidSystem::computeFugacityCoeff(mutParams, lPhaseIdx, lCompIdx); + Scalar phi_l2 = FluidSystem::computeFugacityCoeff(mutParams, lPhaseIdx, gCompIdx); + Scalar phi_g1 = FluidSystem::computeFugacityCoeff(mutParams, gPhaseIdx, lCompIdx); + Scalar phi_g2 = FluidSystem::computeFugacityCoeff(mutParams, gPhaseIdx, gCompIdx); + Scalar pl = mutParams.pressure(lPhaseIdx); + Scalar pg = mutParams.pressure(gPhaseIdx); + Valgrind::CheckDefined(phi_l1); + Valgrind::CheckDefined(phi_l2); + Valgrind::CheckDefined(phi_g1); + Valgrind::CheckDefined(phi_g2); + + Scalar xg2 = (phi_g2/phi_l1 - pl/pg) / (phi_g1/phi_l1 - phi_g2/phi_l2); + Scalar xg1 = 1 - xg2; + Scalar xl2 = (xg2*pg*phi_g2)/(pl*phi_l2); + Scalar xl1 = 1 - xl2; + + mutParams.setMoleFrac(lPhaseIdx, lCompIdx, xl1); + mutParams.setMoleFrac(lPhaseIdx, gCompIdx, xl2); + mutParams.setMoleFrac(gPhaseIdx, lCompIdx, xg1); + mutParams.setMoleFrac(gPhaseIdx, gCompIdx, xg2); + + Scalar Vml = FluidSystem::computeMolarVolume(mutParams, lPhaseIdx); + Scalar Vmg = FluidSystem::computeMolarVolume(mutParams, gPhaseIdx); + mutParams.setMolarVolume(lPhaseIdx, Vml); + mutParams.setMolarVolume(gPhaseIdx, Vmg); + } + + + // Mobilities + Scalar muL = FluidSystem::computeViscosity(mutParams, lPhaseIdx); + Scalar krL = MaterialLaw::krw(materialParams, mutParams.saturation(lPhaseIdx)); + mobility_[lPhaseIdx] = krL / muL; + Valgrind::CheckDefined(mobility_[lPhaseIdx]); + + // ATTENTION: krn requires the liquid saturation as parameter! + Scalar muG = FluidSystem::computeViscosity(mutParams, gPhaseIdx); + Scalar krG = MaterialLaw::krn(materialParams, mutParams.saturation(lPhaseIdx)); + mobility_[gPhaseIdx] = krG / muG; + Valgrind::CheckDefined(mobility_[gPhaseIdx]); + +#if 0 + // binary diffusion coefficents + diffCoeff_[phaseIdx] = + FluidSystem::diffCoeff(phaseIdx, + lCompIdx, + gCompIdx, + fluidState_.temperature(), + fluidState_.phasePressure(phaseIdx), + fluidState_); + Valgrind::CheckDefined(diffCoeff_[phaseIdx]); +#endif + + // porosity + porosity_ = problem.spatialParameters().porosity(element, + elemGeom, + scvIdx); + Valgrind::CheckDefined(porosity_); + + asImp_().updateEnergy(mutParams, priVars, element, elemGeom, scvIdx, problem); + + // assign the equilibrium fluid state from the generic one + fluidState_.assign(mutParams); + } + + /*! + * \brief Returns the phase state for the control-volume. + */ + const FluidState &fluidState() const + { return fluidState_; } + + /*! + * \brief Returns the effective saturation of a given phase within + * the control volume. + * + * \param phaseIdx The phase index + */ + Scalar saturation(int phaseIdx) const + { return fluidState_.saturation(phaseIdx); } + + /*! + * \brief Returns the mass density of a given phase within the + * control volume. + * + * \param phaseIdx The phase index + */ + Scalar density(int phaseIdx) const + { return fluidState_.density(phaseIdx); } + + /*! + * \brief Returns the mass density of a given phase within the + * control volume. + * + * \param phaseIdx The phase index + */ + Scalar molarDensity(int phaseIdx) const + { return fluidState_.molarDensity(phaseIdx); } + + /*! + * \brief Returns the effective pressure of a given phase within + * the control volume. + * + * \param phaseIdx The phase index + */ + Scalar pressure(int phaseIdx) const + { return fluidState_.pressure(phaseIdx); } + + /*! + * \brief Returns temperature inside the sub-control volume. + * + * Note that we assume thermodynamic equilibrium, i.e. the + * temperature of the rock matrix and of all fluid phases are + * identical. + */ + Scalar temperature() const + { return fluidState_.temperature(); } + + /*! + * \brief Returns the effective mobility of a given phase within + * the control volume. + * + * \param phaseIdx The phase index + */ + Scalar mobility(int phaseIdx) const + { + return mobility_[phaseIdx]; + } + + /*! + * \brief Returns the effective capillary pressure within the control volume. + */ + Scalar capillaryPressure() const + { return fluidState_.pressure(gPhaseIdx) - fluidState_.pressure(lPhaseIdx); } + + /*! + * \brief Returns the average porosity within the control volume. + */ + Scalar porosity() const + { return porosity_; } + +#if 0 + /*! + * \brief Returns the binary diffusion coefficients for a phase + */ + Scalar diffCoeff(int phaseIdx) const + { return diffCoeff_[phaseIdx]; } +#endif + +protected: + Scalar getTemperature(const PrimaryVariables &priVars, + const Element &element, + const FVElementGeometry &elemGeom, + int scvIdx, + const Problem &problem) + { + return problem.temperature(element, elemGeom, scvIdx); + } + + template <class MutableParameters> + void updateEnergy(MutableParameters &mutParams, + const PrimaryVariables &priVars, + const Element &element, + const FVElementGeometry &elemGeom, + int scvIdx, + const Problem &problem) + {}; + + Scalar porosity_; //!< Effective porosity within the control volume + Scalar mobility_[numPhases]; //!< Effective mobility within the control volume +// Scalar diffCoeff_[numPhases]; //!< Binary diffusion coefficients for the phases + FluidState fluidState_; + +private: + Implementation &asImp_() + { return *static_cast<Implementation*>(this); } + + const Implementation &asImp_() const + { return *static_cast<const Implementation*>(this); } +}; + +} // end namepace + +#endif diff --git a/dumux/boxmodels/new_2p2c/Makefile.am b/dumux/boxmodels/new_2p2c/Makefile.am new file mode 100644 index 0000000000..f15c521752 --- /dev/null +++ b/dumux/boxmodels/new_2p2c/Makefile.am @@ -0,0 +1,4 @@ +2p2cdir = $(includedir)/dumux/boxmodels/2p2c +2p2c_HEADERS = *.hh + +include $(top_srcdir)/am/global-rules diff --git a/dumux/boxmodels/new_2p2cni/2p2cniboundaryvariables.hh b/dumux/boxmodels/new_2p2cni/2p2cniboundaryvariables.hh new file mode 100644 index 0000000000..6856d2068c --- /dev/null +++ b/dumux/boxmodels/new_2p2cni/2p2cniboundaryvariables.hh @@ -0,0 +1,272 @@ +// $Id$ +/***************************************************************************** + * Copyright (C) 2010 by Katherina Baber, Klaus Mosthaf * + * Copyright (C) 2008-2009 by Bernd Flemisch, Andreas Lauser * + * Institute of Hydraulic Engineering * + * 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 This file contains the data which is required to calculate + * all fluxes of the fluid phase over the boundary of a finite volume. + * + * This means pressure and temperature gradients, phase densities at + * the integration point of the boundary, etc. + */ +#ifndef DUMUX_2P2CNI_BOUNDARY_VARIABLES_HH +#define DUMUX_2P2CNI_BOUNDARY_VARIABLES_HH + +#include <dumux/common/math.hh> + +namespace Dumux +{ + +/*! + * \ingroup TwoPTwoCNIModel + * \brief This template class contains the data which is required to + * calculate the fluxes of the fluid phases over the boundary of a + * finite volume for the 2p2cni model. + * + * This means pressure and velocity gradients, phase density and viscosity at + * the integration point of the boundary, etc. + */ +template <class TypeTag> +class TwoPTwoCNIBoundaryVariables +{ + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Problem)) Problem; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(VolumeVariables)) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(ElementVolumeVariables)) ElementVolumeVariables; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(TwoPTwoCIndices)) Indices; + + enum { + dim = GridView::dimension, + }; + + typedef typename GridView::template Codim<0>::Entity Element; + typedef Dune::FieldVector<Scalar, dim> VelocityVector; + typedef Dune::FieldVector<Scalar, dim> ScalarGradient; + typedef Dune::FieldMatrix<Scalar, dim, dim> VectorGradient; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + typedef typename FVElementGeometry::SubControlVolume SCV; + typedef typename FVElementGeometry::BoundaryFace BoundaryFace; + + enum { + lPhaseIdx = Indices::lPhaseIdx, + gPhaseIdx = Indices::gPhaseIdx, + + lCompIdx = Indices::lCompIdx, + gCompIdx = Indices::gCompIdx, + + numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)) + }; + +public: + TwoPTwoCNIBoundaryVariables(const Problem &problem, + const Element &element, + const FVElementGeometry &elemGeom, + int boundaryFaceIdx, + const ElementVolumeVariables &elemDat, + int scvIdx) + : fvElemGeom_(elemGeom), scvIdx_(scvIdx) + { + boundaryFace_ = &fvElemGeom_.boundaryFace[boundaryFaceIdx]; + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + densityAtIP_[phaseIdx] = Scalar(0); + molarDensityAtIP_[phaseIdx] = Scalar(0); + potentialGrad_[phaseIdx] = Scalar(0); + concentrationGrad_[phaseIdx] = Scalar(0); + molarConcGrad_[phaseIdx] = Scalar(0); + } + + calculateBoundaryValues_(problem, element, elemDat); + }; + + Scalar KmvpNormal(int phaseIdx) const + { return KmvpNormal_[phaseIdx]; } + + /*! + * \brief The binary diffusion coefficient for each fluid phase. + */ + Scalar porousDiffCoeff(int phaseIdx) const + { return porousDiffCoeff_[phaseIdx]; }; + + /*! + * \brief Return density \f$\mathrm{[kg/m^3]}\f$ of a phase at the integration + * point. + */ + Scalar densityAtIP(int phaseIdx) const + { return densityAtIP_[phaseIdx]; } + + /*! + * \brief Return molar density \f$\mathrm{[mol/m^3]}\f$ of a phase at the integration + * point. + */ + Scalar molarDensityAtIP(int phaseIdx) const + { return molarDensityAtIP_[phaseIdx]; } + + /*! + * \brief The concentration gradient of a component in a phase. + */ + const ScalarGradient &concentrationGrad(int phaseIdx) const + { return concentrationGrad_[phaseIdx]; }; + + /*! + * \brief The molar concentration gradient of a component in a phase. + */ + const ScalarGradient &molarConcGrad(int phaseIdx) const + { return molarConcGrad_[phaseIdx]; }; + + /*! + * \brief The total heat flux \f$\mathrm{[J/s]}\f$ due to heat conduction + * of the rock matrix over the sub-control volume's face in + * direction of the face normal. + */ + Scalar normalMatrixHeatFlux() const + { return normalMatrixHeatFlux_; } + + const BoundaryFace& boundaryFace() const + { return *boundaryFace_; } + +protected: + void calculateBoundaryValues_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemDat) + { + ScalarGradient tmp(0.0); + + // calculate gradients and secondary variables at IPs of the boundary + for (int idx = 0; + idx < fvElemGeom_.numVertices; + idx++) // loop over adjacent vertices + { + // FE gradient at vertex idx + const ScalarGradient& feGrad = boundaryFace_->grad[idx]; + + // compute sum of pressure gradients for each phase + for (int phaseIdx = 0; phaseIdx < numPhases; phaseIdx++) + { + // the pressure gradient + tmp = feGrad; + tmp *= elemDat[idx].pressure(phaseIdx); + potentialGrad_[phaseIdx] += tmp; + } + + // the concentration gradient of the non-wetting + // component in the wetting phase + tmp = feGrad; + tmp *= elemDat[idx].fluidState().massFrac(lPhaseIdx, gCompIdx); + concentrationGrad_[lPhaseIdx] += tmp; + + tmp = feGrad; + tmp *= elemDat[idx].fluidState().moleFrac(lPhaseIdx, gCompIdx); + molarConcGrad_[lPhaseIdx] += tmp; + + // the concentration gradient of the wetting component + // in the non-wetting phase + tmp = feGrad; + tmp *= elemDat[idx].fluidState().massFrac(gPhaseIdx, lCompIdx); + concentrationGrad_[gPhaseIdx] += tmp; + + tmp = feGrad; + tmp *= elemDat[idx].fluidState().moleFrac(gPhaseIdx, lCompIdx); + molarConcGrad_[gPhaseIdx] += tmp; + + tmp = feGrad; + tmp *= elemDat[idx].temperature(); + temperatureGrad_ += tmp; + + for (int phaseIdx=0; phaseIdx < numPhases; phaseIdx++) + { + densityAtIP_[phaseIdx] += elemDat[idx].density(phaseIdx)*boundaryFace_->shapeValue[idx]; + molarDensityAtIP_[phaseIdx] += elemDat[idx].molarDensity(phaseIdx)*boundaryFace_->shapeValue[idx]; + } + } + + for (int phaseIdx=0; phaseIdx < numPhases; phaseIdx++) + { + tmp = problem.gravity(); + tmp *= densityAtIP_[phaseIdx]; + + potentialGrad_[phaseIdx] -= tmp; + + Scalar k = problem.spatialParameters().intrinsicPermeability(element, fvElemGeom_, scvIdx_); + VectorGradient K(0); + K[0][0] = K[1][1] = k; + ScalarGradient Kmvp; + K.mv(potentialGrad_[phaseIdx], Kmvp); + KmvpNormal_[phaseIdx] = - (Kmvp*boundaryFace_->normal); + + const VolumeVariables &vertDat = elemDat[scvIdx_]; + + if (vertDat.saturation(phaseIdx) <= 0) + porousDiffCoeff_[phaseIdx] = 0.0; + else + { + Scalar tau = 1.0/(vertDat.porosity()*vertDat.porosity())* + pow(vertDat.porosity()*vertDat.saturation(phaseIdx), 7.0/3); + + porousDiffCoeff_[phaseIdx] = vertDat.porosity()*vertDat.saturation(phaseIdx)*tau*vertDat.diffCoeff(phaseIdx); + } + } + + // The spatial parameters calculates the actual heat flux vector + problem.spatialParameters().boundaryMatrixHeatFlux(tmp, + elemDat, + temperatureGrad_, + element, + fvElemGeom_, + scvIdx_); + // project the heat flux vector on the face's normal vector + normalMatrixHeatFlux_ = tmp*boundaryFace_->normal; + + } + + const FVElementGeometry &fvElemGeom_; + const BoundaryFace *boundaryFace_; + + // gradients + ScalarGradient potentialGrad_[numPhases]; + ScalarGradient concentrationGrad_[numPhases]; + ScalarGradient molarConcGrad_[numPhases]; + + // density of each face at the integration point + Scalar densityAtIP_[numPhases]; + Scalar molarDensityAtIP_[numPhases]; + + // intrinsic permeability times pressure potential gradient + // projected on the face normal + Scalar KmvpNormal_[numPhases]; + + // the diffusion coefficient for the porous medium + Scalar porousDiffCoeff_[numPhases]; + + ScalarGradient temperatureGrad_; + Scalar normalMatrixHeatFlux_; + + int scvIdx_; +}; + +} // end namespace + +#endif diff --git a/dumux/boxmodels/new_2p2cni/2p2cnifluxvariables.hh b/dumux/boxmodels/new_2p2cni/2p2cnifluxvariables.hh new file mode 100644 index 0000000000..df52333574 --- /dev/null +++ b/dumux/boxmodels/new_2p2cni/2p2cnifluxvariables.hh @@ -0,0 +1,131 @@ +// $Id$ +/***************************************************************************** + * Copyright (C) 2008-2009 by Andreas Lauser * + * Copyright (C) 2008-2009 by Melanie Darcis * + * Copyright (C) 2008-2009 by Klaus Mosthaf * + * Copyright (C) 2008-2009 by Bernd Flemisch * + * Institute of Hydraulic Engineering * + * 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 This file contains the data which is required to calculate + * all fluxes (mass of components and energy) over a face of a finite volume. + * + * This means pressure, concentration and temperature gradients, phase + * densities at the integration point, etc. + */ +#ifndef DUMUX_2P2CNI_FLUX_VARIABLES_HH +#define DUMUX_2P2CNI_FLUX_VARIABLES_HH + +#include <dumux/common/math.hh> + +namespace Dumux +{ + +/*! + * \ingroup TwoPTwoCNIModel + * \brief This template class contains the data which is required to + * calculate all fluxes (mass of components and energy) over a face of a finite + * volume for the non-isothermal two-phase, two-component model. + * + * This means pressure and concentration gradients, phase densities at + * the integration point, etc. + */ +template <class TypeTag> +class TwoPTwoCNIFluxVariables : public TwoPTwoCFluxVariables<TypeTag> +{ + typedef TwoPTwoCFluxVariables<TypeTag> ParentType; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Problem)) Problem; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(VolumeVariables)) VolumeVariables; + + typedef typename GridView::ctype CoordScalar; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(ElementVolumeVariables)) ElementVolumeVariables; + + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld, + + numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)), + }; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + typedef typename FVElementGeometry::SubControlVolume SCV; + typedef typename FVElementGeometry::SubControlVolumeFace SCVFace; + + typedef Dune::FieldVector<CoordScalar, dimWorld> Vector; + +public: + /* + * \brief The constructor + * + * \param problem The problem + * \param element The finite element + * \param elemGeom The finite-volume geometry in the box scheme + * \param faceIdx The local index of the SCV (sub-control-volume) face + * \param elemDat The volume variables of the current element + */ + TwoPTwoCNIFluxVariables(const Problem &problem, + const Element &element, + const FVElementGeometry &elemGeom, + int scvfIdx, + const ElementVolumeVariables &elemDat) + : ParentType(problem, element, elemGeom, scvfIdx, elemDat) + { + // calculate temperature gradient using finite element + // gradients + Vector temperatureGrad(0); + Vector tmp(0.0); + for (int vertIdx = 0; vertIdx < elemGeom.numVertices; vertIdx++) + { + tmp = elemGeom.subContVolFace[scvfIdx].grad[vertIdx]; + tmp *= elemDat[vertIdx].temperature(); + temperatureGrad += tmp; + } + + // The spatial parameters calculates the actual heat flux vector + problem.spatialParameters().matrixHeatFlux(tmp, + *this, + elemDat, + temperatureGrad, + element, + elemGeom, + scvfIdx); + // project the heat flux vector on the face's normal vector + normalMatrixHeatFlux_ = tmp*elemGeom.subContVolFace[scvfIdx].normal; + } + + /*! + * \brief The total heat flux \f$\mathrm{[J/s]}\f$ due to heat conduction + * of the rock matrix over the sub-control volume's face in + * direction of the face normal. + */ + Scalar normalMatrixHeatFlux() const + { return normalMatrixHeatFlux_; } + +private: + Scalar normalMatrixHeatFlux_; +}; + +} // end namepace + +#endif diff --git a/dumux/boxmodels/new_2p2cni/2p2cniindices.hh b/dumux/boxmodels/new_2p2cni/2p2cniindices.hh new file mode 100644 index 0000000000..e912f6503d --- /dev/null +++ b/dumux/boxmodels/new_2p2cni/2p2cniindices.hh @@ -0,0 +1,57 @@ +// $Id: 2p2cniproperties.hh 3784 2010-06-24 13:43:57Z bernd $ +/***************************************************************************** + * Copyright (C) 2008-2010 by Andreas Lauser * + * Copyright (C) 2008-2009 by Melanie Darcis * + * Copyright (C) 2008-2009 by Klaus Mosthaf * + * Copyright (C) 2008-2009 by Bernd Flemisch * + * Institute of Hydraulic Engineering * + * 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 Defines the indices used by the 2p2cni box model + */ +#ifndef DUMUX_2P2CNI_INDICES_HH +#define DUMUX_2P2CNI_INDICES_HH + +#include <dumux/boxmodels/new_2p2c/2p2cindices.hh> + +namespace Dumux +{ +/*! + * \ingroup TwoPTwoCNIModel + */ +// \{ + +/*! + * \brief Enumerations for the non-isothermal 2-phase 2-component model + * + * \tparam formulation The formulation, either pwSn or pnSw. + * \tparam PVOffset The first index in a primary variable vector. + */ +template <class TypeTag, int formulation, int PVOffset> +class TwoPTwoCNIIndices : public TwoPTwoCIndices<TypeTag, formulation, PVOffset> +{ +public: + static const int temperatureIdx = PVOffset + 2; //! The index for temperature in primary variable vectors. + static const int energyEqIdx = PVOffset + 2; //! The index for energy in equation vectors. +}; + +} +#endif diff --git a/dumux/boxmodels/new_2p2cni/2p2cnilocalresidual.hh b/dumux/boxmodels/new_2p2cni/2p2cnilocalresidual.hh new file mode 100644 index 0000000000..7bed80375e --- /dev/null +++ b/dumux/boxmodels/new_2p2cni/2p2cnilocalresidual.hh @@ -0,0 +1,183 @@ +// $Id: 2p2cnilocalresidual.hh 3784 2010-06-24 13:43:57Z bernd $ +/***************************************************************************** + * Copyright (C) 2008-2009 by Andreas Lauser * + * Copyright (C) 2008-2009 by Melanie Darcis * + * Copyright (C) 2008-2009 by Klaus Mosthaf * + * Copyright (C) 2008 by Bernd Flemisch * + * Institute of Hydraulic Engineering * + * 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 non-isothermal two-phase two-component box model. + * + */ +#ifndef DUMUX_NEW_2P2CNI_LOCAL_RESIDUAL_HH +#define DUMUX_NEW_2P2CNI_LOCAL_RESIDUAL_HH + +#include <dumux/boxmodels/new_2p2c/2p2clocalresidual.hh> + +#include "2p2cnivolumevariables.hh" +#include "2p2cnifluxvariables.hh" +#include "2p2cniproperties.hh" + +namespace Dumux +{ +/*! + * \ingroup TwoPTwoCNIModel + * \brief Element-wise calculation of the Jacobian matrix for problems + * using the two-phase two-component box model. + */ +template<class TypeTag> +class TwoPTwoCNILocalResidual : public TwoPTwoCLocalResidual<TypeTag> +{ + typedef TwoPTwoCNILocalResidual<TypeTag> ThisType; + typedef TwoPTwoCLocalResidual<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Problem)) Problem; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(PrimaryVariables)) PrimaryVariables; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(TwoPTwoCIndices)) Indices; + + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld, + + numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)), + + energyEqIdx = Indices::energyEqIdx, + temperatureIdx = Indices::temperatureIdx, + + lPhaseIdx = Indices::lPhaseIdx, + gPhaseIdx = Indices::gPhaseIdx + }; + + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(VolumeVariables)) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluxVariables)) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(ElementVolumeVariables)) ElementVolumeVariables; + + typedef Dune::FieldVector<Scalar, dim> LocalPosition; + typedef Dune::FieldVector<Scalar, dimWorld> GlobalPosition; + + static const Scalar mobilityUpwindAlpha = GET_PROP_VALUE(TypeTag, PTAG(MobilityUpwindAlpha)); + +public: + /*! + * \brief Evaluate the amount all conservation quantities + * (e.g. phase mass) within a sub-control volume. + * + * The result should be averaged over the volume (e.g. phase mass + * inside a sub control volume divided by the volume) + * + * \param result The storage of the conservation quantitiy (mass or energy) 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 + */ + void computeStorage(PrimaryVariables &result, int scvIdx, bool usePrevSol) const + { + // compute the storage term for phase mass + ParentType::computeStorage(result, scvIdx, usePrevSol); + + // if flag usePrevSol is set, the solution from the previous + // time step is used, otherwise the current solution is + // used. The secondary variables are used accordingly. This + // is required to compute the derivative of the storage term + // using the implicit euler method. + const ElementVolumeVariables &elemDat = usePrevSol ? this->prevVolVars_() : this->curVolVars_(); + const VolumeVariables &vertDat = elemDat[scvIdx]; + + // compute the energy storage + result[energyEqIdx] = + vertDat.porosity()*(vertDat.density(lPhaseIdx) * + vertDat.fluidState().internalEnergy(lPhaseIdx) * + vertDat.saturation(lPhaseIdx) + + + vertDat.density(gPhaseIdx) * + vertDat.fluidState().internalEnergy(gPhaseIdx) * + vertDat.saturation(gPhaseIdx)) + + + vertDat.temperature()*vertDat.heatCapacity(); + } + + /*! + * \brief Evaluates the advective mass flux and the heat flux + * over a face of a subcontrol volume and writes the result in + * the flux vector. + * + * \param flux The advective flux over the SCV (sub-control-volume) face for each component + * \param fluxData The flux variables at the current SCV face + * + * This method is called by compute flux (base class) + */ + void computeAdvectiveFlux(PrimaryVariables &flux, + const FluxVariables &fluxData) const + { + // advective mass flux + ParentType::computeAdvectiveFlux(flux, fluxData); + + // advective heat flux in all phases + flux[energyEqIdx] = 0; + for (int phase = 0; phase < numPhases; ++phase) { + // vertex data of the upstream and the downstream vertices + const VolumeVariables &up = this->curVolVars_(fluxData.upstreamIdx(phase)); + const VolumeVariables &dn = this->curVolVars_(fluxData.downstreamIdx(phase)); + + flux[energyEqIdx] += + fluxData.KmvpNormal(phase) * ( + mobilityUpwindAlpha * // upstream vertex + ( up.density(phase) * + up.mobility(phase) * + up.fluidState().enthalpy(phase)) + + + (1-mobilityUpwindAlpha) * // downstream vertex + ( dn.density(phase) * + dn.mobility(phase) * + dn.fluidState().enthalpy(phase)) ); + } + } + + /*! + * \brief Adds the diffusive heat flux to the flux vector over + * the face of a sub-control volume. + * + * \param flux The diffusive flux over the SCV (sub-control-volume) face for each conservation quantity (mass, energy) + * \param fluxData The flux variables at the current SCV face + * + * This method is called by compute flux (base class) + */ + void computeDiffusiveFlux(PrimaryVariables &flux, + const FluxVariables &fluxData) const + { + // diffusive mass flux + ParentType::computeDiffusiveFlux(flux, fluxData); + + // diffusive heat flux + flux[temperatureIdx] += + fluxData.normalMatrixHeatFlux(); + } +}; + +} + +#endif diff --git a/dumux/boxmodels/new_2p2cni/2p2cnimodel.hh b/dumux/boxmodels/new_2p2cni/2p2cnimodel.hh new file mode 100644 index 0000000000..73defa1c98 --- /dev/null +++ b/dumux/boxmodels/new_2p2cni/2p2cnimodel.hh @@ -0,0 +1,108 @@ +// $Id$ +/***************************************************************************** + * Copyright (C) 2008-2009 by Andreas Lauser * + * Copyright (C) 2008-2009 by Melanie Darcis * + * Copyright (C) 2008-2009 by Klaus Mosthaf * + * Copyright (C) 2008 by Bernd Flemisch * + * Institute of Hydraulic Engineering * + * 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 Adaption of the BOX scheme to the non-isothermal two-phase two-component flow model. + */ +#ifndef DUMUX_NEW_2P2CNI_MODEL_HH +#define DUMUX_NEW_2P2CNI_MODEL_HH + +#include <dumux/boxmodels/new_2p2c/2p2cmodel.hh> + +namespace Dumux { +/*! + * \ingroup BoxModels + * \defgroup TwoPTwoCNIModel Non-isothermal two-phase two-component box model + */ + +/*! + * \ingroup TwoPTwoCNIModel + * \brief Adaption of the BOX scheme to the non-isothermal two-phase two-component flow model. + * + * This model implements a non-isothermal two-phase flow of two compressible and partly miscible fluids + * \f$\alpha \in \{ w, n \}\f$. Thus each component \f$\kappa \{ w, a \}\f$ can be present in + * each phase. + * Using the standard multiphase Darcy approach a mass balance equation is + * solved: + * \f{eqnarray*} + && \phi \frac{\partial (\sum_\alpha \varrho_\alpha X_\alpha^\kappa S_\alpha )}{\partial t} + - \sum_\alpha \text{div} \left\{ \varrho_\alpha X_\alpha^\kappa + \frac{k_{r\alpha}}{\mu_\alpha} \mbox{\bf K} + (\text{grad}\, p_\alpha - \varrho_{\alpha} \mbox{\bf g}) \right\}\\ + &-& \sum_\alpha \text{div} \left\{{\bf D_{\alpha, pm}^\kappa} \varrho_{\alpha} \text{grad}\, X^\kappa_{\alpha} \right\} + - \sum_\alpha q_\alpha^\kappa = 0 \qquad \kappa \in \{w, a\} \, , + \alpha \in \{w, n\} + * \f} + * For the energy balance, local thermal equilibrium is assumed which results in one + * energy conservation equation for the porous solid matrix and the fluids: + * \f{eqnarray*} + && \phi \frac{\partial \left( \sum_\alpha \varrho_\alpha u_\alpha S_\alpha \right)}{\partial t} + + \left( 1 - \phi \right) \frac{\partial (\varrho_s c_s T)}{\partial t} + - \sum_\alpha \text{div} \left\{ \varrho_\alpha h_\alpha + \frac{k_{r\alpha}}{\mu_\alpha} \mathbf{K} \left( \text{grad}\, + p_\alpha + - \varrho_\alpha \mathbf{g} \right) \right\} \\ + &-& \text{div} \left( \lambda_{pm} \text{grad} \, T \right) + - q^h = 0 \qquad \alpha \in \{w, n\} +\f} + * + * This is discretized using a fully-coupled vertex + * centered finite volume (box) scheme as spatial and + * the implicit Euler method as temporal discretization. + * + * By using constitutive relations for the capillary pressure \f$p_c = + * p_n - p_w\f$ and relative permeability \f$k_{r\alpha}\f$ and taking + * advantage of the fact that \f$S_w + S_n = 1\f$ and \f$X^\kappa_w + X^\kappa_n = 1\f$, the number of + * unknowns can be reduced to two. + * If both phases are present the primary variables are, like in the nonisothermal two-phase model, either \f$p_w\f$, \f$S_n\f$ and + * temperature or \f$p_n\f$, \f$S_w\f$ and temperature. The formulation which ought to be used can be + * specified by setting the <tt>Formulation</tt> property to either + * <tt>TwoPTwoIndices::pWsN</tt> or <tt>TwoPTwoCIndices::pNsW</tt>. By + * default, the model uses \f$p_w\f$ and \f$S_n\f$. + * In case that only one phase (nonwetting or wetting phase) is present the second primary + * variable represents a mass fraction. The correct assignment of the second + * primary variable is performed by a phase state dependent primary variable switch. The phase state is stored for all nodes of the system. The following cases can be distinguished: + * <ul> + * <li> + * Both phases are present: The saturation is used (either\f$S_n\f$ or \f$S_w\f$, dependent on the chosen formulation). + * </li> + * <li> + * Only wetting phase is present: The mass fraction of air in the wetting phase \f$X^a_w\f$ is used. + * </li> + * <li> + * Only non-wetting phase is present: The mass fraction of water in the non-wetting phase, \f$X^w_n\f$, is used. + * </li> + * </ul> + */ +template<class TypeTag> +class TwoPTwoCNIModel : public TwoPTwoCModel<TypeTag> +{ +}; + +} + +#include "2p2cnipropertydefaults.hh" + +#endif diff --git a/dumux/boxmodels/new_2p2cni/2p2cniproblem.hh b/dumux/boxmodels/new_2p2cni/2p2cniproblem.hh new file mode 100644 index 0000000000..703f3b6184 --- /dev/null +++ b/dumux/boxmodels/new_2p2cni/2p2cniproblem.hh @@ -0,0 +1,80 @@ +// $Id$ +/***************************************************************************** + * Copyright (C) 2009 by Andreas Lauser * + * Institute of Hydraulic Engineering * + * 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 Base class for all problems which use the non-isothermal two-phase, + * two-component box model + */ +#ifndef DUMUX_2P2CNI_PROBLEM_HH +#define DUMUX_2P2CNI_PROBLEM_HH + +#include <dumux/boxmodels/new_2p2c/2p2cproblem.hh> + +namespace Dumux +{ +/*! + * \ingroup TwoPTwoCNIModel + * \brief Base class for all problems which use the non-isothermal + * two-phase, two-component box model. + */ +template<class TypeTag> +class TwoPTwoCNIProblem : public TwoPTwoCProblem<TypeTag> +{ + typedef TwoPTwoCProblem<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(TimeManager)) TimeManager; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + +public: + /*! + * \brief The constructor + * + * \param timeManager The time manager + * \param gridView The grid view + */ + TwoPTwoCNIProblem(TimeManager &timeManager, const GridView &gridView) + : ParentType(timeManager, gridView) + { + } + + /*! + * \name Problem parameters + */ + // \{ + + /*! + * \brief Returns the temperature within the domain. + * + * This is a non-isothermal model, so this method just throws an + * exception. This method MUST NOT be overwritten by the actual + * problem. + */ + Scalar temperature() const + { DUNE_THROW(Dune::Exception, "temperature() method called for a 2p2cni problem"); }; + + // \} +}; + +} + +#endif diff --git a/dumux/boxmodels/new_2p2cni/2p2cniproperties.hh b/dumux/boxmodels/new_2p2cni/2p2cniproperties.hh new file mode 100644 index 0000000000..3bff17423b --- /dev/null +++ b/dumux/boxmodels/new_2p2cni/2p2cniproperties.hh @@ -0,0 +1,54 @@ +// $Id$ +/***************************************************************************** + * Copyright (C) 2008 by Klaus Mosthaf, Andreas Lauser, Bernd Flemisch * + * Institute of Hydraulic Engineering * + * 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/>. * + *****************************************************************************/ +/*! + * \ingroup TwoPTwoCNIModel + */ +/*! + * \file + * + * \brief Defines the properties required for the non-isothermal two-phase, + * two-component BOX model. + */ +#ifndef DUMUX_2P2CNI_PROPERTIES_HH +#define DUMUX_2P2CNI_PROPERTIES_HH + +#include <dumux/boxmodels/new_2p2c/2p2cproperties.hh> + +namespace Dumux +{ + +namespace Properties +{ +////////////////////////////////////////////////////////////////// +// Type tags +////////////////////////////////////////////////////////////////// + +//! The type tag for the non-isothermal two-phase, two-component problems +NEW_TYPE_TAG(BoxTwoPTwoCNI, INHERITS_FROM(BoxTwoPTwoC)); + +////////////////////////////////////////////////////////////////// +// Property tags +////////////////////////////////////////////////////////////////// +NEW_PROP_TAG(TwoPTwoCNIIndices); //!< Enumerations for the 2p2cni models +} +} + +#endif diff --git a/dumux/boxmodels/new_2p2cni/2p2cnipropertydefaults.hh b/dumux/boxmodels/new_2p2cni/2p2cnipropertydefaults.hh new file mode 100644 index 0000000000..9b02d09a3d --- /dev/null +++ b/dumux/boxmodels/new_2p2cni/2p2cnipropertydefaults.hh @@ -0,0 +1,85 @@ +// $Id$ +/***************************************************************************** + * Copyright (C) 2008-2010 by Andreas Lauser * + * Copyright (C) 2008-2009 by Melanie Darcis * + * Copyright (C) 2008-2009 by Klaus Mosthaf * + * Copyright (C) 2008-2009 by Bernd Flemisch * + * Institute of Hydraulic Engineering * + * 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/>. * + *****************************************************************************/ +/*! + * \ingroup TwoPTwoCNIModel + */ +/*! + * \file + * + * \brief Defines default values for most properties required by the 2p2cni + * box model. + */ +#ifndef DUMUX_2P2CNI_PROPERTY_DEFAULTS_HH +#define DUMUX_2P2CNI_PROPERTY_DEFAULTS_HH + +#include <dumux/boxmodels/new_2p2c/2p2cpropertydefaults.hh> + +#include "2p2cnimodel.hh" +#include "2p2cniproblem.hh" +#include "2p2cniindices.hh" +#include "2p2cnilocalresidual.hh" +#include "2p2cnivolumevariables.hh" +#include "2p2cnifluxvariables.hh" + +namespace Dumux +{ + +namespace Properties +{ +////////////////////////////////////////////////////////////////// +// Property values +////////////////////////////////////////////////////////////////// + +SET_INT_PROP(BoxTwoPTwoCNI, NumEq, 3); //!< set the number of equations to 3 + +//! Use the 2p2cni local jacobian operator for the 2p2cni model +SET_TYPE_PROP(BoxTwoPTwoCNI, + LocalResidual, + TwoPTwoCNILocalResidual<TypeTag>); + +//! the Model property +SET_TYPE_PROP(BoxTwoPTwoCNI, Model, TwoPTwoCNIModel<TypeTag>); + +//! the VolumeVariables property +SET_TYPE_PROP(BoxTwoPTwoCNI, VolumeVariables, TwoPTwoCNIVolumeVariables<TypeTag>); + + +//! the FluxVariables property +SET_TYPE_PROP(BoxTwoPTwoCNI, FluxVariables, TwoPTwoCNIFluxVariables<TypeTag>); + +//! The indices required by the non-isothermal 2p2c model +SET_PROP(BoxTwoPTwoCNI, TwoPTwoCIndices) +{ typedef typename GET_PROP_TYPE(TypeTag, PTAG(TwoPTwoCNIIndices)) type; }; + +SET_PROP(BoxTwoPTwoCNI, TwoPTwoCNIIndices) +{ private: + enum { formulation = GET_PROP_VALUE(TypeTag, PTAG(Formulation)) }; +public: + typedef TwoPTwoCNIIndices<TypeTag, formulation, 0> type; +}; + +} + +} +#endif diff --git a/dumux/boxmodels/new_2p2cni/2p2cnivolumevariables.hh b/dumux/boxmodels/new_2p2cni/2p2cnivolumevariables.hh new file mode 100644 index 0000000000..b5ff5240ec --- /dev/null +++ b/dumux/boxmodels/new_2p2cni/2p2cnivolumevariables.hh @@ -0,0 +1,129 @@ +// $Id$ +/***************************************************************************** + * Copyright (C) 2008-2009 by Melanie Darcis * + * Copyright (C) 2008-2010 by Andreas Lauser * + * Copyright (C) 2008-2009 by Klaus Mosthaf * + * Copyright (C) 2008-2009 by Bernd Flemisch * + * Institute of Hydraulic Engineering * + * University of Stuttgart, Germany * + * * + * 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 quantities which are constant within a + * finite volume in the non-isothermal two-phase, two-component + * model. + */ +#ifndef DUMUX_2P2CNI_VOLUME_VARIABLES_HH +#define DUMUX_2P2CNI_VOLUME_VARIABLES_HH + +#include <dumux/boxmodels/new_2p2c/2p2cvolumevariables.hh> + +namespace Dumux +{ + +/*! + * \ingroup TwoPTwoCNIModel + * \brief Contains the quantities which are are constant within a + * finite volume in the non-isothermal two-phase, two-component + * model. + */ +template <class TypeTag> +class TwoPTwoCNIVolumeVariables : public TwoPTwoCVolumeVariables<TypeTag> +{ + //! \cond 0 + typedef TwoPTwoCVolumeVariables<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Problem)) Problem; + + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld, + }; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(TwoPTwoCIndices)) Indices; + enum { numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)) }; + enum { temperatureIdx = Indices::temperatureIdx }; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(PrimaryVariables)) PrimaryVariables; + typedef Dune::FieldVector<Scalar, numPhases> PhasesVector; + //! \endcond + +public: + /*! + * \brief Update the temperature of the sub-control volume. + */ + Scalar getTemperature(const PrimaryVariables &sol, + const Element &element, + const FVElementGeometry &elemGeom, + int scvIdx, + const Problem &problem) const + { + // retrieve temperature from solution vector + return sol[temperatureIdx]; + } + + /*! + * \brief Update all quantities for a given control volume. + * + * \param sol The solution primary variables + * \param problem The problem + * \param element The element + * \param elemGeom Evaluate function with solution of current or previous time step + * \param vertIdx The local index of the SCV (sub-control volume) + * \param isOldSol Evaluate function with solution of current or previous time step + */ + template <class MutableParams> + void updateEnergy(MutableParams &mutParams, + const PrimaryVariables &sol, + const Element &element, + const FVElementGeometry &elemGeom, + int scvIdx, + const Problem &problem) + { + heatCapacity_ = + problem.spatialParameters().heatCapacity(element, elemGeom, scvIdx); + Valgrind::CheckDefined(heatCapacity_); + + // the internal energies and the enthalpies + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + Scalar h = + FluidSystem::computeEnthalpy(mutParams, phaseIdx); + mutParams.setEnthalpy(phaseIdx, h); + } + } + + /*! + * \brief Returns the total heat capacity \f$\mathrm{[J/(K*m^3]}\f$ of the rock matrix in + * the sub-control volume. + */ + Scalar heatCapacity() const + { return heatCapacity_; }; + +protected: + Scalar heatCapacity_; +}; + +} // end namepace + +#endif diff --git a/dumux/boxmodels/new_2p2cni/Makefile.am b/dumux/boxmodels/new_2p2cni/Makefile.am new file mode 100644 index 0000000000..79135402f3 --- /dev/null +++ b/dumux/boxmodels/new_2p2cni/Makefile.am @@ -0,0 +1,4 @@ +2p2cnidir = $(includedir)/dumux/boxmodels/2p2cni +2p2cni_HEADERS = *.hh + +include $(top_srcdir)/am/global-rules diff --git a/test/boxmodels/2p2cni/waterairproblem.hh b/test/boxmodels/2p2cni/waterairproblem.hh index f329e1888e..8ccc478661 100644 --- a/test/boxmodels/2p2cni/waterairproblem.hh +++ b/test/boxmodels/2p2cni/waterairproblem.hh @@ -33,7 +33,6 @@ #include <dune/grid/io/file/dgfparser/dgfyasp.hh> #include <dumux/material/fluidsystems/h2o_n2_system.hh> -#include <dumux/material/new_fluidsystems/h2on2fluidsystem.hh> #include <dumux/boxmodels/2p2cni/2p2cnimodel.hh> @@ -75,12 +74,8 @@ SET_PROP(WaterAirProblem, Problem) typedef Dumux::WaterAirProblem<TypeTag> type; }; -// Set fluid configuration -SET_PROP(WaterAirProblem, FluidSystem) -{ -private: typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; -public: typedef Dumux::H2ON2FluidSystem<Scalar> type; -}; +// Set the wetting phase +SET_TYPE_PROP(WaterAirProblem, FluidSystem, Dumux::H2O_N2_System<TypeTag>); // Set the spatial parameters SET_TYPE_PROP(WaterAirProblem, @@ -131,7 +126,7 @@ SET_BOOL_PROP(WaterAirProblem, NewtonWriteConvergence, false); * To run the simulation execute the following line in shell: * <tt>./test_2p2cni ./grids/test_2p2cni.dgf 300000 1000</tt> * */ -template <class TypeTag> +template <class TypeTag > class WaterAirProblem : public TwoPTwoCNIProblem<TypeTag> { typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; diff --git a/test/boxmodels/Makefile.am b/test/boxmodels/Makefile.am index d64fbf4578..29b296e1ec 100644 --- a/test/boxmodels/Makefile.am +++ b/test/boxmodels/Makefile.am @@ -1,5 +1,5 @@ -SUBDIRS = 1p 1p2c 2p 2p2c 2p2cni 2pni richards - +SUBDIRS = 1p 1p2c 2p 2p2c 2p2cni new_2p2c new_2p2cni 2pni richards + EXTRA_DIST = CMakeLists.txt include $(top_srcdir)/am/global-rules diff --git a/test/boxmodels/new_2p2c/CMakeLists.txt b/test/boxmodels/new_2p2c/CMakeLists.txt new file mode 100644 index 0000000000..9faa5f7802 --- /dev/null +++ b/test/boxmodels/new_2p2c/CMakeLists.txt @@ -0,0 +1,16 @@ +# build target for the twophase-twocomponent test problem +ADD_EXECUTABLE("test_2p2c" test_2p2c.cc) +TARGET_LINK_LIBRARIES("test_2p2c" ${DumuxLinkLibraries}) + +# add required libraries and includes to the build flags +LINK_DIRECTORIES(${DumuxLinkDirectories}) +INCLUDE_DIRECTORIES(${DumuxIncludeDirectories}) + +# make sure the grids are present in the build directory +add_custom_command(TARGET "test_2p2c" + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E + copy_directory + "${CMAKE_CURRENT_SOURCE_DIR}/grids" + "${CMAKE_CURRENT_BINARY_DIR}/grids") + diff --git a/test/boxmodels/new_2p2c/Makefile.am b/test/boxmodels/new_2p2c/Makefile.am new file mode 100644 index 0000000000..9d441002d4 --- /dev/null +++ b/test/boxmodels/new_2p2c/Makefile.am @@ -0,0 +1,8 @@ +check_PROGRAMS = test_2p2c + +noinst_HEADERS = *.hh +EXTRA_DIST = grids/*.dgf CMakeLists.txt + +test_2p2c_SOURCES = test_2p2c.cc + +include $(top_srcdir)/am/global-rules diff --git a/test/boxmodels/new_2p2c/grids/test_2p2c.dgf b/test/boxmodels/new_2p2c/grids/test_2p2c.dgf new file mode 100644 index 0000000000..765233c04a --- /dev/null +++ b/test/boxmodels/new_2p2c/grids/test_2p2c.dgf @@ -0,0 +1,10 @@ +DGF +Interval +0 0 % first corner +60 40 % second corner +24 16 % 24 cells in x and 16 in y direction +# +BOUNDARYDOMAIN +default 1 % all boundaries have id 1 +#BOUNDARYDOMAIN +# unitcube.dgf diff --git a/test/boxmodels/new_2p2c/injectionproblem.hh b/test/boxmodels/new_2p2c/injectionproblem.hh new file mode 100644 index 0000000000..b77da478e0 --- /dev/null +++ b/test/boxmodels/new_2p2c/injectionproblem.hh @@ -0,0 +1,379 @@ +// $Id$ +/***************************************************************************** + * Copyright (C) 2008-2009 by Klaus Mosthaf * + * Copyright (C) 2008-2009 by Andreas Lauser * + * Copyright (C) 2008-2009 by Bernd Flemisch * + * Institute of Hydraulic Engineering * + * 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 Definition of a problem, where air is injected under a low permeable layer. + */ + +#ifndef DUMUX_INJECTIONPROBLEM_HH +#define DUMUX_INJECTIONPROBLEM_HH + +#include <dune/grid/io/file/dgfparser/dgfug.hh> +#include <dune/grid/io/file/dgfparser/dgfs.hh> +#include <dune/grid/io/file/dgfparser/dgfyasp.hh> + +#include <dumux/boxmodels/new_2p2c/2p2cmodel.hh> +#include <dumux/material/new_fluidsystems/h2on2fluidsystem.hh> + +#include "injectionspatialparameters.hh" + + +namespace Dumux +{ + +template <class TypeTag> +class InjectionProblem; + +namespace Properties +{ +NEW_TYPE_TAG(InjectionProblem, INHERITS_FROM(BoxTwoPTwoC)); + +// Set the grid type +SET_PROP(InjectionProblem, Grid) +{ + typedef Dune::SGrid<2,2> type; +}; + + +#if HAVE_DUNE_PDELAB +SET_PROP(InjectionProblem, LocalFEMSpace) +{ + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + enum{dim = GridView::dimension}; + +public: + typedef Dune::PDELab::Q1LocalFiniteElementMap<Scalar,Scalar,dim> type; // for cubes +// typedef Dune::PDELab::P1LocalFiniteElementMap<Scalar,Scalar,dim> type; // for simplices +}; +#endif // HAVE_DUNE_PDELAB + +// Set the problem property +SET_PROP(InjectionProblem, Problem) +{ + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; +public: + typedef Dumux::InjectionProblem<TTAG(InjectionProblem)> type; +}; + +// Set fluid configuration +SET_PROP(InjectionProblem, FluidSystem) +{ +private: typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; +public: typedef Dumux::H2ON2FluidSystem<Scalar> type; +}; + +// Set the spatial parameters +SET_TYPE_PROP(InjectionProblem, + SpatialParameters, + Dumux::InjectionSpatialParameters<TypeTag>); + +// Enable gravity +SET_BOOL_PROP(InjectionProblem, EnableGravity, true); + +// Enable gravity +SET_INT_PROP(InjectionProblem, NewtonLinearSolverVerbosity, 0); + +SET_BOOL_PROP(InjectionProblem, EnableJacobianRecycling, false); +SET_BOOL_PROP(InjectionProblem, EnablePartialReassemble, false); +} + + +/*! + * \ingroup TwoPTwoCModel + * \brief Problem where air is injected under a low permeable layer in a depth of 800m. + * + * The domain is sized 60m times 40m and consists of two layers, a moderately + * permeable spatial parameters (\f$ K=10e-12\f$) for \f$ y>22m\f$ and one with a lower permeablility (\f$ K=10e-13\f$) + * in the rest of the domain. + * + * Air enters a water-filled aquifer, which is situated 800m below sea level, at the right boundary + * (\f$ 5m<y<15m\f$) and migrates upwards due to buoyancy. It accumulates and + * partially enters the lower permeable aquitard. + * This problem uses the \ref TwoPTwoCModel. + * + * To run the simulation execute the following line in shell: + * <tt>./test_2p2c grids/test_2p2c.dgf 1e6 1e4 </tt> + */ +template <class TypeTag = TTAG(InjectionProblem) > +class InjectionProblem : public TwoPTwoCProblem<TypeTag> +{ + typedef InjectionProblem<TypeTag> ThisType; + typedef TwoPTwoCProblem<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + + enum { + // Grid and world dimension + dim = GridView::dimension, + dimWorld = GridView::dimensionworld, + }; + + // copy some indices for convenience + typedef typename GET_PROP_TYPE(TypeTag, PTAG(TwoPTwoCIndices)) Indices; + enum { + lPhaseIdx = Indices::lPhaseIdx, + gPhaseIdx = Indices::gPhaseIdx, + + lCompIdx = Indices::lCompIdx, + gCompIdx = Indices::gCompIdx, + + contiLEqIdx = Indices::contiLEqIdx, + contiGEqIdx = Indices::contiGEqIdx, + }; + + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(PrimaryVariables)) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(BoundaryTypes)) BoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(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, PTAG(FVElementGeometry)) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + + typedef Dune::FieldVector<Scalar, dimWorld> GlobalPosition; + +public: + /*! + * \brief The constructor + * + * \param timeManager The time manager + * \param gridView The grid view + */ + InjectionProblem(TimeManager &timeManager, const GridView &gridView) + : ParentType(timeManager, gridView) + { + Scalar Tmin = 273.0 + 35; + Scalar Tmax = 273.0 + 45; + Scalar nT = 5; + + Scalar pmin = 25e6; + Scalar pmax = 30e6; + Scalar np = 250; + FluidSystem::init(Tmin, Tmax, nT, + pmin, pmax, np); + } + + /*! + * \brief Called directly after the time integration. + */ + void postTimeStep() + { + // Calculate storage terms + PrimaryVariables storageL, storageG; + this->model().globalPhaseStorage(storageL, lPhaseIdx); + this->model().globalPhaseStorage(storageG, gPhaseIdx); + + // Write mass balance information for rank 0 + if (this->gridView().comm().rank() == 0) { + std::cout<<"Storage: liquid=[" << storageL << "]" + << " gas=[" << storageG << "]\n"; + } + } + + + /*! + * \name Problem parameters + */ + // \{ + + /*! + * \brief The problem name. + * + * This is used as a prefix for files generated by the simulation. + */ + const char *name() const + { return "injection"; } + + /*! + * \brief Returns the temperature within the domain. + * + * \param element The element + * \param fvElemGeom The finite-volume geometry in the box scheme + * \param scvIdx The local vertex index (SCV index) + * + * This problem assumes a temperature of 10 degrees Celsius. + */ + Scalar temperature(const Element &element, + const FVElementGeometry &fvElemGeom, + int scvIdx) const + { + return temperature_; + }; + + // \} + + /*! + * \name Boundary conditions + */ + // \{ + + /*! + * \brief Specifies which kind of boundary condition should be + * used for which equation on a given boundary segment. + * + * \param values The boundary types for the conservation equations + * \param vertex The vertex for which the boundary type is set + */ + void boundaryTypes(BoundaryTypes &values, const Vertex &vertex) const + { + const GlobalPosition globalPos = vertex.geometry().center(); + + if (globalPos[0] < eps_) + values.setAllDirichlet(); + else + values.setAllNeumann(); + } + + /*! + * \brief Evaluate the boundary conditions for a dirichlet + * boundary segment. + * + * \param values The dirichlet values for the primary variables + * \param vertex The vertex for which the boundary type is set + * + * For this method, the \a values parameter stores primary variables. + */ + void dirichlet(PrimaryVariables &values, const Vertex &vertex) const + { + const GlobalPosition globalPos = vertex.geometry().center(); + + initial_(values, globalPos); + } + + /*! + * \brief Evaluate the boundary conditions for a neumann + * boundary segment. + * + * \param values The neumann values for the conservation equations + * \param element The finite element + * \param fvElemGeom The finite-volume geometry in the box scheme + * \param is The intersection between element and boundary + * \param scvIdx The local vertex index + * \param boundaryFaceIdx The index of the boundary face + * + * For this method, the \a values parameter stores the mass flux + * in normal direction of each phase. Negative values mean influx. + */ + void neumann(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvElemGeom, + const Intersection &is, + int scvIdx, + int boundaryFaceIdx) const + { + const GlobalPosition &globalPos = element.geometry().corner(scvIdx); + + values = 0; + if (globalPos[1] < 15 && globalPos[1] > 5) { + values[contiGEqIdx] = -1e-3; // kg/(s*m^2) + } + } + + // \} + + /*! + * \name Volume terms + */ + // \{ + + /*! + * \brief Evaluate the source term for all phases within a given + * sub-control-volume. + * + * \param values The source and sink values for the conservation equations + * \param element The finite element + * \param fvElemGeom The finite-volume geometry in the box scheme + * \param scvIdx The local vertex index + * + * For this method, the \a values parameter stores the rate mass + * generated or annihilate per volume unit. Positive values mean + * that mass is created, negative ones mean that it vanishes. + */ + void source(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvElemGeom, + int scvIdx) const + { + values = Scalar(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 fvElemGeom The finite-volume geometry in the box scheme + * \param scvIdx The local vertex index + * + * For this method, the \a values parameter stores primary + * variables. + */ + void initial(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvElemGeom, + int scvIdx) const + { + const GlobalPosition &globalPos = element.geometry().corner(scvIdx); + 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, + int &globalIdx, + const GlobalPosition &globalPos) const + { return Indices::lPhaseOnly; } + + // \} + +private: + // the internal method for the initial condition + void initial_(PrimaryVariables &values, + const GlobalPosition &globalPos) const + { + Scalar densityW = FluidSystem::H2O::liquidDensity(temperature_, 1e5); + + values[Indices::plIdx] = 1e5 - densityW*this->gravity()[1]*(depthBOR_ - globalPos[1]); + values[Indices::SgOrXIdx] = + values[Indices::plIdx]*0.95/ + BinaryCoeff::H2O_N2::henry(temperature_); + } + + static const Scalar temperature_ = 273.15 + 40; // [K] + static const Scalar depthBOR_ = 2700.0; // [m] + static const Scalar eps_ = 1e-6; +}; +} //end namespace + +#endif diff --git a/test/boxmodels/new_2p2c/injectionspatialparameters.hh b/test/boxmodels/new_2p2c/injectionspatialparameters.hh new file mode 100644 index 0000000000..54715db3d6 --- /dev/null +++ b/test/boxmodels/new_2p2c/injectionspatialparameters.hh @@ -0,0 +1,268 @@ +// $Id$ +/***************************************************************************** + * Copyright (C) 2008-2009 by Klaus Mosthaf * + * Copyright (C) 2008-2009 by Andreas Lauser * + * Institute of Hydraulic Engineering * + * 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 Definition of the spatial parameters for the injection + * problem which uses the isothermal 2p2c box model + */ + +#ifndef DUMUX_INJECTION_SPATIAL_PARAMETERS_HH +#define DUMUX_INJECTION_SPATIAL_PARAMETERS_HH + +#include <dumux/material/spatialparameters/boxspatialparameters.hh> +#include <dumux/material/fluidmatrixinteractions/2p/linearmaterial.hh> +#include <dumux/material/fluidmatrixinteractions/2p/regularizedbrookscorey.hh> +#include <dumux/material/fluidmatrixinteractions/2p/efftoabslaw.hh> + +namespace Dumux +{ + +/*! + * \ingroup TwoPTwoCModel + * + * \brief Definition of the spatial parameters for the injection + * problem which uses the isothermal 2p2c box model + */ +template<class TypeTag> +class InjectionSpatialParameters : public BoxSpatialParameters<TypeTag> +{ + typedef BoxSpatialParameters<TypeTag> ParentType; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Grid)) Grid; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename Grid::ctype CoordScalar; + enum { + dim=GridView::dimension, + dimWorld=GridView::dimensionworld, + }; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + enum { + lPhaseIdx = FluidSystem::lPhaseIdx, + gPhaseIdx = FluidSystem::gPhaseIdx, + }; + + typedef Dune::FieldVector<CoordScalar,dim> LocalPosition; + typedef Dune::FieldVector<CoordScalar,dimWorld> GlobalPosition; + typedef Dune::FieldVector<CoordScalar,dimWorld> Vector; + + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(SolutionVector)) SolutionVector; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(VolumeVariables)) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluxVariables)) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(ElementVolumeVariables)) ElementVolumeVariables; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + typedef typename GridView::template Codim<0>::Entity Element; + + typedef RegularizedBrooksCorey<Scalar> EffMaterialLaw; + //typedef LinearMaterial<Scalar> EffMaterialLaw; +public: + typedef EffToAbsLaw<EffMaterialLaw> MaterialLaw; + typedef typename MaterialLaw::Params MaterialLawParams; + + /*! + * \brief The constructor + * + * \param gv The grid view + */ + InjectionSpatialParameters(const GridView &gv) + : ParentType(gv) + { + layerBottom_ = 22.0; + + // intrinsic permeabilities + fineK_ = 1e-13; + coarseK_ = 1e-12; + + // porosities + finePorosity_ = 0.3; + coarsePorosity_ = 0.3; + + // residual saturations + fineMaterialParams_.setSwr(0.2); + fineMaterialParams_.setSnr(0.0); + coarseMaterialParams_.setSwr(0.2); + coarseMaterialParams_.setSnr(0.0); + + // parameters for the Brooks-Corey law + fineMaterialParams_.setPe(1e4); + coarseMaterialParams_.setPe(1e4); + fineMaterialParams_.setAlpha(2.0); + coarseMaterialParams_.setAlpha(2.0); + } + + ~InjectionSpatialParameters() + {} + + + /*! + * \brief Update the spatial parameters with the flow solution + * after a timestep. + * + * \param globalSolution The global solution vector + */ + void update(const SolutionVector &globalSolution) + { + }; + + /*! + * \brief Apply the intrinsic permeability tensor to a pressure + * potential gradient. + * + * \param element The current finite element + * \param fvElemGeom The current finite volume geometry of the element + * \param scvIdx The index of the sub-control volume + */ + const Scalar intrinsicPermeability(const Element &element, + const FVElementGeometry &fvElemGeom, + int scvIdx) const + { + const GlobalPosition &pos = fvElemGeom.subContVol[scvIdx].global; + if (isFineMaterial_(pos)) + return fineK_; + return coarseK_; + } + + /*! + * \brief Define the porosity \f$[-]\f$ of the spatial parameters + * + * \param element The finite element + * \param fvElemGeom The finite volume geometry + * \param scvIdx The local index of the sub-control volume where + * the porosity needs to be defined + */ + double porosity(const Element &element, + const FVElementGeometry &fvElemGeom, + int scvIdx) const + { + const GlobalPosition &pos = fvElemGeom.subContVol[scvIdx].global; + if (isFineMaterial_(pos)) + return finePorosity_; + return coarsePorosity_; + } + + + /*! + * \brief return the parameter object for the Brooks-Corey material law which depends on the position + * + * \param element The current finite element + * \param fvElemGeom The current finite volume geometry of the element + * \param scvIdx The index of the sub-control volume + */ + const MaterialLawParams& materialLawParams(const Element &element, + const FVElementGeometry &fvElemGeom, + int scvIdx) const + { + const GlobalPosition &pos = fvElemGeom.subContVol[scvIdx].global; + if (isFineMaterial_(pos)) + return fineMaterialParams_; + return coarseMaterialParams_; + } + + /*! + * \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 fvElemGeom The finite volume geometry + * \param scvIdx The local index of the sub-control volume where + * the heat capacity needs to be defined + */ + double heatCapacity(const Element &element, + const FVElementGeometry &fvElemGeom, + int scvIdx) const + { + return + 790 // specific heat capacity of granite [J / (kg K)] + * 2700 // density of granite [kg/m^3] + * (1 - porosity(element, fvElemGeom, scvIdx)); + } + + /*! + * \brief Calculate the heat flux \f$[W/m^2]\f$ through the + * rock matrix based on the temperature gradient \f$[K / m]\f$ + * + * This is only required for non-isothermal models. + * + * \param heatFlux The resulting heat flux vector + * \param fluxDat The flux variables + * \param vDat The volume variables + * \param tempGrad The temperature gradient + * \param element The current finite element + * \param fvElemGeom The finite volume geometry of the current element + * \param scvfIdx The local index of the sub-control volume face where + * the matrix heat flux should be calculated + */ + void matrixHeatFlux(Vector &heatFlux, + const FluxVariables &fluxDat, + const ElementVolumeVariables &vDat, + const Vector &tempGrad, + const Element &element, + const FVElementGeometry &fvElemGeom, + int scvfIdx) const + { + static const Scalar lWater = 0.6; + static const Scalar lGranite = 2.8; + + // arithmetic mean of the liquid saturation and the porosity + const int i = fvElemGeom.subContVolFace[scvfIdx].i; + const int j = fvElemGeom.subContVolFace[scvfIdx].j; + Scalar Sl = std::max(0.0, (vDat[i].saturation(lPhaseIdx) + + vDat[j].saturation(lPhaseIdx)) / 2); + Scalar poro = (porosity(element, fvElemGeom, i) + + porosity(element, fvElemGeom, j)) / 2; + + Scalar lsat = pow(lGranite, (1-poro)) * pow(lWater, poro); + Scalar ldry = pow(lGranite, (1-poro)); + + // the heat conductivity of the matrix. in general this is a + // tensorial value, but we assume isotropic heat conductivity. + Scalar heatCond = ldry + sqrt(Sl) * (ldry - lsat); + + // the matrix heat flux is the negative temperature gradient + // times the heat conductivity. + heatFlux = tempGrad; + heatFlux *= -heatCond; + } + +private: + bool isFineMaterial_(const GlobalPosition &pos) const + { return pos[dim-1] > layerBottom_; }; + + Scalar fineK_; + Scalar coarseK_; + Scalar layerBottom_; + + Scalar finePorosity_; + Scalar coarsePorosity_; + + MaterialLawParams fineMaterialParams_; + MaterialLawParams coarseMaterialParams_; +}; + +} + +#endif diff --git a/test/boxmodels/new_2p2c/test_2p2c.cc b/test/boxmodels/new_2p2c/test_2p2c.cc new file mode 100644 index 0000000000..05d1c1ccdd --- /dev/null +++ b/test/boxmodels/new_2p2c/test_2p2c.cc @@ -0,0 +1,34 @@ +// $Id$ +/***************************************************************************** + * Copyright (C) 2008 by Klaus Mosthaf * + * Institute of Hydraulic Engineering * + * 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 test for the 2p2c box model + */ +#include "config.h" +#include "injectionproblem.hh" +#include <dumux/common/start.hh> + +int main(int argc, char** argv) +{ + typedef TTAG(InjectionProblem) ProblemTypeTag; + return Dumux::startFromDGF<ProblemTypeTag>(argc, argv); +} diff --git a/test/boxmodels/new_2p2cni/CMakeLists.txt b/test/boxmodels/new_2p2cni/CMakeLists.txt new file mode 100644 index 0000000000..b88b7db7f3 --- /dev/null +++ b/test/boxmodels/new_2p2cni/CMakeLists.txt @@ -0,0 +1,16 @@ +# build target for the 2p2cni test problem +ADD_EXECUTABLE("test_2p2cni" test_2p2cni.cc) +TARGET_LINK_LIBRARIES("test_2p2cni" ${DumuxLinkLibraries}) + +# add required libraries and includes to the build flags +LINK_DIRECTORIES(${DumuxLinkDirectories}) +INCLUDE_DIRECTORIES(${DumuxIncludeDirectories}) + +# make sure the grids are present in the build directory +add_custom_command(TARGET "test_2p2cni" + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E + copy_directory + "${CMAKE_CURRENT_SOURCE_DIR}/grids" + "${CMAKE_CURRENT_BINARY_DIR}/grids") + diff --git a/test/boxmodels/new_2p2cni/Makefile.am b/test/boxmodels/new_2p2cni/Makefile.am new file mode 100644 index 0000000000..5b3d1ff7e0 --- /dev/null +++ b/test/boxmodels/new_2p2cni/Makefile.am @@ -0,0 +1,8 @@ +check_PROGRAMS = test_2p2cni + +noinst_HEADERS = *.hh +EXTRA_DIST = grids/*.dgf CMakeLists.txt + +test_2p2cni_SOURCES = test_2p2cni.cc + +include $(top_srcdir)/am/global-rules diff --git a/test/boxmodels/new_2p2cni/grids/test_2p2cni.dgf b/test/boxmodels/new_2p2cni/grids/test_2p2cni.dgf new file mode 100644 index 0000000000..a3f2629ae7 --- /dev/null +++ b/test/boxmodels/new_2p2cni/grids/test_2p2cni.dgf @@ -0,0 +1,15 @@ +DGF +Interval +0 0 % first corner +40 40 % second corner +64 64 % 24 cells in x and 16 in y direction +# +GridParameter +overlap 0 +periodic +closure green +#GridParameter +BOUNDARYDOMAIN +default 1 % all boundaries have id 1 +#BOUNDARYDOMAIN +# unitcube.dgf diff --git a/test/boxmodels/new_2p2cni/test_2p2cni.cc b/test/boxmodels/new_2p2cni/test_2p2cni.cc new file mode 100644 index 0000000000..76b5ffb3e8 --- /dev/null +++ b/test/boxmodels/new_2p2cni/test_2p2cni.cc @@ -0,0 +1,37 @@ +// $Id$ +/***************************************************************************** + * Copyright (C) 2007-2008 by Klaus Mosthaf * + * Copyright (C) 2007-2008 by Melanie Darcis * + * Copyright (C) 2007-2008 by Bernd Flemisch * + * Copyright (C) 2008-2009 by Andreas Lauser * + * Institute of Hydraulic Engineering * + * 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 test for the 2p2cni box model + */ +#include "config.h" +#include "waterairproblem.hh" +#include <dumux/common/start.hh> + +int main(int argc, char** argv) +{ + typedef TTAG(WaterAirProblem) ProblemTypeTag; + return Dumux::startFromDGF<ProblemTypeTag>(argc, argv); +} diff --git a/test/boxmodels/new_2p2cni/waterairproblem.hh b/test/boxmodels/new_2p2cni/waterairproblem.hh new file mode 100644 index 0000000000..3582d954d8 --- /dev/null +++ b/test/boxmodels/new_2p2cni/waterairproblem.hh @@ -0,0 +1,397 @@ +// $Id$ +/***************************************************************************** + * Copyright (C) 2009 by Klaus Mosthaf * + * Copyright (C) 2009 by Andreas Lauser * + * Institute of Hydraulic Engineering * + * 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 Non-isothermal gas injection problem where a gas (e.g. air) + * is injected into a fully water saturated medium. + */ +#ifndef DUMUX_WATERAIRPROBLEM_HH +#define DUMUX_WATERAIRPROBLEM_HH + +#include <dune/grid/io/file/dgfparser/dgfug.hh> +#include <dune/grid/io/file/dgfparser/dgfs.hh> +#include <dune/grid/io/file/dgfparser/dgfyasp.hh> + +//#include <dumux/material/fluidsystems/h2o_n2_system.hh> +#include <dumux/material/new_fluidsystems/h2on2fluidsystem.hh> + +#include <dumux/boxmodels/new_2p2cni/2p2cnimodel.hh> + +#include "waterairspatialparameters.hh" + +#define ISOTHERMAL 0 + +namespace Dumux +{ +template <class TypeTag> +class WaterAirProblem; + +namespace Properties +{ +NEW_TYPE_TAG(WaterAirProblem, INHERITS_FROM(BoxTwoPTwoCNI)); + +// Set the grid type +SET_PROP(WaterAirProblem, Grid) +{ + typedef Dune::YaspGrid<2> type; +}; + +#if HAVE_DUNE_PDELAB +SET_PROP(WaterAirProblem, LocalFEMSpace) +{ + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + enum{dim = GridView::dimension}; + +public: + typedef Dune::PDELab::Q1LocalFiniteElementMap<Scalar,Scalar,dim> type; // for cubes +// typedef Dune::PDELab::P1LocalFiniteElementMap<Scalar,Scalar,dim> type; // for simplices +}; +#endif // HAVE_DUNE_PDELAB + +// Set the problem property +SET_PROP(WaterAirProblem, Problem) +{ + typedef Dumux::WaterAirProblem<TypeTag> type; +}; + +// Set fluid configuration +SET_PROP(WaterAirProblem, FluidSystem) +{ +private: typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; +public: typedef Dumux::H2ON2FluidSystem<Scalar> type; +}; + +// Set the spatial parameters +SET_TYPE_PROP(WaterAirProblem, + SpatialParameters, + Dumux::WaterAirSpatialParameters<TypeTag>); + +// Enable gravity +SET_BOOL_PROP(WaterAirProblem, EnableGravity, true); + +// Use forward differences instead of central differences +SET_INT_PROP(WaterAirProblem, NumericDifferenceMethod, +1); + +// Write newton convergence +SET_BOOL_PROP(WaterAirProblem, NewtonWriteConvergence, false); +} + + +/*! + * \ingroup TwoPTwoCNIBoxModel + * + * \brief Non-isothermal gas injection problem where a gas (e.g. air) + * is injected into a fully water saturated medium. During + * buoyancy driven upward migration the gas passes a high + * temperature area. + * + * The domain is sized 40 m times 40 m. The rectangular area with the + * increased temperature (380 K) starts at (20 m, 5 m) and ends at (30 + * m, 35 m). + * + * For the mass conservation equation neumann boundary conditions are used on + * the top and on the bottom of the domain, while dirichlet conditions + * apply on the left and the right boundary. + * For the energy conservation equation dirichlet boundary conditions are applied + * on all boundaries. + * + * Gas is injected at the bottom boundary from 15 m to 25 m at a rate of + * 0.001 kg/(s m), the remaining neumann boundaries are no-flow + * boundaries. + * + * At the dirichlet boundaries a hydrostatic pressure, a gas saturation of zero and + * a geothermal temperature gradient of 0.03 K/m are applied. + * + * This problem uses the \ref TwoPTwoCNIModel. + * + * This problem should typically be simulated for 300000 s. + * A good choice for the initial time step size is 1000 s. + * + * To run the simulation execute the following line in shell: + * <tt>./test_2p2cni ./grids/test_2p2cni.dgf 300000 1000</tt> + * */ +template <class TypeTag> +class WaterAirProblem : public TwoPTwoCNIProblem<TypeTag> +{ + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Model)) Model; + typedef typename GridView::Grid Grid; + + typedef WaterAirProblem<TypeTag> ThisType; + typedef TwoPTwoCNIProblem<TypeTag> ParentType; + + // copy some indices for convenience + typedef typename GET_PROP_TYPE(TypeTag, PTAG(TwoPTwoCIndices)) Indices; + enum { + numEq = GET_PROP_VALUE(TypeTag, PTAG(NumEq)), + + pressureIdx = Indices::pressureIdx, + switchIdx = Indices::switchIdx, +#if !ISOTHERMAL + temperatureIdx = Indices::temperatureIdx, + energyEqIdx = Indices::energyEqIdx, +#endif + + // Phase State + lPhaseOnly = Indices::lPhaseOnly, + gPhaseOnly = Indices::gPhaseOnly, + bothPhases = Indices::bothPhases, + + // Grid and world dimension + dim = GridView::dimension, + dimWorld = GridView::dimensionworld, + }; + + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(PrimaryVariables)) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(BoundaryTypes)) BoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(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, PTAG(FVElementGeometry)) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + + typedef Dune::FieldVector<Scalar, dim> LocalPosition; + typedef Dune::FieldVector<Scalar, dimWorld> GlobalPosition; + +public: + /*! + * \brief The constructor + * + * \param timeManager The time manager + * \param gridView The grid view + */ + WaterAirProblem(TimeManager &timeManager, const GridView &gridView) + : ParentType(timeManager, gridView) + { + Scalar Tmin = 280.0; + Scalar Tmax = 390.0; + Scalar nT = 50; + + Scalar pmin = 8e6; + Scalar pmax = 12e6; + Scalar np = 250; + FluidSystem::init(Tmin, Tmax, nT, + pmin, pmax, np); + } + + /*! + * \name Problem parameters + */ + // \{ + + /*! + * \brief The problem name. + * + * This is used as a prefix for files generated by the simulation. + */ + const char *name() const + { return "waterair"; } + +#if ISOTHERMAL + /*! + * \brief Returns the temperature within the domain. + * + * \param element The element + * \param fvElemGeom The finite-volume geometry in the box scheme + * \param scvIdx The local vertex index (SCV index) + * + * This problem assumes a temperature of 10 degrees Celsius. + */ + Scalar temperature(const Element &element, + const FVElementGeometry &fvElemGeom, + int scvIdx) const + { + return 273.15 + 10; // -> 10°C + }; +#endif + + // \} + + /*! + * \name Boundary conditions + */ + // \{ + + /*! + * \brief Specifies which kind of boundary condition should be + * used for which equation on a given boundary segment. + * + * \param values The boundary types for the conservation equations + * \param vertex The vertex for which the boundary type is set + */ + void boundaryTypes(BoundaryTypes &values, const Vertex &vertex) const + { + const GlobalPosition globalPos = vertex.geometry().center(); + + if(globalPos[0] > 40 - eps_ || globalPos[0] < eps_) + values.setAllDirichlet(); + else + values.setAllNeumann(); + +#if !ISOTHERMAL + values.setDirichlet(temperatureIdx, energyEqIdx); +#endif + } + + /*! + * \brief Evaluate the boundary conditions for a dirichlet + * boundary segment. + * + * \param values The dirichlet values for the primary variables + * \param vertex The vertex for which the boundary type is set + * + * For this method, the \a values parameter stores primary variables. + */ + void dirichlet(PrimaryVariables &values, const Vertex &vertex) const + { + const GlobalPosition globalPos = vertex.geometry().center(); + + initial_(values, globalPos); + } + + /*! + * \brief Evaluate the boundary conditions for a neumann + * boundary segment. + * + * \param values The neumann values for the conservation equations + * \param element The finite element + * \param fvElemGeom The finite-volume geometry in the box scheme + * \param is The intersection between element and boundary + * \param scvIdx The local vertex index + * \param boundaryFaceIdx The index of the boundary face + * + * For this method, the \a values parameter stores the mass flux + * in normal direction of each phase. Negative values mean influx. + */ + void neumann(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvElemGeom, + const Intersection &is, + int scvIdx, + int boundaryFaceIdx) const + { + const GlobalPosition &globalPos = element.geometry().corner(scvIdx); + values = 0; + + // negative values for injection + if (globalPos[0] > 15 && globalPos[0] < 25 && + globalPos[1] < eps_) + { + values[Indices::contiGEqIdx] = -1e-3; + } + } + + // \} + + /*! + * \name Volume terms + */ + // \{ + + /*! + * \brief Evaluate the source term for all phases within a given + * sub-control-volume. + * + * \param values The source and sink values for the conservation equations + * \param element The finite element + * \param fvElemGeom The finite-volume geometry in the box scheme + * \param scvIdx The local vertex index + * + * For this method, the \a values parameter stores the rate mass + * generated or annihilate per volume unit. Positive values mean + * that mass is created, negative ones mean that it vanishes. + */ + void source(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvElemGeom, + int scvIdx) const + { + values = Scalar(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 fvElemGeom The finite-volume geometry in the box scheme + * \param scvIdx The local vertex index + * + * For this method, the \a values parameter stores primary + * variables. + */ + void initial(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvElemGeom, + int scvIdx) const + { + const GlobalPosition &globalPos = element.geometry().corner(scvIdx); + + initial_(values, globalPos); + +#if !ISOTHERMAL + if (globalPos[0] > 20 && globalPos[0] < 30 && globalPos[1] < 30) + values[temperatureIdx] = 380; +#endif + } + + /*! + * \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, + int &globalIdx, + const GlobalPosition &globalPos) const + { + return lPhaseOnly; + } + +private: + // internal method for the initial condition (reused for the + // dirichlet conditions!) + void initial_(PrimaryVariables &values, + const GlobalPosition &globalPos) const + { + Scalar densityW = 1000.0; + values[pressureIdx] = 1e5 + (depthBOR_ - globalPos[1])*densityW*9.81; + values[switchIdx] = 0.0; +#if !ISOTHERMAL + values[temperatureIdx] = 283.0 + (depthBOR_ - globalPos[1])*0.03; +#endif + } + + static const Scalar depthBOR_ = 1000.0; // [m] + static const Scalar eps_ = 1e-6; +}; +} //end namespace + +#endif diff --git a/test/boxmodels/new_2p2cni/waterairspatialparameters.hh b/test/boxmodels/new_2p2cni/waterairspatialparameters.hh new file mode 100644 index 0000000000..b525b1e639 --- /dev/null +++ b/test/boxmodels/new_2p2cni/waterairspatialparameters.hh @@ -0,0 +1,269 @@ +// $Id$ +/***************************************************************************** + * Copyright (C) 2008-2010 by Andreas Lauser * + * Copyright (C) 2008-2009 by Klaus Mosthaf * + * Institute of Hydraulic Engineering * + * 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 Definition of the spatial parameters for the water-air problem. + */ +#ifndef DUMUX_WATER_AIR_SPATIAL_PARAMETERS_HH +#define DUMUX_WATER_AIR_SPATIAL_PARAMETERS_HH + +#include <dumux/material/spatialparameters/boxspatialparameters.hh> +#include <dumux/material/fluidmatrixinteractions/2p/linearmaterial.hh> +#include <dumux/material/fluidmatrixinteractions/2p/regularizedbrookscorey.hh> +#include <dumux/material/fluidmatrixinteractions/2p/efftoabslaw.hh> + +namespace Dumux +{ + +/*! + * \ingroup TwoPTwoCNIModel + * + * \brief Definition of the spatial parameters for the water-air problem + */ +template<class TypeTag> +class WaterAirSpatialParameters : public BoxSpatialParameters<TypeTag> +{ + typedef BoxSpatialParameters<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Grid)) Grid; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename Grid::ctype CoordScalar; + enum { + dim=GridView::dimension, + dimWorld=GridView::dimensionworld, + }; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(TwoPTwoCIndices)) Indices; + enum { + lPhaseIdx = Indices::lPhaseIdx, + gPhaseIdx = Indices::gPhaseIdx, + }; + + typedef Dune::FieldVector<CoordScalar,dim> LocalPosition; + typedef Dune::FieldVector<CoordScalar,dimWorld> GlobalPosition; + typedef Dune::FieldVector<CoordScalar,dimWorld> Vector; + + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(SolutionVector)) SolutionVector; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(VolumeVariables)) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluxVariables)) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(ElementVolumeVariables)) ElementVolumeVariables; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + typedef typename GridView::template Codim<0>::Entity Element; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + + typedef RegularizedBrooksCorey<Scalar> EffMaterialLaw; +public: + typedef EffToAbsLaw<EffMaterialLaw> MaterialLaw; + typedef typename MaterialLaw::Params MaterialLawParams; + + /*! + * \brief The constructor + * + * \param gv The grid view + */ + WaterAirSpatialParameters(const GridView &gv) + : ParentType(gv) + { + layerBottom_ = 22.0; + + // intrinsic permeabilities + fineK_ = 1e-13; + coarseK_ = 1e-12; + + // porosities + finePorosity_ = 0.3; + coarsePorosity_ = 0.3; + + // residual saturations + fineMaterialParams_.setSwr(0.2); + fineMaterialParams_.setSnr(0.0); + coarseMaterialParams_.setSwr(0.2); + coarseMaterialParams_.setSnr(0.0); + + // parameters for the Brooks-Corey law + fineMaterialParams_.setPe(1e4); + coarseMaterialParams_.setPe(1e4); + fineMaterialParams_.setAlpha(2.0); + coarseMaterialParams_.setAlpha(2.0); + } + + ~WaterAirSpatialParameters() + {} + + + /*! + * \brief Update the spatial parameters with the flow solution + * after a timestep. + * + * \param globalSolution The global solution vector + */ + void update(const SolutionVector &globalSolution) + { + }; + + /*! + * \brief Apply the intrinsic permeability tensor to a pressure + * potential gradient. + * + * \param element The current finite element + * \param fvElemGeom The current finite volume geometry of the element + * \param scvIdx The index of the sub-control volume + */ + const Scalar intrinsicPermeability(const Element &element, + const FVElementGeometry &fvElemGeom, + int scvIdx) const + { + const GlobalPosition &pos = fvElemGeom.subContVol[scvIdx].global; + if (isFineMaterial_(pos)) + return fineK_; + return coarseK_; + } + + /*! + * \brief Define the porosity \f$[-]\f$ of the spatial parameters + * + * \param element The finite element + * \param fvElemGeom The finite volume geometry + * \param scvIdx The local index of the sub-control volume where + * the porosity needs to be defined + */ + double porosity(const Element &element, + const FVElementGeometry &fvElemGeom, + int scvIdx) const + { + const GlobalPosition &pos = fvElemGeom.subContVol[scvIdx].global; + if (isFineMaterial_(pos)) + return finePorosity_; + else + return coarsePorosity_; + } + + + /*! + * \brief return the parameter object for the Brooks-Corey material law which depends on the position + * + * \param element The current finite element + * \param fvElemGeom The current finite volume geometry of the element + * \param scvIdx The index of the sub-control volume + */ + const MaterialLawParams& materialLawParams(const Element &element, + const FVElementGeometry &fvElemGeom, + int scvIdx) const + { + const GlobalPosition &pos = fvElemGeom.subContVol[scvIdx].global; + if (isFineMaterial_(pos)) + return fineMaterialParams_; + else + return coarseMaterialParams_; + } + + /*! + * \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 fvElemGeom The finite volume geometry + * \param scvIdx The local index of the sub-control volume where + * the heat capacity needs to be defined + */ + double heatCapacity(const Element &element, + const FVElementGeometry &fvElemGeom, + int scvIdx) const + { + return + 790 // specific heat capacity of granite [J / (kg K)] + * 2700 // density of granite [kg/m^3] + * (1 - porosity(element, fvElemGeom, scvIdx)); + } + + /*! + * \brief Calculate the heat flux \f$[W/m^2]\f$ through the + * rock matrix based on the temperature gradient \f$[K / m]\f$ + * + * This is only required for non-isothermal models. + * + * \param heatFlux The resulting heat flux vector + * \param fluxDat The flux variables + * \param vDat The volume variables + * \param tempGrad The temperature gradient + * \param element The current finite element + * \param fvElemGeom The finite volume geometry of the current element + * \param scvfIdx The local index of the sub-control volume face where + * the matrix heat flux should be calculated + */ + void matrixHeatFlux(Vector &heatFlux, + const FluxVariables &fluxDat, + const ElementVolumeVariables &vDat, + const Vector &tempGrad, + const Element &element, + const FVElementGeometry &fvElemGeom, + int scvfIdx) const + { + static const Scalar lWater = 0.6; + static const Scalar lGranite = 2.8; + + // arithmetic mean of the liquid saturation and the porosity + const int i = fvElemGeom.subContVolFace[scvfIdx].i; + const int j = fvElemGeom.subContVolFace[scvfIdx].j; + Scalar Sl = std::max(0.0, (vDat[i].saturation(lPhaseIdx) + + vDat[j].saturation(lPhaseIdx)) / 2); + Scalar poro = (porosity(element, fvElemGeom, i) + + porosity(element, fvElemGeom, j)) / 2; + + Scalar lsat = pow(lGranite, (1-poro)) * pow(lWater, poro); + Scalar ldry = pow(lGranite, (1-poro)); + + // the heat conductivity of the matrix. in general this is a + // tensorial value, but we assume isotropic heat conductivity. + Scalar heatCond = ldry + sqrt(Sl) * (ldry - lsat); + + // the matrix heat flux is the negative temperature gradient + // times the heat conductivity. + heatFlux = tempGrad; + heatFlux *= -heatCond; + } + +private: + bool isFineMaterial_(const GlobalPosition &pos) const + { return pos[dim-1] > layerBottom_; }; + + Scalar fineK_; + Scalar coarseK_; + Scalar layerBottom_; + + Scalar finePorosity_; + Scalar coarsePorosity_; + + MaterialLawParams fineMaterialParams_; + MaterialLawParams coarseMaterialParams_; +}; + +} + +#endif -- GitLab