diff --git a/configure.ac b/configure.ac index 702ef8b0ac89bbb28ab187a5a19df64a26e19656..4a22e14aefb9d920a7c7af7b25a8552da1b3ba02 100644 --- a/configure.ac +++ b/configure.ac @@ -29,6 +29,7 @@ AC_CONFIG_FILES([dumux.pc dumux/boxmodels/2p2cni/Makefile dumux/boxmodels/2pni/Makefile dumux/boxmodels/common/Makefile + dumux/boxmodels/MpNc/Makefile dumux/boxmodels/richards/Makefile dumux/common/Makefile dumux/decoupled/Makefile @@ -50,9 +51,14 @@ AC_CONFIG_FILES([dumux.pc dumux/material/binarycoefficients/Makefile dumux/material/components/Makefile dumux/material/components/iapws/Makefile + dumux/material/constraintsolvers/Makefile + dumux/material/eos/Makefile dumux/material/fluidmatrixinteractions/Makefile dumux/material/fluidmatrixinteractions/2p/Makefile + dumux/material/fluidmatrixinteractions/Mp/Makefile + dumux/material/fluidstates/Makefile dumux/material/fluidsystems/Makefile + dumux/material/new_fluidsystems/Makefile dumux/material/spatialparameters/Makefile dumux/nonlinear/Makefile m4/Makefile @@ -64,6 +70,7 @@ AC_CONFIG_FILES([dumux.pc test/boxmodels/1p2c/Makefile test/boxmodels/2p2c/Makefile test/boxmodels/2p2cni/Makefile + test/boxmodels/MpNc/Makefile test/boxmodels/richards/Makefile test/common/Makefile test/common/generalproblem/Makefile diff --git a/dumux/boxmodels/Makefile.am b/dumux/boxmodels/Makefile.am index 0fd56c7e682abe8584cb21eab317367bf6cd563e..5f795772a5c222ffceeef8e8af13849a0e8a712c 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 2pni MpNc richards boxmodelsdir = $(includedir)/dumux/boxmodels diff --git a/dumux/boxmodels/MpNc/Makefile.am b/dumux/boxmodels/MpNc/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..0df2b8036be53697cb070f10d551abc9c0e6399f --- /dev/null +++ b/dumux/boxmodels/MpNc/Makefile.am @@ -0,0 +1,4 @@ +MpNcdir = $(includedir)/dumux/boxmodels/MpNc +MpNc_HEADERS = *.hh + +include $(top_srcdir)/am/global-rules diff --git a/dumux/boxmodels/MpNc/MpNcfluxvariables.hh b/dumux/boxmodels/MpNc/MpNcfluxvariables.hh new file mode 100644 index 0000000000000000000000000000000000000000..88201aba269a208fbb3102f6ed2161b8c330a20e --- /dev/null +++ b/dumux/boxmodels/MpNc/MpNcfluxvariables.hh @@ -0,0 +1,322 @@ +/***************************************************************************** + * Copyright (C) 2009-2010 by Andreas Lauser * + * 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 This file contains the data which is required to calculate + * all fluxes of components over a face of a finite volume. + * + * This means pressure, concentration and temperature gradients, phase + * densities at the integration point, etc. + */ +#ifndef DUMUX_MPNC_FLUX_VARIABLES_HH +#define DUMUX_MPNC_FLUX_VARIABLES_HH + +#include <dumux/common/spline.hh> + +#include "diffusion/fluxvariables.hh" +#include "energy/MpNcfluxvariablesenergy.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, three-component model. + * + * This means pressure and concentration gradients, phase densities at + * the intergration point, etc. + */ +template <class TypeTag> +class MPNCFluxVariables +{ + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Problem)) Problem; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(SpatialParameters)) SpatialParameters; + 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(ElementVolumeVariables)) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(VolumeVariables)) VolumeVariables; + + enum { + dimWorld = GridView::dimensionworld, + numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)), + numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)), + + enableDiffusion = GET_PROP_VALUE(TypeTag, PTAG(EnableDiffusion)), + enableEnergy = GET_PROP_VALUE(TypeTag, PTAG(EnableEnergy)), + enableKinetic = GET_PROP_VALUE(TypeTag, PTAG(EnableKinetic)), + enableKineticEnergy = GET_PROP_VALUE(TypeTag, PTAG(EnableKineticEnergy)), + enableGravity = GET_PROP_VALUE(TypeTag, PTAG(EnableGravity)), + }; + + typedef typename GridView::ctype CoordScalar; + typedef Dune::FieldVector<CoordScalar, dimWorld> Vector; + typedef Dune::FieldMatrix<CoordScalar, dimWorld, dimWorld> Tensor; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + typedef typename FVElementGeometry::SubControlVolume SCV; + typedef typename FVElementGeometry::SubControlVolumeFace SCVFace; + + typedef Dune::FieldVector<Scalar, numPhases> PhasesVector; + + typedef MPNCFluxVariablesDiffusion<TypeTag, enableDiffusion> FluxVariablesDiffusion; + typedef MPNCFluxVariablesEnergy<TypeTag, enableEnergy, enableKineticEnergy> FluxVariablesEnergy; + +public: + MPNCFluxVariables(const Problem &problem, + const Element &element, + const FVElementGeometry &elemGeom, + int scvfIdx, + const ElementVolumeVariables &elemVolVars) + : fvElemGeom_(elemGeom), volVars_(elemVolVars) + { + scvfIdx_ = scvfIdx; + + // update the base module (i.e. advection) + calculateGradients_(problem, element, elemVolVars); + calculateVelocities_(problem, element, elemVolVars); + + // update the flux data of the energy module (i.e. isothermal + // or non-isothermal) + energyDat_.update(problem, element, elemGeom, scvfIdx, *this, elemVolVars); + + // update the flux data of the diffusion module (i.e. with or + // without diffusion) + diffusionDat_.update(problem, element, elemGeom, scvfIdx, elemVolVars); + + extrusionFactor_ = + (elemVolVars[face().i].extrusionFactor() + + elemVolVars[face().j].extrusionFactor()) / 2; + } + + + /*! + * \brief Calculate a phase's darcy velocity [m/s] at a + * sub-control volume face. + * + * So far, this method only exists in the Mp-Nc model! + * + * Of course, in the setting of a finite volume scheme, the velocities are + * on the faces rather than in the volume. Therefore, the velocity + * + * \param problem The problem object + * \param element The current 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 computeDarcy(Vector & vDarcy, + const ElementVolumeVariables &elemVolVars, + int phaseIdx) const + { + intrinsicPermeability().mv(potentialGrad(phaseIdx), + vDarcy); + // darcy velocity is along *negative* potential gradient + // (i.e. from high to low pressures), this means that we need + // to negate the product of the intrinsic permeability and the + // potential gradient! + vDarcy *= -1; + + // JUST for upstream decision + Scalar normalFlux = vDarcy * face().normal; + // data attached to upstream and the downstream vertices + // of the current phase + int upIdx = face().i; + int dnIdx = face().j; + + if (!std::isfinite(normalFlux)) + DUNE_THROW(NumericalProblem, "Calculated non-finite normal flux"); + + if (normalFlux < 0) + std::swap(upIdx, dnIdx); + + const VolumeVariables &up = elemVolVars[upIdx]; + + //////// + // Jipie this is a velocity now, finally deserves the name + //////// + vDarcy *= up.mobility(phaseIdx); + } + + const SCVFace &face() const + { return fvElemGeom_.subContVolFace[scvfIdx_]; } + + const VolumeVariables &volVars(int idx) const + { return volVars_[idx]; } + + /*! + * \brief Returns th extrusion factor for the sub-control volume face + */ + Scalar extrusionFactor() const + { return extrusionFactor_; } + + /*! + * \brief Return the intrinsic permeability. + */ + const Tensor &intrinsicPermeability() const + { return K_; } + + /*! + * \brief Return the pressure potential gradient. + */ + const Vector &potentialGrad(int phaseIdx) const + { return potentialGrad_[phaseIdx]; } + + //////////////////////////////////////////////// + // forward calls to the diffusion module + Scalar porousDiffCoeffL(int compIdx) const + { return diffusionDat_.porousDiffCoeffL(compIdx); }; + + Scalar porousDiffCoeffG(int compIIdx, int compJIdx) const + { return diffusionDat_.porousDiffCoeffG(compIIdx, compJIdx); }; + + const Scalar moleFrac(int phaseIdx, int compIdx) const + { return diffusionDat_.moleFrac(phaseIdx, compIdx); }; + + const Vector &moleFracGrad(int phaseIdx, + int compIdx) const + { return diffusionDat_.moleFracGrad(phaseIdx, compIdx); }; + // end of forward calls to the diffusion module + //////////////////////////////////////////////// + + //////////////////////////////////////////////// + // forward calls to the temperature module + const Vector &temperatureGrad() const + { return energyDat_.temperatureGrad(); }; + + const FluxVariablesEnergy &energyData() const + { return energyDat_; } + // end of forward calls to the temperature module + //////////////////////////////////////////////// + +private: + void calculateGradients_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemVolVars) + { + for (int phaseIdx=0; phaseIdx < numPhases; phaseIdx++) { + potentialGrad_[phaseIdx] = Scalar(0); + } + + // calculate pressure gradients using finite element 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]; + + // TODO: only calculate the gradients for the present + // phases. + // + // compute sum of pressure gradients for each phase + for (int phaseIdx = 0; phaseIdx < numPhases; phaseIdx++) + { + // the pressure gradient + tmp = feGrad; + tmp *= elemVolVars[idx].fluidState().pressure(phaseIdx); + potentialGrad_[phaseIdx] += tmp; + } + } + + /////////////// + // correct the pressure gradients by the gravitational acceleration + /////////////// + if (enableGravity) { + // estimate the gravitational acceleration at a given SCV face + // using the arithmetic mean + Vector g(problem.boxGravity(element, fvElemGeom_, face().i)); + g += problem.boxGravity(element, fvElemGeom_, face().j); + g /= 2; + + for (int phaseIdx=0; phaseIdx < numPhases; phaseIdx++) + { + // calculate the phase density at the integration point. we + // only do this if the wetting phase is present in both cells + Scalar SI = elemVolVars[face().i].fluidState().saturation(phaseIdx); + Scalar SJ = elemVolVars[face().j].fluidState().saturation(phaseIdx); + Scalar rhoI = elemVolVars[face().i].fluidState().density(phaseIdx); + Scalar rhoJ = elemVolVars[face().j].fluidState().density(phaseIdx); + Scalar fI = std::max(0.0, std::min(SI/1e-5, 0.5)); + Scalar fJ = std::max(0.0, std::min(SJ/1e-5, 0.5)); + if (fI + fJ == 0) + // doesn't matter because no wetting phase is present in + // both cells! + fI = fJ = 0.5; + Scalar density = (fI*rhoI + fJ*rhoJ)/(fI + fJ); + + // make gravity acceleration a force + Vector f(g); + f *= density; + + // calculate the final potential gradient + potentialGrad_[phaseIdx] -= f; + } + } + } + + void calculateVelocities_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemVolVars) + { + // multiply the pressure potential with the intrinsic + // permeability + const SpatialParameters &sp = problem.spatialParameters(); + for (int phaseIdx=0; phaseIdx < numPhases; phaseIdx++) + { + sp.meanK(K_, + sp.intrinsicPermeability(element, + fvElemGeom_, + face().i), + sp.intrinsicPermeability(element, + fvElemGeom_, + face().j)); + } + } + + + + + const FVElementGeometry &fvElemGeom_; + int scvfIdx_; + + const ElementVolumeVariables &volVars_; + + // The extrusion factor for the sub-control volume face + Scalar extrusionFactor_; + + // pressure potential gradients + Vector potentialGrad_[numPhases]; + + // intrinsic permeability + Tensor K_; + + FluxVariablesDiffusion diffusionDat_; + FluxVariablesEnergy energyDat_; +}; + +} // end namepace + +#endif diff --git a/dumux/boxmodels/MpNc/MpNcindices.hh b/dumux/boxmodels/MpNc/MpNcindices.hh new file mode 100644 index 0000000000000000000000000000000000000000..799761d10331882fa1495a13bb541c8c54767801 --- /dev/null +++ b/dumux/boxmodels/MpNc/MpNcindices.hh @@ -0,0 +1,105 @@ +/***************************************************************************** + * Copyright (C) 2008-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/>. * + *****************************************************************************/ +#ifndef DUMUX_MPNC_INDICES_HH +#define DUMUX_MPNC_INDICES_HH + +#include "MpNcproperties.hh" + +#include "mass/MpNcindicesmass.hh" +#include "energy/MpNcindicesenergy.hh" + +namespace Dumux +{ +/*! + * \brief The primary variable and equation indices for the MpNc + * model. + */ +template <class TypeTag, int BasePVOffset = 0> +struct MPNCIndices : + public MPNCMassIndices<BasePVOffset, + TypeTag, + GET_PROP_VALUE(TypeTag, PTAG(EnableKinetic)) >, + public MPNCEnergyIndices<BasePVOffset + + MPNCMassIndices<0, TypeTag, GET_PROP_VALUE(TypeTag, PTAG(EnableKinetic)) >::NumPrimaryVars, + GET_PROP_VALUE(TypeTag, PTAG(EnableEnergy)), + GET_PROP_VALUE(TypeTag, PTAG(EnableKineticEnergy))> +{ +private: + enum { enableEnergy = GET_PROP_VALUE(TypeTag, PTAG(EnableEnergy)) }; + enum { enableDiffusion = GET_PROP_VALUE(TypeTag, PTAG(EnableDiffusion)) }; + enum { enableKinetic = GET_PROP_VALUE(TypeTag, PTAG(EnableKinetic)) }; //mass transfer + enum { enableKineticEnergy = GET_PROP_VALUE(TypeTag, PTAG(EnableKineticEnergy)) }; // energy transfer + + typedef MPNCMassIndices<BasePVOffset, TypeTag, enableKinetic> MassIndices; + typedef MPNCEnergyIndices<BasePVOffset + MassIndices::NumPrimaryVars, enableEnergy, enableKineticEnergy> EnergyIndices; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + enum { numComponents = FluidSystem::numComponents }; + enum { numPhases = FluidSystem::numPhases }; + +public: + /*! + * \brief The number of primary variables / equations. + */ + // temperature + Mass Balance + constraints for switch stuff + static const int NumPrimaryVars = + MassIndices::NumPrimaryVars + + EnergyIndices::NumPrimaryVars + + numPhases; + + /*! + * \brief The number of primary variables / equations of the erngy module. + */ + static const int NumPrimaryEnergyVars = + EnergyIndices::NumPrimaryVars ; + + /*! + * \brief Index of the saturation of the first phase in a vector + * of primary variables. + * + * The following (numPhases - 1) primary variables represent the + * saturations for the phases [1, ..., numPhases - 1] + */ + static const int S0Idx = + MassIndices::NumPrimaryVars + + EnergyIndices::NumPrimaryVars; + + /*! + * \brief Index of the first phase' pressure in a vector of + * primary variables. + */ + static const int p0Idx = + MassIndices::NumPrimaryVars + + EnergyIndices::NumPrimaryVars + + numPhases - 1; + + /*! + * \brief Index of the first phase NCP equation. + * + * The index for the remaining phases are consecutive. + */ + static const int phase0NcpIdx = + MassIndices::NumPrimaryVars + + EnergyIndices::NumPrimaryVars; +}; + +} + +#endif diff --git a/dumux/boxmodels/MpNc/MpNclocalresidual.hh b/dumux/boxmodels/MpNc/MpNclocalresidual.hh new file mode 100644 index 0000000000000000000000000000000000000000..1c3152994de253b92a2180bc9a4113dbf2d554fe --- /dev/null +++ b/dumux/boxmodels/MpNc/MpNclocalresidual.hh @@ -0,0 +1,264 @@ +/***************************************************************************** + * 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/>. * + *****************************************************************************/ +#ifndef DUMUX_MPNC_LOCAL_RESIDUAL_HH +#define DUMUX_MPNC_LOCAL_RESIDUAL_HH + +#include "MpNcfluxvariables.hh" +#include "diffusion/diffusion.hh" +#include "energy/MpNclocalresidualenergy.hh" +#include "mass/MpNclocalresidualmass.hh" + +#include <dumux/boxmodels/common/boxmodel.hh> + +#include <dumux/common/math.hh> + + +namespace Dumux +{ +/*! + * \brief two-phase, N-component specific details needed to + * approximately calculate the local defect in the BOX scheme. + * + * This class is used to fill the gaps in BoxLocalResidual for the + * two-phase, N-component twophase flow. + */ +template<class TypeTag> +class MPNCLocalResidual : public BoxLocalResidual<TypeTag> +{ + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MPNCIndices)) Indices; + +protected: + typedef typename GET_PROP_TYPE(TypeTag, PTAG(LocalResidual)) Implementation; + typedef MPNCLocalResidual<TypeTag> ThisType; + typedef BoxLocalResidual<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(FluidSystem)) FluidSystem; + + 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)), + + enableEnergy = GET_PROP_VALUE(TypeTag, PTAG(EnableEnergy)), + enableKineticEnergy = GET_PROP_VALUE(TypeTag, PTAG(EnableKineticEnergy)), + + enableDiffusion = GET_PROP_VALUE(TypeTag, PTAG(EnableDiffusion)), + enableKinetic = GET_PROP_VALUE(TypeTag, PTAG(EnableKinetic)), + enableSmoothUpwinding = GET_PROP_VALUE(TypeTag, PTAG(EnableSmoothUpwinding)), + + phase0NcpIdx = Indices::phase0NcpIdx + }; + + + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + typedef typename GridView::IntersectionIterator IntersectionIterator; + typedef typename GridView::template Codim<dim>::Entity Vertex; + typedef typename GridView::template Codim<dim>::Iterator VertexIterator; + + typedef typename GridView::CollectiveCommunication CollectiveCommunication; + + 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(FluxVariables)) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(ElementVolumeVariables)) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(ElementBoundaryTypes)) ElementBoundaryTypes; + + typedef Dune::FieldVector<Scalar, numPhases> PhasesVector; + typedef Dune::FieldVector<Scalar, numComponents> ComponentVector; + + typedef Dune::FieldVector<Scalar, dim> LocalPosition; + typedef Dune::FieldVector<Scalar, dimWorld> GlobalPosition; + + typedef Dune::FieldVector<Scalar, dimWorld> Vector; + typedef Dune::FieldMatrix<Scalar, dimWorld, dimWorld> Tensor; + + typedef MPNCLocalResidualEnergy<TypeTag, enableEnergy, enableKineticEnergy> EnergyResid; + typedef MPNCLocalResidualMass<TypeTag, enableKinetic> MassResid; + +public: + /*! + * \brief Evaluate the amount all conservation quantites + * (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) + */ + void computeStorage(PrimaryVariables &storage, 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 mass and energy storage terms + MassResid::computeStorage(storage, volVars); + EnergyResid::computeStorage(storage, volVars); + Valgrind::CheckDefined(storage); + } + + /*! + * \brief Evaluate the amount all conservation quantites + * (e.g. phase mass) within all sub-control volumes of an + * element. + */ + void addPhaseStorage(PrimaryVariables &storage, + const Element &element, + int phaseIdx) const + { + // create a finite volume element geometry + FVElementGeometry fvElemGeom; + fvElemGeom.update(this->gridView_(), element); + + // calculate volume variables + ElementVolumeVariables volVars; + this->model_().setHints(element, volVars); + volVars.update(this->problem_(), + element, + fvElemGeom, + /*useOldSolution=*/false); + + // calculate the phase storage for all sub-control volumes + for (int scvIdx=0; + scvIdx < fvElemGeom.numVertices; + scvIdx++) + { + PrimaryVariables tmp(0.0); + + // compute mass and energy storage terms in terms of + // averaged quantities + MassResid::addPhaseStorage(tmp, + volVars[scvIdx], + phaseIdx); + EnergyResid::addPhaseStorage(tmp, + volVars[scvIdx], + phaseIdx); + + // multiply with volume of sub-control volume + tmp *= fvElemGeom.subContVol[scvIdx].volume; + + // Add the storage of the current SCV to the total storage + storage += tmp; + } + } + + /*! + * \brief Calculate the source term of the equation + */ + void computeSource(PrimaryVariables &source, + int scvIdx) + { + Valgrind::SetUndefined(source); + this->problem_().boxSDSource(source, + this->elem_(), + this->fvElemGeom_(), + scvIdx, + this->curVolVars_() ); + const VolumeVariables &volVars = this->curVolVars_(scvIdx); + + PrimaryVariables tmp(0); + MassResid::computeSource(tmp, volVars); + source += tmp; + + tmp = 0.; + EnergyResid::computeSource(tmp, volVars); + source += tmp; + Valgrind::CheckDefined(source); + }; + + + /*! + * \brief Evaluates the total flux of all conservation quantities + * over a face of a subcontrol volume. + */ + void computeFlux(PrimaryVariables &flux, int faceIdx) const + { + FluxVariables fluxVars(this->problem_(), + this->elem_(), + this->fvElemGeom_(), + faceIdx, + this->curVolVars_()); + + flux = 0.0; + MassResid::computeFlux(flux, fluxVars, this->curVolVars_() ); + Valgrind::CheckDefined(flux); +/* + * EnergyResid also called in the MassResid + * 1) Makes some sense because energy is also carried by mass + * 2) The component-wise mass flux in each phase is needed. + */ + } + + /*! + * \brief Compute the local residual, i.e. the deviation of the + * equations from zero. + */ + void eval(const Element &element) + { ParentType::eval(element); } + + /*! + * \brief Evaluate the local residual. + */ + void eval(const Element &element, + const FVElementGeometry &fvGeom, + const ElementVolumeVariables &prevVolVars, + const ElementVolumeVariables &curVolVars, + const ElementBoundaryTypes &bcType) + { + ParentType::eval(element, + fvGeom, + prevVolVars, + curVolVars, + bcType); + + for (int i = 0; i < this->fvElemGeom_().numVertices; ++i) { + // add the two auxiliary equations, make sure that the + // dirichlet boundary condition is conserved + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + { + if (!bcType[i].isDirichlet(phase0NcpIdx + phaseIdx)) + { + this->residual_[i][phase0NcpIdx + phaseIdx] = + this->curVolVars_(i).phaseNcp(phaseIdx); + } + } + } + } + +protected: + 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/MpNc/MpNcmodel.hh b/dumux/boxmodels/MpNc/MpNcmodel.hh new file mode 100644 index 0000000000000000000000000000000000000000..0d50817d39d9b310ad3ac04deb561c639f1727ef --- /dev/null +++ b/dumux/boxmodels/MpNc/MpNcmodel.hh @@ -0,0 +1,201 @@ +/***************************************************************************** + * 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/>. * + *****************************************************************************/ +#ifndef DUMUX_MPNC_MODEL_HH +#define DUMUX_MPNC_MODEL_HH + +#include "MpNcproperties.hh" +#include "MpNcvtkwriter.hh" + +#include <dumux/boxmodels/common/boxmodel.hh> +#include <array> + +namespace Dumux +{ +/*! + * \ingroup BoxProblems + * \defgroup MPNCProblems Two-phase three-component box problems + */ + +/*! + * \ingroup BoxModels + * \defgroup MPNCModel Two-phase three-component box model + */ + +/*! + * \ingroup MPNCModel + * \brief Adaption of the box scheme to compositional twophase flows. + * + * This model implements a two-phase flow of a fluid mixture composed + * of \f$N\f$ chemical species. The phases are denoted by \f$\alpha + * \in \{ l, g \}\f$ for liquid and gas. The liquid and the gas phase + * both are a mixture of of \f$N \geq 2\f$ species. The model assumes, + * a fluid configuration of a solvent species and \f$N-1\f$ solute + * species with very low solubility. + * + * The standard multiphase Darcy approach is used as the equation for + * the conservation of momentum: + * \f[ + v_\alpha = - \frac{k_{r\alpha}}{\mu_\alpha} \boldsymbol{K} + \left(\text{grad} p_\alpha - \varrho_{\alpha} \boldsymbol{g} \right) + * \f] + * + * By inserting this into the equations for the conservation of the + * components, one gets one transport equation for each component \f$\kappa\f$ + * \f{eqnarray*} + && \sum_{\kappa \in \alpha} \left( + % + \phi \frac{\partial \varrho_\alpha x_{\alpha\kappa} S_\alpha}{\partial t} + - + \nabla \cdot + \left\{ + \frac{\varrho_\alpha}{\overline M_\alpha} x_{\alpha\kappa} + \frac{k_{r\alpha}}{\mu_\alpha} \boldsymbol{K} + (\nabla p_\alpha - \varrho_{\alpha} \boldsymbol{g}) + \right\} + \left) + \nonumber \\ + \nonumber \ + &-& \sum_{\kappa \in \ \nabla \cdot \left\{{\bf D_{pm}^\kappa} \frac{\varrho_{\alpha}}{\overline M_\alpha} {\bf \nabla} x^\kappa_{\alpha} \right\} + - \sum_{\kappa \in \alpha} q_\alpha^\kappa = \quad 0 \qquad \kappa \in \{1, \dots, N\} \, , + \alpha \in \{l, g\} + \f} + * with \f$\overline M_\alpha\f$ being the average molar mass of the phase \f$\alpha\f$: + * \f[ \overline M_\alpha = \sum_{\kappa = 1}^N M_\kappa \; x_{\alpha\kappa} \f] + * + * This is discretized in the model using the fully-coupled vertex + * centered finite volume (box) scheme as spatial and + * the implicit Euler method as temporal discretization. + * + * The model uses \f$x_{l1}, \dots, x_{lN}, S_g, p_g \f$ as primary + * variables. \f$x_{g\kappa}\f$ is calculated using Henry's law (if + * the componentis a solute), or the vapor pressure (if \f$\kappa\f$ + * is the solvent) as \f$\beta_\kappa\f$, the constant of + * proportionality between the liquid mole fraction and the partial + * pressure of a component: + * \f[ x_{g\kappa} = \frac{\beta_kappa x_{l\kappa}}{\sum_{i=1}^N x_{l\kappa} \beta_kappa} \f] + * + * Additionally two auxiliary conditions are used to keep the solution physical. + * \TODO: describe NCP approach + */ +template<class TypeTag> +class MPNCModel : public BoxModel<TypeTag> +{ + typedef MPNCModel<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 GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + + 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(FluxVariables)) FluxVariables; + + 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(SolutionVector)) SolutionVector; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MPNCIndices)) Indices; + typedef Dune::BlockVector<Dune::FieldVector<Scalar, 1> > ScalarField; + + typedef Dumux::MPNCVtkWriter<TypeTag> MPNCVtkWriter; + + + enum { + enableEnergy = GET_PROP_VALUE(TypeTag, PTAG(EnableEnergy)), + enableDiffusion = GET_PROP_VALUE(TypeTag, PTAG(EnableDiffusion)), + enableKinetic = GET_PROP_VALUE(TypeTag, PTAG(EnableKinetic)), + enableKineticEnergy = GET_PROP_VALUE(TypeTag, PTAG(EnableKineticEnergy)), + enableSmoothUpwinding = GET_PROP_VALUE(TypeTag, PTAG(EnableSmoothUpwinding)), + enablePartialReassemble = GET_PROP_VALUE(TypeTag, PTAG(EnablePartialReassemble)), + enableJacobianRecycling = GET_PROP_VALUE(TypeTag, PTAG(EnableJacobianRecycling)), + numDiffMethod = GET_PROP_VALUE(TypeTag, PTAG(NumericDifferenceMethod)), + numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)), + numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)), + numEq = GET_PROP_VALUE(TypeTag, PTAG(NumEq)), + dimWorld = GridView::dimensionworld, + dim = GridView::dimension + }; + +public: + ~MPNCModel() + { delete vtkWriter_; }; + + void init(Problem &problem) + { + ParentType::init(problem); + vtkWriter_ = new MPNCVtkWriter(problem); + + if (this->gridView_().comm().rank() == 0) + std::cout + << "Initializing M-phase N-component model: \n" + << " phases: " << numPhases << "\n" + << " components: " << numComponents << "\n" + << " equations: " << numEq << "\n" + << " kinetic mass transfer: " << enableKinetic<< "\n" + << " kinetic energy transfer: " << enableKineticEnergy<< "\n" + << " diffusion: " << enableDiffusion << "\n" + << " energy equation: " << enableEnergy << "\n" + << " smooth upwinding: " << enableSmoothUpwinding << "\n" + << " partial jacobian reassembly: " << enablePartialReassemble << "\n" + << " numeric differentiation method: " << numDiffMethod << " (-1: backward, 0: central, +1 forward)\n" + << " jacobian recycling: " << enableJacobianRecycling << "\n"; + } + + /*! + * \brief Compute the total storage inside one phase of all + * conservation quantities. + */ + 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().addPhaseStorage(dest, *elemIt, phaseIdx); + }; + + this->gridView_().comm().sum(dest); + } + + /*! + * \brief Add the result of the current timestep to the VTK output. + */ + template <class MultiWriter> + void addOutputVtkFields(const SolutionVector &sol, + MultiWriter &writer) + { + vtkWriter_->addCurrentSolution(writer); + } + + MPNCVtkWriter *vtkWriter_; +}; + +} + +#include "MpNcpropertydefaults.hh" + +#endif diff --git a/dumux/boxmodels/MpNc/MpNcnewtoncontroller.hh b/dumux/boxmodels/MpNc/MpNcnewtoncontroller.hh new file mode 100644 index 0000000000000000000000000000000000000000..2977cd89d18cc9053cdbd64494ea04313b38c38e --- /dev/null +++ b/dumux/boxmodels/MpNc/MpNcnewtoncontroller.hh @@ -0,0 +1,273 @@ +/***************************************************************************** + * Copyright (C) 2009-2010 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 A MpNc 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_MPNC_NEWTON_CONTROLLER_HH +#define DUMUX_MPNC_NEWTON_CONTROLLER_HH + +#include "MpNcproperties.hh" + +#include <dumux/nonlinear/newtoncontroller.hh> +#include <algorithm> + +namespace Dumux { + +template <class TypeTag, bool enableKinetic /* = false */> +class MpNcNewtonChop +{ + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MPNCIndices)) Indices; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(SolutionVector)) SolutionVector; + + enum { numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)) }; + enum { fug0Idx = Indices::fug0Idx }; + enum { S0Idx = Indices::S0Idx }; + enum { p0Idx = Indices::p0Idx }; + +public: + static void chop(SolutionVector &uCurrentIter, + const SolutionVector &uLastIter) + { + for (int i = 0; i < uLastIter.size(); ++i) { + for (int phaseIdx = 0; phaseIdx < numPhases - 1; ++phaseIdx) + saturationChop_(uCurrentIter[i][S0Idx + phaseIdx], + uLastIter[i][S0Idx + phaseIdx]); + pressureChop_(uCurrentIter[i][p0Idx], uLastIter[i][p0Idx]); + for (int comp = 0; comp < numComponents; ++comp) { + pressureChop_(uCurrentIter[i][fug0Idx + comp], uLastIter[i][fug0Idx + comp]); + } + + } + }; + +private: + static void clampValue_(Scalar &val, Scalar minVal, Scalar maxVal) + { + val = std::max(minVal, std::min(val, maxVal)); + }; + + static void pressureChop_(Scalar &val, Scalar oldVal) + { + const Scalar maxDelta = std::max(oldVal/4.0, 10e3); + clampValue_(val, oldVal - maxDelta, oldVal + maxDelta); + val = std::max(0.0, val); // don't allow negative pressures + } + + static void saturationChop_(Scalar &val, Scalar oldVal) + { + const Scalar maxDelta = 0.25; + clampValue_(val, oldVal - maxDelta, oldVal + maxDelta); + clampValue_(val, -0.001, 1.001); + } + +}; + +template <class TypeTag> +class MpNcNewtonChop<TypeTag, /*enableKinetic=*/true> +{ + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MPNCIndices)) Indices; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(SolutionVector)) SolutionVector; + + enum { numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)) }; + enum { moleFrac00Idx = Indices::moleFrac00Idx }; + enum { S0Idx = Indices::S0Idx }; + enum { p0Idx = Indices::p0Idx }; + +public: + static void chop(SolutionVector &uCurrentIter, + const SolutionVector &uLastIter) + { + for (int i = 0; i < uLastIter.size(); ++i) { + for (int phaseIdx = 0; phaseIdx < numPhases - 1; ++phaseIdx) + saturationChop_(uCurrentIter[i][S0Idx + phaseIdx], + uLastIter[i][S0Idx + phaseIdx]); + pressureChop_(uCurrentIter[i][p0Idx], uLastIter[i][p0Idx]); + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + for (int compIdx = 0; compIdx < numComponents; ++compIdx) { + moleFracChop_(uCurrentIter[i][moleFrac00Idx + phaseIdx*numComponents + compIdx], + uLastIter[i][moleFrac00Idx + phaseIdx*numComponents + compIdx]); + } + }; + + } + }; + +private: + static void clampValue_(Scalar &val, Scalar minVal, Scalar maxVal) + { + val = std::max(minVal, std::min(val, maxVal)); + }; + + static void pressureChop_(Scalar &val, Scalar oldVal) + { + const Scalar maxDelta = std::max(oldVal/4.0, 10e3); + clampValue_(val, oldVal - maxDelta, oldVal + maxDelta); + val = std::max(0.0, val); // don't allow negative pressures + } + + static void saturationChop_(Scalar &val, Scalar oldVal) + { + const Scalar maxDelta = 0.25; + clampValue_(val, oldVal - maxDelta, oldVal + maxDelta); + clampValue_(val, -0.001, 1.001); + } + + static void moleFracChop_(Scalar &val, Scalar oldVal) + { + // no component mole fraction can change by more than 20% per iteration + const Scalar maxDelta = 0.20; + clampValue_(val, oldVal - maxDelta, oldVal + maxDelta); + clampValue_(val, -0.001, 1.001); + } + +}; + +/*! + * \brief A MpNc 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 MPNCNewtonController : public NewtonController<TypeTag> +{ + typedef MPNCNewtonController<TypeTag> ThisType; + typedef NewtonController<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(PrimaryVariables)) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(SolutionVector)) SolutionVector; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(NewtonMethod)) NewtonMethod; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(JacobianAssembler)) JacobianAssembler; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MPNCIndices)) Indices; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Problem)) Problem; + + enum { + numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)), + numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)), + enableEnergy = GET_PROP_VALUE(TypeTag, PTAG(EnableEnergy)), + enableKinetic = GET_PROP_VALUE(TypeTag, PTAG(EnableKinetic)), + numEq = GET_PROP_VALUE(TypeTag, PTAG(NumEq)), + + enablePartialReassemble = GET_PROP_VALUE(TypeTag, PTAG(EnablePartialReassemble)), + + p0Idx = Indices::p0Idx, + S0Idx = Indices::S0Idx, + + Red = JacobianAssembler::Red + }; + + typedef MpNcNewtonChop<TypeTag, enableKinetic> NewtonChop; + +public: + MPNCNewtonController(const Problem &problem) + : ParentType(problem) + { + enableChop_ = GET_PARAM(TypeTag, bool, Newton, EnableChop); + Dune::FMatrixPrecision<>::set_singular_limit(1e-35); + }; + + 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 minReasmTol = 0.5*this->tolerance_; + Scalar reassembleTol = Dumux::geometricMean(this->error_, minReasmTol); + reassembleTol = std::max(reassembleTol, minReasmTol); + + this->model_().jacobianAssembler().updateDiscrepancy(uLastIter, deltaU); + this->model_().jacobianAssembler().computeColors(reassembleTol); + } + + if (GET_PROP_VALUE(TypeTag, PTAG(NewtonUseLineSearch))) { + lineSearchUpdate_(uCurrentIter, uLastIter, deltaU); + } + else { + Scalar lambda = 1.0; + /* + if (this->error_ > 10) { + // Do a "poor man's line search" + lambda /= std::sqrt((this->numSteps_ + 1)*this->error_/10); + lambda = std::max(0.2, lambda); + } + */ + + uCurrentIter = deltaU; + uCurrentIter *= - lambda; + uCurrentIter += uLastIter; + + if (this->numSteps_ < 4 && enableChop_) { + // put crash barriers along the update path at the + // beginning... + NewtonChop::chop(uCurrentIter, uLastIter); + } + } + } + +private: + void lineSearchUpdate_(SolutionVector &uCurrentIter, + const SolutionVector &uLastIter, + const SolutionVector &deltaU) + { + Scalar lambda = 1.0; + Scalar globDef; + + SolutionVector tmp(uLastIter); + Scalar oldGlobDef = this->model_().globalResidual(tmp, uLastIter); + while (true) { + uCurrentIter = deltaU; + uCurrentIter *= -lambda; + uCurrentIter += uLastIter; + + globDef = this->model_().globalResidual(tmp, uCurrentIter); + if (globDef < oldGlobDef || lambda <= 1.0/64) { + this->endIterMsg() << ", defect " << oldGlobDef << "->" << globDef << "@lambda=1/" << 1/lambda; + return; + } + + // try with a smaller update + lambda /= 2; + } + }; + + bool enableChop_; +}; +} + +#endif diff --git a/dumux/boxmodels/MpNc/MpNcproblem.hh b/dumux/boxmodels/MpNc/MpNcproblem.hh new file mode 100644 index 0000000000000000000000000000000000000000..11b8a458ecf6416fae64e9543ec99e5077503e9a --- /dev/null +++ b/dumux/boxmodels/MpNc/MpNcproblem.hh @@ -0,0 +1,179 @@ +/***************************************************************************** + * 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 Base class for all problems which use the two-phase, + * three-component box model + */ +#ifndef DUMUX_MPNC_BOX_PROBLEM_HH +#define DUMUX_MPNC_BOX_PROBLEM_HH + +#include "dumux/boxmodels/MpNc/MpNcnewtoncontroller.hh" + +#include <dumux/boxmodels/common/boxproblem.hh> + +namespace Dumux +{ +/*! + * \ingroup MPNCProblems + * \brief Base class for all problems which use the two-phase, three-component box model + * + * \todo Please doc me more! + */ +template<class TypeTag> +class MPNCProblem : public BoxProblem<TypeTag> +{ + typedef BoxProblem<TypeTag> ParentType; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Problem)) Implementation; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + typedef typename GridView::Grid Grid; + typedef typename GridView::template Codim<0>::Entity Element; + 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; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + + enum { + dim = Grid::dimension, + dimWorld = Grid::dimensionworld + }; + + typedef Dune::FieldVector<typename GridView::Grid::ctype, dimWorld> GlobalPosition; + +public: + MPNCProblem(TimeManager &timeManager, const GridView &gridView) + : ParentType(timeManager, gridView), + gravity_(0), + spatialParameters_(gridView) + { + if (GET_PROP_VALUE(TypeTag, PTAG(EnableGravity))) + gravity_[dim-1] = -9.81; + } + + /*! + * \name Problem parameters + */ + // \{ + + /*! + * \brief Returns the temperature \f$\mathrm{[K]}\f$ within a control volume. + * + * This is the discretization specific interface for the box + * method. By default it just calls temperature(pos). + * + * \param element The DUNE Codim<0> enitiy which intersects with + * the finite volume. + * \param fvGeom The finite volume geometry of the element. + * \param scvIdx The local index of the sub control volume inside the element + */ + Scalar boxTemperature(const Element &element, + const FVElementGeometry fvGeom, + int scvIdx) const + { return asImp_().temperatureAtPos(fvGeom.subContVol[scvIdx].global); } + + /*! + * \brief Returns the temperature \f$\mathrm{[K]}\f$ at a given global position. + * + * This is not specific to the discretization. By default it just + * calls temperature(). + * + * \param pos The position in global coordinates where the temperature should be specified. + */ + Scalar temperatureAtPos(const GlobalPosition &pos) const + { return asImp_().temperature(); } + + /*! + * \brief Returns the temperature \f$\mathrm{[K]}\f$ for an isothermal problem. + * + * This is not specific to the discretization. By default it just + * throws an exception so it must be overloaded by the problem if + * no energy equation is used. + */ + Scalar temperature() const + { DUNE_THROW(Dune::NotImplemented, "temperature() method not implemented by the actual problem"); }; + + /*! + * \brief Returns the acceleration due to gravity \f$\mathrm{[m/s^2]}\f$. + * + * This is the box discretization specific interface. By default + * it just calls gravityAtPos(). + */ + const GlobalPosition &boxGravity(const Element &element, + const FVElementGeometry &fvGeom, + int scvIdx) const + { return asImp_().gravityAtPos(fvGeom.subContVol[scvIdx].global); } + + /*! + * \brief Returns the acceleration due to gravity \f$\mathrm{[m/s^2]}\f$. + * + * This is discretization independent interface. By default it + * just calls gravity(). + */ + const GlobalPosition &gravityAtPos(const GlobalPosition &pos) const + { return asImp_().gravity(); } + + /*! + * \brief Returns the acceleration due to gravity \f$\mathrm{[m/s^2]}\f$. + * + * This method is used for problems where the gravitational + * acceleration does not depend on the spatial position. The + * default behaviour is that if the <tt>EnableGravity</tt> + * property is true, \f$\boldsymbol{g} = ( 0,\dots,\ -9.81)^T \f$ holds, + * else \f$\boldsymbol{g} = ( 0,\dots, 0)^T \f$. + */ + const GlobalPosition &gravity() const + { return gravity_; } + + /*! + * \brief Returns the spatial parameters object. + */ + SpatialParameters &spatialParameters() + { return spatialParameters_; } + + /*! + * \copydoc spatialParameters() + */ + const SpatialParameters &spatialParameters() const + { return spatialParameters_; } + + // \} + +protected: + //! 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_; + + // fluids and material properties + SpatialParameters spatialParameters_; +}; + +} + +#endif diff --git a/dumux/boxmodels/MpNc/MpNcproperties.hh b/dumux/boxmodels/MpNc/MpNcproperties.hh new file mode 100644 index 0000000000000000000000000000000000000000..40edab43e95b44eb195e8f2fd6dfc4b3b7534b84 --- /dev/null +++ b/dumux/boxmodels/MpNc/MpNcproperties.hh @@ -0,0 +1,113 @@ +/***************************************************************************** + * Copyright (C) 2008-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/>. * + *****************************************************************************/ +#ifndef DUMUX_MPNC_PROPERTIES_HH +#define DUMUX_MPNC_PROPERTIES_HH + +#include <dumux/boxmodels/common/boxproperties.hh> + +namespace Dumux +{ +namespace Properties +{ + +////////////////////////////////////////////////////////////////// +// Type tags +////////////////////////////////////////////////////////////////// + +/*! + * \brief Define the type tag for the compositional twophase box model. + */ +NEW_TYPE_TAG(BoxMPNC, 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(MPNCIndices); //!< Enumerations for the 2pNc model +NEW_PROP_TAG(MPNCEnergyIndices); //!< Enumerations for the 2pNc model + +NEW_PROP_TAG(MPNCVtkCommonModule); //!< Vtk writer module for writing the common quantities into the VTK output file +NEW_PROP_TAG(MPNCVtkMassModule); //!< Vtk writer module for writing the mass related quantities into the VTK output file +NEW_PROP_TAG(MPNCVtkEnergyModule); //!< Vtk writer module for writing the energy related quantities into the VTK output file +NEW_PROP_TAG(MPNCVtkCustomModule); //!< Vtk writer module for writing the user-specified quantities into the VTK output file + +NEW_PROP_TAG(VelocityAveragingInProblem);//!< Should the averaging of velocities be done in the problem? + +//! specify which quantities are written to the vtk output files +NEW_PROP_TAG(MPNCVtkAddPorosity); +NEW_PROP_TAG(MPNCVtkAddBoundaryTypes); +NEW_PROP_TAG(MPNCVtkAddSaturations); +NEW_PROP_TAG(MPNCVtkAddPressures); +NEW_PROP_TAG(MPNCVtkAddVarPressures); +NEW_PROP_TAG(MPNCVtkAddVelocities); +NEW_PROP_TAG(MPNCVtkAddDensities); +NEW_PROP_TAG(MPNCVtkAddMobilities); +NEW_PROP_TAG(MPNCVtkAddMeanMolarMass); +NEW_PROP_TAG(MPNCVtkAddMassFractions); +NEW_PROP_TAG(MPNCVtkAddMoleFractions); +NEW_PROP_TAG(MPNCVtkAddMolarities); +NEW_PROP_TAG(MPNCVtkAddFugacities); +NEW_PROP_TAG(MPNCVtkAddFugacityCoefficients); +NEW_PROP_TAG(MPNCVtkAddTemperatures); +NEW_PROP_TAG(MPNCVtkAddEnthalpies); +NEW_PROP_TAG(MPNCVtkAddInternalEnergies); + +NEW_PROP_TAG(MPNCVtkAddReynolds); +NEW_PROP_TAG(MPNCVtkAddPrandtl); +NEW_PROP_TAG(MPNCVtkAddNusselt); +NEW_PROP_TAG(MPNCVtkAddInterfacialArea); + +NEW_PROP_TAG(SpatialParameters); //!< The type of the soil properties object + +NEW_PROP_TAG(MaterialLaw); //!< The material law which ought to be used (extracted from the soil) +NEW_PROP_TAG(MaterialLawParams); //!< The context material law (extracted from the soil) + +//! The compositional twophase system of fluids which is considered +NEW_PROP_TAG(FluidSystem); + +//! The themodynamic constraint solver which calculates the +//! composition of any phase given all component fugacities. +NEW_PROP_TAG(CompositionFromFugacitiesSolver); + +//! Enable the energy equation? +NEW_PROP_TAG(EnableEnergy); + +//! Enable diffusive fluxes? +NEW_PROP_TAG(EnableDiffusion); + +//! Enable kinetic resolution of mass transfer processes? +NEW_PROP_TAG(EnableKinetic); + +//! Enable kinetic resolution of energy transfer processes? +NEW_PROP_TAG(EnableKineticEnergy); + +//! Enable gravity? +NEW_PROP_TAG(EnableGravity); + +//! Use the smooth upwinding method? +NEW_PROP_TAG(EnableSmoothUpwinding); + +//! Chop the Newton update at the beginning of the non-linear solver? +NEW_PROP_TAG(NewtonEnableChop); +} +} + +#endif diff --git a/dumux/boxmodels/MpNc/MpNcpropertydefaults.hh b/dumux/boxmodels/MpNc/MpNcpropertydefaults.hh new file mode 100644 index 0000000000000000000000000000000000000000..f3e912a486f87eeba6f68ea8e9e164ebe9d50ad2 --- /dev/null +++ b/dumux/boxmodels/MpNc/MpNcpropertydefaults.hh @@ -0,0 +1,226 @@ +/***************************************************************************** + * Copyright (C) 2008-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/>. * + *****************************************************************************/ +#ifndef DUMUX_MPNC_PROPERTY_DEFAULTS_HH +#define DUMUX_MPNC_PROPERTY_DEFAULTS_HH + +#include "MpNcindices.hh" + +#include "MpNcmodel.hh" + +#include "MpNcproblem.hh" +#include "MpNcindices.hh" +#include "MpNclocalresidual.hh" +#include "MpNcfluxvariables.hh" +#include "MpNcvolumevariables.hh" +#include "MpNcproperties.hh" +#include "MpNcnewtoncontroller.hh" + +#include "MpNcvtkwritermodule.hh" +#include "MpNcvtkwritercommon.hh" +#include "mass/MpNcvtkwritermass.hh" +#include "energy/MpNcvtkwriterenergy.hh" + +#include <dumux/material/constraintsolvers/compositionfromfugacities.hh> + +namespace Dumux +{ +namespace Properties +{ +////////////////////////////////////////////////////////////////// +// default property values +////////////////////////////////////////////////////////////////// + +/*! + * \brief Set the property for the number of components. + * + * We just forward the number from the fluid system. + */ +SET_PROP(BoxMPNC, NumComponents) +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + +public: + static const int value = FluidSystem::numComponents; +}; + +/*! + * \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(BoxMPNC, NumPhases) +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + +public: + static const int value = FluidSystem::numPhases; +}; + +/*! + * \brief Set the property for the number of equations and primary variables. + */ +SET_PROP(BoxMPNC, NumEq) +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MPNCIndices)) Indices; + +public: + static const int value = Indices::NumPrimaryVars; +}; + +/*! + * \brief Set the property for the material parameters by extracting + * it from the material law. + */ +SET_PROP(BoxMPNC, MaterialLawParams) +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MaterialLaw)) MaterialLaw; + +public: + typedef typename MaterialLaw::Params type; +}; + +/*! + * \brief Set the themodynamic constraint solver which calculates the + * composition of any phase given all component fugacities. + */ +SET_PROP(BoxMPNC, CompositionFromFugacitiesSolver) +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + +public: + typedef Dumux::CompositionFromFugacities<Scalar, FluidSystem> type; +}; + +//! Use the MpNc local jacobian operator for the MpNc model +SET_TYPE_PROP(BoxMPNC, + LocalResidual, + MPNCLocalResidual<TypeTag>); + +//! Use the MpNc specific newton controller for the MpNc model +SET_PROP(BoxMPNC, NewtonController) +{public: + typedef MPNCNewtonController<TypeTag> type; +}; + +//! the Model property +SET_TYPE_PROP(BoxMPNC, Model, MPNCModel<TypeTag>); + +//! use an isothermal model by default +SET_BOOL_PROP(BoxMPNC, EnableEnergy, false); + +//! disable diffusion by default +SET_BOOL_PROP(BoxMPNC, EnableDiffusion, false); + +//! disable kinetic mass transfer by default +SET_BOOL_PROP(BoxMPNC, EnableKinetic, false); + +//! disable kinetic energy transfer by default +SET_BOOL_PROP(BoxMPNC, EnableKineticEnergy, false); + +//! enable smooth upwinding by default +SET_BOOL_PROP(BoxMPNC, EnableSmoothUpwinding, true); + +//! the VolumeVariables property +SET_TYPE_PROP(BoxMPNC, VolumeVariables, MPNCVolumeVariables<TypeTag>); + +//! the FluxVariables property +SET_TYPE_PROP(BoxMPNC, FluxVariables, MPNCFluxVariables<TypeTag>); + +// enable jacobian matrix recycling by default +SET_BOOL_PROP(BoxMPNC, EnableJacobianRecycling, false); +// enable partial reassembling by default +SET_BOOL_PROP(BoxMPNC, EnablePartialReassemble, true); +// truncate the newton update in the beginning +SET_BOOL_PROP(BoxMPNC, NewtonEnableChop, true); + + +//! The indices required by the compositional twophase model +SET_PROP(BoxMPNC, MPNCIndices) +{ + typedef MPNCIndices<TypeTag, 0> type; +}; + +//! The VTK writer module for common quantities +SET_PROP(BoxMPNC, MPNCVtkCommonModule) +{ + typedef MPNCVtkWriterCommon<TypeTag> type; +}; + +//! The VTK writer module for quantities which are specific for each +//! mass module +SET_PROP(BoxMPNC, MPNCVtkMassModule) +{ +private: enum { enableKinetic = GET_PROP_VALUE(TypeTag, PTAG(EnableKinetic)) }; +public: typedef MPNCVtkWriterMass<TypeTag, enableKinetic> type; +}; + +//! The VTK writer module for quantities which are specific for each +//! energy module +SET_PROP(BoxMPNC, MPNCVtkEnergyModule) +{ +private: + enum { enableEnergy = GET_PROP_VALUE(TypeTag, PTAG(EnableEnergy)) }; + enum { enableKineticEnergy = GET_PROP_VALUE(TypeTag, PTAG(EnableKineticEnergy)) }; +public: + typedef MPNCVtkWriterEnergy<TypeTag, enableEnergy, enableKineticEnergy> type; +}; + +//! The VTK writer for user specified data (does nothing by default) +SET_PROP(BoxMPNC, MPNCVtkCustomModule) +{ typedef MPNCVtkWriterModule<TypeTag> type; }; + + +//!< Should the averaging of velocities be done in the problem? (By default in the output) +SET_BOOL_PROP(BoxMPNC, VelocityAveragingInProblem, false); + +//! Specify what to add to the VTK output by default +SET_BOOL_PROP(BoxMPNC, MPNCVtkAddPorosity, true); +SET_BOOL_PROP(BoxMPNC, MPNCVtkAddBoundaryTypes, false); +SET_BOOL_PROP(BoxMPNC, MPNCVtkAddSaturations, true); +SET_BOOL_PROP(BoxMPNC, MPNCVtkAddPressures, true); +SET_BOOL_PROP(BoxMPNC, MPNCVtkAddVarPressures, false); +SET_BOOL_PROP(BoxMPNC, MPNCVtkAddVelocities, false); +SET_BOOL_PROP(BoxMPNC, MPNCVtkAddDensities, true); +SET_BOOL_PROP(BoxMPNC, MPNCVtkAddMobilities, true); +SET_BOOL_PROP(BoxMPNC, MPNCVtkAddMeanMolarMass, false); +SET_BOOL_PROP(BoxMPNC, MPNCVtkAddMassFractions, false); +SET_BOOL_PROP(BoxMPNC, MPNCVtkAddMoleFractions, true); +SET_BOOL_PROP(BoxMPNC, MPNCVtkAddMolarities, false); +SET_BOOL_PROP(BoxMPNC, MPNCVtkAddFugacities, false); +SET_BOOL_PROP(BoxMPNC, MPNCVtkAddFugacityCoefficients, false); +SET_BOOL_PROP(BoxMPNC, MPNCVtkAddTemperatures, false); +SET_BOOL_PROP(BoxMPNC, MPNCVtkAddEnthalpies, true); +SET_BOOL_PROP(BoxMPNC, MPNCVtkAddInternalEnergies, false); +SET_BOOL_PROP(BoxMPNC, MPNCVtkAddReynolds, false); +SET_BOOL_PROP(BoxMPNC, MPNCVtkAddPrandtl, false); +SET_BOOL_PROP(BoxMPNC, MPNCVtkAddNusselt, false); +SET_BOOL_PROP(BoxMPNC, MPNCVtkAddInterfacialArea, false); +} + +} + +#endif diff --git a/dumux/boxmodels/MpNc/MpNcvolumevariables.hh b/dumux/boxmodels/MpNc/MpNcvolumevariables.hh new file mode 100644 index 0000000000000000000000000000000000000000..9df8e9f3d2740d59783f0a79baa8b4d66292717a --- /dev/null +++ b/dumux/boxmodels/MpNc/MpNcvolumevariables.hh @@ -0,0 +1,359 @@ +/***************************************************************************** + * Copyright (C) 2009-2011 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 Contains the secondary variables (Quantities which are + * constant within a finite volume) of the M-phase, N-component + * model. + */ +#ifndef DUMUX_MPNC_VOLUME_VARIABLES_HH +#define DUMUX_MPNC_VOLUME_VARIABLES_HH + +#include "diffusion/volumevariables.hh" +#include "energy/MpNcvolumevariablesenergy.hh" +#include "mass/MpNcvolumevariablesmass.hh" +#include "MpNcvolumevariablesia.hh" + +#include <dumux/boxmodels/common/boxvolumevariables.hh> + +namespace Dumux +{ +/*! + * \brief Contains the quantities which are are constant within a + * finite volume in the M-phase, N-component model. + */ +template <class TypeTag> +class MPNCVolumeVariables + : public BoxVolumeVariables<TypeTag> + , public MPNCVolumeVariablesIA<TypeTag, GET_PROP_VALUE(TypeTag, PTAG(EnableKinetic)), GET_PROP_VALUE(TypeTag, PTAG(EnableKineticEnergy))> + , public MPNCVolumeVariablesMass<TypeTag, GET_PROP_VALUE(TypeTag, PTAG(EnableKinetic))> + , public MPNCVolumeVariablesDiffusion<TypeTag, GET_PROP_VALUE(TypeTag, PTAG(EnableDiffusion)) || GET_PROP_VALUE(TypeTag, PTAG(EnableKinetic))> + , public MPNCVolumeVariablesEnergy<TypeTag, GET_PROP_VALUE(TypeTag, PTAG(EnableEnergy)), GET_PROP_VALUE(TypeTag, PTAG(EnableKineticEnergy))> +{ + typedef BoxVolumeVariables<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(VolumeVariables)) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Problem)) Problem; + 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(FVElementGeometry)) FVElementGeometry; + 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(PrimaryVariables)) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(SpatialParameters)) SpatialParameters; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MPNCIndices)) Indices; + enum { + numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)), + numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)), + dimWorld = GridView::dimensionworld, + + enableEnergy = GET_PROP_VALUE(TypeTag, PTAG(EnableEnergy)), + enableKinetic = GET_PROP_VALUE(TypeTag, PTAG(EnableKinetic)), + enableKineticEnergy = GET_PROP_VALUE(TypeTag, PTAG(EnableKineticEnergy)), + enableDiffusion = GET_PROP_VALUE(TypeTag, PTAG(EnableDiffusion)) || enableKinetic, + + numEnergyEqs = Indices::NumPrimaryEnergyVars, + + S0Idx = Indices::S0Idx, + p0Idx = Indices::p0Idx, + }; + + typedef typename GridView::template Codim<0>::Entity Element; + + typedef MPNCVolumeVariablesMass<TypeTag, enableKinetic> MassVolumeVariables; + typedef MPNCVolumeVariablesEnergy<TypeTag, enableEnergy, enableKineticEnergy> EnergyVolumeVariables; + typedef MPNCVolumeVariablesIA<TypeTag, enableKinetic, enableKineticEnergy> IAVolumeVariables; + typedef MPNCVolumeVariablesDiffusion<TypeTag, enableDiffusion> DiffusionVolumeVariables; + + typedef Dune::FieldVector<Scalar, dimWorld> GlobalPosition; + + +public: + //! The return type of the fluidState() method + typedef typename MassVolumeVariables::FluidState FluidState; + + MPNCVolumeVariables() + { hint_ = NULL; }; + + /*! + * \brief Set the volume variables which should be used as initial + * conditions for complex calculations. + */ + void setHint(const Implementation *hint) + { + hint_ = hint; + } + + /*! + * \brief Update all quantities for a given control volume. + */ + void update(const PrimaryVariables &priVars, + const Problem &problem, + const Element &element, + const FVElementGeometry &elemGeom, + int scvIdx, + bool isOldSol) + { + Valgrind::CheckDefined(priVars); + ParentType::update(priVars, + problem, + element, + elemGeom, + scvIdx, + isOldSol); + + typename FluidSystem::MutableParameters mutParams; + + ///////////// + // set the phase saturations + ///////////// + Scalar sumSat = 0; + for (int i = 0; i < numPhases - 1; ++i) { + sumSat += priVars[S0Idx + i]; + mutParams.setSaturation(i, priVars[S0Idx + i]); + } + Valgrind::CheckDefined(sumSat); + mutParams.setSaturation(numPhases - 1, 1.0 - sumSat); + + ///////////// + // set the fluid phase temperatures + ///////////// + // update the temperature part of the energy module + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx){ + Scalar T = EnergyVolumeVariables::getTemperature(priVars, + element, + elemGeom, + scvIdx, + problem, + phaseIdx); + mutParams.setTemperature(phaseIdx, T); + } + + ///////////// + // set the phase pressures + ///////////// + + // capillary pressure parameters + const MaterialLawParams &materialParams = + problem.spatialParameters().materialLawParams(element, elemGeom, scvIdx); + // capillary pressures + Scalar capPress[numPhases]; + MaterialLaw::capillaryPressures(capPress, materialParams, mutParams); + // add to the pressure of the first fluid phase + Scalar p0 = priVars[p0Idx]; + for (int i = 0; i < numPhases; ++ i) + mutParams.setPressure(i, p0 - capPress[0] + capPress[i]); + + ///////////// + // set the fluid compositions + ///////////// + MassVolumeVariables::update(mutParams, + priVars, + hint_, + problem, + element, + elemGeom, + scvIdx); + MassVolumeVariables::checkDefined(); + ParentType::checkDefined(); + + ///////////// + // Porosity + ///////////// + + // porosity + porosity_ = problem.spatialParameters().porosity(element, + elemGeom, + scvIdx); + Valgrind::CheckDefined(porosity_); + ParentType::checkDefined(); + + ///////////// + // Phase mobilities + ///////////// + + // relative permeabilities + MaterialLaw::relativePermeabilities(relativePermeability_, + materialParams, + mutParams); + + // dynamic viscosities + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + // viscosities + Scalar mu = FluidSystem::computeViscosity(mutParams, phaseIdx); + mutParams.setViscosity(phaseIdx, mu); + } + ParentType::checkDefined(); + + ///////////// + // diffusion + ///////////// + + // update the diffusion part of the volume data + DiffusionVolumeVariables::update(mutParams, *this, problem); + DiffusionVolumeVariables::checkDefined(); + ParentType::checkDefined(); + + ///////////// + // energy + ///////////// + + // update the remaining parts of the energy module + EnergyVolumeVariables::update(mutParams, + priVars, + element, + elemGeom, + scvIdx, + problem); + + EnergyVolumeVariables::checkDefined(); + ParentType::checkDefined(); + + // specific interfacial area, well also all the dimensionless numbers :-) + IAVolumeVariables::update(mutParams, + priVars, + problem, + element, + elemGeom, + scvIdx); + IAVolumeVariables::checkDefined(); + + // assign the fluid state to be stored + fluidState_.assign(mutParams); + fluidState_.checkDefined(); + ParentType::checkDefined(); + + checkDefined(); + + ParentType::checkDefined(); + } + + /*! + * \brief Return the fluid configuration at the given primary + * variables + */ + const FluidState &fluidState() const + { return fluidState_; } + + /*! + * \brief Returns the effective mobility of a given phase within + * the control volume. + */ + Scalar mobility(int phaseIdx) const + { return relativePermability(phaseIdx)/fluidState_.viscosity(phaseIdx); } + + /*! + * \brief Returns the viscosity of a given phase within + * the control volume. + */ + Scalar viscosity(int phaseIdx) const + { return fluidState_.viscosity(phaseIdx); } + + /*! + * \brief Returns the relative permeability of a given phase within + * the control volume. + */ + Scalar relativePermability(int phaseIdx) const + { return relativePermeability_[phaseIdx]; } + + /*! + * \brief Returns the average porosity within the control volume. + */ + Scalar porosity() const + { return porosity_; } + + /*! + * \brief Returns true iff the fluid state is in the active set + * for a phase, + */ + bool isPhaseActive(int phaseIdx) const + { + return + phasePresentIneq(fluidState(), phaseIdx) - + phaseNotPresentIneq(fluidState(), phaseIdx) + >= 0; + } + + /*! + * \brief Returns the value of the NCP-function for a phase. + */ + Scalar phaseNcp(int phaseIdx) const + { + Scalar aEval = phaseNotPresentIneq(this->evalPoint().fluidState(), phaseIdx); + Scalar bEval = phasePresentIneq(this->evalPoint().fluidState(), phaseIdx); + if (aEval > bEval) + return phasePresentIneq(fluidState(), phaseIdx); + return phaseNotPresentIneq(fluidState(), phaseIdx); + }; + + /*! + * \brief Returns the value of the inequality where a phase is + * present. + */ + Scalar phasePresentIneq(const FluidState &fluidState, int phaseIdx) const + { return fluidState.saturation(phaseIdx); } + + /*! + * \brief Returns the value of the inequality where a phase is not + * present. + */ + Scalar phaseNotPresentIneq(const FluidState &fluidState, int phaseIdx) const + { + // difference of sum of mole fractions in the phase from 100% + Scalar a = 1; + for (int i = 0; i < numComponents; ++i) + a -= fluidState.moleFrac(phaseIdx, i); + return a; + } + + /*! + * \brief If running in valgrind this makes sure that all + * quantities in the volume variables are defined. + */ + void checkDefined() const + { +#if !defined NDEBUG && HAVE_VALGRIND + ParentType::checkDefined(); + + Valgrind::CheckDefined(porosity_); + Valgrind::CheckDefined(hint_); + Valgrind::CheckDefined(relativePermeability_); + + fluidState_.checkDefined(); +#endif + } + +protected: + Scalar porosity_; //!< Effective porosity within the control volume + Scalar relativePermeability_[numPhases]; //!< Effective mobility within the control volume + + const Implementation *hint_; + + //! Mass fractions of each component within each phase + FluidState fluidState_; +}; + +} // end namepace + +#endif diff --git a/dumux/boxmodels/MpNc/MpNcvolumevariablesia.hh b/dumux/boxmodels/MpNc/MpNcvolumevariablesia.hh new file mode 100644 index 0000000000000000000000000000000000000000..05bb5dfdaba02ef3f7ebee7ba73d6faeea658ea1 --- /dev/null +++ b/dumux/boxmodels/MpNc/MpNcvolumevariablesia.hh @@ -0,0 +1,83 @@ +/***************************************************************************** + * Copyright (C) 2010-2011 by Philipp Nuske * + * Copyright (C) 2011 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 This class contains the volume variables required for the + * modules which require the specific interfacial area between + * fluid phases. + */ +#ifndef DUMUX_MPNC_VOLUME_VARIABLES_IA_HH +#define DUMUX_MPNC_VOLUME_VARIABLES_IA_HH + +namespace Dumux +{ + +/*! + * \brief This class contains the volume variables required for the + * modules which require the specific interfacial area between + * fluid phases. + * + * This is the specialization for the cases which do _not_ require + * specific interfacial area. + */ +template <class TypeTag, bool enableKinetic /* = false */, bool enableKineticEnergy /* = false */> +class MPNCVolumeVariablesIA +{ + static_assert(!enableKinetic && !enableKineticEnergy, + "The kinetic energy modules need specific interfacial area " + "but no suitable specialization of the IA volume variables module " + "has been included."); + + 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(FVElementGeometry)) FVElementGeometry; + 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(Scalar)) Scalar; + + typedef typename GridView::template Codim<0>::Entity Element; + +public: + /*! + * \brief Updates the volume specific interfacial area [m^2 / m^3] between the phases. + */ + template <class MutableParams> + void update(const MutableParams & mutParams, + const PrimaryVariables &priVars, + const Problem &problem, + const Element & element, + const FVElementGeometry & elemGeom, + const int scvIdx) + { + } + + /*! + * \brief If running in valgrind this makes sure that all + * quantities in the volume variables are defined. + */ + void checkDefined() const + { } +}; + +} // namespace Dumux + +#endif diff --git a/dumux/boxmodels/MpNc/MpNcvtkwriter.hh b/dumux/boxmodels/MpNc/MpNcvtkwriter.hh new file mode 100644 index 0000000000000000000000000000000000000000..088d57198e715de7f95e5e0ca97c55b29613e62e --- /dev/null +++ b/dumux/boxmodels/MpNc/MpNcvtkwriter.hh @@ -0,0 +1,140 @@ +/***************************************************************************** + * Copyright (C) 2011 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/>. * + *****************************************************************************/ +#ifndef DUMUX_MPNC_VTK_WRITER_HH +#define DUMUX_MPNC_VTK_WRITER_HH + +#include "MpNcproperties.hh" + +#include <dumux/io/vtkmultiwriter.hh> + +namespace Dumux +{ +/*! + * \ingroup BoxProblems + * \defgroup MPNCProblems Two-phase three-component box problems + */ + +/*! + * \ingroup BoxModels + * \defgroup MPNCModel Two-phase three-component box model + */ + +/*! + * \ingroup MPNCModel + * \brief Writes the VTK output files for a + * solution of the Mp-Nc model. + */ +template<class TypeTag> +class MPNCVtkWriter +{ + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MPNCVtkCommonModule)) MPNCVtkCommonModule; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MPNCVtkMassModule)) MPNCVtkMassModule; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MPNCVtkEnergyModule)) MPNCVtkEnergyModule; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MPNCVtkCustomModule)) MPNCVtkCustomModule; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(ElementVolumeVariables)) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(ElementBoundaryTypes)) ElementBoundaryTypes; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Problem)) Problem; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + enum { dim = GridView::dimension }; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + +public: + MPNCVtkWriter(const Problem &problem) + : problem_(problem) + , commonWriter_(problem) + , massWriter_(problem) + , energyWriter_(problem) + , customWriter_(problem) + { + } + + /*! + * \brief Add the current solution to the VtkMultiWriter. + */ + template <class MultiWriter> + void addCurrentSolution(MultiWriter &writer) + { + // tell sub-writers to allocate their buffers + commonWriter_.allocBuffers(writer); + massWriter_.allocBuffers(writer); + energyWriter_.allocBuffers(writer); + customWriter_.allocBuffers(writer); + + // iterate over grid + FVElementGeometry fvElemGeom; + ElementVolumeVariables elemVolVars; + ElementBoundaryTypes elemBcTypes; + + ElementIterator elemIt = problem_.gridView().template begin<0>(); + ElementIterator elemEndIt = problem_.gridView().template end<0>(); + for (; elemIt != elemEndIt; ++elemIt) + { + fvElemGeom.update(problem_.gridView(), *elemIt); + elemBcTypes.update(problem_, *elemIt, fvElemGeom); + this->problem_.model().setHints(*elemIt, elemVolVars); + elemVolVars.update(problem_, + *elemIt, + fvElemGeom, + false); + this->problem_.model().updateCurHints(*elemIt, elemVolVars); + + // tell the sub-writers to do what ever they need to with + // their internal buffers when a given element is seen. + commonWriter_.processElement(*elemIt, + fvElemGeom, + elemVolVars, + elemBcTypes); + massWriter_.processElement(*elemIt, + fvElemGeom, + elemVolVars, + elemBcTypes); + energyWriter_.processElement(*elemIt, + fvElemGeom, + elemVolVars, + elemBcTypes); + customWriter_.processElement(*elemIt, + fvElemGeom, + elemVolVars, + elemBcTypes); + } + + // write everything to the output file + commonWriter_.commitBuffers(writer); + massWriter_.commitBuffers(writer); + energyWriter_.commitBuffers(writer); + customWriter_.commitBuffers(writer); + } + +private: + const Problem &problem_; + + MPNCVtkCommonModule commonWriter_; + MPNCVtkMassModule massWriter_; + MPNCVtkEnergyModule energyWriter_; + MPNCVtkCustomModule customWriter_; +}; + +} + +#endif diff --git a/dumux/boxmodels/MpNc/MpNcvtkwritercommon.hh b/dumux/boxmodels/MpNc/MpNcvtkwritercommon.hh new file mode 100644 index 0000000000000000000000000000000000000000..63f91de4470f61a2b8a8a3a78b6d71560cbf5166 --- /dev/null +++ b/dumux/boxmodels/MpNc/MpNcvtkwritercommon.hh @@ -0,0 +1,290 @@ +/***************************************************************************** + * Copyright (C) 2011 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 VTK writer module for the common quantities of the MpNc + * model. + */ +#ifndef DUMUX_MPNC_VTK_WRITER_COMMON_HH +#define DUMUX_MPNC_VTK_WRITER_COMMON_HH + +#include "MpNcvtkwritermodule.hh" + + +namespace Dumux +{ +/*! + * \ingroup MPNCModel + * + * \brief VTK writer module for the common quantities of the MpNc + * model. + */ +template<class TypeTag> +class MPNCVtkWriterCommon : public MPNCVtkWriterModule<TypeTag> +{ + typedef MPNCVtkWriterModule<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Problem)) Problem; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + 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(VolumeVariables)) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluxVariables)) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(LocalResidual)) LocalResidual; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + + enum { dim = GridView::dimension }; + + typedef typename ParentType::ScalarBuffer ScalarBuffer; + typedef typename ParentType::PhaseBuffer PhaseBuffer; + typedef typename ParentType::ComponentBuffer ComponentBuffer; + typedef typename ParentType::PhaseComponentBuffer PhaseComponentBuffer; + + enum { numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)) }; + enum { numEq = GET_PROP_VALUE(TypeTag, PTAG(NumEq)) }; + + enum { velocityAveragingInProblem = GET_PROP_VALUE(TypeTag, PTAG(VelocityAveragingInProblem)) }; + + typedef Dune::FieldVector<Scalar, dim> VelocityVector; + typedef Dune::BlockVector<VelocityVector> VelocityField; + typedef VelocityField VectorField; + typedef std::array<VelocityField, numPhases> PhaseVelocityField; + +public: + MPNCVtkWriterCommon(const Problem &problem) + : ParentType(problem) + { + porosityOutput_ = GET_PARAM(TypeTag, bool, MPNC, VtkAddPorosity); + boundaryTypesOutput_ = GET_PARAM(TypeTag, bool, MPNC, VtkAddBoundaryTypes); + saturationOutput_ = GET_PARAM(TypeTag, bool, MPNC, VtkAddSaturations); + pressureOutput_ = GET_PARAM(TypeTag, bool, MPNC, VtkAddPressures); + velocityOutput_ = GET_PARAM(TypeTag, bool, MPNC, VtkAddVelocities); + densityOutput_ = GET_PARAM(TypeTag, bool, MPNC, VtkAddDensities); + mobilityOutput_ = GET_PARAM(TypeTag, bool, MPNC, VtkAddMobilities); + meanMolarMassOutput_ = GET_PARAM(TypeTag, bool, MPNC, VtkAddMeanMolarMass); + massFracOutput_ = GET_PARAM(TypeTag, bool, MPNC, VtkAddMassFractions); + moleFracOutput_ = GET_PARAM(TypeTag, bool, MPNC, VtkAddMoleFractions); + molarityOutput_ = GET_PARAM(TypeTag, bool, MPNC, VtkAddMolarities); + } + + /*! + * \brief Allocate memory for the scalar fields we would like to + * write to the VTK file. + */ + template <class MultiWriter> + void allocBuffers(MultiWriter &writer) + { + if (porosityOutput_) + this->resizeScalarBuffer_(porosity_); + if (boundaryTypesOutput_) + this->resizeScalarBuffer_(boundaryTypes_); + + if (velocityOutput_) { + Scalar nVerts = this->problem_.gridView().size(dim); + for (int phaseIdx = 0; phaseIdx < numPhases; ++ phaseIdx) { + velocity_[phaseIdx].resize(nVerts); + velocity_[phaseIdx] = 0; + } + this->resizeScalarBuffer_(boxSurface_); + } + + if (saturationOutput_) this->resizePhaseBuffer_(saturation_); + if (pressureOutput_) this->resizePhaseBuffer_(pressure_); + if (densityOutput_) this->resizePhaseBuffer_(density_); + if (mobilityOutput_) this->resizePhaseBuffer_(mobility_); + if (meanMolarMassOutput_) this->resizePhaseBuffer_(meanMolarMass_); + + if (moleFracOutput_) this->resizePhaseComponentBuffer_(moleFrac_); + if (massFracOutput_) this->resizePhaseComponentBuffer_(massFrac_); + if (molarityOutput_) this->resizePhaseComponentBuffer_(molarity_); + } + + /*! + * \brief Modify the internal buffers according to the volume + * variables seen on an element + */ + void processElement(const Element &elem, + const FVElementGeometry &fvElemGeom, + const ElementVolumeVariables &elemVolVars, + const ElementBoundaryTypes &elemBcTypes) + { + int n = elem.template count<dim>(); + for (int i = 0; i < n; ++i) { + int I = this->problem_.vertexMapper().map(elem, i, dim); + const VolumeVariables &volVars = elemVolVars[i]; + + if (porosityOutput_) porosity_[I] = volVars.porosity(); + + // calculate a single value for the boundary type: use one + // bit for each equation and set it to 1 if the equation + // is used for a dirichlet condition + int tmp = 0; + for (int j = 0; j < numEq; ++j) { + if (elemBcTypes[i].isDirichlet(j)) + tmp += (1 << j); + } + if (boundaryTypesOutput_) boundaryTypes_[I] = tmp; + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + if (saturationOutput_) saturation_[phaseIdx][I] = volVars.fluidState().saturation(phaseIdx); + if (pressureOutput_) pressure_[phaseIdx][I] = volVars.fluidState().pressure(phaseIdx); + if (densityOutput_) density_[phaseIdx][I] = volVars.fluidState().density(phaseIdx); + if (mobilityOutput_) mobility_[phaseIdx][I] = volVars.mobility(phaseIdx); + if (meanMolarMassOutput_) meanMolarMass_[phaseIdx][I] = volVars.fluidState().meanMolarMass(phaseIdx); + for (int compIdx = 0; compIdx < numComponents; ++compIdx) { + if (moleFracOutput_) moleFrac_[phaseIdx][compIdx][I] = volVars.fluidState().moleFrac(phaseIdx, compIdx); + if (massFracOutput_) massFrac_[phaseIdx][compIdx][I] = volVars.fluidState().massFrac(phaseIdx, compIdx); + if (molarityOutput_) molarity_[phaseIdx][compIdx][I] = volVars.fluidState().molarity(phaseIdx, compIdx); + } + } + } + + // calculate velocities if requested by the problem + if (velocityOutput_) { + for (int faceIdx = 0; faceIdx < fvElemGeom.numEdges; ++ faceIdx) { + int i = fvElemGeom.subContVolFace[faceIdx].i; + int I = this->problem_.vertexMapper().map(elem, i, dim); + + int j = fvElemGeom.subContVolFace[faceIdx].j; + int J = this->problem_.vertexMapper().map(elem, j, dim); + + FluxVariables fluxVars(this->problem_, + elem, + fvElemGeom, + faceIdx, + elemVolVars); + + Scalar scvfArea = fvElemGeom.subContVolFace[faceIdx].normal.two_norm(); + scvfArea *= fluxVars.extrusionFactor(); + + boxSurface_[I] += scvfArea; + boxSurface_[J] += scvfArea; + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + Dune::FieldVector<Scalar, dim> darcyVelocity; + fluxVars.computeDarcy(darcyVelocity, + elemVolVars, + phaseIdx); + darcyVelocity *= scvfArea; + velocity_[phaseIdx][I] += darcyVelocity; + velocity_[phaseIdx][J] += darcyVelocity; + + } // end for all phases + } // end for all faces + } // end if velocityOutput_ + } + + /*! + * \brief Add all buffers to the VTK output writer. + */ + template <class MultiWriter> + void commitBuffers(MultiWriter &writer) + { + if (saturationOutput_) + this->commitPhaseBuffer_(writer, "S_%s", saturation_); + + if (pressureOutput_) + this->commitPhaseBuffer_(writer, "p_%s", pressure_); + + if (densityOutput_) + this->commitPhaseBuffer_(writer, "rho_%s", density_); + + if (meanMolarMassOutput_) + this->commitPhaseBuffer_(writer, "M_%s", meanMolarMass_); + + if (mobilityOutput_) + this->commitPhaseBuffer_(writer, "lambda_%s", mobility_); + + if (porosityOutput_) + this->commitScalarBuffer_(writer, "porosity", porosity_); + + if (boundaryTypesOutput_) + this->commitScalarBuffer_(writer, "boundary types", boundaryTypes_); + + if (moleFracOutput_) + this->commitPhaseComponentBuffer_(writer, "x_%s^%s", moleFrac_); + + if (massFracOutput_) + this->commitPhaseComponentBuffer_(writer, "X_%s^%s", massFrac_); + + if(molarityOutput_) + this->commitPhaseComponentBuffer_(writer, "c_%s^%s", molarity_); + + if (velocityOutput_) { + int nVerts = this->problem_.gridView().size(dim); + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + // first, divide the velocity field by the + // respective finite volume's surface area + for (int i = 0; i < nVerts; ++i) + velocity_[phaseIdx][i] /= boxSurface_[i]; + // commit the phase velocity + std::string name; + const char *phaseName = FluidSystem::phaseName(phaseIdx); + name = (boost::format("velocity_%s")%phaseName).str(); + writer.attachVertexData(velocity_[phaseIdx], + name.c_str(), + dim); + } + } + } + +private: + bool porosityOutput_; + bool boundaryTypesOutput_; + bool saturationOutput_; + bool pressureOutput_; + bool velocityOutput_; + bool densityOutput_; + bool mobilityOutput_; + bool massFracOutput_; + bool moleFracOutput_; + bool molarityOutput_; + bool meanMolarMassOutput_; + + PhaseBuffer saturation_; + PhaseBuffer pressure_; + PhaseBuffer density_; + PhaseBuffer mobility_; + PhaseBuffer meanMolarMass_; + + PhaseVelocityField velocity_; + ScalarBuffer boxSurface_; + + ScalarBuffer porosity_; + ScalarBuffer temperature_; + ScalarBuffer boundaryTypes_; + + PhaseComponentBuffer moleFrac_; + PhaseComponentBuffer massFrac_; + PhaseComponentBuffer molarity_; + + ComponentBuffer fugacity_; +}; + +} + +#endif diff --git a/dumux/boxmodels/MpNc/MpNcvtkwritermodule.hh b/dumux/boxmodels/MpNc/MpNcvtkwritermodule.hh new file mode 100644 index 0000000000000000000000000000000000000000..4aed0cfcf329e58487014d991961e1be2cf7a1d9 --- /dev/null +++ b/dumux/boxmodels/MpNc/MpNcvtkwritermodule.hh @@ -0,0 +1,276 @@ +/***************************************************************************** + * Copyright (C) 2011 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/>. * + *****************************************************************************/ +#ifndef DUMUX_MPNC_VTK_BASE_WRITER_HH +#define DUMUX_MPNC_VTK_BASE_WRITER_HH + +#include "MpNcproperties.hh" + +#include <dumux/io/vtkmultiwriter.hh> +#include <dune/istl/bvector.hh> + +#include <array> + +namespace Dumux +{ +/*! + * \ingroup BoxProblems + * \defgroup MPNCProblems Two-phase three-component box problems + */ + +/*! + * \ingroup BoxModels + * \defgroup MPNCModel Two-phase three-component box model + */ + +/*! + * \ingroup MPNCModel + * + * \brief A VTK writer module which adheres to the required API but + * does nothing. + * + * This class also provides some convenience methods for buffer + * management. + */ +template<class TypeTag> +class MPNCVtkWriterModule +{ + enum { numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)) }; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Problem)) Problem; + 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(FVElementGeometry)) FVElementGeometry; + 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(FluidSystem)) FluidSystem; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MPNCIndices)) Indices; + + typedef typename GridView::template Codim<0>::Entity Element; + + enum { dim = GridView::dimension }; + +public: + typedef std::vector<Dune::FieldVector<Scalar, 1> > ScalarBuffer; + typedef std::array<ScalarBuffer, numPhases> PhaseBuffer; + typedef std::array<ScalarBuffer, numComponents> ComponentBuffer; + typedef std::array<ComponentBuffer, numPhases> PhaseComponentBuffer; + +// typedef Dune::FieldVector<Scalar, dim> VelocityVector; +// typedef Dune::BlockVector<VelocityVector> VelocityField; +// typedef std::array<VelocityField, numPhases> PhaseVelocityField; + + MPNCVtkWriterModule(const Problem &problem) + : problem_(problem) + { + } + + /*! + * \brief Allocate memory for the scalar fields we would like to + * write to the VTK file. + */ + template <class MultiWriter> + void allocBuffers(MultiWriter &writer) + { + } + + /*! + * \brief Modify the internal buffers according to the volume + * variables seen on an element + */ + void processElement(const Element &elem, + const FVElementGeometry &fvElemGeom, + const ElementVolumeVariables &elemCurVolVars, + const ElementBoundaryTypes &elemBcTypes) + { + } + + /*! + * \brief Add all buffers to the VTK output writer. + */ + template <class MultiWriter> + void commitBuffers(MultiWriter &writer) + { + } + +protected: + /*! + * \brief Allocate the space for a buffer storing a scalar quantity + */ + void resizeScalarBuffer_(ScalarBuffer &buffer, + bool vertexCentered = true) + { + Scalar n; + if (vertexCentered) + n = problem_.gridView().size(dim); + else + n = problem_.gridView().size(0); + + buffer.resize(n); + std::fill(buffer.begin(), buffer.end(), 0.0); + } + + /*! + * \brief Allocate the space for a buffer storing a phase-specific + * quantity + */ + void resizePhaseBuffer_(PhaseBuffer &buffer, + bool vertexCentered = true) + { + Scalar n; + if (vertexCentered) + n = problem_.gridView().size(dim); + else + n = problem_.gridView().size(0); + + for (int i = 0; i < numPhases; ++i) { + buffer[i].resize(n); + std::fill(buffer[i].begin(), buffer[i].end(), 0.0); + } + } + + /*! + * \brief Allocate the space for a buffer storing a component + * specific quantity + */ + void resizeComponentBuffer_(ComponentBuffer &buffer, + bool vertexCentered = true) + { + Scalar n; + if (vertexCentered) + n = problem_.gridView().size(dim); + else + n = problem_.gridView().size(0); + + for (int i = 0; i < numComponents; ++i) { + buffer[i].resize(n); + std::fill(buffer[i].begin(), buffer[i].end(), 0.0); + } + } + + /*! + * \brief Allocate the space for a buffer storing a phase and + * component specific buffer + */ + void resizePhaseComponentBuffer_(PhaseComponentBuffer &buffer, + bool vertexCentered = true) + { + Scalar n; + if (vertexCentered) + n = problem_.gridView().size(dim); + else + n = problem_.gridView().size(0); + + for (int i = 0; i < numPhases; ++i) { + for (int j = 0; j < numComponents; ++j) { + buffer[i][j].resize(n); + std::fill(buffer[i][j].begin(), buffer[i][j].end(), 0.0); + } + } + } + + /*! + * \brief Add a phase-specific buffer to the VTK result file. + */ + template <class MultiWriter> + void commitScalarBuffer_(MultiWriter &writer, + const char *name, + ScalarBuffer &buffer, + bool vertexCentered = true) + { + if (vertexCentered) + writer.attachVertexData(buffer, name, 1); + else + writer.attachCellData(buffer, name, 1); + } + + /*! + * \brief Add a phase-specific buffer to the VTK result file. + */ + template <class MultiWriter> + void commitPhaseBuffer_(MultiWriter &writer, + const char *pattern, + PhaseBuffer &buffer, + bool vertexCentered = true) + { + for (int i = 0; i < numPhases; ++i) { + std::string phaseName = FluidSystem::phaseName(i); + std::string name; + name = (boost::format(pattern)%phaseName).str(); + + if (vertexCentered) + writer.attachVertexData(buffer[i], name.c_str(), 1); + else + writer.attachCellData(buffer[i], name.c_str(), 1); + } + } + + /*! + * \brief Add a component-specific buffer to the VTK result file. + */ + template <class MultiWriter> + void commitComponentBuffer_(MultiWriter &writer, + const char *pattern, + ComponentBuffer &buffer, + bool vertexCentered = true) + { + for (int i = 0; i < numComponents; ++i) { + std::string compName = FluidSystem::componentName(i); + std::string name; + name = (boost::format(pattern)%compName).str(); + + if (vertexCentered) + writer.attachVertexData(buffer[i], name.c_str(), 1); + else + writer.attachCellData(buffer[i], name.c_str(), 1); + } + } + + /*! + * \brief Add a phase and component specific quantities to the output. + */ + template <class MultiWriter> + void commitPhaseComponentBuffer_(MultiWriter &writer, + const char *pattern, + PhaseComponentBuffer &buffer, + bool vertexCentered = true) + { + for (int i= 0; i < numPhases; ++i) { + std::string phaseName = FluidSystem::phaseName(i); + for (int j = 0; j < numComponents; ++j) { + std::string compName = FluidSystem::componentName(j); + std::string name; + name = (boost::format(pattern)%phaseName%compName).str(); + + if (vertexCentered) + writer.attachVertexData(buffer[i][j], name.c_str(), 1); + else + writer.attachCellData(buffer[i][j], name.c_str(), 1); + } + } + } + + const Problem &problem_; +}; + +} + +#endif diff --git a/dumux/boxmodels/MpNc/diffusion/diffusion.hh b/dumux/boxmodels/MpNc/diffusion/diffusion.hh new file mode 100644 index 0000000000000000000000000000000000000000..34eb2618715230db4dcb67339efcf2b2988ee88b --- /dev/null +++ b/dumux/boxmodels/MpNc/diffusion/diffusion.hh @@ -0,0 +1,152 @@ +/***************************************************************************** + * 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 This file contains parts to calculate the diffusive flux in + * the fully coupled two-phase N-component model + */ +#ifndef DUMUX_MPNC_DIFFUSION_HH +#define DUMUX_MPNC_DIFFUSION_HH + +namespace Dumux { + +template <class TypeTag, bool enableDiffusion> +class MPNCDiffusion +{ + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MPNCIndices)) Indices; + + enum { numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents))}; + enum { enableKinetic= GET_PROP_VALUE(TypeTag, PTAG(EnableKinetic))}; + enum { gPhaseIdx = FluidSystem::gPhaseIdx }; + enum { lPhaseIdx = FluidSystem::lPhaseIdx }; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluxVariables)) FluxVariables; + + typedef Dune::FieldMatrix<Scalar, numComponents, numComponents> DiffMatrix; + typedef Dune::FieldVector<Scalar, numComponents> DiffVector; + typedef Dune::FieldVector<Scalar, numComponents> CompVector; + +public: + static void flux(CompVector &fluxes, + int phaseIdx, + const FluxVariables &fluxDat, + Scalar molarDensity) + { + if (phaseIdx == gPhaseIdx) + gasFlux_(fluxes, fluxDat, molarDensity); + else if (phaseIdx == lPhaseIdx){ + #if MACROSCALE_DIFFUSION_ONLY_GAS + return ; // in the case that only the diffusion in the gas phase is considered, the liquidFlux should not be called + #endif + liquidFlux_(fluxes, fluxDat, molarDensity); + } + else + DUNE_THROW(Dune::InvalidStateException, + "Invalid phase index: " << phaseIdx); + } + +protected: + static void liquidFlux_(CompVector &fluxes, + const FluxVariables &fluxDat, + Scalar molarDensity) + { + for (int compIdx = 0; compIdx < numComponents; ++compIdx) { + // TODO: tensorial diffusion coefficients + Scalar xGrad = fluxDat.moleFracGrad(lPhaseIdx, compIdx)*fluxDat.face().normal; + fluxes[compIdx] = + - xGrad * + molarDensity * + fluxDat.face().normal.two_norm() * // because we want a mole flux and not an area specific flux + fluxDat.porousDiffCoeffL(compIdx) ; + } + } + + static void gasFlux_(CompVector &fluxes, + const FluxVariables &fluxDat, + Scalar molarDensity) + { + // Stefan-Maxwell equation + // + // See: R. Reid, et al.: "The Properties of Liquids and + // Gases", 4th edition, 1987, McGraw-Hill, p 596 + + // TODO: tensorial diffusion coefficients + DiffMatrix M(0); + + for (int i = 0; i < numComponents - 1; ++i) { + for (int j = 0; j < numComponents; ++j) { + Scalar Dij = fluxDat.porousDiffCoeffG(i, j); + if (Dij) { + M[i][j] += fluxDat.moleFrac(gPhaseIdx, i) / Dij; + M[i][i] -= fluxDat.moleFrac(gPhaseIdx, j) / Dij; + } + } + }; + + for (int i = 0; i < numComponents; ++i) { + M[numComponents - 1][i] = 1.0; + } + + DiffVector rightHandSide ; // see source cited above + for (int i = 0; i < numComponents - 1; ++i) { + rightHandSide[i] = molarDensity*(fluxDat.moleFracGrad(gPhaseIdx, i)*fluxDat.face().normal); + } + rightHandSide[numComponents - 1] = 0.0; + + M.solve(fluxes, rightHandSide); + } + + // return whether a concentration can be assumed to be a trace + // component in the context of diffusion + static Scalar isTraceComp_(Scalar x) + { return x < 0.5/numComponents; } +}; + +/*! + * \brief Specialization of the diffusion module for the case where + * diffusion is disabled. + * + * This class just does nothing. + */ +template <class TypeTag> +class MPNCDiffusion<TypeTag, false> +{ + enum { numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)) }; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluxVariables)) FluxVariables; + typedef Dune::FieldVector<Scalar, numComponents> CompVector; + +public: + static void flux(CompVector &fluxes, + int phaseIdx, + const FluxVariables &fluxVars, + Scalar totalConcentration) + { fluxes = 0; } +}; + +} + +#endif // DUMUX_MPNC_DIFFUSION_HH diff --git a/dumux/boxmodels/MpNc/diffusion/fluxvariables.hh b/dumux/boxmodels/MpNc/diffusion/fluxvariables.hh new file mode 100644 index 0000000000000000000000000000000000000000..acb0d48bc1df183a5fcca58434f50a65eb6c791e --- /dev/null +++ b/dumux/boxmodels/MpNc/diffusion/fluxvariables.hh @@ -0,0 +1,237 @@ +/***************************************************************************** + * 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 This file contains the diffusion module for the flux data of + * the fully coupled two-phase N-component model + */ +#ifndef DUMUX_MPNC_DIFFUSION_FLUX_VARIABLES_HH +#define DUMUX_MPNC_DIFFUSION_FLUX_VARIABLES_HH + +#include <dune/common/fvector.hh> + +#include "../MpNcproperties.hh" + +namespace Dumux { + +template<class TypeTag, bool enableDiffusion> +class MPNCFluxVariablesDiffusion +{ + 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(FluidSystem)) FluidSystem; + + 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(FluxVariables)) FluxVariables; + + typedef typename GridView::template Codim<0>::Entity Element; + + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld, + + numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)), + numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)), + + lPhaseIdx = FluidSystem::lPhaseIdx, + gPhaseIdx = FluidSystem::gPhaseIdx, + }; + + typedef Dune::FieldVector<Scalar, dimWorld> GlobalPosition; + typedef Dune::FieldVector<Scalar, dimWorld> Vector; + typedef Dune::FieldVector<Scalar, dim> LocalPosition; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + typedef typename FVElementGeometry::SubControlVolume SCV; + typedef typename FVElementGeometry::SubControlVolumeFace SCVFace; + + typedef Dune::FieldVector<Scalar, numPhases> PhasesVector; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MPNCIndices)) Indices; + +public: + MPNCFluxVariablesDiffusion() + {} + + void update(const Problem &problem, + const Element &element, + const FVElementGeometry &elemGeom, + int scvfIdx, + const ElementVolumeVariables &vDat) + { + int i = elemGeom.subContVolFace[scvfIdx].i; + int j = elemGeom.subContVolFace[scvfIdx].j; + + for (int phase = 0; phase < numPhases; ++phase) { + for (int comp = 0; comp < numComponents; ++comp) { + moleFrac_[phase][comp] = vDat[i].fluidState().moleFrac(phase, comp); + moleFrac_[phase][comp] += vDat[j].fluidState().moleFrac(phase, comp); + moleFrac_[phase][comp] /= 2; + } + } + + // update the concentration gradients using two-point + // gradients + const GlobalPosition &normal = elemGeom.subContVolFace[scvfIdx].normal; + + GlobalPosition tmp = element.geometry().corner(j); + tmp -= element.geometry().corner(i); + Scalar dist = tmp.two_norm()*normal.two_norm(); + for (int phaseIdx = 0; phaseIdx < numPhases; phaseIdx++) + { + // concentration gradients + for (int compIdx = 0; compIdx < numComponents; ++compIdx) { + moleFracGrad_[phaseIdx][compIdx] = normal; + moleFracGrad_[phaseIdx][compIdx] + *= + (vDat[j].fluidState().moleFrac(phaseIdx, compIdx) - + vDat[i].fluidState().moleFrac(phaseIdx, compIdx)) + / dist; + } + } + + // initialize the diffusion coefficients to zero + for (int i = 0; i < numComponents; ++i) { + porousDiffCoeffL_[i] = 0.0; + for (int j = 0; j < numComponents; ++j) + porousDiffCoeffG_[i][j] = 0.0; + } + + // calculate the diffusion coefficients at the integration + // point in the porous medium + 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].fluidState().saturation(phaseIdx) <= 1e-4 || + vDat[j].fluidState().saturation(phaseIdx) <= 1e-4) + { + continue; + } + + // reduction factor for the diffusion coefficients in the + // porous medium in nodes i and j. this is the tortuosity + // times porosity times phase saturation at the nodes i + // and j + // + // TODO (?): move this calculation to the soil (possibly + // that's a bad idea, though) + Scalar red_i = + vDat[i].fluidState().saturation(phaseIdx)/vDat[i].porosity() * + pow(vDat[i].porosity() * vDat[i].fluidState().saturation(phaseIdx), 7.0/3); + Scalar red_j = + vDat[j].fluidState().saturation(phaseIdx)/vDat[j].porosity() * + pow(vDat[j].porosity() * vDat[j].fluidState().saturation(phaseIdx), 7.0/3); + + if (phaseIdx == FluidSystem::lPhaseIdx) { + // Liquid phase diffusion coefficients in the porous medium + for (int i = 0; i < numComponents; ++i) { + // -> arithmetic mean + porousDiffCoeffL_[i] + = 1./2*(red_i * vDat[i].diffCoeff(lPhaseIdx, 0, i) + + red_j * vDat[j].diffCoeff(lPhaseIdx, 0, i)); + } + } + else { + // Gas phase diffusion coefficients in the porous medium + for (int i = 0; i < numComponents; ++i) { + for (int j = 0; j < numComponents; ++j) { + // -> arithmetic mean + porousDiffCoeffG_[i][j] + = 1./2*(red_i * vDat[i].diffCoeff(gPhaseIdx, i, j) + + red_j * vDat[j].diffCoeff(gPhaseIdx, i, j)); + } + } + } + } + }; + + Scalar porousDiffCoeffL(int compIdx) const + { + // TODO: tensorial diffusion coefficients + return porousDiffCoeffL_[compIdx]; + }; + + Scalar porousDiffCoeffG(int compIIdx, int compJIdx) const + { + // TODO: tensorial diffusion coefficients + return porousDiffCoeffG_[compIIdx][compJIdx]; + }; + + Scalar moleFrac(int phaseIdx, + int compIdx) const + { + return moleFrac_[phaseIdx][compIdx]; + }; + + const GlobalPosition &moleFracGrad(int phaseIdx, + int compIdx) const + { + return moleFracGrad_[phaseIdx][compIdx]; + }; + +protected: + // the diffusion coefficients for the porous medium for the + // liquid phase + Scalar porousDiffCoeffL_[numComponents]; + + // the diffusion coefficients for the porous medium for the + // gas phase + Scalar porousDiffCoeffG_[numComponents][numComponents]; + + // the concentration gradients of all components in all phases + GlobalPosition moleFracGrad_[numPhases][numComponents]; + + // the mole fractions of each component at the integration point + Scalar moleFrac_[numPhases][numComponents]; +}; + + +template<class TypeTag> +class MPNCFluxVariablesDiffusion<TypeTag, false> +{ + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Problem)) Problem; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + 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(FluxVariables)) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + +public: + MPNCFluxVariablesDiffusion() + {} + + void update(const Problem &problem, + const Element &element, + const FVElementGeometry &elemGeom, + int scvfIdx, + const ElementVolumeVariables &vDat) + { + }; +}; + +}; + +#endif // DUMUX_MPNC_DIFFUSION_FLUX_VARIABLES_HH diff --git a/dumux/boxmodels/MpNc/diffusion/volumevariables.hh b/dumux/boxmodels/MpNc/diffusion/volumevariables.hh new file mode 100644 index 0000000000000000000000000000000000000000..2056a66a345c93f8c50019380cf1132581628a9e --- /dev/null +++ b/dumux/boxmodels/MpNc/diffusion/volumevariables.hh @@ -0,0 +1,161 @@ +/***************************************************************************** + * 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 This file contains the diffusion module for the vertex data + * of the fully coupled two-phase N-component model + */ +#ifndef DUMUX_MPNC_DIFFUSION_VOLUME_VARIABLES_HH +#define DUMUX_MPNC_DIFFUSION_VOLUME_VARIABLES_HH + +namespace Dumux { + +template<class TypeTag, bool enableDiffusion> +class MPNCVolumeVariablesDiffusion +{ + 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(FluidSystem)) FluidSystem; + 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(PrimaryVariables)) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MPNCIndices)) Indices; + + typedef typename FluidSystem::MutableParameters MutableParameters; + + enum { numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)) }; + enum { lPhaseIdx = FluidSystem::lPhaseIdx }; + enum { gPhaseIdx = FluidSystem::gPhaseIdx }; + +public: + MPNCVolumeVariablesDiffusion() + {} + + void update(MutableParameters &mutParams, + const VolumeVariables &volVars, + const Problem &problem) + { + Valgrind::SetUndefined(*this); + + // diffusion coefficents in liquid + diffCoeffL_[0] = 0.0; + for (int compIdx = 1; compIdx < numComponents; ++compIdx) { + diffCoeffL_[compIdx] = + FluidSystem::binaryDiffCoeff(mutParams, + lPhaseIdx, + 0, + compIdx); + } + Valgrind::CheckDefined(diffCoeffL_); + + // diffusion coefficents in gas + for (int compIIdx = 0; compIIdx < numComponents; ++compIIdx) { + diffCoeffG_[compIIdx][compIIdx] = 0; + for (int compJIdx = compIIdx + 1; compJIdx < numComponents; ++compJIdx) { + diffCoeffG_[compIIdx][compJIdx] = + FluidSystem::binaryDiffCoeff(mutParams, + gPhaseIdx, + compIIdx, + compJIdx); + + // fill the symmetric part of the diffusion coefficent + // matrix + diffCoeffG_[compJIdx][compIIdx] = diffCoeffG_[compIIdx][compJIdx]; + } + } + Valgrind::CheckDefined(diffCoeffG_); + }; + + + Scalar diffCoeff(int phaseIdx, int compIIdx, int compJIdx) const + { + if (phaseIdx == gPhaseIdx) + // TODO: tensorial diffusion coefficients + return diffCoeffG_[compIIdx][compJIdx]; + + int i = std::min(compIIdx, compJIdx); + int j = std::max(compIIdx, compJIdx); + if (i != 0) + return 0; + return diffCoeffL_[j]; + }; + + /*! + * \brief If running under valgrind this produces an error message + * if some of the object's attributes is undefined. + */ + void checkDefined() const + { + Valgrind::CheckDefined(diffCoeffL_); + Valgrind::CheckDefined(diffCoeffG_); + } + + +protected: + // the diffusion coefficients for the porous medium for the + // liquid phase + Scalar diffCoeffL_[numComponents]; + + // the diffusion coefficients for the porous medium for the + // gas phase + Scalar diffCoeffG_[numComponents][numComponents]; +}; + +// dummy class for the case where diffusion is disabled +template<class TypeTag> +class MPNCVolumeVariablesDiffusion<TypeTag, false> +{ + 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(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + typedef typename FluidSystem::MutableParameters MutableParameters; + +public: + MPNCVolumeVariablesDiffusion() + {} + + void update(MutableParameters &mutParams, + const VolumeVariables &volVars, + const Problem &problem) + { + }; + + Scalar diffCoeffL(int compIdx) const + { return 0; }; + + Scalar diffCoeffG(int compIIdx, int compJIdx) const + { return 0; }; + + /*! + * \brief If running under valgrind this produces an error message + * if some of the object's attributes is undefined. + */ + void checkDefined() const + { } +}; + +}; + +#endif // DUMUX_MPNC_DIFFUSION_VOLUME_VARIABLES_HH diff --git a/dumux/boxmodels/MpNc/energy/MpNcfluxvariablesenergy.hh b/dumux/boxmodels/MpNc/energy/MpNcfluxvariablesenergy.hh new file mode 100644 index 0000000000000000000000000000000000000000..d4589e3b4c0899605f3717ccb5a793948a9befa0 --- /dev/null +++ b/dumux/boxmodels/MpNc/energy/MpNcfluxvariablesenergy.hh @@ -0,0 +1,194 @@ +/***************************************************************************** + * Copyright (C) 2008,2009 by Andreas Lauser * + * Copyright (C) 2008,2009 by Melanie Darcis * + * * + * 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 to calculate the energy flux in the + * MpNc box model. + */ +#ifndef DUMUX_MPNC_ENERGY_FLUX_VARIABLES_HH +#define DUMUX_MPNC_ENERGY_FLUX_VARIABLES_HH + +namespace Dumux +{ + +template <class TypeTag, bool enableEnergy/*=false*/, bool kineticEnergyTransfer/*=false*/> +class MPNCFluxVariablesEnergy +{ + static_assert(!(kineticEnergyTransfer && !enableEnergy), + "No kinetic energy transfer may only be enabled " + "if energy is enabled in general."); + static_assert(!kineticEnergyTransfer, + "No kinetic energy transfer module included, " + "but kinetic energy transfer enabled."); + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Problem)) Problem; + + 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(VolumeVariables)) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(ElementVolumeVariables)) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluxVariables)) FluxVariables; + +public: + MPNCFluxVariablesEnergy() + { + } + + void update(const Problem &problem, + const Element &element, + const FVElementGeometry &fvElemGeom, + int scvfIdx, + const FluxVariables &fluxDat, + const ElementVolumeVariables &elemVolVars) + {}; +}; + +template <class TypeTag> +class MPNCFluxVariablesEnergy<TypeTag, /*enableEnergy=*/true, /*kineticEnergyTransfer=*/false> +{ + 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(FluxVariables)) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + + typedef typename GridView::ctype CoordScalar; + typedef typename GridView::template Codim<0>::Entity Element; + + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld, + gPhaseIdx = FluidSystem::gPhaseIdx, + lPhaseIdx = FluidSystem::lPhaseIdx, + numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)), + }; + + typedef Dune::FieldVector<CoordScalar, dimWorld> Vector; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + typedef typename FVElementGeometry::SubControlVolume SCV; + typedef typename FVElementGeometry::SubControlVolumeFace SCVFace; + +public: + MPNCFluxVariablesEnergy() + {} + + void update(const Problem & problem, + const Element &element, + const FVElementGeometry &fvElemGeom, + int scvfIdx, + const FluxVariables &fluxDat, + const ElementVolumeVariables &elemVolVars) + { + // calculate temperature gradient using finite element + // gradients + Vector tmp(0.0); + Vector temperatureGradient(0.); + for (int scvIdx = 0; scvIdx < fvElemGeom.numVertices; scvIdx++) + { + tmp = fvElemGeom.subContVolFace[scvfIdx].grad[scvIdx]; + tmp *= elemVolVars[scvIdx].temperature(); + temperatureGradient += tmp; + } + + // project the heat flux vector on the face's normal vector + temperatureGradientNormal_ = temperatureGradient * fvElemGeom.subContVolFace[scvfIdx].normal; + + + lambdaPm_ = lumpedLambdaPm(problem, + fvElemGeom, + scvfIdx, + elemVolVars) ; + + } + + Scalar lumpedLambdaPm(const Problem & problem, + const FVElementGeometry & fvElemGeom, + int faceIdx, + const ElementVolumeVariables & elemVolVars) + { + // arithmetic mean of the liquid saturation and the porosity + const int i = fvElemGeom.subContVolFace[faceIdx].i; + const int j = fvElemGeom.subContVolFace[faceIdx].j; + + const Scalar Sli = elemVolVars[i].fluidState().saturation(lPhaseIdx); + const Scalar Slj = elemVolVars[j].fluidState().saturation(lPhaseIdx); + + const Scalar Sl = std::max<Scalar>(0.0, 0.5*(Sli + Slj)); + + // const Scalar lambdaDry = 0.583; // W / (K m) // works, orig + // const Scalar lambdaWet = 1.13; // W / (K m) // works, orig + + const typename FluidSystem::MutableParameters mutParams; //dummy + const Scalar lambdaDry = 0.5 * (problem.spatialParameters().soilThermalConductivity() + FluidSystem::thermalConductivity(mutParams, gPhaseIdx) ); // W / (K m) + const Scalar lambdaWet = 0.5 * (problem.spatialParameters().soilThermalConductivity() + FluidSystem::thermalConductivity(mutParams, lPhaseIdx)) ; // W / (K m) + + // the heat conductivity of the matrix. in general this is a + // tensorial value, but we assume isotropic heat conductivity. + // This is the Sommerton approach with lambdaDry = + // lambdaSn100%. Taken from: H. Class: "Theorie und + // numerische Modellierung nichtisothermer Mehrphasenprozesse + // in NAPL-kontaminierten poroesen Medien", PhD Thesis, University of + // Stuttgart, Institute of Hydraulic Engineering, p. 57 + + Scalar result; + if (Sl < 0.1) { + // regularization + Dumux::Spline<Scalar> sp(0, 0.1, // x1, x2 + 0, sqrt(0.1), // y1, y2 + 5*0.5/sqrt(0.1), 0.5/sqrt(0.1)); // m1, m2 + result = lambdaDry + sp.eval(Sl)*(lambdaWet - lambdaDry); + } + else + result = lambdaDry + std::sqrt(Sl)*(lambdaWet - lambdaDry); + + return result; + } + + /*! + * \brief The lumped / average conductivity of solid plus phases \f$[W/mK]\f$. + */ + Scalar lambdaPm() const + { return lambdaPm_; } + + /*! + * \brief The normal of the gradient of temperature . + */ + Scalar temperatureGradientNormal() const + { + return temperatureGradientNormal_; + } + +private: + Scalar lambdaPm_; + Scalar temperatureGradientNormal_; +}; + +} // end namepace + +#endif diff --git a/dumux/boxmodels/MpNc/energy/MpNcindicesenergy.hh b/dumux/boxmodels/MpNc/energy/MpNcindicesenergy.hh new file mode 100644 index 0000000000000000000000000000000000000000..b9b8f9c46e54d6f7a65ad561dda4a6b6a149fe23 --- /dev/null +++ b/dumux/boxmodels/MpNc/energy/MpNcindicesenergy.hh @@ -0,0 +1,76 @@ +/***************************************************************************** + * Copyright (C) 2009-2011 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/>. * + *****************************************************************************/ +/*! + * \brief The indices for the non-isothermal part of the compositional + * multi-phase model. + */ +#ifndef DUMUX_MPNC_INDICES_ENERGY_HH +#define DUMUX_MPNC_INDICES_ENERGY_HH + +namespace Dumux +{ +/*! + * \brief The indices for the energy equation. + * + * This is a dummy class for the isothermal case. + */ +template <int PVOffset, bool enableEnergy/*=false*/, bool kineticEnergyTransfer/*=false*/> +struct MPNCEnergyIndices +{ + static_assert(!(kineticEnergyTransfer && !enableEnergy), + "No kinetic energy transfer may only be enabled " + "if energy is enabled in general."); + static_assert(!kineticEnergyTransfer, + "No kinetic energy transfer module included, " + "but kinetic energy transfer enabled."); +public: + /*! + * \brief This module does not define any primary variables in the + * isothermal case. + */ + static const int NumPrimaryVars = 0; +}; + +/*! + * \brief The indices required for the energy equation. + */ +template <int PVOffset> +struct MPNCEnergyIndices<PVOffset, /*isNonIsothermal=*/true, /*kineticEnergyTransfer*/false> +{ +public: + /*! + * \brief This module defines one new primary variable. + */ + static const int NumPrimaryVars = 1; + + /*! + * \brief Index for the temperature in a vector of primary + * variables. + */ + static const int temperatureIdx = PVOffset + 0; + /*! + * \brief Equation index of the energy equation. + */ + static const int energyEqIdx = PVOffset + 0; +}; + +} + +#endif diff --git a/dumux/boxmodels/MpNc/energy/MpNclocalresidualenergy.hh b/dumux/boxmodels/MpNc/energy/MpNclocalresidualenergy.hh new file mode 100644 index 0000000000000000000000000000000000000000..d5cbc78bfa5addcf5f637b19d48f81fe5e6a6674 --- /dev/null +++ b/dumux/boxmodels/MpNc/energy/MpNclocalresidualenergy.hh @@ -0,0 +1,233 @@ +/***************************************************************************** + * Copyright (C) 2009-2011 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 This file contains the parts of the local residual to + * calculate the heat flux in the fully coupled two-phase + * N-component model + */ +#ifndef DUMUX_MPNC_LOCAL_RESIDUAL_ENERGY_HH +#define DUMUX_MPNC_LOCAL_RESIDUAL_ENERGY_HH + +namespace Dumux { + +/*! + * \brief Specialization of the energy module for the isothermal case. + * + * This class just does nothing. + */ +template <class TypeTag, bool enableEnergy/*=false*/, bool kineticEnergyTransfer /*=false*/> +class MPNCLocalResidualEnergy +{ + static_assert(!(kineticEnergyTransfer && !enableEnergy), + "No kinetic energy transfer may only be enabled " + "if energy is enabled in general."); + static_assert(!kineticEnergyTransfer, + "No kinetic energy transfer module included, " + "but kinetic energy transfer enabled."); + + 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(FluxVariables)) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(ElementVolumeVariables)) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + + enum { numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)) }; + typedef typename Dune::FieldVector<Scalar, numComponents> ComponentVector; + +public: + static void computeStorage(PrimaryVariables &result, + const VolumeVariables &volVars) + { + // do nothing, we're isothermal! + }; + + + static void addPhaseStorage(PrimaryVariables &storage, + const VolumeVariables &volVars, + int phaseIdx) + { + // do nothing, we're isothermal! + }; + + static void phaseEnthalpyFlux(PrimaryVariables &result, + int phaseIdx, + const PrimaryVariables &compMolFlux, + const ElementVolumeVariables &volVars, + const FluxVariables &fluxVars) + { + // do nothing, we're isothermal! + }; + + static void heatConduction(PrimaryVariables &result, + const ElementVolumeVariables &volVars, + const FluxVariables &fluxVars) + { + // do nothing, we're isothermal! + }; + + + static void computeFlux(PrimaryVariables & flux, + const FluxVariables & fluxVars, + const ElementVolumeVariables & volVars, + const ComponentVector molarPhaseComponentValuesMassTransport[numPhases]) + { + // do nothing, we're isothermal! + } + + static void computeSource(PrimaryVariables &result, + const VolumeVariables &volVars) + { + // do nothing, we're isothermal! + } +}; + +template <class TypeTag> +class MPNCLocalResidualEnergy<TypeTag, /*enableEnergy=*/true, /*kineticenergyTransfer=*/false> +{ + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + 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(PrimaryVariables)) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluxVariables)) FluxVariables; + 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(MPNCIndices)) Indices; + + enum { dim = GridView::dimension }; + enum { numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)) }; + enum { conti0EqIdx = Indices::conti0EqIdx }; + enum { energyEqIdx = Indices::energyEqIdx }; + + typedef Dune::FieldVector<Scalar, dim> Vector; + typedef typename Dune::FieldVector<Scalar, numComponents> ComponentVector; + typedef typename Dune::FieldMatrix<Scalar, numPhases, numComponents> PhaseComponentMatrix; + + + +public: + static void computeStorage(PrimaryVariables &result, + const VolumeVariables &volVars) + { + result[energyEqIdx] = 0; + + // energy of the fluids + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + addPhaseStorage(result, volVars, phaseIdx); + } + + // heat stored in the rock matrix + result[energyEqIdx] += + volVars.temperature() + * volVars.soilDensity() + * (1.0 - volVars.porosity()) + * volVars.heatCapacity(); + } + + static void addPhaseStorage(PrimaryVariables &storage, + const VolumeVariables &volVars, + int phaseIdx) + { + const typename VolumeVariables::FluidState &fs = + volVars.fluidState(); + + // energy of the fluid + storage[energyEqIdx] += + fs.density(phaseIdx) + * fs.internalEnergy(phaseIdx) + * fs.saturation(phaseIdx) + * volVars.porosity(); + } + + static void computeFlux(PrimaryVariables & flux, + const FluxVariables & fluxVars, + const ElementVolumeVariables & volVars, + const ComponentVector molarPhaseComponentValuesMassTransport[numPhases]) + { + flux[energyEqIdx] = 0.0; + + // fluid phases transport enthalpy individually + for(int phaseIdx=0; phaseIdx<numPhases; ++phaseIdx) + computePhaseEnthalpyFlux(flux, + fluxVars, + volVars, + phaseIdx, + molarPhaseComponentValuesMassTransport[phaseIdx]); + + //conduction is treated lumped in this model + computeHeatConduction(flux, + fluxVars, + volVars); + } + + static void computePhaseEnthalpyFlux(PrimaryVariables & result, + const FluxVariables & fluxVars, + const ElementVolumeVariables & volVars, + const int phaseIdx, + const ComponentVector & molarComponentValuesMassTransport) + { + Scalar massFlux = 0; + + // calculate the mass flux in the phase i.e. make mass flux out of mole flux and add up the fluxes of a phase + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + massFlux += molarComponentValuesMassTransport[compIdx] + * FluidSystem::molarMass(compIdx); + + int upIdx = fluxVars.face().i; + if (massFlux < 0) upIdx = fluxVars.face().j; + + // use the phase enthalpy of the upstream vertex to calculate + // the enthalpy transport + const VolumeVariables &up = volVars[upIdx]; + result[energyEqIdx] += up.fluidState().enthalpy(phaseIdx) * massFlux; + } + + static void computeHeatConduction(PrimaryVariables & result, + const FluxVariables & fluxVars, + const ElementVolumeVariables & volVars) + { + //lumped heat conduction of the rock matrix and the fluid phases + Scalar lumpedConductivity = fluxVars.energyData().lambdaPm() ; + Scalar temperatureGradientNormal = fluxVars.energyData().temperatureGradientNormal() ; + Scalar lumpedHeatConduction = - lumpedConductivity * temperatureGradientNormal ; + result[energyEqIdx] += lumpedHeatConduction ; + } + + + + static void computeSource(PrimaryVariables &result, + const VolumeVariables &volVars) + { + result[energyEqIdx] = 0.0; + } +}; + + + + + +}; + +#endif // DUMUX_MPNC_ENERGY_HH diff --git a/dumux/boxmodels/MpNc/energy/MpNcvolumevariablesenergy.hh b/dumux/boxmodels/MpNc/energy/MpNcvolumevariablesenergy.hh new file mode 100644 index 0000000000000000000000000000000000000000..8d0bba1ecf544607e5f4a92a826761a8c8038614 --- /dev/null +++ b/dumux/boxmodels/MpNc/energy/MpNcvolumevariablesenergy.hh @@ -0,0 +1,232 @@ +/***************************************************************************** + * Copyright (C) 2008-2011 by Andreas Lauser * + * Copyright (C) 2008-2009 by Melanie Darcis * + * 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 energy part of volume variables of the M-phase, + * N-component model. + */ +#ifndef DUMUX_MPNC_ENERGY_VOLUME_VARIABLES_HH +#define DUMUX_MPNC_ENERGY_VOLUME_VARIABLES_HH + +namespace Dumux +{ +/*! + * \brief Contains the energy related quantities which are constant within a + * finite volume in the two-phase, N-component model. + * + * This is the dummy class for the isothermal case. Note that we're + * only isothermal in the sense that the temperature at a location and + * a time is specified outside of the model! + */ +template <class TypeTag, bool enableEnergy/*=false*/, bool kineticEnergyTransfer /*=don't care*/> +class MPNCVolumeVariablesEnergy +{ + static_assert(!(kineticEnergyTransfer && !enableEnergy), + "No kinetic energy transfer may only be enabled " + "if energy is enabled in general."); + static_assert(!kineticEnergyTransfer, + "No kinetic energy transfer module included, " + "but kinetic energy transfer enabled."); + + 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(VolumeVariables)) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(PrimaryVariables)) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + enum { numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)) }; + //typedef typename GET_PROP_TYPE(TypeTag, PTAG(MPNCEnergyIndices)) EnergyIndices; + + +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 int temperatureIdx) const + { + return problem.boxTemperature(element, elemGeom, scvIdx); + } + + + /*! + * \brief Update the enthalpy and the internal energy for a given + * control volume. + * + * Since we are isothermal, we don't need to do anything! + */ + template <class MutableParams> + void update(MutableParams &mutParams, + const PrimaryVariables &sol, + const Element &element, + const FVElementGeometry &elemGeom, + int scvIdx, + const Problem &problem) + { + temperature_ = problem.boxTemperature(element, elemGeom, scvIdx); + + // set the fluid temperatures + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + mutParams.setTemperature(phaseIdx, temperature_); + } + + /*! + * \brief Return the temperature of any given phase [K] + */ + Scalar temperature(int phaseIdx = 0) const + { return temperature_; } + + /*! + * \brief If running under valgrind this produces an error message + * if some of the object's attributes is undefined. + */ + void checkDefined() const + { + Valgrind::CheckDefined(temperature_); + } + +protected: + Scalar temperature_; + +}; + +/*! + * \brief Contains the energy related quantities which are constant within a + * finite volume in the two-phase, N-component model. + */ +template <class TypeTag> +class MPNCVolumeVariablesEnergy<TypeTag, /*enableEnergy=*/true, /*kineticEnergyTransfer=*/false> +{ + 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(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(PrimaryVariables)) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MPNCIndices)) Indices; + + enum { numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)) }; + enum { temperatureIdx = Indices::temperatureIdx }; + enum { numEnergyEqs = Indices::NumPrimaryEnergyVars}; + enum { temperature0Idx = Indices::temperatureIdx }; + +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 int dummy) const + { + // retrieve temperature from solution vector + return sol[temperatureIdx]; + } + + /*! + * \brief Update the enthalpy and the internal energy for a given + * control volume. + */ + template <class MutableParams> + void update(MutableParams &mutParams, + const PrimaryVariables &sol, + const Element &element, + const FVElementGeometry &elemGeom, + int scvIdx, + const Problem &problem) + { + Valgrind::SetUndefined(*this); + + // set the fluid temperatures + temperature_ = sol[temperature0Idx]; + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + mutParams.setTemperature(phaseIdx, temperature_); + + heatCapacity_ = + problem.spatialParameters().heatCapacity(element, elemGeom, scvIdx); + Valgrind::CheckDefined(heatCapacity_); + + soilDensity_ = + problem.spatialParameters().soilDensity(element, elemGeom, scvIdx); + Valgrind::CheckDefined(soilDensity_); + + // set 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 [J/(K m^3)] of the rock matrix in + * the sub-control volume. + */ + Scalar heatCapacity() const + { return heatCapacity_; }; + + /*! + * \brief Returns the temperature in fluid / solid phase(s) + * the sub-control volume. + */ + Scalar temperature(int phaseIdx = 0) const + { return temperature_; } + + /*! + * \brief Returns the total density of the given soil [kg / m^3] in + * the sub-control volume. + */ + Scalar soilDensity() const + { return soilDensity_; }; + + /*! + * \brief If running under valgrind this produces an error message + * if some of the object's attributes is undefined. + */ + void checkDefined() const + { + Valgrind::CheckDefined(heatCapacity_); + Valgrind::CheckDefined(soilDensity_); + Valgrind::CheckDefined(temperature_); + }; + +protected: + Scalar heatCapacity_; + Scalar soilDensity_; + Scalar temperature_; +}; + +} // end namepace + +#endif diff --git a/dumux/boxmodels/MpNc/energy/MpNcvtkwriterenergy.hh b/dumux/boxmodels/MpNc/energy/MpNcvtkwriterenergy.hh new file mode 100644 index 0000000000000000000000000000000000000000..c09ba8b3800cb0dc036b3b1afe23e605b68d3017 --- /dev/null +++ b/dumux/boxmodels/MpNc/energy/MpNcvtkwriterenergy.hh @@ -0,0 +1,232 @@ +/***************************************************************************** + * Copyright (C) 2011 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 VTK writer module for the energy related quantities of the + * MpNc model. + */ +#ifndef DUMUX_MPNC_VTK_WRITER_ENERGY_HH +#define DUMUX_MPNC_VTK_WRITER_ENERGY_HH + +#include "../MpNcvtkwritermodule.hh" + +namespace Dumux +{ +/*! + * \ingroup MPNCModel + * + * \brief VTK writer module for the energy related quantities of the + * MpNc model. + * + * This is the specialization for the case without energy. + */ +template<class TypeTag, + bool enableEnergy /* = false */, + bool enableKineticEnergy /* = false */> +class MPNCVtkWriterEnergy : public MPNCVtkWriterModule<TypeTag> +{ + static_assert(enableKineticEnergy == false, + "If you enable kinetic energy transfer between fluids, you" + "also have to enable the energy in general!"); + + typedef MPNCVtkWriterModule<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Problem)) Problem; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + 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(VolumeVariables)) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluxVariables)) FluxVariables; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + + enum { dim = GridView::dimension }; + enum { numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)) }; + + typedef typename ParentType::ScalarBuffer ScalarBuffer; + typedef typename ParentType::PhaseBuffer PhaseBuffer; + typedef typename ParentType::ComponentBuffer ComponentBuffer; + typedef typename ParentType::PhaseComponentBuffer PhaseComponentBuffer; + +public: + MPNCVtkWriterEnergy(const Problem &problem) + : ParentType(problem) + { + temperatureOutput_ = GET_PARAM(TypeTag, bool, MPNC, VtkAddTemperatures); + } + + /*! + * \brief Allocate memory for the scalar fields we would like to + * write to the VTK file. + */ + template <class MultiWriter> + void allocBuffers(MultiWriter &writer) + { + if (temperatureOutput_) this->resizeScalarBuffer_(temperature_); + } + + /*! + * \brief Modify the internal buffers according to the volume + * variables seen on an element + */ + void processElement(const Element &elem, + const FVElementGeometry &fvElemGeom, + const ElementVolumeVariables &elemVolVars, + const ElementBoundaryTypes &elemBcTypes) + { + int n = elem.template count<dim>(); + for (int i = 0; i < n; ++i) { + int I = this->problem_.vertexMapper().map(elem, i, dim); + const VolumeVariables &volVars = elemVolVars[i]; + + if (temperatureOutput_) + temperature_[I] = volVars.fluidState().temperature(0); + } + } + + /*! + * \brief Add all buffers to the VTK output writer. + */ + template <class MultiWriter> + void commitBuffers(MultiWriter &writer) + { + if (temperatureOutput_) + this->commitScalarBuffer_(writer, "T", temperature_); + } + +private: + bool temperatureOutput_; + + ScalarBuffer temperature_; +}; + +/*! + * \ingroup MPNCModel + * + * \brief VTK writer module for the energy related quantities of the + * MpNc model. + * + * This is the specialization for the case with an energy equation but + * local thermal equilibrium. (i.e. no kinetic energy transfer) + */ +template<class TypeTag> +class MPNCVtkWriterEnergy<TypeTag, /* enableEnergy = */ true, /* enableKineticEnergy = */ false > + : public MPNCVtkWriterModule<TypeTag> +{ + typedef MPNCVtkWriterModule<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Problem)) Problem; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + 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(VolumeVariables)) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluxVariables)) FluxVariables; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + + enum { dim = GridView::dimension }; + enum { numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)) }; + + typedef typename ParentType::ScalarBuffer ScalarBuffer; + typedef typename ParentType::PhaseBuffer PhaseBuffer; + typedef typename ParentType::ComponentBuffer ComponentBuffer; + typedef typename ParentType::PhaseComponentBuffer PhaseComponentBuffer; + + +public: + MPNCVtkWriterEnergy(const Problem &problem) + : ParentType(problem) + { + temperatureOutput_ = GET_PARAM(TypeTag, bool, MPNC, VtkAddTemperatures); + enthalpyOutput_ = GET_PARAM(TypeTag, bool, MPNC, VtkAddEnthalpies); + internalEnergyOutput_ = GET_PARAM(TypeTag, bool, MPNC, VtkAddInternalEnergies); + } + + /*! + * \brief Allocate memory for the scalar fields we would like to + * write to the VTK file. + */ + template <class MultiWriter> + void allocBuffers(MultiWriter &writer) + { + if (temperatureOutput_) this->resizeScalarBuffer_(temperature_); + if (enthalpyOutput_) this->resizePhaseBuffer_(enthalpy_); + if (internalEnergyOutput_) this->resizePhaseBuffer_(internalEnergy_); + } + + /*! + * \brief Modify the internal buffers according to the volume + * variables seen on an element + */ + void processElement(const Element &elem, + const FVElementGeometry &fvElemGeom, + const ElementVolumeVariables &elemVolVars, + const ElementBoundaryTypes &elemBcTypes) + { + int n = elem.template count<dim>(); + for (int i = 0; i < n; ++i) { + int I = this->problem_.vertexMapper().map(elem, i, dim); + const VolumeVariables &volVars = elemVolVars[i]; + + if (temperatureOutput_) temperature_[I] = volVars.fluidState().temperature(0); + for (int phaseIdx = 0; phaseIdx < numPhases; ++ phaseIdx) { + if (enthalpyOutput_) + enthalpy_[phaseIdx][I] = volVars.fluidState().temperature(phaseIdx); + if (internalEnergyOutput_) + internalEnergy_[phaseIdx][I] = volVars.fluidState().internalEnergy(phaseIdx); + } + } + } + + /*! + * \brief Add all buffers to the VTK output writer. + */ + template <class MultiWriter> + void commitBuffers(MultiWriter &writer) + { + if (temperatureOutput_) + this->commitScalarBuffer_(writer, "T", temperature_); + if (enthalpyOutput_) + this->commitPhaseBuffer_(writer, "h_%s", enthalpy_); + if (internalEnergyOutput_) + this->commitPhaseBuffer_(writer, "u_%s", internalEnergy_); + } + +private: + bool temperatureOutput_; + bool enthalpyOutput_; + bool internalEnergyOutput_; + + ScalarBuffer temperature_; + PhaseBuffer enthalpy_; + PhaseBuffer internalEnergy_; +}; + +} + +#endif diff --git a/dumux/boxmodels/MpNc/mass/MpNcindicesmass.hh b/dumux/boxmodels/MpNc/mass/MpNcindicesmass.hh new file mode 100644 index 0000000000000000000000000000000000000000..175c9dd68dbf9763b00a8e2127fe6acdadb99db3 --- /dev/null +++ b/dumux/boxmodels/MpNc/mass/MpNcindicesmass.hh @@ -0,0 +1,85 @@ +/***************************************************************************** + * Copyright (C) 2010-2011 by Philipp Nuske * + * Copyright (C) 2011 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/>. * + *****************************************************************************/ +/*! + * \brief The indices for the mass flow part of the compositional + * multi-phase model. + */ +#ifndef DUMUX_MPNC_MASS_INDICES_HH +#define DUMUX_MPNC_MASS_INDICES_HH + +namespace Dumux +{ +/*! + * \brief The indices required for conservation of mass. + * + * This is the specialization for the case without kinetic mass + * transfer (i.e. assuming chemical equilibrium) + */ +template <int PVOffset, + class TypeTag, + bool enableKinetic /*=false*/> +class MPNCMassIndices +{ + static_assert(!enableKinetic, + "No kinetic mass transfer module included, " + "but kinetic mass transfer enabled."); + + enum { numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)) }; + +public: + /*! + * \brief This module defines one new primary variable. + */ + static const int NumPrimaryVars = numComponents; + + /*! + * \brief Index for the fugacity of the first component in the + * first phase in a vector of primary variables. + * + * The next numComponents indices represent the remaining + * fugacities: + * + * fug0Idx + 0 = fugacity of component 0 + * fug0Idx + 1 = fugacity of component 1 + * ... + * fug0Idx + N - 1 = fugacity of component N + */ + static const int fug0Idx = PVOffset + 0; + + /*! + * \brief Equation index of the mass conservation equation for the + * first component. + * + * The next numComponents indices represent the equations of all + * components in all phases: + * + * conti00EqIdx + 0 = continuity of component 0 + * conti00EqIdx + 1 = continuity of component 1 + * ... + * conti00EqIdx + N - 1 = continuity of component N + */ + static const int conti0EqIdx = PVOffset + 0; +}; + +} + +#endif diff --git a/dumux/boxmodels/MpNc/mass/MpNclocalresidualmass.hh b/dumux/boxmodels/MpNc/mass/MpNclocalresidualmass.hh new file mode 100644 index 0000000000000000000000000000000000000000..80047434e854a3345743e26ddc1d43c3ecb2ec4b --- /dev/null +++ b/dumux/boxmodels/MpNc/mass/MpNclocalresidualmass.hh @@ -0,0 +1,346 @@ +/***************************************************************************** + * Copyright (C) 2009-2011 by Andreas Lauser * + * Copyright (C) 2011 by Philipp Nuske * + * 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/>. * + *****************************************************************************/ +#ifndef DUMUX_MPNC_LOCAL_RESIDUAL_MASS_HH +#define DUMUX_MPNC_LOCAL_RESIDUAL_MASS_HH + +#include <dune/common/fvector.hh> +#include <dumux/material/constraintsolvers/compositionfromfugacities.hh> + +#include "../diffusion/diffusion.hh" + +namespace Dumux +{ +/*! + * \brief The mass conservation part of the Mp-Nc model. + * + * This is the class represents methods which are shared amongst all + * mass conservation modules. + */ +template<class TypeTag> +class MPNCLocalResidualMassCommon +{ +protected: + 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(VolumeVariables)) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluxVariables)) FluxVariables; + + enum { dim = GridView::dimension }; + enum { numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)) }; + enum { enableDiffusion = GET_PROP_VALUE(TypeTag, PTAG(EnableDiffusion)) }; + enum { enableEnergy = GET_PROP_VALUE(TypeTag, PTAG(EnableEnergy)) }; + enum { enableKineticEnergy = GET_PROP_VALUE(TypeTag, PTAG(EnableKineticEnergy)) }; + + typedef typename Dune::FieldVector<Scalar, dim> Vector; + typedef typename Dune::FieldVector<Scalar, numComponents> ComponentVector; + typedef MPNCDiffusion<TypeTag, enableDiffusion> Diffusion; + + typedef MPNCLocalResidualEnergy<TypeTag, enableEnergy, enableKineticEnergy> EnergyResid; + +public: + /*! + * \brief Evaluate the amount moles within a sub-control volume in + * a phase. + * + * The result should be averaged over the volume. + */ + static void computePhaseStorage(ComponentVector &result, + const VolumeVariables &volVars, + int phaseIdx) + { + // compute storage term of all components within all phases + result = 0; + for (int compIdx = 0; compIdx < numComponents; ++ compIdx) { + result[compIdx] += + volVars.fluidState().saturation(phaseIdx)* + volVars.fluidState().molarity(phaseIdx, compIdx); + } + + result *= volVars.porosity(); + } + + /*! + * \brief Evaluates the advective flux of all conservation + * quantities over a face of a subcontrol volume via a + * fluid phase. + */ + static void computeAdvectivePhaseFlux(ComponentVector &phaseComponentValues, + const FluxVariables &fluxVars, + const int phaseIdx) + { + static bool enableSmoothUpwinding_ = GET_PARAM(TypeTag, bool, EnableSmoothUpwinding); + + Vector tmpVec; + fluxVars.intrinsicPermeability().mv(fluxVars.potentialGrad(phaseIdx), + tmpVec); + + // advective part is flux over face, therefore needs to be multiplied by normal vector + // (length: area of face) + Scalar normalFlux = - (tmpVec*fluxVars.face().normal); + + // data attached to upstream and the downstream vertices + // of the current phase + int upIdx = fluxVars.face().i; + int dnIdx = fluxVars.face().j; +#ifndef NDEBUG + if (!std::isfinite(normalFlux)) + DUNE_THROW(NumericalProblem, "Calculated non-finite normal flux"); +#endif + + if (normalFlux < 0) std::swap(upIdx, dnIdx); + const VolumeVariables &up = fluxVars.volVars(upIdx); + const VolumeVariables &dn = fluxVars.volVars(dnIdx); + + //////// + // advective fluxes of all components in the phase + //////// + for (int compIdx = 0; compIdx < numComponents; ++compIdx) { + // add advective flux of current component in current + // phase. we use full upwinding. + + if (enableSmoothUpwinding_) { + Scalar mobUp = up.mobility(phaseIdx); + Scalar cUp = up.fluidState().molarity(phaseIdx, compIdx); + + Scalar mobDn = dn.mobility(phaseIdx); + Scalar cDn = dn.fluidState().molarity(phaseIdx, compIdx); + + Scalar mUp = mobUp*cUp; + Scalar mDn = mobDn*cDn; + Scalar m0 = Dumux::harmonicMean(mUp, mDn); + + Scalar x = std::abs(normalFlux); + Scalar sign = (normalFlux < 0)?-1:1; + + // the direction from upstream to downstream + //GlobalPosition delta = this->curElement_().geometry().corner(upIdx); + //delta -= this->curElement_().geometry().corner(dnIdx); + + // approximate the mean viscosity at the face + Scalar meanVisc = (up.fluidState().viscosity(phaseIdx) + + dn.fluidState().viscosity(phaseIdx))/2; + + // put the mean viscosity and permeanbility in + // relation to the viscosity of water at + // approximatly 20 degrees Celsius. + const Scalar pGradRef = 10; // [Pa/m] + const Scalar muRef = 1e-3; // [Ns/m^2] + const Scalar Kref = 1e-12; // [m^2] = approx 1 Darcy + + Scalar faceArea = fluxVars.face().normal.two_norm(); + Scalar eps = pGradRef * Kref * faceArea * meanVisc/muRef; // * (1e3/18e-3)/meanC; + + Scalar compFlux; + if (x >= eps) { + // we only do tricks if x is below the epsilon + // value + compFlux = x*mUp; + } + else { + Scalar xPos[] = { 0, eps }; + Scalar yPos[] = { 0, eps*mUp }; + Spline<Scalar> sp2(xPos, yPos, m0, mUp); + compFlux = sp2.eval(x); + } + + phaseComponentValues[compIdx] = sign*compFlux; + } + else {// !use smooth upwinding + phaseComponentValues[compIdx] = + up.mobility(phaseIdx) * + up.fluidState().molarity(phaseIdx, compIdx) * + normalFlux; + } + } + } + + + /*! + * \brief Evaluates the advective flux of all conservation + * quantities over a face of a subcontrol volume via a + * fluid phase. + */ + static void computeDiffusivePhaseFlux(ComponentVector &flux, + const FluxVariables &fluxVars, + int phaseIdx) + { + if (!enableDiffusion) { + flux = 0.0; + return; + } + + + const VolumeVariables &volVarsI = fluxVars.volVars(fluxVars.face().i); + const VolumeVariables &volVarsJ = fluxVars.volVars(fluxVars.face().j); + if (volVarsI.fluidState().saturation(phaseIdx) < 1e-4 || + volVarsJ.fluidState().saturation(phaseIdx) < 1e-4) + { + return; // phase is not present in one of the finite volumes + } + + // approximate the total concentration of the phase at the + // integration point by the arithmetic mean of the + // concentration of the sub-control volumes + Scalar molarDensityAtIP; + molarDensityAtIP = volVarsI.fluidState().molarDensity(phaseIdx); + molarDensityAtIP += volVarsJ.fluidState().molarDensity(phaseIdx); + molarDensityAtIP /= 2; + + Diffusion::flux(flux, phaseIdx, fluxVars, molarDensityAtIP); + } +}; + +/*! + * \brief The mass conservation part of the Mp-Nc model. + * + * This is the specialization for the case where kinetic mass transfer + * is _not_ considered. + */ +template<class TypeTag, bool enableKinetic /*=false*/> +class MPNCLocalResidualMass +{ + static_assert(!enableKinetic, + "No kinetic mass transfer module included, " + "but kinetic mass transfer enabled."); + + typedef MPNCLocalResidualMassCommon<TypeTag> MassCommon; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MPNCIndices)) Indices; + 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(FluxVariables)) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + + enum { numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)) }; + enum { conti0EqIdx = Indices::conti0EqIdx }; + enum { numEnergyEqs = Indices::NumPrimaryEnergyVars}; + + typedef typename Dune::FieldVector<Scalar, numComponents> ComponentVector; + + enum { enableEnergy = GET_PROP_VALUE(TypeTag, PTAG(EnableEnergy)) }; + enum { enableKineticEnergy = GET_PROP_VALUE(TypeTag, PTAG(EnableKineticEnergy)) }; + typedef MPNCLocalResidualEnergy<TypeTag, enableEnergy, enableKineticEnergy> EnergyResid; + +public: + /*! + * \brief Calculate the storage for all mass balance equations + */ + static void computeStorage(PrimaryVariables &storage, + const VolumeVariables &volVars) + { + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + storage[conti0EqIdx + compIdx] = 0.0; + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + addPhaseStorage(storage, volVars, phaseIdx); + } + }; + + /*! + * \brief Calculate the storage for all mass balance equations + * within a single fluid phase + */ + static void addPhaseStorage(PrimaryVariables &storage, + const VolumeVariables &volVars, + int phaseIdx) + { + // calculate the component-wise mass storage + ComponentVector phaseComponentValues; + MassCommon::computePhaseStorage(phaseComponentValues, + volVars, + phaseIdx); + + // copy to the primary variables + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + storage[conti0EqIdx + compIdx] += phaseComponentValues[compIdx]; + }; + + /*! + * \brief Calculate the storage for all mass balance equations + */ + static void computeFlux(PrimaryVariables &flux, + const FluxVariables &fluxVars, + const ElementVolumeVariables & elemVolVars) + { + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + flux[conti0EqIdx + compIdx] = 0.0; + + ComponentVector phaseComponentValuesAdvection(0.); + ComponentVector phaseComponentValuesDiffusion(0.); + ComponentVector phaseComponentValuesMassTransport[numPhases]; // what goes into the energy module + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + MassCommon::computeAdvectivePhaseFlux(phaseComponentValuesAdvection, fluxVars, phaseIdx); + Valgrind::CheckDefined(phaseComponentValuesAdvection); + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + flux[conti0EqIdx + compIdx] += + phaseComponentValuesAdvection[compIdx]; + + MassCommon::computeDiffusivePhaseFlux(phaseComponentValuesDiffusion, fluxVars, phaseIdx); + Valgrind::CheckDefined(phaseComponentValuesDiffusion); + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + flux[conti0EqIdx + compIdx] += + phaseComponentValuesDiffusion[compIdx]; + + // Right now I think that adding the two contributions individually into the flux is best for debugging and understanding. + // The Energy module needs both contributions. + phaseComponentValuesMassTransport[phaseIdx] = phaseComponentValuesDiffusion + phaseComponentValuesAdvection ; + Valgrind::CheckDefined(flux); + } + + // \todo + // + // The computeflux() of the Energy module needs a + // component-wise flux (for the diffusive enthalpy transport) + // It makes some sense calling energy from here, because energy + // is carried by mass However, it is not really a clean + // solution. + + // energy transport in fluid phases + EnergyResid::computeFlux(flux, + fluxVars, + elemVolVars, + phaseComponentValuesMassTransport); + Valgrind::CheckDefined(flux); + } + + + + /*! + * \brief Calculate the source terms for all mass balance + * equations + */ + static void computeSource(PrimaryVariables &source, + const VolumeVariables &volVars) + { + // mass transfer is not considered in this mass module + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + source[conti0EqIdx + compIdx] = 0.0; + }; +}; + +} // end namepace + +#endif diff --git a/dumux/boxmodels/MpNc/mass/MpNcvolumevariablesmass.hh b/dumux/boxmodels/MpNc/mass/MpNcvolumevariablesmass.hh new file mode 100644 index 0000000000000000000000000000000000000000..e1775f476ff1f079a7d47a25bee40da5eac7b668 --- /dev/null +++ b/dumux/boxmodels/MpNc/mass/MpNcvolumevariablesmass.hh @@ -0,0 +1,142 @@ +/***************************************************************************** + * Copyright (C) 2010-2011 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 Contains the mass conservation part of the volume variables + */ +#ifndef DUMUX_MPNC_VOLUME_VARIABLES_MASS_HH +#define DUMUX_MPNC_VOLUME_VARIABLES_MASS_HH + +#include <dumux/material/fluidstates/genericfluidstate.hh> +#include <dumux/material/fluidstates/equilibriumfluidstate.hh> + +namespace Dumux +{ +/*! + * \brief The compositional part of the volume variables if chemical + * equilibrium _is_ assumed + */ +template <class TypeTag, bool enableKinetic /* = false */> +class MPNCVolumeVariablesMass +{ + static_assert(!enableKinetic, + "No kinetic mass transfer module included, " + "but kinetic mass transfer enabled."); + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(PrimaryVariables)) PrimaryVariables; + + 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(MPNCIndices)) Indices; + 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(FVElementGeometry)) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(CompositionFromFugacitiesSolver)) CompositionFromFugacitiesSolver; + typedef typename GridView::template Codim<0>::Entity Element; + + + enum { numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)) }; + enum { fug0Idx = Indices::fug0Idx }; + + typedef Dune::FieldVector<Scalar, numComponents> ComponentVector; + +public: + /*! + * \brief The fluid state which is used by the volume variables to + * store the thermodynamic state. + * + * If chemical equilibrium is assumed, we use the fluid state + * which saves some memory. + */ + typedef EquilibriumFluidState<Scalar, FluidSystem> FluidState; + + /*! + * \brief Update composition of all phases in the mutable + * parameters from the primary variables. + */ + template <class MutableParams> + void update(MutableParams &mutParams, + const PrimaryVariables &priVars, + const VolumeVariables *hint, + + const Problem &problem, + const Element &element, + const FVElementGeometry &elemGeom, + int scvIdx) + { + ComponentVector fug; + // retrieve component fugacities + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + fug[compIdx] = priVars[fug0Idx + compIdx]; + + // calculate phase compositions + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + // initial guess + for (int compIdx = 0; compIdx < numComponents; ++compIdx) { + Scalar x_ij = 1.0/numComponents; + if (hint) + // use the hint for the initial mole fraction! + x_ij = hint->fluidState().moleFrac(phaseIdx, compIdx); + + // set initial guess of the component's mole fraction + mutParams.setMoleFrac(phaseIdx, + compIdx, + x_ij); + + } + + // calculate the phase composition from the component + // fugacities + if (!hint) + // if no hint was given, we ask the constraint solver + // to make an initial guess + CompositionFromFugacitiesSolver::guessInitial(mutParams, phaseIdx, fug); + CompositionFromFugacitiesSolver::solve(mutParams, phaseIdx, fug); + + /* + std::cout << "final composition: " << FluidSystem::phaseName(phaseIdx) << "=" + << mutParams.moleFrac(phaseIdx, 0) << " " + << mutParams.moleFrac(phaseIdx, 1) << " " + << mutParams.moleFrac(phaseIdx, 2) << " " + << mutParams.moleFrac(phaseIdx, 3) << " " + << mutParams.moleFrac(phaseIdx, 4) << " " + << mutParams.moleFrac(phaseIdx, 5) << " " + << mutParams.moleFrac(phaseIdx, 6) << "\n"; + */ + + } + } + + /*! + * \brief If running in valgrind this makes sure that all + * quantities in the volume variables are defined. + */ + void checkDefined() const + { + } +}; + +} // end namepace + +#endif diff --git a/dumux/boxmodels/MpNc/mass/MpNcvtkwritermass.hh b/dumux/boxmodels/MpNc/mass/MpNcvtkwritermass.hh new file mode 100644 index 0000000000000000000000000000000000000000..ebc9fcc611a50440066c595c3b3a698818d90c3d --- /dev/null +++ b/dumux/boxmodels/MpNc/mass/MpNcvtkwritermass.hh @@ -0,0 +1,128 @@ +/***************************************************************************** + * Copyright (C) 2011 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 VTK writer module for the mass related quantities of the + * MpNc model. + */ +#ifndef DUMUX_MPNC_VTK_WRITER_MASS_HH +#define DUMUX_MPNC_VTK_WRITER_MASS_HH + +#include "../MpNcvtkwritermodule.hh" + +namespace Dumux +{ +/*! + * \ingroup MPNCModel + * + * \brief VTK writer module for the mass related quantities of the + * MpNc model. + * + * This is the specialization for the case _without_ kinetic mass + * transfer between phases. + */ +template<class TypeTag, bool enableKinetic /* = false */> +class MPNCVtkWriterMass : public MPNCVtkWriterModule<TypeTag> +{ + static_assert(!enableKinetic, + "No kinetic mass transfer module included, " + "but kinetic mass transfer enabled."); + + typedef MPNCVtkWriterModule<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Problem)) Problem; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + 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(VolumeVariables)) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluxVariables)) FluxVariables; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(GridView)) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + + enum { dim = GridView::dimension }; + enum { numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)) }; + bool fugacityOutput_; + + typedef typename ParentType::ScalarBuffer ScalarBuffer; + typedef typename ParentType::PhaseBuffer PhaseBuffer; + typedef typename ParentType::ComponentBuffer ComponentBuffer; + typedef typename ParentType::PhaseComponentBuffer PhaseComponentBuffer; + + +public: + MPNCVtkWriterMass(const Problem &problem) + : ParentType(problem) + { + fugacityOutput_ = GET_PARAM(TypeTag, bool, MPNC, VtkAddFugacities); + } + + /*! + * \brief Allocate memory for the scalar fields we would like to + * write to the VTK file. + */ + template <class MultiWriter> + void allocBuffers(MultiWriter &writer) + { + if (fugacityOutput_) this->resizeComponentBuffer_(fugacity_); + } + + /*! + * \brief Modify the internal buffers according to the volume + * variables seen on an element + */ + void processElement(const Element &elem, + const FVElementGeometry &fvElemGeom, + const ElementVolumeVariables &elemVolVars, + const ElementBoundaryTypes &elemBcTypes) + { + int n = elem.template count<dim>(); + for (int i = 0; i < n; ++i) { + int I = this->problem_.vertexMapper().map(elem, i, dim); + const VolumeVariables &volVars = elemVolVars[i]; + + if (fugacityOutput_) { + for (int compIdx = 0; compIdx < numComponents; ++compIdx) { + fugacity_[compIdx][I] = volVars.fluidState().fugacity(compIdx); + } + } + } + } + + /*! + * \brief Add all buffers to the VTK output writer. + */ + template <class MultiWriter> + void commitBuffers(MultiWriter &writer) + { + if (fugacityOutput_) + this->commitComponentBuffer_(writer, "f_%s", fugacity_); + } + +private: + ComponentBuffer fugacity_; +}; + +} + +#endif diff --git a/dumux/material/constraintsolvers/Makefile.am b/dumux/material/constraintsolvers/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..70f8f174617e3dd8488fc44a23ad1680eef4c247 --- /dev/null +++ b/dumux/material/constraintsolvers/Makefile.am @@ -0,0 +1 @@ +include $(top_srcdir)/am/global-rules diff --git a/dumux/material/constraintsolvers/compositionfromfugacities.hh b/dumux/material/constraintsolvers/compositionfromfugacities.hh new file mode 100644 index 0000000000000000000000000000000000000000..65716f9a6d2c12d27335a240804c591897c8e76d --- /dev/null +++ b/dumux/material/constraintsolvers/compositionfromfugacities.hh @@ -0,0 +1,356 @@ +/***************************************************************************** + * Copyright (C) 2010-2011 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 Determines the fluid composition given the component + * fugacities and an arbitary equation of state. + */ +#ifndef DUMUX_COMPOSITION_FROM_FUGACITIES_HH +#define DUMUX_COMPOSITION_FROM_FUGACITIES_HH + +#include "../fluidstates/genericfluidstate.hh" + +namespace Dumux { + +/*! + * \brief Calculates the chemical equilibrium from the component + * fugacities in a phase. + */ +template <class Scalar, class FluidSystem> +class CompositionFromFugacities +{ + typedef typename FluidSystem::MutableParameters MutableParameters; + + enum { numPhases = FluidSystem::numPhases }; + enum { numComponents = FluidSystem::numComponents }; + +public: + typedef Dune::FieldVector<Scalar, numComponents> ComponentVector; + + /*! + * \brief Guess an initial value for the composition of the phase. + */ + static void guessInitial(MutableParameters &mutParams, int phaseIdx, const ComponentVector &fugVec) + { + if (FluidSystem::isIdealMixture(phaseIdx)) + return; + + // Pure component fugacities + for (int i = 0; i < numComponents; ++ i) { + //std::cout << f << " -> " << mutParams.fugacity(phaseIdx, i)/f << "\n"; + mutParams.setMoleFrac(phaseIdx, + i, + 1.0/numComponents); + } + }; + + /*! + * \brief Calculates the chemical equilibrium from the component + * fugacities in a phase. + * + * The phase's fugacities must already be set. + */ + static void solve(MutableParameters &mutParams, + int phaseIdx, + const ComponentVector &targetFug) + { + // use a much more efficient method in case the phase is an + // ideal mixture + if (FluidSystem::isIdealMixture(phaseIdx)) { + solveIdealMix_(mutParams, phaseIdx, targetFug); + return; + } + + Dune::FMatrixPrecision<Scalar>::set_singular_limit(1e-25); + + // save initial composition in case something goes wrong + Dune::FieldVector<Scalar, numComponents> xInit; + for (int i = 0; i < numComponents; ++i) { + xInit[i] = mutParams.moleFrac(phaseIdx, i); + }; + + ///////////////////////// + // Newton method + ///////////////////////// + + // Jacobian matrix + Dune::FieldMatrix<Scalar, numComponents, numComponents> J; + // solution, i.e. phase composition + Dune::FieldVector<Scalar, numComponents> x; + // right hand side + Dune::FieldVector<Scalar, numComponents> b; + + // maximum number of iterations + const int nMax = 25; + for (int nIdx = 0; nIdx < nMax; ++nIdx) { + // calculate Jacobian matrix and right hand side + linearize_(J, b, mutParams, phaseIdx, targetFug); + Valgrind::CheckDefined(J); + Valgrind::CheckDefined(b); + + /* + std::cout << FluidSystem::phaseName(phaseIdx) << "Phase composition: "; + for (int i = 0; i < FluidSystem::numComponents; ++i) + std::cout << mutParams.moleFrac(phaseIdx, i) << " "; + std::cout << "\n"; + std::cout << FluidSystem::phaseName(phaseIdx) << "Phase phi: "; + for (int i = 0; i < FluidSystem::numComponents; ++i) + std::cout << mutParams.fugacityCoeff(phaseIdx, i) << " "; + std::cout << "\n"; + */ + + // Solve J*x = b + x = 0; + try { J.solve(x, b); } + catch (Dune::FMatrixError e) + { throw Dumux::NumericalProblem(e.what()); } + + //std::cout << "original delta: " << x << "\n"; + + Valgrind::CheckDefined(x); + + /* + std::cout << FluidSystem::phaseName(phaseIdx) << "Phase composition: "; + for (int i = 0; i < FluidSystem::numComponents; ++i) + std::cout << mutParams.moleFrac(phaseIdx, i) << " "; + std::cout << "\n"; + std::cout << "J: " << J << "\n"; + std::cout << "rho: " << mutParams.density(phaseIdx) << "\n"; + std::cout << "delta: " << x << "\n"; + std::cout << "defect: " << b << "\n"; + + std::cout << "J: " << J << "\n"; + + std::cout << "---------------------------\n"; + */ + + // update the fluid composition. b is also used to store + // the defect for the next iteration. + Scalar relError = update_(mutParams, x, b, phaseIdx, targetFug); + //std::cout << "relError: " << relError << "\n"; + + if (relError < 1e-9) { + mutParams.updateMeanMolarMass(phaseIdx); + + Scalar Vm = FluidSystem::computeMolarVolume(mutParams, phaseIdx); + mutParams.setMolarVolume(phaseIdx, Vm); + + //std::cout << "num iterations: " << nIdx << "\n"; + return; + } + } + + DUNE_THROW(NumericalProblem, + "Calculating the " << FluidSystem::phaseName(phaseIdx) + << "Phase composition failed. Initial {x} = {" + << xInit + << "}, {fug_t} = {" << targetFug << "}, p = " << mutParams.pressure(phaseIdx) + << ", T = " << mutParams.temperature(phaseIdx)); + }; + + +protected: + // update the phase composition in case the phase is an ideal + // mixture, i.e. the component's fugacity coefficients are + // independent of the phase's composition. + static void solveIdealMix_(MutableParameters &mutParams, + int phaseIdx, + const ComponentVector &fugacities) + { + for (int i = 0; i < numComponents; ++ i) { + Scalar phi = FluidSystem::computeFugacityCoeff(mutParams, phaseIdx, i); + Scalar gamma = phi * mutParams.pressure(phaseIdx); + mutParams.setFugacityCoeff(phaseIdx, i, phi); + mutParams.setMoleFrac(phaseIdx, i, fugacities[i]/gamma); + }; + mutParams.updateMeanMolarMass(phaseIdx); + + Scalar Vm = FluidSystem::computeMolarVolume(mutParams, phaseIdx); + mutParams.setMolarVolume(phaseIdx, Vm); + return; + }; + + static void linearize_(Dune::FieldMatrix<Scalar, numComponents, numComponents> &J, + Dune::FieldVector<Scalar, numComponents> &defect, + MutableParameters &mutParams, + int phaseIdx, + const ComponentVector &targetFug) + { + // reset jacobian + J = 0; + + // calculate the defect (deviation of the current fugacities + // from the target fugacities) + for (int i = 0; i < numComponents; ++ i) { + Scalar phi = FluidSystem::computeFugacityCoeff(mutParams, + phaseIdx, + i); + Scalar f = phi*mutParams.pressure(phaseIdx)*mutParams.moleFrac(phaseIdx, i); + mutParams.setFugacityCoeff(phaseIdx, i, phi); + + defect[i] = targetFug[i] - f; + } + + // assemble jacobian matrix of the constraints for the composition + for (int i = 0; i < numComponents; ++ i) { + const Scalar eps = 1e-11; //std::max(1e-16, std::abs(x_i)*1e-9); + + //////// + // approximately calculate partial derivatives of the + // fugacity defect of all components in regard to the mole + // fraction of the i-th component. This is done via + // forward differences + + // deviate the mole fraction of the i-th component + Scalar x_i = mutParams.moleFrac(phaseIdx, i); + mutParams.setMoleFrac(phaseIdx, i, x_i + eps); + + // compute new defect and derivative for all component + // fugacities + for (int j = 0; j < numComponents; ++j) { + // compute the j-th component's fugacity coefficient ... + Scalar phi = FluidSystem::computeFugacityCoeff(mutParams, + phaseIdx, + j); + // ... and its fugacity ... + Scalar f = + phi * + mutParams.pressure(phaseIdx) * + mutParams.moleFrac(phaseIdx, j); + // as well as the defect for this fugacity + Scalar defJPlusEps = targetFug[j] - f; + + // use forward differences to calculate the defect's + // derivative + J[j][i] = (defJPlusEps - defect[j])/eps; + } + + // reset composition to original value + mutParams.setMoleFrac(phaseIdx, i, x_i); + + // end forward differences + //////// + } + } + + static Scalar update_(MutableParameters &mutParams, + Dune::FieldVector<Scalar, numComponents> &x, + Dune::FieldVector<Scalar, numComponents> &b, + int phaseIdx, + const Dune::FieldVector<Scalar, numComponents> &targetFug) + { + // store original composition and calculate relative error + Dune::FieldVector<Scalar, numComponents> origComp; + Scalar relError = 0; + Scalar sumDelta = 0; + Scalar sumx = 0; + for (int i = 0; i < numComponents; ++i) { + origComp[i] = mutParams.moleFrac(phaseIdx, i); + relError = std::max(relError, std::abs(x[i])); + + sumx += std::abs(mutParams.moleFrac(phaseIdx, i)); + sumDelta += std::abs(x[i]); + }; + +#if 1 + // chop update to at most 20% change in composition + const Scalar maxDelta = 0.2; + if (sumDelta > maxDelta) + x /= (sumDelta/maxDelta); +#endif + + //Scalar curDefect = calculateDefect_(mutParams, phaseIdx, targetFug); + //Scalar nextDefect; + //Scalar sumMoleFrac = 0.0; + //ComponentVector newB(1e100); + //for (int numTries = 0; numTries < 1; ++numTries) { + // change composition + for (int i = 0; i < numComponents; ++i) { + Scalar newx = origComp[i] - x[i]; +#if 1 + // only allow negative mole fractions if the target fugacity is negative + if (targetFug[i] > 0) + newx = std::max(0.0, newx); + // only allow positive mole fractions if the target fugacity is positive + else if (targetFug[i] < 0) + newx = std::min(0.0, newx); + // if the target fugacity is zero, the mole fraction must also be zero + else + newx = 0; +#endif + mutParams.setMoleFrac(phaseIdx, i, newx); + //sumMoleFrac += std::abs(newx); + } + + /* + // if the sum of the mole fractions gets 0, we take the + // original composition divided by 100 + if (sumMoleFrac < 1e-10) { + for (int i = 0; i < numComponents; ++i) { + mutParams.setMoleFrac(phaseIdx, i, origComp[i]/100); + } + return relError; + } + */ + + /* + // calculate new residual + for (int i = 0; i < numComponents; ++i) { + Scalar phi = FluidSystem::computeFugacityCoeff(mutParams, + phaseIdx, + i); + mutParams.setFugacityCoeff(phaseIdx, i, phi); + } + + nextDefect = calculateDefect_(mutParams, phaseIdx, targetFug); + //std::cout << "try delta: " << x << "\n"; + //std::cout << "defect: old=" << curDefect << " new=" << nextDefect << "\n"; + if (nextDefect <= curDefect) + break; + + // divide delta vector + x /= 2; + } + */ + + return relError; + } + + static Scalar calculateDefect_(const MutableParameters ¶ms, + int phaseIdx, + const ComponentVector &targetFug) + { + Scalar result = 0.0; + for (int i = 0; i < numComponents; ++i) { + // sum of the fugacity defect weighted by the inverse + // fugacity coefficient + result += std::abs( + (targetFug[i] - params.fugacity(phaseIdx, i)) + / + params.fugacityCoeff(phaseIdx, i) ); + }; + return result; + } +}; + +} // end namespace Dumux + +#endif diff --git a/dumux/material/eos/Makefile.am b/dumux/material/eos/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..70f8f174617e3dd8488fc44a23ad1680eef4c247 --- /dev/null +++ b/dumux/material/eos/Makefile.am @@ -0,0 +1 @@ +include $(top_srcdir)/am/global-rules diff --git a/dumux/material/eos/pengrobinson.hh b/dumux/material/eos/pengrobinson.hh new file mode 100644 index 0000000000000000000000000000000000000000..793ff06d7566331b194c9e85f07f2d86c826659c --- /dev/null +++ b/dumux/material/eos/pengrobinson.hh @@ -0,0 +1,483 @@ +/***************************************************************************** + * Copyright (C) 2010-2011 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 Implements the Peng-Robinson equation of state for liquids + * and gases. + * + * See: + * + * D.-Y. Peng, D.B. Robinson: A new two-constant equation of state, + * Industrial & Engineering Chemistry Fundamentals, 1976, 15 (1), + * pp. 59–64 + * + * R. Reid, et al.: The Properties of Gases and Liquids, + * 4th edition, McGraw-Hill, 1987, pp. 42-44, 82 + */ +#ifndef DUMUX_PENG_ROBINSON_HH +#define DUMUX_PENG_ROBINSON_HH + +#include <dumux/material/idealgas.hh> +#include <dumux/common/math.hh> + +#include <iostream> + +namespace Dumux +{ + +/*! + * + * \brief Implements the Peng-Robinson equation of state for liquids + * and gases. + * + * See: + * + * D.-Y. Peng, D.B. Robinson: A new two-constant equation of state, + * Industrial & Engineering Chemistry Fundamentals, 1976, 15 (1), + * pp. 59–64 + * + * R. Reid, et al.: The Properties of Gases and Liquids, + * 4th edition, McGraw-Hill, 1987, pp. 42-44, 82 + */ +template <class Scalar> +class PengRobinson +{ + //! The ideal gas constant [Pa * m^3/mol/K] + static constexpr Scalar R = Dumux::Constants<Scalar>::R; + + PengRobinson() + { } + +public: + /*! + * \brief Predicts the vapor pressure for the temperature given in + * setTP(). + * + * Initially, the vapor pressure is roughly estimated by using the + * Ambrose-Walton method, then the Newton method is used to make + * difference between the gas and liquid phase fugacity zero. + */ + template <class Params> + static Scalar computeVaporPressure(const Params ¶ms, Scalar T) + { + typedef typename Params::Component Component; + if (T >= Component::criticalTemperature()) + return Component::criticalPressure(); + + // initial guess of the vapor pressure + Scalar Vm[3]; + const Scalar eps = Component::criticalPressure()*1e-10; + + // use the Ambrose-Walton method to get an initial guess of + // the vapor pressure + Scalar pVap = ambroseWalton_(params, T); + + // Newton-Raphson method + for (int i = 0; i < 5; ++i) { + // calculate the molar densities + int numSol = molarVolumes(Vm, params, T, pVap); + assert(numSol == 3); + + Scalar f = fugacityDifference_(params, T, pVap, Vm[0], Vm[2]); + Scalar df_dp = + fugacityDifference_(params, T, pVap + eps, Vm[0], Vm[2]) + - + fugacityDifference_(params, T, pVap - eps, Vm[0], Vm[2]); + df_dp /= 2*eps; + + Scalar delta = f/df_dp; + pVap = pVap - delta; + + if (std::abs(delta/pVap) < 1e-10) + break; + } + + return pVap; + } + + /*! + * \brief Computes molar volumes where the Peng-Robinson EOS is + * true. + */ + template <class Params> + static Scalar computeMolarVolume(const Params ¶ms, + int phaseIdx, + bool gasPhase) + { + Valgrind::CheckDefined(params.temperature(phaseIdx)); + Valgrind::CheckDefined(params.pressure(phaseIdx)); + + Scalar Vm; + Valgrind::SetUndefined(Vm); + + Scalar T = params.temperature(phaseIdx); + Scalar p = params.pressure(phaseIdx); + + Scalar RT= R*T; + Scalar Astar = params.a(phaseIdx)*p/(RT*RT); + Scalar Bstar = params.b(phaseIdx)*p/RT; + + Scalar a1 = 1.0; + Scalar a2 = - (1 - Bstar); + Scalar a3 = Astar - Bstar*(3*Bstar + 2); + Scalar a4 = Bstar*(- Astar + Bstar*(1 + Bstar)); + + // ignore the first two results if the smallest + // compressibility factor is <= 0.0. (this means that if we + // would get negative molar volumes for the liquid phase, we + // consider the liquid phase non-existant.) + Scalar Z[3] = {0.0,0.0,0.0}; + int numSol = invertCubicPolynomial(Z, a1, a2, a3, a4); + if (numSol == 3) { + // the EOS has three intersections with the pressure, + // i.e. the molar volume of gas is the largest one and the + // molar volume of liquid is the smallest one + if (gasPhase) + Vm = Z[2]*RT/p; + else + Vm = Z[0]*RT/p; + } + else if (numSol == 1) { + // the EOS only has one intersection with the pressure, + // for the other phase, we take the extremum of the EOS + // with the largest distance from the intersection. + Scalar VmCubic = Z[0]*RT/p; + + // find the extrema (if they are present) + Scalar Vmin, Vmax; + bool hasExtrema; + hasExtrema = findExtrema_(Vmin, Vmax, params, phaseIdx); + + if (!hasExtrema) { + // if the EOS does not exhibit any extrema, the fluid + // is critical... + Vm = VmCubic; + handleCriticalFluid_(Vm, params, phaseIdx, gasPhase); + } + else { + if (gasPhase) + Vm = std::max(Vmax, VmCubic); + else + Vm = std::min(Vmin, VmCubic); + } + } + + Valgrind::CheckDefined(Vm); + return Vm; + } + + /*! + * \brief Returns the fugacity coefficient for a given pressure + * and molar volume. + * + * This is the same value as computeFugacity() because the mole + * fraction of a component in a pure fluid is obviously always + * 100%. + * + * \param T pressure [K] + * \param p pressure [Pa] + * \param Vm Molar volume [m^3/mol] + */ + template <class Params> + static Scalar computeFugacityCoeff(const Params ¶ms) + { + Scalar T = params.temperature(); + Scalar p = params.pressure(); + Scalar Vm = params.molarVolume(); + + Scalar RT = R*T; + Scalar Z = p*Vm/RT; + Scalar Bstar = p*params.b() / RT; + + Scalar tmp = + (Vm + params.b()*(1 + std::sqrt(2))) / + (Vm + params.b()*(1 - std::sqrt(2))); + Scalar expo = - params.a()/(RT * 2 * params.b() * std::sqrt(2)); + Scalar fugCoeff = + std::exp(Z - 1) / (Z - Bstar) * + std::pow(tmp, expo); + + return fugCoeff; + }; + + /*! + * \brief Returns the fugacity coefficient for a given pressure + * and molar volume. + * + * This is the fugacity coefficient times the pressure. The mole + * fraction of a component in a pure fluid is obviously always + * 100%, so it is not required. + * + * \param T pressure [K] + * \param p pressure [Pa] + * \param Vm Molar volume [m^3/mol] + */ + template <class Params> + static Scalar computeFugacity(const Params ¶ms) + { return params.pressure()*computeFugacityCoeff(params); } + +protected: + template <class Params> + static void handleCriticalFluid_(Scalar &Vm, + const Params ¶ms, + int phaseIdx, + bool gasPhase) + { + Scalar Vcrit; + findCriticalMolarVolume_(Vcrit, + params, + phaseIdx, + Vm, + gasPhase); + if (gasPhase) + Vm = std::max(Vm, Vcrit); + else + Vm = std::min(Vm, Vcrit); + } + + template <class Params> + static void findCriticalMolarVolume_(Scalar &Vcrit, + const Params ¶ms, + int phaseIdx, + Scalar Vcubic, + bool gasPhase) + { + Params tmpParams(params); + + // use an isotherm where the EOS should exhibit two maxima + Scalar Tmin = 150; // [K] + + Scalar minVm; + Scalar maxVm; + Scalar T = Tmin; + for (int i = 0; ; ++i) { + tmpParams.setTemperature(phaseIdx, T); + tmpParams.updateEosParams(phaseIdx); + + if (findExtrema_(minVm, maxVm, tmpParams, phaseIdx, /*hintSet=*/false)) + break; + + T = (T + params.temperature(phaseIdx)) / 2; + if (i >= 3) { + DUNE_THROW(NumericalProblem, + "TODO: better way to identify a non-critical temperature"); + }; + } + + // Newton's method: Start at minimum temperature and minimize + // the "gap" between the extrema of the EOS + for (int i = 0; i < 25; ++i) { + // calculate function we would like to minimize + Scalar f = maxVm - minVm; + + // check if we're converged + if (f < 1e-8 || + (gasPhase && maxVm < Vcubic) || + (!gasPhase && minVm > Vcubic)) + { + Vcrit = (maxVm + minVm)/2; + return; + } + + // backward differences. Forward differences are not + // robust, since the isotherm could be critical if an + // epsilon was added to the temperature. (this is case + // rarely happens, though) + const Scalar eps = - 1e-8; + T = T + eps; + tmpParams.updateEosParams(phaseIdx); + tmpParams.setTemperature(phaseIdx, T); + if (!findExtrema_(minVm, maxVm, tmpParams, phaseIdx, /*hintSet=*/true)) + DUNE_THROW(NumericalProblem, + "Error when calculating derivative of extrema " + "to calculate the critical point of phase " + << phaseIdx); + T = T - eps; + Scalar fStar = maxVm - minVm; + + // derivative of the difference between the maximum's + // molar volume and the minimum's molar volume regarding + // temperature + Scalar fPrime = (fStar - f)/eps; + + // update value for the current iteration + Scalar delta = f/fPrime; + if (delta > 0) + delta = -10; + + // line search (we have to make sure that both extrema + // still exist after the update) + Scalar Tstar = T - delta; + for (int j = 0; ; ++j) { + if (j >= 10) { + DUNE_THROW(NumericalProblem, + "Could not determine the critical point of phase " + << phaseIdx); + } + + tmpParams.setTemperature(phaseIdx, Tstar); + tmpParams.updateEosParams(phaseIdx); + if (findExtrema_(minVm, maxVm, tmpParams, phaseIdx, /*hintSet=*/(j==0))) { + // if the isotherm at Tstar exhibits two extrema + // the update is finished + T = Tstar; + break; + } + else { + delta /= 2; + Tstar = T - delta; + } + }; + } + }; + + // find the two molar volumes where the EOS exhibits extrema and + // which are larger than the covolume of the phase + template <class Params> + static bool findExtrema_(Scalar &Vmin, + Scalar &Vmax, + const Params ¶ms, + int phaseIdx, + bool hintSet = false) + { + Scalar a = params.a(phaseIdx); + Scalar b = params.b(phaseIdx); + Scalar u = 2; + Scalar w = -1; + + Scalar RT = R*params.temperature(phaseIdx); + + // calculate coefficients of the 4th order polynominal in + // monomial basis + Scalar a1 = RT; + Scalar a2 = 2*RT*u*b - 2*a; + Scalar a3 = 2*RT*w*b*b + RT*u*u*b*b + 4*a*b - u*a*b; + Scalar a4 = 2*RT*u*w*b*b*b + 2*u*a*b*b - 2*a*b*b; + Scalar a5 = RT*w*w*b*b*b*b - u*a*b*b*b; + + // Newton method to find first root + + // if the values which we got on Vmin and Vmax are usefull, we + // will reuse them as initial value, else we will start 10% + // above the covolume + Scalar V = hintSet?Vmin:b*1.1; + Scalar delta = 1.0; + for (int i = 0; std::abs(delta) > 1e-9; ++i) { + Scalar f = a5 + V*(a4 + V*(a3 + V*(a2 + V*a1))); + Scalar fPrime = a4 + V*(2*a3 + V*(3*a2 + V*4*a1)); + + if (std::abs(fPrime) < 1e-20) { + // give up if the derivative is zero + Vmin = 0; + Vmax = 0; + return false; + } + + + delta = f/fPrime; + V -= delta; + + if (i > 20) { + // give up after 20 iterations... + Vmin = 0; + Vmax = 0; + return false; + } + } + + // polynomial division + Scalar b1 = a1; + Scalar b2 = a2 + V*b1; + Scalar b3 = a3 + V*b2; + Scalar b4 = a4 + V*b3; + + // invert resulting cubic polynomial analytically + Scalar allV[4]; + allV[0] = V; + int numSol = 1 + Dumux::invertCubicPolynomial(&allV[1], b1, b2, b3, b4); + + // sort all roots of the derivative + std::sort(allV + 0, allV + numSol); + + // check whether the result is physical + if (allV[numSol - 2] < b) { + // the second largest extremum is smaller than the phase's + // covolume which is physically impossible + Vmin = Vmax = 0; + return false; + } + + // it seems that everything is okay... + Vmin = allV[numSol - 2]; + Vmax = allV[numSol - 1]; + return true; + } + + /*! + * \brief The Ambrose-Walton method to estimate the vapor + * pressure. + * + * \return Vapor pressure estimate in bar + * + * See: + * + * D. Ambrose, J. Walton: "Vapor Pressures up to Their Critical + * Temperatures of Normal Alkanes and 1-Alkanols", Pure + * Appl. Chem., 61, 1395-1403, 1989 + */ + template <class Params> + static Scalar ambroseWalton_(const Params ¶ms, Scalar T) + { + typedef typename Params::Component Component; + + Scalar Tr = T / Component::criticalTemperature(); + Scalar tau = 1 - Tr; + Scalar omega = Component::acentricFactor(); + + Scalar f0 = (tau*(-5.97616 + std::sqrt(tau)*(1.29874 - tau*0.60394)) - 1.06841*std::pow(tau, 5))/Tr; + Scalar f1 = (tau*(-5.03365 + std::sqrt(tau)*(1.11505 - tau*5.41217)) - 7.46628*std::pow(tau, 5))/Tr; + Scalar f2 = (tau*(-0.64771 + std::sqrt(tau)*(2.41539 - tau*4.26979)) + 3.25259*std::pow(tau, 5))/Tr; + + return Component::criticalPressure()*std::exp(f0 + omega * (f1 + omega*f2)); + }; + + /*! + * \brief Returns the difference between the liquid and the gas phase + * fugacities in [bar] + * + * \param p Pressure [bar] + * + * \param VmLiquid Molar volume of the liquid phase [cm^3/mol] + * + * \param VmGas Molar volume of the gas phase [cm^3/mol] + */ + template <class Params> + static Scalar fugacityDifference_(const Params ¶ms, + Scalar T, + Scalar p, + Scalar VmLiquid, + Scalar VmGas) + { return fugacity(params, T, p, VmLiquid) - fugacity(params, T, p, VmGas); }; +}; + +} // end namepace + +#endif diff --git a/dumux/material/eos/pengrobinsonmixture.hh b/dumux/material/eos/pengrobinsonmixture.hh new file mode 100644 index 0000000000000000000000000000000000000000..06e994c84c1b9e2423a8316eabac7575d9fb0ad2 --- /dev/null +++ b/dumux/material/eos/pengrobinsonmixture.hh @@ -0,0 +1,157 @@ +/***************************************************************************** + * Copyright (C) 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 Implements the Peng-Robinson equation of state for a + * mixture. + */ +#ifndef DUMUX_PENG_ROBINSON_MIXTURE_HH +#define DUMUX_PENG_ROBINSON_MIXTURE_HH + +#include <stdlib.h> +#include "pengrobinson.hh" + +#include <dumux/material/constants.hh> + +namespace Dumux +{ + +/*! + * \brief Implements the Peng-Robinson equation of state for a + * mixture. + */ +template <class Scalar, class StaticParameters> +class PengRobinsonMixture +{ + enum { numComponents = StaticParameters::numComponents }; + typedef Dumux::PengRobinson<Scalar> PengRobinson; + + // this class cannot be instantiated! + PengRobinsonMixture() {}; + + // the ideal gas constant + static constexpr Scalar R = Dumux::Constants<Scalar>::R; + + // the u and w parameters as given by the Peng-Robinson EOS + static constexpr Scalar u = 2.0; + static constexpr Scalar w = -1.0; + +public: + /*! + * \brief Computes molar volumes where the Peng-Robinson EOS is + * true. + * + * \return Number of solutions. + */ + template <class MutableParams, class FluidState> + static int computeMolarVolumes(Scalar *Vm, + const MutableParams ¶ms, + int phaseIdx, + const FluidState &fs) + { + return PengRobinson::computeMolarVolumes(Vm, params, phaseIdx, fs); + } + + /*! + * \brief Returns the fugacity coefficient of an individual + * component in the phase. + * + * The fugacity coefficient \f$\phi_i\f$ of a component $i$ is + * defined as + * \f[ + f_i = \phi_i x_i \;, + \f] + * where \f$f_i\f$ is the component's fugacity and \f$x_i\f$ is + * the component's mole fraction. + * + * See: + * + * R. Reid, et al.: The Properties of Gases and Liquids, + * 4th edition, McGraw-Hill, 1987, pp. 42-44, 143-145 + */ + template <class Params> + static Scalar computeFugacityCoeff(const Params ¶ms, + int phaseIdx, + int compIdx) + { + // note that we normalize the component mole fractions, so + // that their sum is 100%. This increases numerical stability + // considerably if the fluid state is not physical. + + Scalar Vm = params.molarVolume(phaseIdx); + + // Calculate b_i / b + Scalar bi_b = params.bPure(phaseIdx, compIdx) / params.b(phaseIdx); + + // Calculate the compressibility factor + Scalar RT = R*params.temperature(phaseIdx); + Scalar p = params.pressure(phaseIdx); // molar volume in [bar] + Scalar Z = p*Vm/RT; // compressibility factor + + // Calculate A^* and B^* (see: Reid, p. 42) + Scalar Astar = params.a(phaseIdx)*p/(RT*RT); + Scalar Bstar = params.b(phaseIdx)*p/(RT); + + + // calculate delta_i (see: Reid, p. 145) + Scalar deltai = 2*std::sqrt(params.aPure(phaseIdx, compIdx))/params.a(phaseIdx); + Scalar tmp = 0; + for (int j = 0; j < numComponents; ++j) { + tmp += + params.moleFrac(phaseIdx, j) / params.sumMoleFrac(phaseIdx) * + std::sqrt(params.aPure(phaseIdx, j)) + *(1.0 - StaticParameters::interactionCoefficient(compIdx, j)); + }; + deltai *= tmp; + + Scalar base = + (2*Z + Bstar*(u + std::sqrt(u*u - 4*w))) / + (2*Z + Bstar*(u - std::sqrt(u*u - 4*w))); + Scalar expo = Astar/(Bstar*std::sqrt(u*u - 4*w))*(bi_b - deltai); + + Scalar fugCoeff = + std::exp(bi_b*(Z - 1))/std::max(1e-9, Z - Bstar) * + std::pow(base, expo); + + //////// + // limit the fugacity coefficient to a reasonable range: + // + // on one side, we want the mole fraction to be at + // least 10^-3 if the fugacity is at the current pressure + // + fugCoeff = std::min(1e10, fugCoeff); + // + // on the other hand, if the mole fraction of the component is 100%, we want the + // fugacity to be at least 10^-3 Pa + // + fugCoeff = std::max(1e-10, fugCoeff); + /////////// + if (!std::isfinite(fugCoeff)) { + std::cout << "Non finite phi: " << fugCoeff << "\n"; + } + + return fugCoeff; + }; + +}; +} // end namepace Dumux + +#endif diff --git a/dumux/material/eos/pengrobinsonparams.hh b/dumux/material/eos/pengrobinsonparams.hh new file mode 100644 index 0000000000000000000000000000000000000000..19d20cfb54508b912eabc3c20eb112f6c4d4bc48 --- /dev/null +++ b/dumux/material/eos/pengrobinsonparams.hh @@ -0,0 +1,95 @@ +/***************************************************************************** + * Copyright (C) 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 Base class for Peng-Robinson parameters of a + * single-component fluid or a mixture + * + * See: + * + * R. Reid, et al.: The Properties of Gases and Liquids, 4th edition, + * McGraw-Hill, 1987, pp. 43-44 + */ +#ifndef DUMUX_PENG_ROBINSON_PARAMS_HH +#define DUMUX_PENG_ROBINSON_PARAMS_HH + +namespace Dumux +{ +/*! + * \brief Stores and provides access to the Peng-Robinson parameters + * + * See: + * + * R. Reid, et al.: The Properties of Gases and Liquids, 4th edition, + * McGraw-Hill, 1987, pp. 43-44 + */ +template <class Scalar> +class PengRobinsonParams +{ +public: + /*! + * \brief Returns the attractive parameter 'a' of the + * Peng-Robinson fluid. + */ + Scalar a() const + { return a_; } + + /*! + * \brief Returns the repulsive parameter 'b' of the Peng-Robinson + * fluid. + */ + Scalar b() const + { return b_; } + + /*! + * \brief If run under valgrind, this method produces an warning + * if the parameters where not determined correctly. + */ + void checkDefined() const + { +#ifndef NDEBUG + Valgrind::CheckDefined(a_); + Valgrind::CheckDefined(b_); +#endif + }; + + /*! + * \brief Set the attractive parameter 'a' of the Peng-Robinson + * fluid. + */ + void setA(Scalar value) + { a_ = value; } + + /*! + * \brief Set the repulsive parameter 'b' of the Peng-Robinson + * fluid. + */ + void setB(Scalar value) + { b_ = value; } + +protected: + Scalar a_; + Scalar b_; +}; + +} // end namepace + +#endif diff --git a/dumux/material/eos/pengrobinsonparamsmixture.hh b/dumux/material/eos/pengrobinsonparamsmixture.hh new file mode 100644 index 0000000000000000000000000000000000000000..4828ae8f2e7d99d978cd3a179463ac831bd5ea38 --- /dev/null +++ b/dumux/material/eos/pengrobinsonparamsmixture.hh @@ -0,0 +1,177 @@ +/***************************************************************************** + * Copyright (C) 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 The Peng-Robinson parameters for a mixture + * + * See: + * + * R. Reid, et al.: The Properties of Gases and Liquids, 4th edition, + * McGraw-Hill, 1987, pp. 43-44 + */ +#ifndef DUMUX_PENG_ROBINSON_PARAMS_MIXTURE_HH +#define DUMUX_PENG_ROBINSON_PARAMS_MIXTURE_HH + +#include "pengrobinsonparams.hh" +#include "pengrobinson.hh" + +#include <dumux/material/constants.hh> + +namespace Dumux +{ +/*! + * \brief The mixing rule for the Peng-Robinson equation of state as given in Reid, p. 82 + * + * See: + * + * R. Reid, et al.: The Properties of Gases and Liquids, 4th edition, + * McGraw-Hill, 1987, p. 82 + */ +template <class Scalar, class StaticParams, int phaseIdx> +class PengRobinsonParamsMixture : public PengRobinsonParams<Scalar> +{ + typedef Dumux::PengRobinsonParams<Scalar> ParentType; + + // Peng-Robinson parameters for pure substances + typedef Dumux::PengRobinsonParams<Scalar> PureParams; + + // The Peng-Robinson EOS for this mixture + typedef Dumux::PengRobinson<Scalar> PengRobinson; + + // number of components of which the fluid is composed + enum { numComponents = StaticParams::numComponents }; + + // ideal gas constant + static constexpr Scalar R = Dumux::Constants<Scalar>::R; + +public: + typedef StaticParams StaticParameters; + + /*! + * \brief Update Peng-Robinson parameters for the pure components. + */ + template <class FluidState> + void updatePure(const FluidState &fluidState) + { + updatePure(fluidState.temperature(phaseIdx), + fluidState.pressure(phaseIdx)); + } + + /*! + * \brief Update Peng-Robinson parameters for the pure components. + * + * This method is given by the SPE5 paper. + */ + void updatePure(Scalar temperature, Scalar pressure) + { + // Calculate the Peng-Robinson parameters of the pure + // components + // + // See: R. Reid, et al.: The Properties of Gases and Liquids, + // 4th edition, McGraw-Hill, 1987, p. 43 + for (int i = 0; i < numComponents; ++i) { + Scalar pc = StaticParams::criticalPressure(i); + Scalar omega = StaticParams::acentricFactor(i); + Scalar Tr = temperature/StaticParams::criticalTemperature(i); + Scalar RTc = R*StaticParams::criticalTemperature(i); + Scalar f_omega = 0.37464 + omega*(1.54226 - omega*0.26992); + + Scalar tmp = 1 + f_omega*(1 - std::sqrt(Tr)); + this->pureParams_[i].setA(0.4572355*RTc*RTc/pc + * + tmp*tmp); + this->pureParams_[i].setB(0.0777961 * RTc / pc); + } + } + + /*! + * \brief Calculates the "a" and "b" Peng-Robinson parameters for + * the mixture. + * + * The updatePure() method needs to be called _before_ calling + * this method! + */ + template <class FluidState> + void updateMix(const FluidState &fluidState) + { + // Calculate the Peng-Robinson parameters of the mixture + // + // See: R. Reid, et al.: The Properties of Gases and Liquids, + // 4th edition, McGraw-Hill, 1987, p. 82 + Scalar a = 0; + Scalar b = 0; + for (int i = 0; i < numComponents; ++i) { + Scalar xi = fluidState.moleFrac(phaseIdx, i); + for (int j = i; j < numComponents; ++j) { + Scalar xj = fluidState.moleFrac(phaseIdx, j); + + // interaction coefficient as given in SPE5 + Scalar Psi = StaticParams::interactionCoefficient(i, j); + + // mixing rule from Reid, page 82 + a += xi*xj*std::sqrt(pureParams_[i].a()*pureParams_[j].a())*(1 - Psi); + } + + // mixing rule from Reid, page 82 + b += xi * pureParams_[i].b(); + } + + this->setA(a); + this->setB(b); + } + + /*! + * \brief Returns the Peng-Robinson parameters for a pure component. + */ + const PureParams &operator[](int compIdx) const + { + assert(0 <= compIdx && compIdx < numComponents); + return pureParams_[compIdx]; + } + + /*! + * \brief If run under valgrind, this method produces an warning + * if the parameters where not determined correctly. + */ + void checkDefined() const + { +#ifndef NDEBUG + ParentType::checkDefined(); + for (int i = 0; i < numComponents; ++i) + pureParams_[i].checkDefined(); +#endif + }; + + /*! + * \brief Return the Peng-Robinson parameters of a pure substance, + */ + const PureParams &pureParams(int compIdx) const + { return pureParams_[compIdx]; } + + +protected: + PureParams pureParams_[numComponents]; +}; + + +} // end namepace + +#endif diff --git a/dumux/material/eos/pengrobinsonparamspure.hh b/dumux/material/eos/pengrobinsonparamspure.hh new file mode 100644 index 0000000000000000000000000000000000000000..eebad5abb637c0475a13d85db0d60ef5f5f84550 --- /dev/null +++ b/dumux/material/eos/pengrobinsonparamspure.hh @@ -0,0 +1,156 @@ +/***************************************************************************** + * Copyright (C) 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 The Peng-Robinson parameters for a pure component + * + * See: + * + * R. Reid, et al.: The Properties of Gases and Liquids, 4th edition, + * McGraw-Hill, 1987, pp. 43-44 + */ +#ifndef DUMUX_PENG_ROBINSON_PARAMS_PURE_HH +#define DUMUX_PENG_ROBINSON_PARAMS_PURE_HH + +#include "pengrobinsonparams.hh" + +#include <dumux/material/constants.hh> + +namespace Dumux +{ +/*! + * \brief Stores, provides access to and calculates the Peng-Robinson + * parameters of a pure component. + * + * See: + * + * R. Reid, et al.: The Properties of Gases and Liquids, 4th edition, + * McGraw-Hill, 1987, pp. 43-44 + */ +template <class Scalar, class ComponentT> +class PengRobinsonParamsPure : public PengRobinsonParams<Scalar> +{ + typedef PengRobinsonParams<Scalar> ParentType; + + // the ideal gas constant + static const Scalar R = Dumux::Constants<Scalar>::R; + +public: + typedef ComponentT Component; + + /*! + * \brief Calculates the "a" and "b" Peng-Robinson parameters for + * the component. + * + * See: + * + * R. Reid, et al.: The Properties of Gases and Liquids, 4th edition, + * McGraw-Hill, 1987, pp. 43-44 + */ + void update(Scalar T, Scalar p) + { + temperature_ = T; + pressure_ = p; + + Scalar pc = Component::criticalPressure(); + Scalar omega = Component::acentricFactor(); + Scalar Tr = T/Component::criticalTemperature(); + Scalar RTc = R*Component::criticalTemperature(); + Scalar f_omega = 0.37464 + omega*(1.54226 - omega*0.26992); + Scalar tmp = 1.0 + f_omega*(1.0 - std::sqrt(Tr)); + tmp = tmp*tmp; + + Scalar a = 0.4572355 * RTc*RTc/pc * tmp; + Scalar b = 0.0777961 * RTc/pc; + + this->setA(a); + this->setB(b); + } + + /*! + * \brief Sets the molar volume [m^3/mol] of the substance. + * + * The phaseIdx parameter is there to adhere to the common + * interface with the multi-phase stuff and is just ignored. + */ + void setMolarVolume(int phaseIdx, Scalar Vm) + { setMolarVolume(Vm); } + + /*! + * \brief Sets the molar volume [m^3/mol] of the substance. + */ + void setMolarVolume(Scalar Vm) + { molarVolume_ = Vm; } + + /*! + * \brief Returns the temperature [K] of the system. + * + * The phaseIdx parameter is there to adhere to the common + * interface with the multi-phase stuff and is just ignored. + */ + Scalar temperature(int phaseIdx = 0) const + { return temperature_; } + + /*! + * \brief Returns the pressure [Pa] of the system. + * + * The phaseIdx parameter is there to adhere to the common + * interface with the multi-phase stuff and is just ignored. + */ + Scalar pressure(int phaseIdx = 0) const + { return pressure_; } + + /*! + * \brief Returns the molar volume [m^3/mol] of the substance. + * + * The phaseIdx parameter is there to adhere to the common + * interface with the multi-phase stuff and is just ignored. + */ + Scalar molarVolume(int phaseIdx = 0) const + { return molarVolume_; } + + /*! + * \brief Returns the attractive parameter "a" [Pa (m^3/mol)^2] for the cubic EOS. + * + * The phaseIdx parameter is there to adhere to the common + * interface with the multi-phase stuff and is just ignored. + */ + Scalar a(int phaseIdx = 0) const + { return ParentType::a(); } + + /*! + * \brief Returns the covolume of the substance [m^3/mol] + * + * The phaseIdx parameter is there to adhere to the common + * interface with the multi-phase stuff and is just ignored. + */ + Scalar b(int phaseIdx = 0) const + { return ParentType::b(); } + + +private: + Scalar temperature_; + Scalar pressure_; + Scalar molarVolume_; +}; +} // end namepace + +#endif diff --git a/dumux/material/fluidmatrixinteractions/Makefile.am b/dumux/material/fluidmatrixinteractions/Makefile.am index 631c0d226d1333fc9ac4eef24e2a1d4b8b7d6c94..1e1fef223fd8ee5915237290d11ba55b31b6615d 100644 --- a/dumux/material/fluidmatrixinteractions/Makefile.am +++ b/dumux/material/fluidmatrixinteractions/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = 2p +SUBDIRS = 2p Mp fluidmatrixinteractionsdir = $(includedir)/dumux/material/fluidmatrixinteractions diff --git a/dumux/material/fluidmatrixinteractions/Mp/2padapter.hh b/dumux/material/fluidmatrixinteractions/Mp/2padapter.hh new file mode 100644 index 0000000000000000000000000000000000000000..e0c2c67b0e86eb84ed91680d4a5d845f108c7e2f --- /dev/null +++ b/dumux/material/fluidmatrixinteractions/Mp/2padapter.hh @@ -0,0 +1,82 @@ +/***************************************************************************** + * Copyright (C) 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 2padapter.hh + * + * Makes the twophase capillary pressure-saturation relations + * available under the M-phase API for material laws + */ +#ifndef DUMUX_MP_2P_ADAPTER_HH +#define DUMUX_MP_2P_ADAPTER_HH + +#include <algorithm> + +namespace Dumux +{ +/*! + * \ingroup material + * + * \brief Implements a brookscorey saturation-capillary pressure relation + * + * Implements a brookscorey saturation-capillary pressure relation for + * M-phase fluid systems. + * + * \sa MpBrookscoreyMaterialParams + */ +template <int wPhaseIdx, class TwoPLaw > +class TwoPAdapter +{ + enum { nPhaseIdx = (wPhaseIdx == 0)?1:0 }; + +public: + typedef typename TwoPLaw::Params Params; + typedef typename Params::Scalar Scalar; + enum { numPhases = 2 }; + + /*! + * \brief The capillary pressure-saturation curve. + */ + template <class ContainerT, class FluidState> + static void capillaryPressures(ContainerT &values, + const Params ¶ms, + const FluidState &state) + { + // non-wetting phase gets the capillary pressure added + values[nPhaseIdx] = 0; + + // wetting phase does not get anything added + values[wPhaseIdx] = - TwoPLaw::pC(params, state.saturation(wPhaseIdx)); + } + + /*! + * \brief The relative permeability of all phases. + */ + template <class ContainerT, class FluidState> + static void relativePermeabilities(ContainerT &values, + const Params ¶ms, + const FluidState &state) + { + values[wPhaseIdx] = TwoPLaw::krw(params, state.saturation(wPhaseIdx)); + values[nPhaseIdx] = TwoPLaw::krn(params, state.saturation(wPhaseIdx)); + }; +}; +} + +#endif diff --git a/dumux/material/fluidmatrixinteractions/Mp/2poftadapter.hh b/dumux/material/fluidmatrixinteractions/Mp/2poftadapter.hh new file mode 100644 index 0000000000000000000000000000000000000000..e13abdddaabee8dac199494d5b5e5271d5ad46f4 --- /dev/null +++ b/dumux/material/fluidmatrixinteractions/Mp/2poftadapter.hh @@ -0,0 +1,83 @@ +/***************************************************************************** + * Copyright (C) 2010 by Philipp Nuske * + * 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 2poftadapter.hh + * + * Makes the twophase capillary pressure-saturation relations + * available under the M-phase API for material laws. + * + * Also use the temperature dependent version of the material laws. + */ +#ifndef DUMUX_MP_2P_OFT_ADAPTER_HH +#define DUMUX_MP_2P_OFT_ADAPTER_HH + +#include <algorithm> + +namespace Dumux +{ +/*! + * \ingroup material + * + * \brief Adapts the interface of the MpNc material law to the standard-\Dumux material law. + * + * Also use the temperature dependent version of the material laws. + */ +template <int wPhaseIdx, class TwoPLaw> +class TwoPOfTAdapter +{ + enum { nPhaseIdx = (wPhaseIdx == 0)?1:0 }; + +public: + typedef typename TwoPLaw::Params Params; + typedef typename Params::Scalar Scalar; + enum { numPhases = 2 }; + + /*! + * \brief The capillary pressure-saturation curve. + */ + template <class pcContainerT, class FluidState> + static void capillaryPressures(pcContainerT &pc, + const Params ¶ms, + const FluidState &fluidState) + { + // non-wetting phase gets the capillary pressure added + pc[nPhaseIdx] = 0; + + // wetting phase does not get anything added + pc[wPhaseIdx] = - TwoPLaw::pC(params, fluidState.saturation(wPhaseIdx), fluidState.temperature(wPhaseIdx)); + } + + + + /*! + * \brief The relative permeability of all phases. + */ + template <class krContainerT, class FluidState> + static void relativePermeabilities(krContainerT &kr, + const Params ¶ms, + const FluidState &fluidState) + { + kr[wPhaseIdx] = TwoPLaw::krw(params, fluidState.saturation(wPhaseIdx)); + kr[nPhaseIdx] = TwoPLaw::krn(params, fluidState.saturation(wPhaseIdx)); + }; +}; +} + +#endif diff --git a/dumux/material/fluidmatrixinteractions/Mp/Mpbrookscorey.hh b/dumux/material/fluidmatrixinteractions/Mp/Mpbrookscorey.hh new file mode 100644 index 0000000000000000000000000000000000000000..70058f0c5d3d233987423678a69a2acfa046e83b --- /dev/null +++ b/dumux/material/fluidmatrixinteractions/Mp/Mpbrookscorey.hh @@ -0,0 +1,152 @@ +/***************************************************************************** + * 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 Mpbrookscoreymaterial.hh + * + * Implements a Brooks-Corey saturation-capillary pressure relation + * for M-phase fluid systems. + */ +#ifndef DUMUX_MP_BROOKSCOREY_MATERIAL_HH +#define DUMUX_MP_BROOKSCOREY_MATERIAL_HH + +#include "Mpbrookscoreymaterialparams.hh" + +#include <algorithm> + +namespace Dumux +{ +/*! + * \ingroup material + * + * \brief Implements a brookscorey saturation-capillary pressure relation + * + * Implements a brookscorey saturation-capillary pressure relation for + * M-phase fluid systems. + * + * \sa MpBrookscoreyMaterialParams + */ +template <int numPhasesV, class ScalarT, + class ParamsT = MpBrookscoreyMaterialParams<numPhasesV, ScalarT> > +class MpBrookscoreyMaterial +{ +public: + typedef ParamsT Params; + typedef typename Params::Scalar Scalar; + enum { numPhases = numPhasesV }; + + /*! + * \brief The Brooks-Corey capillary pressure-saturation curve. + * + * The Brooks-Corey material law is given by the relation: + * \f[ + p_i - p_{i - 1} = p_{e,i}\overline{S}_{i}^{-1/\alpha} + \f] + */ + template <class pcContainerT, class SatContainerT> + static void pC(pcContainerT &pc, + const Params ¶ms, + const SatContainerT &saturations, + Scalar temperature) + { + for (int i = 0; i < numPhases; ++i) { + Scalar S = saturations[i]; + assert(0 <= S && S <= 1); + pc[i] = + params.entryPressure(i) * + std::pow(S, -1.0/params.alpha(i)); + } + } + + /*! + * \brief The saturation-capillary pressure curve. + * + * This is the inverse of the capillary pressure-saturation curve: + * \f[ + S_w = 1 - \frac{p_C - p_{C,entry}}{p_{C,max} - p_{C,entry}} + \f] + * + * \param pC Capillary pressure \f$\p_C\f$ + * \return The effective saturaion of the wetting phase \f$\overline{S}_w\f$ + */ + template <class SatContainerT, class pcContainerT> + static void S(SatContainerT &saturations, + const Params ¶ms, + const pcContainerT &pc, + Scalar temperature) + { + int refPhaseIdx = -1; + for (int i = 0; i < numPhases; ++i) { + Scalar p_Ci = pc[i]; + assert(0 =< p_Ci); + if (params.entryPressure(i) == 0) { + assert(refPhaseIdx == -1); + refPhaseIdx = i; + continue; + } + + Scalar tmp = pow(p_Ci/params.entryPressure(i), -params.alpha(i)); + saturations[i] = tmp; + } + } + +#warning TODO +#if 0 + /*! + * \brief Returns the partial derivative of the capillary + * pressure to the effective saturation. + * + * This is equivalent to + * \f[ + \frac{\partial p_C}{\partial \overline{S}_w} = + - (p_{C,max} - p_{C,min}) + \f] + */ + static Scalar dpC_dSw(const Params ¶ms, Scalar Swe) + { + return - (params.maxPC() - params.entryPC()); + } + + /*! + * \brief Returns the partial derivative of the effective + * saturation to the capillary pressure. + */ + static Scalar dSw_dpC(const Params ¶ms, Scalar pC) + { + return - 1/(params.maxPC() - params.entryPC()); + } +#endif + + /*! + * \brief The relative permeability of all phases. + */ + template <class krContainerT, class SatContainerT> + static void kr(krContainerT &kr, + const Params ¶ms, + const SatContainerT &saturations, + Scalar temperature) + { + for (int i = 0; i < numPhases; ++i) + // TODO: probably incorrect! + kr[i] = pow(saturations[i], 2.0/params.alpha(i) + 3); + }; +}; +} + +#endif diff --git a/dumux/material/fluidmatrixinteractions/Mp/Mpbrookscoreyparams.hh b/dumux/material/fluidmatrixinteractions/Mp/Mpbrookscoreyparams.hh new file mode 100644 index 0000000000000000000000000000000000000000..bcc373110ebfdf782b0aefa36e5aa6fc6d75bc48 --- /dev/null +++ b/dumux/material/fluidmatrixinteractions/Mp/Mpbrookscoreyparams.hh @@ -0,0 +1,83 @@ +/***************************************************************************** + * Copyright (C) 2008-2010 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 Mpbrookscoreymaterialparams.hh + * + * Reference implementation of parameters for the M-phase brookscorey + * material material. + */ +#ifndef DUMUX_MP_BROOKS_COREY_MATERIAL_PARAMS_HH +#define DUMUX_MP_BROOKS_COREY_MATERIAL_PARAMS_HH + +namespace Dumux +{ +/*! + * \brief Reference implementation of a parameter class for the + * M-phase Brooks-Corey material law. + */ +template<int numPhasesV, class ScalarT> +class MpBrooksCoreyMaterialParams +{ +public: + typedef ScalarT Scalar; + enum { numPhases = numPhasesV }; + + MpBrooksCoreyMaterialParams() + { + for (int i = 0; i < numPhases; ++i) { + setEntryPressure(i, 0.0); + setLambda(i, 0.0); + } + } + + /*! + * \brief Return the entry pressure for a phase. + */ + Scalar entryPressure(int phaseIdx) const + { return entryPressure_[phaseIdx]; } + + /*! + * \brief Set the entry pressure for a phase. + */ + void setEntryPressure(int phaseIdx, Scalar val) const + { entryPressure_[phaseIdx] = val; } + + /*! + * \brief Return the alpha shape parameter of the Brooks-Corey law + * for a phase. + */ + Scalar alpha(int phaseIdx) const + { return alpha_[phaseIdx]; } + + /*! + * \brief Set the alpha shape parameter of the Brooks-Corey law + * for a phase. + */ + void setLambda(int phaseIdx, Scalar val) const + { alpha_[phaseIdx] = val; } + +private: + Scalar entryPressure_[numPhases]; + Scalar alpha_[numPhases]; +}; +} // namespace Dumux + +#endif diff --git a/dumux/material/fluidmatrixinteractions/Mp/Mplinearmaterial.hh b/dumux/material/fluidmatrixinteractions/Mp/Mplinearmaterial.hh new file mode 100644 index 0000000000000000000000000000000000000000..15583d9a12857147320687bf609c85d3242fcf66 --- /dev/null +++ b/dumux/material/fluidmatrixinteractions/Mp/Mplinearmaterial.hh @@ -0,0 +1,118 @@ +/***************************************************************************** + * 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 Mplinearmaterial.hh + * + * Implements a linear saturation-capillary pressure relation for + * M-phase fluid systems. + */ +#ifndef MP_LINEAR_MATERIAL_HH +#define MP_LINEAR_MATERIAL_HH + +#include "Mplinearmaterialparams.hh" + +#include <algorithm> + +namespace Dumux +{ +/*! + * \ingroup material + * + * \brief Implements a linear saturation-capillary pressure relation + * + * Implements a linear saturation-capillary pressure relation for + * M-phase fluid systems. + * + * \sa MpLinearMaterialParams + */ +template <int numPhasesV, class ScalarT, class ParamsT = MpLinearMaterialParams<numPhasesV, ScalarT> > +class MpLinearMaterial +{ +public: + typedef ParamsT Params; + typedef typename Params::Scalar Scalar; + enum { numPhases = numPhasesV }; + + /*! + * \brief The linear capillary pressure-saturation curve. + * + * This material law is linear: + * \f[ + p_C = (1 - \overline{S}_w) (p_{C,max} - p_{C,entry}) + p_{C,entry} + \f] + * + * \param Swe Effective saturation of of the wetting phase \f$\overline{S}_w\f$ + */ + template <class ContainerT, class FluidState> + static void capillaryPressures(ContainerT &values, + const Params ¶ms, + const FluidState &state) + { + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + Scalar S = state.saturation(phaseIdx); + values[phaseIdx] = + S*params.pcMaxSat(phaseIdx) + + (1 - S)*params.pcMinSat(phaseIdx); + } + } + +#if 0 +#warning TODO + /*! + * \brief The saturation-capillary pressure curve. + * + * This is the inverse of the capillary pressure-saturation curve: + * \f[ + S_w = 1 - \frac{p_C - p_{C,entry}}{p_{C,max} - p_{C,entry}} + \f] + * + * \param pC Capillary pressure \f$\p_C\f$ + * \return The effective saturaion of the wetting phase \f$\overline{S}_w\f$ + */ + template <class SatContainerT, class FluidState> + static void saturations(SatContainerT &saturations, + const Params ¶ms, + const FluidState &state) + { + for (int i = 0; i < numPhases; ++i) { + saturations[i] = + (pc[i] - params.pcMaxSat(i)) + / + (params.pcMinSat(i) - params.pcMaxSat(i)); + } + } + +#endif + + /*! + * \brief The relative permeability of all phases. + */ + template <class ContainerT, class FluidState> + static void relativePermeabilities(ContainerT &values, + const Params ¶ms, + const FluidState &state) + { + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + values[phaseIdx] = std::max(std::min(state.saturation(phaseIdx),1.0),0.0); + }; +}; +} + +#endif diff --git a/dumux/material/fluidmatrixinteractions/Mp/Mplinearmaterialparams.hh b/dumux/material/fluidmatrixinteractions/Mp/Mplinearmaterialparams.hh new file mode 100644 index 0000000000000000000000000000000000000000..57ac059d998624611975e971904d1282e9a983af --- /dev/null +++ b/dumux/material/fluidmatrixinteractions/Mp/Mplinearmaterialparams.hh @@ -0,0 +1,110 @@ +/***************************************************************************** + * Copyright (C) 2008-2010 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 linearmaterialparams.hh + * + * Reference implementation of parameters for the M-phase linear + * material material. + */ +#ifndef MP_LINEAR_MATERIAL_PARAMS_HH +#define MP_LINEAR_MATERIAL_PARAMS_HH + +namespace Dumux +{ +/*! + * \brief Reference implementation of params for the linear M-phase + * material material. + */ +template<int numPhasesV, class ScalarT> +class MpLinearMaterialParams +{ +public: + typedef ScalarT Scalar; + enum { numPhases = numPhasesV }; + + MpLinearMaterialParams() + { + for (int i = 0; i < numPhases; ++i) { + setPcMinSat(i, 0.0); + setPcMaxSat(i, 0.0); + } + } + + /*! + * \brief Return the threshold saturation at which the relative + * permeability starts to get regularized. + * + * This is simply 10% + */ + Scalar Sreg(int phaseIdx) const + { return 0.10; } + + /*! + * \brief Return the capillary pressure for a phase \alpha at S_\alpha=0. + */ + Scalar pcMinSat(int phaseIdx) const + { return pcMinSat_[phaseIdx]; } + + /*! + * \brief Set the capillary pressure for a phase \alpha at S_\alpha=0. + */ + void setPcMinSat(int phaseIdx, Scalar val) + { pcMinSat_[phaseIdx] = val; } + + /*! + * \brief Return the capillary pressure for a phase \alpha at S_\alpha=1. + */ + Scalar pcMaxSat(int phaseIdx) const + { return pcMaxSat_[phaseIdx]; } + + /*! + * \brief Set the capillary pressure for a phase \alpha at S_\alpha=1. + */ + void setPcMaxSat(int phaseIdx, Scalar val) + { pcMaxSat_[phaseIdx] = val; } + + /*! + * \brief Return the threshold saturation respective phase below + * which the relative permeability gets regularized. + * + * This is just 5%. If you need a different value, write your own + * parameter class. + */ + Scalar krLowS(int phaseIdx) const + { return 0.05; } + + /*! + * \brief Return the threshold saturation of the respective phase + * above which the relative permeability gets regularized. + * + * This is just 95%. If you need a different value, write your own + * parameter class. + */ + Scalar krHighS(int phaseIdx) const + { return 0.95; } + +private: + Scalar pcMaxSat_[numPhases]; + Scalar pcMinSat_[numPhases]; +}; +} // namespace Dumux + +#endif diff --git a/dumux/material/fluidstates/Makefile.am b/dumux/material/fluidstates/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..70f8f174617e3dd8488fc44a23ad1680eef4c247 --- /dev/null +++ b/dumux/material/fluidstates/Makefile.am @@ -0,0 +1 @@ +include $(top_srcdir)/am/global-rules diff --git a/dumux/material/fluidstates/equilibriumfluidstate.hh b/dumux/material/fluidstates/equilibriumfluidstate.hh new file mode 100644 index 0000000000000000000000000000000000000000..5f2e2d611c661cf89049dedfe524f225cfbd4ae2 --- /dev/null +++ b/dumux/material/fluidstates/equilibriumfluidstate.hh @@ -0,0 +1,349 @@ +/***************************************************************************** + * Copyright (C) 2010-2011 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 Represents all relevant thermodynamic quantities of a + * multi-phase, multi-component fluid system assuming + * thermodynamic equilibrium. + */ +#ifndef DUMUX_EQUILIBRIUM_FLUID_STATE_HH +#define DUMUX_EQUILIBRIUM_FLUID_STATE_HH + +#include <dumux/common/valgrind.hh> + +namespace Dumux +{ +/*! + * \brief Represents all relevant thermodynamic quantities of a + * multi-phase, multi-component fluid system assuming + * thermodynamic equilibrium. + */ +template <class Scalar, class StaticParameters> +class EquilibriumFluidState +{ +public: + enum { numComponents = StaticParameters::numComponents }; + enum { numPhases = StaticParameters::numPhases }; + + EquilibriumFluidState() + { Valgrind::SetUndefined(*this); } + + template <class FluidState> + EquilibriumFluidState(FluidState &fs) + { assign(fs); } + + /***************************************************** + * Generic access to fluid properties (No assumptions + * on thermodynamic equilibrium required) + *****************************************************/ + /*! + * \brief Returns the saturation of a phase [] + */ + Scalar saturation(int phaseIdx) const + { return saturation_[phaseIdx]; } + + /*! + * \brief The mole fraction of a component in a phase [] + */ + Scalar moleFrac(int phaseIdx, int compIdx) const + { return moleFrac_[phaseIdx][compIdx]; } + + /*! + * \brief The mass fraction of a component in a phase [] + */ + Scalar massFrac(int phaseIdx, int compIdx) const + { + return + sumMassFrac(phaseIdx)* + molarity(phaseIdx, compIdx)* + StaticParameters::molarMass(compIdx) + / + density(phaseIdx); + } + + /*! + * \brief The sum of all component mole fractions in a phase [] + * + * We define this to be the same as the sum of all mass fractions. + */ + Scalar sumMoleFrac(int phaseIdx) const + { return sumMoleFrac_[phaseIdx]; } + + /*! + * \brief The sum of all component mass fractions in a phase [] + * + * We define this to be the same as the sum of all mole fractions. + */ + Scalar sumMassFrac(int phaseIdx) const + { return sumMoleFrac_[phaseIdx]; } + + /*! + * \brief The mean molar mass of a fluid phase [kg/mol] + */ + Scalar meanMolarMass(int phaseIdx) const + { return meanMolarMass_[phaseIdx]; } + + /*! + * \brief The concentration of a component in a phase [mol/m^3] + * + * This is usually just called "molar concentration" or just + * "concentration", but there are many other (though less common) + * measures for concentration. + * + * http://en.wikipedia.org/wiki/Concentration + */ + Scalar molarity(int phaseIdx, int compIdx) const + { return molarDensity(phaseIdx)*moleFrac(phaseIdx, compIdx); } + + /*! + * \brief The fugacity of a component in a phase [Pa] + */ + Scalar fugacity(int phaseIdx, int compIdx) const + { return fugacityCoeff(phaseIdx, compIdx)*moleFrac(phaseIdx, compIdx)*pressure(phaseIdx); } + + /*! + * \brief The fugacity coefficient of a component in a phase [Pa] + */ + Scalar fugacityCoeff(int phaseIdx, int compIdx) const + { return fugacityCoeff_[phaseIdx][compIdx]; } + + /*! + * \brief The molar volume of a fluid phase [m^3/mol] + */ + Scalar molarVolume(int phaseIdx) const + { return molarVolume_[phaseIdx]; } + + /*! + * \brief The mass density of a fluid phase [kg/m^3] + */ + Scalar density(int phaseIdx) const + { return molarDensity(phaseIdx)*meanMolarMass(phaseIdx); } + + /*! + * \brief The molar density of a fluid phase [mol/m^3] + */ + Scalar molarDensity(int phaseIdx) const + { return 1/molarVolume(phaseIdx); } + + /*! + * \brief The temperature of a fluid phase [K] + */ + Scalar temperature(int phaseIdx) const + { return temperature_; } + + /*! + * \brief The pressure of a fluid phase [Pa] + */ + Scalar pressure(int phaseIdx) const + { return pressure_[phaseIdx]; } + + /*! + * \brief The specific enthalpy of a fluid phase [J/kg] + */ + Scalar enthalpy(int phaseIdx) const + { return enthalpy_[phaseIdx]; } + + /*! + * \brief The specific internal energy of a fluid phase [J/kg] + */ + Scalar internalEnergy(int phaseIdx) const + { return enthalpy(phaseIdx) - pressure(phaseIdx)/(density(phaseIdx)); } + + /*! + * \brief The dynamic viscosity of a fluid phase [Pa s] + */ + Scalar viscosity(int phaseIdx) const + { return viscosity_[phaseIdx]; } + + + /***************************************************** + * Access to fluid properties which only make sense + * if assuming thermodynamic equilibrium + *****************************************************/ + + /*! + * \brief The temperature within the domain [K] + */ + Scalar temperature() const + { return temperature_; } + + /*! + * \brief The capillary pressure [Pa] between a phase and a + * reference phase. + * + * In order to make the term "capillary pressure" meaningful in a + * physical sense, mechanic equilibrium needs to be assumed. + */ + Scalar capillaryPressure(int refPhaseIdx, int phaseIdx) const + { return pressure_[phaseIdx] - pressure_[refPhaseIdx]; } + + /*! + * \brief The fugacity of a component + * + * This assumes chemical equilibrium. + */ + Scalar fugacity(int compIdx) const + { return fugacity(0, compIdx); } + + + /***************************************************** + * Setter methods. Note that these are not part of the + * generic FluidState interface but specific for each + * implementation... + *****************************************************/ + + /*! + * \brief Retrieve all parameters from an arbitrary fluid + * state. + * + * \note If the other fluid state object is inconsistent with the + * thermodynamic equilibrium, the result of this method is + * undefined. + */ + template <class FluidState> + void assign(const FluidState &fs) + { + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + for (int compIdx = 0; compIdx < numComponents; ++compIdx) { + moleFrac_[phaseIdx][compIdx] = fs.moleFrac(phaseIdx, compIdx); + fugacityCoeff_[phaseIdx][compIdx] = fs.fugacityCoeff(phaseIdx, compIdx); + } + meanMolarMass_[phaseIdx] = fs.meanMolarMass(phaseIdx); + pressure_[phaseIdx] = fs.pressure(phaseIdx); + saturation_[phaseIdx] = fs.saturation(phaseIdx); + molarVolume_[phaseIdx] = fs.molarVolume(phaseIdx); + enthalpy_[phaseIdx] = fs.enthalpy(phaseIdx); + viscosity_[phaseIdx] = fs.viscosity(phaseIdx); + } + temperature_ = fs.temperature(0); + }; + + /*! + * \brief Set the temperature [K] of a fluid phase + */ + void setTemperature(Scalar value) + { temperature_ = value; } + + /*! + * \brief Set the fluid pressure of a phase [Pa] + */ + void setPressure(int phaseIdx, Scalar value) + { pressure_[phaseIdx] = value; } + + /*! + * \brief Set the saturation of a phase [] + */ + void setSaturation(int phaseIdx, Scalar value) + { saturation_[phaseIdx] = value; } + + /*! + * \brief Set the mole fraction of a component in a phase [] + */ + void setMoleFrac(int phaseIdx, int compIdx, Scalar value) + { moleFrac_[phaseIdx][compIdx] = value; } + + /*! + * \brief Set the fugacity of a component in a phase [] + */ + void setFugacityCoeff(int phaseIdx, int compIdx, Scalar value) + { fugacityCoeff_[phaseIdx][compIdx] = value; } + + /*! + * \brief Set the molar volume of a phase [m^3/mol] + */ + void setMolarVolume(int phaseIdx, Scalar value) + { molarVolume_[phaseIdx] = value; } + + /*! + * \brief Set the specific enthalpy of a phase [J/m^3] + */ + void setEnthalpy(int phaseIdx, Scalar value) + { enthalpy_[phaseIdx] = value; } + + /*! + * \brief Set the dynamic viscosity of a phase [Pa s] + */ + void setViscosity(int phaseIdx, Scalar value) + { viscosity_[phaseIdx] = value; } + + /*! + * \brief Calculatate the mean molar mass of a phase given that + * all mole fractions have been set + */ + void updateMeanMolarMass(int phaseIdx) + { + meanMolarMass_[phaseIdx] = 0; + sumMoleFrac_[phaseIdx] = 0; + + for (int compIdx = 0; compIdx < numComponents; ++compIdx) { + sumMoleFrac_[phaseIdx] += moleFrac_[phaseIdx][compIdx]; + meanMolarMass_[phaseIdx] += moleFrac_[phaseIdx][compIdx]*StaticParameters::molarMass(compIdx); + } + sumMoleFrac_[phaseIdx] = std::max(1e-15, sumMoleFrac_[phaseIdx]); + Valgrind::CheckDefined(meanMolarMass_[phaseIdx]); + Valgrind::CheckDefined(sumMoleFrac_[phaseIdx]); + } + + /*! + * \brief Make sure that all attributes are defined. + * + * This method does not do anything if the program is not run + * under valgrind. If it is, then valgrind will print an error + * message if some attributes of the object have not been properly + * defined. + */ + void checkDefined() const + { +#if HAVE_VALGRIND && ! defined NDEBUG + for (int i = 0; i < numPhases; ++i) { + for (int j = 0; j < numComponents; ++j) { + Valgrind::CheckDefined(moleFrac_[i][j]); + Valgrind::CheckDefined(fugacityCoeff_[i][j]); + } + Valgrind::CheckDefined(meanMolarMass_[i]); + Valgrind::CheckDefined(pressure_[i]); + Valgrind::CheckDefined(saturation_[i]); + Valgrind::CheckDefined(molarVolume_[i]); + //Valgrind::CheckDefined(enthalpy_[i]); + Valgrind::CheckDefined(viscosity_[i]); + } + + Valgrind::CheckDefined(temperature_); +#endif // HAVE_VALGRIND + } + +protected: + Scalar moleFrac_[numPhases][numComponents]; + Scalar fugacityCoeff_[numPhases][numComponents]; + + Scalar meanMolarMass_[numPhases]; + Scalar sumMoleFrac_[numPhases]; + Scalar pressure_[numPhases]; + Scalar saturation_[numPhases]; + Scalar molarVolume_[numPhases]; + Scalar enthalpy_[numPhases]; + Scalar viscosity_[numPhases]; + Scalar temperature_; +}; + +} // end namepace Dumux + +#endif diff --git a/dumux/material/fluidstates/genericfluidstate.hh b/dumux/material/fluidstates/genericfluidstate.hh new file mode 100644 index 0000000000000000000000000000000000000000..16a00e2038635ebc10d8d2033e465db78e0974e0 --- /dev/null +++ b/dumux/material/fluidstates/genericfluidstate.hh @@ -0,0 +1,316 @@ +/***************************************************************************** + * Copyright (C) 2010-2011 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 Represents all relevant thermodynamic quantities of a + * multi-phase, multi-component fluid system without using + * any assumptions. + */ +#ifndef DUMUX_GENERIC_FLUID_STATE_HH +#define DUMUX_GENERIC_FLUID_STATE_HH + +#include <dumux/common/valgrind.hh> + +#include <cmath> +#include <algorithm> + +namespace Dumux +{ +/*! + * \brief Represents all relevant thermodynamic quantities of a + * multi-phase, multi-component fluid system without using + * any assumptions. + */ +template <class Scalar, class StaticParameters> +class GenericFluidState +{ +public: + enum { numComponents = StaticParameters::numComponents }; + enum { numPhases = StaticParameters::numPhases }; + + GenericFluidState() + { Valgrind::SetUndefined(*this); } + + /***************************************************** + * Generic access to fluid properties (No assumptions + * on thermodynamic equilibrium required) + *****************************************************/ + /*! + * \brief Returns the saturation of a phase [] + */ + Scalar saturation(int phaseIdx) const + { return saturation_[phaseIdx]; } + + /*! + * \brief The mole fraction of a component in a phase [] + */ + Scalar moleFrac(int phaseIdx, int compIdx) const + { return moleFrac_[phaseIdx][compIdx]; } + + + /*! + * \brief The mass fraction of a component in a phase [] + */ + Scalar massFrac(int phaseIdx, int compIdx) const + { + return + sumMassFrac(phaseIdx) * + moleFrac_[phaseIdx][compIdx] * + StaticParameters::molarMass(compIdx) / + meanMolarMass_[phaseIdx]; + } + + /*! + * \brief The sum of all component mole fractions in a phase [] + * + * We define this to be the same as the sum of all mass fractions. + */ + Scalar sumMoleFrac(int phaseIdx) const + { return sumMoleFrac_[phaseIdx]; } + + /*! + * \brief The sum of all component mass fractions in a phase [] + * + * We define this to be the same as the sum of all mole fractions. + */ + Scalar sumMassFrac(int phaseIdx) const + { return sumMoleFrac_[phaseIdx]; } + + /*! + * \brief The mean molar mass of a fluid phase [kg/mol] + */ + Scalar meanMolarMass(int phaseIdx) const + { return meanMolarMass_[phaseIdx]; } + + /*! + * \brief The molar concentration of a component in a phase [mol/m^3] + * + * This is often just called "concentration", but there are many + * other (though less common) measures for concentration. + * + * http://en.wikipedia.org/wiki/Concentration + */ + Scalar molarity(int phaseIdx, int compIdx) const + { return molarDensity(phaseIdx)*moleFrac(phaseIdx, compIdx); } + + /*! + * \brief The fugacity coefficient of a component in a phase [] + */ + Scalar fugacityCoeff(int phaseIdx, int compIdx) const + { return fugacityCoeff_[phaseIdx][compIdx]; } + + /*! + * \brief The fugacity of a component in a phase [Pa] + */ + Scalar fugacity(int phaseIdx, int compIdx) const + { return pressure_[phaseIdx]*fugacityCoeff_[phaseIdx][compIdx]*moleFrac_[phaseIdx][compIdx]; } + + /*! + * \brief The molar volume of a fluid phase [m^3/mol] + */ + Scalar molarVolume(int phaseIdx) const + { return molarVolume_[phaseIdx]; } + + /*! + * \brief The mass density of a fluid phase [kg/m^3] + */ + Scalar density(int phaseIdx) const + { + return + meanMolarMass_[phaseIdx]/molarVolume_[phaseIdx]; + } + + /*! + * \brief The molar density of a fluid phase [mol/m^3] + */ + Scalar molarDensity(int phaseIdx) const + { return 1/molarVolume(phaseIdx); } + + /*! + * \brief The temperature of a fluid phase [K] + */ + Scalar temperature(int phaseIdx) const + { return temperature_[phaseIdx]; } + + /*! + * \brief The pressure of a fluid phase [Pa] + */ + Scalar pressure(int phaseIdx) const + { return pressure_[phaseIdx]; }; + + /*! + * \brief The specific enthalpy of a fluid phase [J/kg] + */ + Scalar enthalpy(int phaseIdx) const + { return enthalpy_[phaseIdx]; }; + + /*! + * \brief The specific internal energy of a fluid phase [J/kg] + */ + Scalar internalEnergy(int phaseIdx) const + { return enthalpy(phaseIdx) - pressure(phaseIdx)/(density(phaseIdx)); }; + + /*! + * \brief The dynamic viscosity of a fluid phase [Pa s] + */ + Scalar viscosity(int phaseIdx) const + { return viscosity_[phaseIdx]; }; + + /***************************************************** + * Setter methods. Note that these are not part of the + * generic FluidState interface but specific for each + * implementation... + *****************************************************/ + /*! + * \brief Set the temperature [K] of a fluid phase + */ + void setTemperature(int phaseIdx, Scalar value) + { temperature_[phaseIdx] = value; } + + /*! + * \brief Set the fluid pressure of a phase [Pa] + */ + void setPressure(int phaseIdx, Scalar value) + { pressure_[phaseIdx] = value; } + + /*! + * \brief Set the saturation of a phase [] + */ + void setSaturation(int phaseIdx, Scalar value) + { saturation_[phaseIdx] = value; } + + /*! + * \brief Set the mole fraction of a component in a phase [] + */ + void setMoleFrac(int phaseIdx, int compIdx, Scalar value) + { moleFrac_[phaseIdx][compIdx] = value; } + + /*! + * \brief Set the fugacity of a component in a phase [] + */ + void setFugacityCoeff(int phaseIdx, int compIdx, Scalar value) + { fugacityCoeff_[phaseIdx][compIdx] = value; } + + /*! + * \brief Set the molar volume of a phase [m^3/mol] + */ + void setMolarVolume(int phaseIdx, Scalar value) + { + molarVolume_[phaseIdx] = value; + } + + /*! + * \brief Set the specific enthalpy of a phase [J/m^3] + */ + void setEnthalpy(int phaseIdx, Scalar value) + { enthalpy_[phaseIdx] = value; } + + /*! + * \brief Set the dynamic viscosity of a phase [Pa s] + */ + void setViscosity(int phaseIdx, Scalar value) + { viscosity_[phaseIdx] = value; } + + /*! + * \brief Calculatate the mean molar mass of a phase given that + * all mole fractions have been set + */ + void updateMeanMolarMass(int phaseIdx) + { + meanMolarMass_[phaseIdx] = 0; + sumMoleFrac_[phaseIdx] = 0; + + for (int compIdx = 0; compIdx < numComponents; ++compIdx) { + sumMoleFrac_[phaseIdx] += moleFrac_[phaseIdx][compIdx]; + meanMolarMass_[phaseIdx] += moleFrac_[phaseIdx][compIdx]*StaticParameters::molarMass(compIdx); + } + sumMoleFrac_[phaseIdx] = std::max(1e-15, sumMoleFrac_[phaseIdx]); + Valgrind::CheckDefined(meanMolarMass_[phaseIdx]); + Valgrind::CheckDefined(sumMoleFrac_[phaseIdx]); + } + + /*! + * \brief Retrieve all parameters from an arbitrary fluid + * state. + */ + template <class FluidState> + void assign(const FluidState& fs) + { + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + for (int compIdx = 0; compIdx < numComponents; ++compIdx) { + moleFrac_[phaseIdx][compIdx] = fs.moleFrac(phaseIdx, compIdx); + fugacityCoeff_[phaseIdx][compIdx] = fs.fugacityCoeff(phaseIdx, compIdx); + } + updateMeanMolarMass(phaseIdx); + + pressure_[phaseIdx] = fs.pressure(phaseIdx); + saturation_[phaseIdx] = fs.saturation(phaseIdx); + molarVolume_[phaseIdx] = fs.molarVolume(phaseIdx); + enthalpy_[phaseIdx] = fs.enthalpy(phaseIdx); + viscosity_[phaseIdx] = fs.viscosity(phaseIdx); + temperature_[phaseIdx] = fs.temperature(phaseIdx); + } + }; + + /*! + * \brief Make sure that all attributes are defined. + * + * This method does not do anything if the program is not run + * under valgrind. If it is, then valgrind will print an error + * message if some attributes of the object have not been properly + * defined. + */ + void checkDefined() const + { +#if HAVE_VALGRIND && ! defined NDEBUG + for (int i = 0; i < numPhases; ++i) { + for (int j = 0; j < numComponents; ++j) { + Valgrind::CheckDefined(fugacityCoeff_[i][j]); + Valgrind::CheckDefined(moleFrac_[i][j]); + } + Valgrind::CheckDefined(meanMolarMass_[i]); + Valgrind::CheckDefined(pressure_[i]); + Valgrind::CheckDefined(saturation_[i]); + Valgrind::CheckDefined(molarVolume_[i]); + Valgrind::CheckDefined(temperature_[i]); + //Valgrind::CheckDefined(enthalpy_[i]); + Valgrind::CheckDefined(viscosity_[i]); + } +#endif // HAVE_VALGRIND + } + +protected: + Scalar moleFrac_[numPhases][numComponents]; + Scalar fugacityCoeff_[numPhases][numComponents]; + + Scalar meanMolarMass_[numPhases]; + Scalar sumMoleFrac_[numPhases]; + Scalar pressure_[numPhases]; + Scalar saturation_[numPhases]; + Scalar molarVolume_[numPhases]; + Scalar enthalpy_[numPhases]; + Scalar viscosity_[numPhases]; + Scalar temperature_[numPhases]; +}; + +} // end namepace Dumux + +#endif diff --git a/dumux/material/fluidstates/trackinggenericfluidstate.hh b/dumux/material/fluidstates/trackinggenericfluidstate.hh new file mode 100644 index 0000000000000000000000000000000000000000..8960a49dc4386f66d9159310c1101181c5dfac23 --- /dev/null +++ b/dumux/material/fluidstates/trackinggenericfluidstate.hh @@ -0,0 +1,398 @@ +/***************************************************************************** + * Copyright (C) 2011 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 A GenericFluidState which keeps track on which variables changed. + */ +#ifndef DUMUX_TRACKING_GENERIC_FLUID_STATE_HH +#define DUMUX_TRACKING_GENERIC_FLUID_STATE_HH + +#include "genericfluidstate.hh" + +#include <dumux/common/valgrind.hh> + +#include <cmath> +#include <algorithm> + +namespace Dumux +{ +/*! + * \brief A GenericFluidState which keeps track on which variables changed. + */ +template <class Scalar, class StaticParameters> +class TrackingGenericFluidState +{ + typedef Dumux::GenericFluidState<Scalar, StaticParameters> GenericFluidState; +public: + enum { numComponents = StaticParameters::numComponents }; + enum { numPhases = StaticParameters::numPhases }; + + TrackingGenericFluidState() + { + for (int i = 0; i < numPhases; ++i) + changed_[i].raw = ~((unsigned) 0x00); + } + + /***************************************************** + * Generic access to fluid properties (No assumptions + * on thermodynamic equilibrium required) + *****************************************************/ + /*! + * \brief Returns the saturation of a phase [] + */ + Scalar saturation(int phaseIdx) const + { return fs_.saturation(phaseIdx); } + + /*! + * \brief The mole fraction of a component in a phase [] + */ + Scalar moleFrac(int phaseIdx, int compIdx) const + { return fs_.moleFrac(phaseIdx, compIdx); } + + /*! + * \brief The mass fraction of a component in a phase [] + */ + Scalar massFrac(int phaseIdx, int compIdx) const + { return fs_.massFrac(phaseIdx, compIdx); } + + /*! + * \brief The sum of all component mole fractions in a phase [] + * + * We define this to be the same as the sum of all mass fractions. + */ + Scalar sumMoleFrac(int phaseIdx) const + { return fs_.sumMoleFrac(phaseIdx); } + + /*! + * \brief The sum of all component mass fractions in a phase [] + * + * We define this to be the same as the sum of all mole fractions. + */ + Scalar sumMassFrac(int phaseIdx) const + { return fs_.sumMassFrac(phaseIdx); } + + /*! + * \brief The mean molar mass of a fluid phase [kg/mol] + */ + Scalar meanMolarMass(int phaseIdx) const + { return fs_.meanMolarMass(phaseIdx); } + + /*! + * \brief The molar concentration of a component in a phase [mol/m^3] + * + * This is often just called "concentration", but there are many + * other (though less common) measures for concentration. + * + * http://en.wikipedia.org/wiki/Concentration + */ + Scalar molarity(int phaseIdx, int compIdx) const + { return fs_.molarity(phaseIdx, compIdx); } + + /*! + * \brief The fugacity of a component in a phase [Pa] + */ + Scalar fugacity(int phaseIdx, int compIdx) const + { return fs_.fugacity(phaseIdx, compIdx); } + + /*! + * \brief The fugacity coefficient of a component in a phase [] + */ + Scalar fugacityCoeff(int phaseIdx, int compIdx) const + { return fs_.fugacityCoeff(phaseIdx, compIdx); } + + /*! + * \brief The molar volume of a fluid phase [m^3/mol] + */ + Scalar molarVolume(int phaseIdx) const + { return fs_.molarVolume(phaseIdx); } + + /*! + * \brief The mass density of a fluid phase [kg/m^3] + */ + Scalar density(int phaseIdx) const + { return fs_.density(phaseIdx); } + + /*! + * \brief The molar density of a fluid phase [mol/m^3] + */ + Scalar molarDensity(int phaseIdx) const + { return fs_.molarDensity(phaseIdx); } + + /*! + * \brief The temperature of a fluid phase [K] + */ + Scalar temperature(int phaseIdx) const + { return fs_.temperature(phaseIdx); } + + /*! + * \brief The pressure of a fluid phase [Pa] + */ + Scalar pressure(int phaseIdx) const + { return fs_.pressure(phaseIdx); } + + /*! + * \brief The specific enthalpy of a fluid phase [J/kg] + */ + Scalar enthalpy(int phaseIdx) const + { return fs_.enthalpy(phaseIdx); } + + /*! + * \brief The specific internal energy of a fluid phase [J/kg] + */ + Scalar internalEnergy(int phaseIdx) const + { return fs_.internalEnergy(phaseIdx); } + + /*! + * \brief The dynamic viscosity of a fluid phase [Pa s] + */ + Scalar viscosity(int phaseIdx) const + { return fs_.viscosity(phaseIdx); }; + + /***************************************************** + * Setter methods. Note that these are not part of the + * generic FluidState interface but specific for each + * implementation... + *****************************************************/ + /*! + * \brief Set the temperature [K] of a fluid phase + */ + void setTemperature(int phaseIdx, Scalar value) + { + changed_[phaseIdx].fields.temperature = 1; + fs_.setTemperature(phaseIdx, value); + }; + + /*! + * \brief Set the fluid pressure of a phase [Pa] + */ + void setPressure(int phaseIdx, Scalar value) + { + changed_[phaseIdx].fields.pressure = 1; + fs_.setPressure(phaseIdx, value); + }; + + /*! + * \brief Set the saturation of a phase [] + */ + void setSaturation(int phaseIdx, Scalar value) + { + changed_[phaseIdx].fields.saturation = 1; + fs_.setSaturation(phaseIdx, value); + }; + + /*! + * \brief Set the mole fraction of a component in a phase [] + */ + void setMoleFrac(int phaseIdx, int compIdx, Scalar value) + { + changed_[phaseIdx].fields.moleFrac |= 1 << compIdx; + fs_.setMoleFrac(phaseIdx, compIdx, value); + }; + + + /*! + * \brief Set the fugacity of a component in a phase [] + */ + void setFugacityCoeff(int phaseIdx, int compIdx, Scalar value) + { + changed_[phaseIdx].fields.fugacityCoeff |= 1 << compIdx; + fs_.setFugacityCoeff(phaseIdx, compIdx, value); + } + + /*! + * \brief Set the molar volume of a phase [m^3/mol] + */ + void setMolarVolume(int phaseIdx, Scalar value) + { + changed_[phaseIdx].fields.molarVolume = 1; + fs_.setMolarVolume(phaseIdx, value); + } + + /*! + * \brief Set the specific enthalpy of a phase [J/m^3] + */ + void setEnthalpy(int phaseIdx, Scalar value) + { + changed_[phaseIdx].fields.enthalpy = 1; + fs_.setEnthalpy(phaseIdx, value); + } + + /*! + * \brief Set the dynamic viscosity of a phase [Pa s] + */ + void setViscosity(int phaseIdx, Scalar value) + { + changed_[phaseIdx].fields.viscosity = 1; + fs_.setViscosity(phaseIdx, value); + } + + /*! + * \brief Calculatate the mean molar mass of a phase given that + * all mole fractions have been set + */ + void updateMeanMolarMass(int phaseIdx) + { fs_.updateMeanMolarMass(phaseIdx); } + + + /*! + * \brief Reset the dirty tracking for a phase. + */ + void setPhaseClean(int phaseIdx) + { + // set all tracking bits for the phase to zero + changed_[phaseIdx].raw = 0; + } + + /*! + * \brief Reset the dirty tracking of all phases. + */ + void setClean() + { + for (int i = 0; i < numPhases; ++i) + changed_[i].raw = 0; + } + + /*! + * \brief Return if all phases are clean. + */ + bool isClean() const + { + for (int i = 0; i < numPhases; ++i) + if (changed_[i].raw != 0) + return false; + return true; + }; + + /*! + * \brief Find out whether the values for a phase been since the + * last call to cleanPhase(). + */ + bool phaseClean(int phaseIdx) const + { return changed_[phaseIdx].raw == 0; } + + + /*! + * \brief Find out whether a specific phase's temperature has been + * changed. + */ + bool temperatureClean(int phaseIdx) const + { return changed_[phaseIdx].fields.temperature == 0; } + + /*! + * \brief Find out whether a specific phase's saturation has been + * changed. + */ + bool saturationClean(int phaseIdx) const + { return changed_[phaseIdx].fields.saturation == 0; } + + /*! + * \brief Find out whether a specific phase's pressure has been + * changed. + */ + bool pressureClean(int phaseIdx) const + { return changed_[phaseIdx].fields.pressure == 0; } + + /*! + * \brief Find out whether a specific phase's enthalpy has been + * changed. + */ + bool enthalpyClean(int phaseIdx) const + { return changed_[phaseIdx].enthalpy == 0; } + + /*! + * \brief Find out whether a specific phase's temperature has been + * changed. + */ + bool molarVolumeClean(int phaseIdx) const + { return changed_[phaseIdx].fields.molarVolume == 0; } + + /*! + * \brief Find out whether some of the mole fractions of a phase + * have been changed since the last call to + * setCleanPhase(). + */ + bool moleFracsClean(int phaseIdx) const + { return changed_[phaseIdx].fields.moleFrac == 0; } + + /*! + * \brief Find out whether a specific mole fraction of a component + * in a phase has been changed since the last call to + * setCleanPhase(). + */ + bool moleFracsClean(int phaseIdx, int compIdx) const + { return (changed_[phaseIdx].fields.moleFrac & (1 << compIdx)) == 0; } + + /*! + * \brief Find out whether at least one fugacity of a component in + * a phase has been changed since the last call to + * setCleanPhase(). + */ + bool fugacityCoeffsClean(int phaseIdx) const + { return changed_[phaseIdx].fields.fugacitCoeffs == 0; } + + /*! + * \brief Find out whether a specific fugacity of a component in a + * phase has been changed since the last call to + * setCleanPhase(). + */ + bool fugacityCoeffClean(int phaseIdx, int compIdx) const + { return (changed_[phaseIdx].fields.fugacityCoeffs & (1 << compIdx)) == 0; } + + + /*! + * \brief Make sure that all attributes are defined. + * + * This method does not do anything if the program is not run + * under valgrind. If it is, then valgrind will print an error + * message if some attributes of the object have not been properly + * defined. + */ + void checkDefined() const + { +#if HAVE_VALGRIND && ! defined NDEBUG + Valgrind::CheckDefined(changed_); + fs_.checkDefined(); +#endif // HAVE_VALGRIND + } + +protected: + static_assert(sizeof(unsigned)*8 >= 5 + 2*numComponents, + "Too many components to use the bitfield trick"); + union { + unsigned raw; + struct { + unsigned char moleFrac : numComponents; + unsigned char fugacityCoeff : numComponents; + + unsigned char molarVolume : 1; + unsigned char temperature : 1; + unsigned char saturation : 1; + unsigned char pressure : 1; + unsigned char enthalpy : 1; + unsigned char viscosity : 1; + } __attribute__((__packed__)) fields; + } changed_[numPhases]; + GenericFluidState fs_; +}; + +} // end namepace Dumux + +#endif diff --git a/dumux/material/new_fluidsystems/Makefile.am b/dumux/material/new_fluidsystems/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..70f8f174617e3dd8488fc44a23ad1680eef4c247 --- /dev/null +++ b/dumux/material/new_fluidsystems/Makefile.am @@ -0,0 +1 @@ +include $(top_srcdir)/am/global-rules diff --git a/dumux/material/new_fluidsystems/basicmutableparameters.hh b/dumux/material/new_fluidsystems/basicmutableparameters.hh new file mode 100644 index 0000000000000000000000000000000000000000..d2a6adb203be3c6b37e60f52d12b0604ee195ba9 --- /dev/null +++ b/dumux/material/new_fluidsystems/basicmutableparameters.hh @@ -0,0 +1,97 @@ +/***************************************************************************** + * 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 Specifies the basic API for mutable parameter objects. + * + * Fluid systems which do not need any complicated mixture parameters + * just need this class to be happy. + */ +#ifndef BASIC_MUTABLE_PARAMETERS_HH +#define BASIC_MUTABLE_PARAMETERS_HH + +#include <dumux/material/fluidstates/genericfluidstate.hh> +#include <assert.h> + +namespace Dumux +{ +/*! + * \brief Specifies the basic API for mutable parameter objects. + * + * Fluid systems which do not need any complicated mixture parameters + * just need this class to be happy. + */ +template <class Scalar, + class StaticParams, + class FluidStateT = GenericFluidState<Scalar, StaticParams> > +class BasicMutableParameters +{ + enum { numPhases = StaticParams::numPhases }; + enum { numComponents = StaticParams::numComponents }; + +public: + /*! + * \brief The type of fluid state objects. + */ + typedef FluidStateT FluidState; + + /*! + * \brief Returns the fluid state for which the parameter object + * is valid. + */ + FluidState &fluidState() + { return fluidState_; } + + /*! + * \brief Returns the fluid state for which the parameter object + * is valid. + */ + const FluidState &fluidState() const + { return fluidState_; } + + /*! + * \brief Update all parameters of a phase which only depend on + * temperature and/or pressure. + * + * This usually means the parameters for the pure components. + */ + void updatePure(int phaseIdx) + { } + + /*! + * \brief Update all parameters of a phase which depend on the + * fluid composition. It is assumed that updatePure() has + * been called before this method. + * + * Here, the mixing rule kicks in. + */ + void updateMix(int phaseIdx) + { + fluidState_.updateMeanMolarMass(phaseIdx); + }; + +private: + FluidState fluidState_; +}; + +} // end namespace Dumux + +#endif diff --git a/dumux/material/new_fluidsystems/h2oh2n2o2fluidsystem.hh b/dumux/material/new_fluidsystems/h2oh2n2o2fluidsystem.hh new file mode 100644 index 0000000000000000000000000000000000000000..843951295243edba7e43c23854a17198887ec487 --- /dev/null +++ b/dumux/material/new_fluidsystems/h2oh2n2o2fluidsystem.hh @@ -0,0 +1,557 @@ +/***************************************************************************** + * 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 A twophase fluid system with water and nitrogen as components. + */ +#ifndef DUMUX_H2O_H2_N2_O2_FLUID_SYSTEM_HH +#define DUMUX_H2O_H2_N2_O2_FLUID_SYSTEM_HH + +#include <dumux/material/fluidstates/genericfluidstate.hh> + +#include <dumux/material/components/simpleh2o.hh> +#include <dumux/material/components/h2o.hh> +#include <dumux/material/components/h2.hh> +#include <dumux/material/components/n2.hh> +#include <dumux/material/components/o2.hh> +#include <dumux/material/components/tabulatedcomponent.hh> +#include <dumux/material/idealgas.hh> + +#include <dumux/material/binarycoefficients/h2o_h2.hh> +#include <dumux/material/binarycoefficients/h2o_n2.hh> +#include <dumux/material/binarycoefficients/h2o_o2.hh> +#include <dumux/material/binarycoefficients/h2_n2.hh> +#include <dumux/material/binarycoefficients/h2_o2.hh> +#include <dumux/material/binarycoefficients/n2_o2.hh> + +#include <dumux/material/fluidstates/genericfluidstate.hh> + +#include <dumux/common/exceptions.hh> + +namespace Dumux +{ + + +template <class Scalar> +struct H2OH2N2O2StaticParameters { + /**************************************** + * Fluid phase parameters + ****************************************/ + + //! Number of phases in the fluid system + static const int numPhases = 2; + + //! Index of the liquid phase + static const int lPhaseIdx = 0; + //! Index of the gas phase + static const int gPhaseIdx = 1; + + //! The component for pure water + typedef Dumux::SimpleH2O<Scalar> SimpleH2O; + typedef Dumux::H2O<Scalar> IapwsH2O; + typedef Dumux::TabulatedComponent<Scalar, IapwsH2O> TabulatedH2O; + + //! The component for pure water + //typedef IapwsH2O H2O; + //typedef TabulatedH2O H2O; + typedef SimpleH2O H2O; + + //! The component for pure hydrogen + typedef Dumux::H2<Scalar> H2; + + //! The component for pure nitrogen + typedef Dumux::N2<Scalar> N2; + + //! The component for pure oxygen + typedef Dumux::O2<Scalar> O2; + + /*! + * \brief Initialize the static parameters. + */ + static void init(Scalar tempMin, Scalar tempMax, unsigned nTemp, + Scalar pressMin, Scalar pressMax, unsigned nPress) + { + if (H2O::isTabulated) { + std::cout << "Initializing tables for the H2O fluid properties (" + << nTemp*nPress + << " entries).\n"; + + TabulatedH2O::init(tempMin, tempMax, nTemp, + pressMin, pressMax, nPress); + } + + } + + /*! + * \brief Return the human readable name of a fluid phase + */ + static const char *phaseName(int phaseIdx) + { + static const char *name[] = { + "l", + "g" + }; + + assert(0 <= phaseIdx && phaseIdx < numPhases); + return name[phaseIdx]; + } + + /**************************************** + * Component related parameters + ****************************************/ + + //! Number of components in the fluid system + static const int numComponents = 4; + + static const int H2OIdx = 0; + static const int H2Idx = 1; + static const int N2Idx = 2; + static const int O2Idx = 3; + + /*! + * \brief Return the human readable name of a component + */ + static const char *componentName(int compIdx) + { + static const char *name[] = { + H2O::name(), + H2::name(), + N2::name(), + O2::name(), + }; + + assert(0 <= compIdx && compIdx < numComponents); + return name[compIdx]; + } + + /*! + * \brief Return the molar mass of a component in [kg/mol]. + */ + static Scalar molarMass(int compIdx) + { + static const Scalar M[] = { + H2O::molarMass(), + H2::molarMass(), + N2::molarMass(), + O2::molarMass(), + }; + + assert(0 <= compIdx && compIdx < numComponents); + return M[compIdx]; + } + + /*! + * \brief Critical temperature of a component [K]. + */ + static Scalar criticalTemperature(int compIdx) + { + static const Scalar Tcrit[] = { + H2O::criticalTemperature(), // H2O + H2::criticalTemperature(), // H2O + N2::criticalTemperature(), // H2O + O2::criticalTemperature(), // H2O + }; + + assert(0 <= compIdx && compIdx < numComponents); + return Tcrit[compIdx]; + }; + + /*! + * \brief Critical pressure of a component [Pa]. + */ + static Scalar criticalPressure(int compIdx) + { + static const Scalar pcrit[] = { + H2O::criticalPressure(), + H2::criticalPressure(), + N2::criticalPressure(), + O2::criticalPressure(), + }; + + assert(0 <= compIdx && compIdx < numComponents); + return pcrit[compIdx]; + }; + + /*! + * \brief Molar volume of a component at the critical point [m^3/mol]. + */ + static Scalar criticalMolarVolume(int compIdx) + { + DUNE_THROW(Dune::NotImplemented, + "H2OH2N2O2StaticParams::criticalMolarVolume()"); + }; + + /*! + * \brief The acentric factor of a component []. + */ + static Scalar acentricFactor(int compIdx) + { + static const Scalar accFac[] = { + H2O::acentricFactor(), // H2O (from Reid, et al.) + H2::acentricFactor(), + N2::acentricFactor(), + O2::acentricFactor(), + }; + + assert(0 <= compIdx && compIdx < numComponents); + return accFac[compIdx]; + }; +}; + +/*! + * \brief A twophase fluid system with water, hydrogen, nitrogen and oxygen as components. + */ +template <class Scalar> +class H2OH2N2O2FluidSystem : public H2OH2N2O2StaticParameters<Scalar> +{ +public: + typedef Dumux::H2OH2N2O2StaticParameters<Scalar> StaticParameters; + typedef Dumux::GenericFluidState<Scalar, StaticParameters> MutableParameters; + +private: + // convenience typedefs + typedef StaticParameters SP; + typedef Dumux::IdealGas<Scalar> IdealGas; + +public: + + /*! + * \brief Initialize the fluid system's static parameters + */ + static void init(Scalar tempMin, Scalar tempMax, unsigned nTemp, + Scalar pressMin, Scalar pressMax, unsigned nPress) + { + SP::init(tempMin, tempMax, nTemp, + pressMin, pressMax, nPress); + } + /*! + * \brief Returns true if and only if a fluid phase is assumed to + * be an ideal mixture. + * + * We define an ideal mixture as a fluid phase where the fugacity + * coefficients of all components times the pressure of the phase + * are indepent on the fluid composition. This assumtion is true + * if Henry's law and Rault's law apply. If you are unsure what + * this function should return, it is safe to return false. The + * only damage done will be (slightly) increased computation times + * in some cases. + */ + static bool isIdealMixture(int phaseIdx) + { + // we assume Henry's and Rault's laws for the water phase and + // and no interaction between gas molecules of different + // components, so all phases are ideal mixtures! + return true; + } + + /*! + * \brief Calculate the molar volume [m^3/mol] of a fluid phase + */ + static Scalar computeMolarVolume(MutableParameters ¶ms, + int phaseIdx) + { + assert(0 <= phaseIdx && phaseIdx <= SP::numPhases); + + params.updateMeanMolarMass(phaseIdx); + Scalar T = params.temperature(phaseIdx); + Scalar p = params.pressure(phaseIdx); + + switch (phaseIdx) { + case SP::lPhaseIdx: + // assume pure water where one water molecule gets + // replaced by one nitrogen molecule + return SP::H2O::molarMass()/SP::H2O::liquidDensity(T, p); + case SP::gPhaseIdx: + // assume ideal gas + return 1.0 / IdealGas::concentration(T, p); + } + + DUNE_THROW(Dune::InvalidStateException, "Unhandled phase index " << phaseIdx); + }; + + /*! + * \brief Calculate the fugacity of a component in a fluid phase + * [Pa] + * + * The components chemical \f$mu_\kappa\f$ potential is connected + * to the component's fugacity \f$f_\kappa\f$ by the relation + * + * \f[ \mu_\kappa = R T_\alpha \mathrm{ln} \frac{f_\kappa}{p_\alpha} \f] + * + * where \f$p_\alpha\f$ and \f$T_\alpha\f$ are the fluid phase' + * pressure and temperature. + */ + static Scalar computeFugacity(MutableParameters ¶ms, + int phaseIdx, + int compIdx) + { + Scalar x = params.moleFrac(phaseIdx,compIdx); + Scalar p = params.pressure(phaseIdx); + return x*p*computeFugacityCoeff(params, phaseIdx, compIdx); + }; + + /*! + * \brief Calculate the fugacity coefficient [Pa] of an individual + * component in a fluid phase + * + * The fugacity coefficient \f$\phi_\kappa\f$ is connected to the + * fugacity \f$f_\kappa\f$ and the component's molarity + * \f$x_\kappa\f$ by means of the relation + * + * \f[ f_\kappa = \phi_\kappa * x_{\kappa} \f] + */ + static Scalar computeFugacityCoeff(MutableParameters ¶ms, + int phaseIdx, + int compIdx) + { + assert(0 <= phaseIdx && phaseIdx <= SP::numPhases); + assert(0 <= compIdx && compIdx <= SP::numComponents); + + params.updateMeanMolarMass(phaseIdx); + Scalar T = params.temperature(phaseIdx); + Scalar p = params.pressure(phaseIdx); + switch (phaseIdx) { + case SP::lPhaseIdx: + switch (compIdx) { + case SP::H2OIdx: return SP::H2O::vaporPressure(T)/p; + case SP::H2Idx: return BinaryCoeff::H2O_H2::henry(T)/p; + case SP::N2Idx: return BinaryCoeff::H2O_N2::henry(T)/p; + case SP::O2Idx: return BinaryCoeff::H2O_O2::henry(T)/p; + }; + case SP::gPhaseIdx: + return 1.0; // ideal gas + }; + + DUNE_THROW(Dune::InvalidStateException, "Unhandled phase or component index"); + } + + /*! + * \brief Calculate the dynamic viscosity of a fluid phase [Pa*s] + */ + static Scalar computeViscosity(MutableParameters ¶ms, + int phaseIdx) + { + assert(0 <= phaseIdx && phaseIdx <= SP::numPhases); + + params.updateMeanMolarMass(phaseIdx); + Scalar T = params.temperature(phaseIdx); + Scalar p = params.pressure(phaseIdx); + switch (phaseIdx) { + case SP::lPhaseIdx: + // assume pure water for the liquid phase + return SP::H2O::liquidViscosity(T, p); + case SP::gPhaseIdx: + // assume pure nitrogen for the gas phase + return SP::N2::gasViscosity(T, p); + } + + DUNE_THROW(Dune::InvalidStateException, "Unhandled phase index " << phaseIdx); + }; + + /*! + * \brief Calculate the binary molecular diffusion coefficient for + * a component in a fluid phase [mol^2 * s / (kg*m^3)] + * + * Molecular diffusion of a compoent $\kappa$ is caused by a + * gradient of the chemical potential and follows the law + * + * \f[ J = - D \grad mu_\kappa \f] + * + * where \f$\mu_\kappa\$ is the component's chemical potential, + * \f$D\f$ is the diffusion coefficient and \f$J\f$ is the + * diffusive flux. \f$mu_\kappa\f$ is connected to the component's + * fugacity \f$f_\kappa\f$ by the relation + * + * \f[ \mu_\kappa = R T_\alpha \mathrm{ln} \frac{f_\kappa}{p_\alpha} \f] + * + * where \f$p_\alpha\f$ and \f$T_\alpha\f$ are the fluid phase' + * pressure and temperature. + */ + static Scalar computeDiffusionCoeff(MutableParameters ¶ms, + int phaseIdx, + int compIdx) + { + // TODO! + DUNE_THROW(Dune::NotImplemented, "Diffusion coefficients"); + }; + + /*! + * \brief Given a phase's composition, temperature and pressure, + * return the binary diffusion coefficient for components + * \f$i\f$ and \f$j\f$ in this phase. + */ + static Scalar binaryDiffCoeff(MutableParameters ¶ms, + int phaseIdx, + int compIIdx, + int compJIdx) + + { + params.updateMeanMolarMass(phaseIdx); + if (compIIdx > compJIdx) + std::swap(compIIdx, compJIdx); + +#ifndef NDEBUG + if (compIIdx == compJIdx || + phaseIdx > SP::numPhases - 1 || + compJIdx > SP::numComponents - 1) + { + DUNE_THROW(Dune::InvalidStateException, + "Binary diffusion coefficient of components " + << compIIdx << " and " << compJIdx + << " in phase " << phaseIdx << " is undefined!\n"); + } +#endif + + Scalar T = params.temperature(phaseIdx); + Scalar p = params.pressure(phaseIdx); + + switch (phaseIdx) { + case SP::lPhaseIdx: + switch (compIIdx) { + case SP::H2OIdx: + switch (compJIdx) { + case SP::H2Idx: return BinaryCoeff::H2O_H2::liquidDiffCoeff(T, p); + case SP::N2Idx: return BinaryCoeff::H2O_N2::liquidDiffCoeff(T, p); + case SP::O2Idx: return BinaryCoeff::H2O_O2::liquidDiffCoeff(T, p); + } + default: + DUNE_THROW(Dune::InvalidStateException, + "Binary diffusion coefficients of trace " + "substances in liquid phase is undefined!\n"); + } + case SP::gPhaseIdx: + switch (compIIdx) { + case SP::H2OIdx: + switch (compJIdx) { + case SP::H2Idx: return BinaryCoeff::H2O_H2::gasDiffCoeff(T, p); + case SP::N2Idx: return BinaryCoeff::H2O_N2::gasDiffCoeff(T, p); + case SP::O2Idx: return BinaryCoeff::H2O_O2::gasDiffCoeff(T, p); + } + + case SP::H2Idx: + switch (compJIdx) { + case SP::N2Idx: return BinaryCoeff::H2_N2::gasDiffCoeff(T, p); + case SP::O2Idx: return BinaryCoeff::H2_O2::gasDiffCoeff(T, p); + } + + case SP::N2Idx: + switch (compJIdx) { + case SP::O2Idx: return BinaryCoeff::N2_O2::gasDiffCoeff(T, p); + } + } + } + + DUNE_THROW(Dune::InvalidStateException, + "Binary diffusion coefficient of components " + << compIIdx << " and " << compJIdx + << " in phase " << phaseIdx << " is undefined!\n"); + }; + + /*! + * \brief Given a phase's composition, temperature, pressure and + * density, calculate its specific enthalpy [J/kg]. + */ + static Scalar computeEnthalpy(MutableParameters ¶ms, + int phaseIdx) + { + params.updateMeanMolarMass(phaseIdx); + Scalar T = params.temperature(phaseIdx); + Scalar p = params.pressure(phaseIdx); + Valgrind::CheckDefined(T); + Valgrind::CheckDefined(p); + if (phaseIdx == SP::lPhaseIdx) { + Scalar cH2 = params.molarity(SP::lPhaseIdx, SP::H2Idx); + Scalar cN2 = params.molarity(SP::lPhaseIdx, SP::N2Idx); + Scalar cO2 = params.molarity(SP::lPhaseIdx, SP::O2Idx); + Scalar pH2 = SP::H2::gasPressure(T, cN2*SP::H2::molarMass()); + Scalar pN2 = SP::N2::gasPressure(T, cN2*SP::N2::molarMass()); + Scalar pO2 = SP::O2::gasPressure(T, cN2*SP::O2::molarMass()); + + Scalar XH2O = params.massFrac(SP::lPhaseIdx, SP::H2OIdx); + Scalar XH2 = params.massFrac(SP::lPhaseIdx, SP::H2Idx); + Scalar XN2 = params.massFrac(SP::lPhaseIdx, SP::N2Idx); + Scalar XO2 = params.massFrac(SP::lPhaseIdx, SP::O2Idx); + + // TODO: correct way to deal with the solutes??? + return + (XH2O*SP::H2O::liquidEnthalpy(T, p) + + XH2*SP::N2::gasEnthalpy(T, pH2) + + XN2*SP::N2::gasEnthalpy(T, pN2) + + XO2*SP::N2::gasEnthalpy(T, pO2)) + / + (XH2O + XN2); + } + else { + Scalar cH2O = params.molarity(SP::gPhaseIdx, SP::H2OIdx); + Scalar cH2 = params.molarity(SP::gPhaseIdx, SP::H2Idx); + Scalar cN2 = params.molarity(SP::gPhaseIdx, SP::N2Idx); + Scalar cO2 = params.molarity(SP::gPhaseIdx, SP::O2Idx); + + // assume ideal mixture for gas + Scalar pH2O = SP::H2O::gasPressure(T, cH2O*SP::H2O::molarMass()); + Scalar pH2 = SP::H2::gasPressure(T, cH2*SP::H2::molarMass()); + Scalar pN2 = SP::N2::gasPressure(T, cN2*SP::N2::molarMass()); + Scalar pO2 = SP::O2::gasPressure(T, cO2*SP::O2::molarMass()); + + Scalar XH2O = params.massFrac(SP::gPhaseIdx, SP::H2OIdx); + Scalar XH2 = params.massFrac(SP::gPhaseIdx, SP::H2Idx); + Scalar XN2 = params.massFrac(SP::gPhaseIdx, SP::N2Idx); + Scalar XO2 = params.massFrac(SP::gPhaseIdx, SP::O2Idx); + + // add up all the "partial enthalpies" + Scalar result = 0; + result += SP::H2O::gasEnthalpy(T, pH2O); + result += SP::H2::gasEnthalpy(T, pH2); + result += SP::N2::gasEnthalpy(T, pN2); + result += SP::O2::gasEnthalpy(T, pO2); + + // normalize the result to 100% + result /= XH2O + XH2 + XN2 + XO2; + + return result; + } + }; + + /*! + * \brief Thermal conductivity of phases [W m / (m^2 K)] + * Use the cunductivity of air and water as a first approximation. + * Source: http://en.wikipedia.org/wiki/List_of_thermal_conductivities + */ + static Scalar thermalConductivity(const MutableParameters ¶ms, + const int phaseIdx) + { +// TODO thermal conductivity is a function of: +// Scalar p = params.pressure(phaseIdx); +// Scalar T = params.temperature(phaseIdx); +// Scalar x = params.moleFrac(phaseIdx,compIdx); + switch (phaseIdx) { + case SP::lPhaseIdx: // use conductivity of pure water + return 0.6; // conductivity of water[W / (m K ) ] + case SP::gPhaseIdx:// use conductivity of pure air + return 0.025; // conductivity of air [W / (m K ) ] + } + DUNE_THROW(Dune::InvalidStateException, "Unhandled phase index " << phaseIdx); + } +}; + +} // end namepace + +#endif diff --git a/dumux/material/new_fluidsystems/h2on2fluidsystem.hh b/dumux/material/new_fluidsystems/h2on2fluidsystem.hh new file mode 100644 index 0000000000000000000000000000000000000000..4d86c5ac5e04c016290394b5dffae05ea9509678 --- /dev/null +++ b/dumux/material/new_fluidsystems/h2on2fluidsystem.hh @@ -0,0 +1,512 @@ +/***************************************************************************** + * 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 A twophase fluid system with water and nitrogen as components. + */ +#ifndef DUMUX_H2O_N2_FLUID_SYSTEM_HH +#define DUMUX_H2O_N2_FLUID_SYSTEM_HH + +#include <dumux/material/fluidstates/genericfluidstate.hh> + +#include <dumux/material/components/simpleh2o.hh> +#include <dumux/material/components/h2o.hh> +#include <dumux/material/components/n2.hh> +#include <dumux/material/components/tabulatedcomponent.hh> +#include <dumux/material/idealgas.hh> + +#include <dumux/material/binarycoefficients/h2o_n2.hh> +#include <dumux/material/fluidstates/genericfluidstate.hh> + +#include <dumux/common/exceptions.hh> + +namespace Dumux +{ + + +template <class Scalar> +struct H2ON2StaticParameters { + /**************************************** + * Fluid phase parameters + ****************************************/ + + //! Number of phases in the fluid system + static constexpr int numPhases = 2; + + //! Index of the liquid phase + static constexpr int lPhaseIdx = 0; + //! Index of the gas phase + static constexpr int gPhaseIdx = 1; + //! Index of the solid phase + static constexpr int sPhaseIdx = 2; + + //! The component for pure water + typedef Dumux::SimpleH2O<Scalar> SimpleH2O; + typedef Dumux::H2O<Scalar> IapwsH2O; + typedef Dumux::TabulatedComponent<Scalar, IapwsH2O> TabulatedH2O; + +// typedef IapwsH2O H2O; + typedef TabulatedH2O H2O; +// typedef SimpleH2O H2O; + + //! The component for pure nitrogen + typedef Dumux::N2<Scalar> N2; + + /*! + * \brief Initialize the static parameters. + */ + static void init(Scalar tempMin, Scalar tempMax, unsigned nTemp, + Scalar pressMin, Scalar pressMax, unsigned nPress) + { + if (H2O::isTabulated) { + std::cout << "Initializing tables for the H2O fluid properties (" + << nTemp*nPress + << " entries).\n"; + + TabulatedH2O::init(tempMin, tempMax, nTemp, + pressMin, pressMax, nPress); + } + + } + + /*! + * \brief Return the human readable name of a fluid phase + */ + static const char *phaseName(int phaseIdx) + { + static const char *name[] = { + "l", + "g", + "s" + }; + + assert(0 <= phaseIdx && phaseIdx < numPhases + 1); + return name[phaseIdx]; + } + + /*! + * \brief Return whether a phase is liquid + */ + static bool phaseIsLiquid(int phaseIdx) + { + assert(0 <= phaseIdx && phaseIdx < numPhases); + return phaseIdx != gPhaseIdx; + } + + /**************************************** + * Component related parameters + ****************************************/ + + //! Number of components in the fluid system + static constexpr int numComponents = 2; + + static constexpr int H2OIdx = 0; + static constexpr int N2Idx = 1; + + /*! + * \brief Return the human readable name of a component + */ + static const char *componentName(int compIdx) + { + static const char *name[] = { + H2O::name(), + N2::name() + }; + + assert(0 <= compIdx && compIdx < numComponents); + return name[compIdx]; + } + + /*! + * \brief Return the molar mass of a component in [kg/mol]. + */ + static Scalar molarMass(int compIdx) + { + static const Scalar M[] = { + H2O::molarMass(), + N2::molarMass(), + }; + + assert(0 <= compIdx && compIdx < numComponents); + return M[compIdx]; + } + + /*! + * \brief Critical temperature of a component [K]. + */ + static Scalar criticalTemperature(int compIdx) + { + static const Scalar Tcrit[] = { + H2O::criticalTemperature(), // H2O + N2::criticalTemperature(), // H2O + }; + + assert(0 <= compIdx && compIdx < numComponents); + return Tcrit[compIdx]; + }; + + /*! + * \brief Critical pressure of a component [Pa]. + */ + static Scalar criticalPressure(int compIdx) + { + static const Scalar pcrit[] = { + H2O::criticalPressure(), + N2::criticalPressure() + }; + + assert(0 <= compIdx && compIdx < numComponents); + return pcrit[compIdx]; + }; + + /*! + * \brief Molar volume of a component at the critical point [m^3/mol]. + */ + static Scalar criticalMolarVolume(int compIdx) + { + DUNE_THROW(Dune::NotImplemented, + "H2ON2StaticParams::criticalMolarVolume()"); + }; + + /*! + * \brief The acentric factor of a component []. + */ + static Scalar acentricFactor(int compIdx) + { + static const Scalar accFac[] = { + H2O::acentricFactor(), // H2O (from Reid, et al.) + N2::acentricFactor() + }; + + assert(0 <= compIdx && compIdx < numComponents); + return accFac[compIdx]; + }; +}; + +/*! + * \brief A twophase fluid system with water and nitrogen as components. + */ +template <class Scalar> +class H2ON2FluidSystem : public H2ON2StaticParameters<Scalar> +{ +public: + typedef Dumux::H2ON2StaticParameters<Scalar> StaticParameters; + typedef Dumux::GenericFluidState<Scalar, StaticParameters> MutableParameters; + +private: + // convenience typedefs + typedef StaticParameters SP; + typedef Dumux::IdealGas<Scalar> IdealGas; + +public: + + /*! + * \brief Initialize the fluid system's static parameters + */ + static void init(Scalar tempMin, Scalar tempMax, unsigned nTemp, + Scalar pressMin, Scalar pressMax, unsigned nPress) + { + SP::init(tempMin, tempMax, nTemp, + pressMin, pressMax, nPress); + } + /*! + * \brief Returns true if and only if a fluid phase is assumed to + * be an ideal mixture. + * + * We define an ideal mixture as a fluid phase where the fugacity + * coefficients of all components times the pressure of the phase + * are indepent on the fluid composition. This assumtion is true + * if Henry's law and Rault's law apply. If you are unsure what + * this function should return, it is safe to return false. The + * only damage done will be (slightly) increased computation times + * in some cases. + */ + static bool isIdealMixture(int phaseIdx) + { + // we assume Henry's and Rault's laws for the water phase and + // and no interaction between gas molecules of different + // components, so all phases are ideal mixtures! + return true; + } + + /*! + * \brief Calculate the molar volume [m^3/mol] of a fluid phase + */ + static Scalar computeMolarVolume(MutableParameters ¶ms, + int phaseIdx) + { + assert(0 <= phaseIdx && phaseIdx <= SP::numPhases); + + params.updateMeanMolarMass(phaseIdx); + Scalar T = params.temperature(phaseIdx); + Scalar p = params.pressure(phaseIdx); + + switch (phaseIdx) { + case SP::lPhaseIdx: + // assume pure water where one water molecule gets + // replaced by one nitrogen molecule + return SP::H2O::molarMass()/SP::H2O::liquidDensity(T, p); + case SP::gPhaseIdx: + // assume ideal gas + return 1.0 / IdealGas::concentration(T, p); + } + + DUNE_THROW(Dune::InvalidStateException, "Unhandled phase index " << phaseIdx); + }; + + /*! + * \brief Calculate the fugacity of a component in a fluid phase + * [Pa] + * + * The components chemical \f$mu_\kappa\f$ potential is connected + * to the component's fugacity \f$f_\kappa\f$ by the relation + * + * \f[ \mu_\kappa = R T_\alpha \mathrm{ln} \frac{f_\kappa}{p_\alpha} \f] + * + * where \f$p_\alpha\f$ and \f$T_\alpha\f$ are the fluid phase' + * pressure and temperature. + */ + static Scalar computeFugacity(const MutableParameters ¶ms, + int phaseIdx, + int compIdx) + { + Scalar x = params.moleFrac(phaseIdx,compIdx); + Scalar p = params.pressure(phaseIdx); + return x*p*computeFugacityCoeff(params, phaseIdx, compIdx); + }; + + /*! + * \brief Calculate the fugacity coefficient [Pa] of an individual + * component in a fluid phase + * + * The fugacity coefficient \f$\phi_\kappa\f$ is connected to the + * fugacity \f$f_\kappa\f$ and the component's molarity + * \f$x_\kappa\f$ by means of the relation + * + * \f[ f_\kappa = \phi_\kappa * x_{\kappa} \f] + */ + static Scalar computeFugacityCoeff(MutableParameters ¶ms, + int phaseIdx, + int compIdx) + { + assert(0 <= phaseIdx && phaseIdx <= SP::numPhases); + assert(0 <= compIdx && compIdx <= SP::numComponents); + + params.updateMeanMolarMass(phaseIdx); + Scalar T = params.temperature(phaseIdx); + Scalar p = params.pressure(phaseIdx); + switch (phaseIdx) { + case SP::lPhaseIdx: + switch (compIdx) { + case SP::H2OIdx: return SP::H2O::vaporPressure(T)/p; + case SP::N2Idx: return BinaryCoeff::H2O_N2::henry(T)/p; + }; + case SP::gPhaseIdx: + return 1.0; // ideal gas + }; + + DUNE_THROW(Dune::InvalidStateException, "Unhandled phase or component index"); + } + + /*! + * \brief Calculate the dynamic viscosity of a fluid phase [Pa*s] + */ + static Scalar computeViscosity(MutableParameters ¶ms, + int phaseIdx) + { + assert(0 <= phaseIdx && phaseIdx <= SP::numPhases); + + params.updateMeanMolarMass(phaseIdx); + Scalar T = params.temperature(phaseIdx); + Scalar p = params.pressure(phaseIdx); + switch (phaseIdx) { + case SP::lPhaseIdx: + // assume pure water for the liquid phase + return SP::H2O::liquidViscosity(T, p); + case SP::gPhaseIdx: + // assume pure water for the gas phase + return SP::N2::gasViscosity(T, p); + } + + DUNE_THROW(Dune::InvalidStateException, "Unhandled phase index " << phaseIdx); + }; + + /*! + * \brief Calculate the binary molecular diffusion coefficient for + * a component in a fluid phase [mol^2 * s / (kg*m^3)] + * + * Molecular diffusion of a compoent $\kappa$ is caused by a + * gradient of the chemical potential and follows the law + * + * \f[ J = - D \grad mu_\kappa \f] + * + * where \f$\mu_\kappa\$ is the component's chemical potential, + * \f$D\f$ is the diffusion coefficient and \f$J\f$ is the + * diffusive flux. \f$mu_\kappa\f$ is connected to the component's + * fugacity \f$f_\kappa\f$ by the relation + * + * \f[ \mu_\kappa = R T_\alpha \mathrm{ln} \frac{f_\kappa}{p_\alpha} \f] + * + * where \f$p_\alpha\f$ and \f$T_\alpha\f$ are the fluid phase' + * pressure and temperature. + */ + static Scalar computeDiffusionCoeff(const MutableParameters ¶ms, + int phaseIdx, + int compIdx) + { + // TODO! + DUNE_THROW(Dune::NotImplemented, "Diffusion coefficients"); + }; + + /*! + * \brief Given a phase's composition, temperature and pressure, + * return the binary diffusion coefficient for components + * \f$i\f$ and \f$j\f$ in this phase. + */ + static Scalar computeBinaryDiffCoeff(MutableParameters ¶ms, + int phaseIdx, + int compIIdx, + int compJIdx) + + { + params.updateMeanMolarMass(phaseIdx); + if (compIIdx > compJIdx) + std::swap(compIIdx, compJIdx); + +#ifndef NDEBUG + if (compIIdx == compJIdx || + phaseIdx > SP::numPhases - 1 || + compJIdx > SP::numComponents - 1) + { + DUNE_THROW(Dune::InvalidStateException, + "Binary diffusion coefficient of components " + << compIIdx << " and " << compJIdx + << " in phase " << phaseIdx << " is undefined!\n"); + } +#endif + + Scalar T = params.temperature(phaseIdx); + Scalar p = params.pressure(phaseIdx); + + switch (phaseIdx) { + case SP::lPhaseIdx: + switch (compIIdx) { + case SP::H2OIdx: + switch (compJIdx) { + case SP::N2Idx: return BinaryCoeff::H2O_N2::liquidDiffCoeff(T, p); + } + default: + DUNE_THROW(Dune::InvalidStateException, + "Binary diffusion coefficients of trace " + "substances in liquid phase is undefined!\n"); + } + case SP::gPhaseIdx: + switch (compIIdx) { + case SP::H2OIdx: + switch (compJIdx) { + case SP::N2Idx: return BinaryCoeff::H2O_N2::gasDiffCoeff(T, p); + } + } + } + + DUNE_THROW(Dune::InvalidStateException, + "Binary diffusion coefficient of components " + << compIIdx << " and " << compJIdx + << " in phase " << phaseIdx << " is undefined!\n"); + }; + + static Scalar binaryDiffCoeff(MutableParameters ¶ms, + int phaseIdx, + int compIIdx, + int compJIdx) + DUNE_DEPRECATED // use computeBinaryDiffCoeff() + { + return computeBinaryDiffCoeff(params, + phaseIdx, + compIIdx, + compJIdx); + } + + /*! + * \brief Given a phase's composition, temperature, pressure and + * density, calculate its specific enthalpy [J/kg]. + */ + static Scalar computeEnthalpy(MutableParameters ¶ms, + int phaseIdx) + { + params.updateMeanMolarMass(phaseIdx); + Scalar T = params.temperature(phaseIdx); + Scalar p = params.pressure(phaseIdx); + Valgrind::CheckDefined(T); + Valgrind::CheckDefined(p); + if (phaseIdx == SP::lPhaseIdx) { + Scalar cN2 = params.molarity(SP::lPhaseIdx, SP::N2Idx); + Scalar pN2 = SP::N2::gasPressure(T, cN2*SP::N2::molarMass()); + + Scalar XH2O = params.massFrac(SP::lPhaseIdx, SP::H2OIdx); + Scalar XN2 = params.massFrac(SP::lPhaseIdx, SP::N2Idx); + + // TODO: correct way to deal with the solutes??? + return + (XH2O*SP::H2O::liquidEnthalpy(T, p) + + XN2*SP::N2::gasEnthalpy(T, pN2)) + / + (XH2O + XN2); + } + else { + Scalar cH2O = params.molarity(SP::gPhaseIdx, SP::H2OIdx); + Scalar cN2 = params.molarity(SP::gPhaseIdx, SP::N2Idx); + + // assume ideal gas + Scalar pH2O = SP::H2O::gasPressure(T, cH2O*SP::H2O::molarMass()); + Scalar pN2 = SP::N2::gasPressure(T, cN2*SP::H2O::molarMass()); + + Scalar XH2O = params.massFrac(SP::gPhaseIdx, SP::H2OIdx); + Scalar XN2 = params.massFrac(SP::gPhaseIdx, SP::N2Idx); + Scalar result = 0; + result += XH2O*SP::H2O::gasEnthalpy(T, pH2O); + result += XN2*SP::N2::gasEnthalpy(T, pN2); + result /= XH2O + XN2; + + return result; + } + }; + + /*! + * \brief Given a phase's composition, temperature, pressure and + * density, calculate its specific internal energy [J/kg]. + */ + static Scalar computeInternalEnergy(MutableParameters ¶ms, + int phaseIdx) + { + params.updateMeanMolarMass(phaseIdx); + Scalar T = params.temperature(phaseIdx); + Scalar p = params.pressure(phaseIdx); + Scalar rho = params.density(phaseIdx); + return + computeEnthalpy(params, phaseIdx) - + p/rho; + } +}; + +} // end namepace + +#endif diff --git a/dumux/material/new_fluidsystems/spe5fluidsystem.hh b/dumux/material/new_fluidsystems/spe5fluidsystem.hh new file mode 100644 index 0000000000000000000000000000000000000000..57ffcf5c701eb90146037fbd7461f0c709609ec4 --- /dev/null +++ b/dumux/material/new_fluidsystems/spe5fluidsystem.hh @@ -0,0 +1,347 @@ +/***************************************************************************** + * 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 The mixing rule for the oil and the gas phases of the SPE5 problem. + * + * This problem comprises \f$H_2O\f$, \f$C_1\f$, \f$C_3\f$, \f$C_6\f$, + * \f$C_10\f$, \f$C_15\f$ and \f$C_20\f$ as components. + * + * See: + * + * J.E. Killough, et al.: Fifth Comparative Solution Project: + * Evaluation of Miscible Flood Simulators, Ninth SPE Symposium on + * Reservoir Simulation, 1987 + */ +#ifndef DUMUX_SPE5_FLUID_SYSTEM_HH +#define DUMUX_SPE5_FLUID_SYSTEM_HH + +#include "dumux/common/spline.hh" +#include "spe5/spe5staticparameters.hh" +#include "spe5/spe5mutableparameters.hh" +#include "spe5/spe5pengrobinsonparams.hh" +#include "../eos/pengrobinsonmixture.hh" + +namespace Dumux +{ +/*! + * \brief The fluid system for the SPE-5 benchmark problem. + * + * This problem comprises \f$H_2O\f$, \f$C_1\f$, \f$C_3\f$, \f$C_6\f$, + * \f$C_10\f$, \f$C_15\f$ and \f$C_20\f$ as components. + * + * See: + * + * J.E. Killough, et al.: Fifth Comparative Solution Project: + * Evaluation of Miscible Flood Simulators, Ninth SPE Symposium on + * Reservoir Simulation, 1987 + */ +template <class Scalar> +class Spe5FluidSystem +{ +public: + typedef Dumux::Spe5StaticParameters<Scalar> StaticParameters; + typedef Dumux::Spe5MutableParameters<Scalar> MutableParameters; + +private: + typedef typename Dumux::PengRobinsonMixture<Scalar, StaticParameters> PengRobinsonMixture; + typedef typename Dumux::PengRobinson<Scalar> PengRobinson; + +public: + // copy number of phases and components from the static parameters + enum { numPhases = StaticParameters::numPhases }; + enum { numComponents = StaticParameters::numComponents }; + + // copy phase indices from the static parameters + enum { + wPhaseIdx = StaticParameters::wPhaseIdx, + oPhaseIdx = StaticParameters::oPhaseIdx, + gPhaseIdx = StaticParameters::gPhaseIdx + }; + + // copy component indices from the static parameters + enum { + H2OIdx = StaticParameters::H2OIdx, + C1Idx = StaticParameters::C1Idx, + C3Idx = StaticParameters::C3Idx, + C6Idx = StaticParameters::C6Idx, + C10Idx = StaticParameters::C10Idx, + C15Idx = StaticParameters::C15Idx, + C20Idx = StaticParameters::C20Idx, + }; + + // copy the component types from the static parameters + typedef typename StaticParameters::H2O H2O; + + /*! + * \brief Initialize the fluid system's static parameters + */ + static void init() + { + } + + /*! + * \brief Returns true if and only if a fluid phase is assumed to + * be an ideal mixture. + * + * We define an ideal mixture as a fluid phase where the fugacity + * coefficients of all components times the pressure of the phase + * are indepent on the fluid composition. This assumtion is true + * if Henry's law and Rault's law apply. If you are unsure what + * this function should return, it is safe to return false. The + * only damage done will be (slightly) increased computation times + * in some cases. + */ + static bool isIdealMixture(int phaseIdx) + { + // always use the reference oil for the fugacity coefficents, + // so they cannot be dependent on composition and they the + // phases thus always an ideal mixture + return phaseIdx == wPhaseIdx; + } + + /*! + * \brief Return the human readable name of a component + */ + static const char *componentName(int compIdx) + { + assert(0 <= compIdx && compIdx <= numComponents); + return StaticParameters::componentName(compIdx); + } + + /*! + * \brief Return the human readable name of a phase + */ + static const char *phaseName(int phaseIdx) + { + assert(0 <= phaseIdx && phaseIdx <= numPhases); + return StaticParameters::phaseName(phaseIdx); + } + + /*! + * \brief Return the molar mass [kg/mol] of a component + */ + static Scalar molarMass(int compIdx) + { + assert(0 <= compIdx && compIdx <= numComponents); + return StaticParameters::molarMass(compIdx); + } + + /*! + * \brief Calculate the molar volume [m^3/mol] of a fluid phase + */ + static Scalar computeMolarVolume(MutableParameters ¶ms, + int phaseIdx) + { + assert(0 <= phaseIdx && phaseIdx <= numPhases); + + params.update(phaseIdx); + return params.molarVolume(phaseIdx); + }; + + /*! + * \brief Calculate the fugacity of a component in a fluid phase + * [Pa] + * + * The components chemical \f$mu_\kappa\f$ potential is connected + * to the component's fugacity \f$f_\kappa\f$ by the relation + * + * \f[ \mu_\kappa = R T_\alpha \mathrm{ln} \frac{f_\kappa}{p_\alpha} \f] + * + * where \f$p_\alpha\f$ and \f$T_\alpha\f$ are the fluid phase' + * pressure and temperature. + */ + static Scalar computeFugacity(MutableParameters ¶ms, + int phaseIdx, + int compIdx) + { + params.update(phaseIdx); + return + params.moleFrac(phaseIdx, compIdx) * + params.pressure(phaseIdx) * + computeFugacityCoeff(params, phaseIdx, compIdx); + }; + + /*! + * \brief Calculate the fugacity coefficient [Pa] of an individual + * component in a fluid phase + * + * The fugacity coefficient \f$\phi_\kappa\f$ is connected to the + * fugacity \f$f_\kappa\f$ and the component's molarity + * \f$x_\kappa\f$ by means of the relation + * + * \f[ f_\kappa = \phi_\kappa * x_{\kappa} \f] + */ + static Scalar computeFugacityCoeff(MutableParameters ¶ms, + int phaseIdx, + int compIdx) + { +// const Scalar phi_g[numComponents] = {0.315214, 0.86048, 0.378601, 0.12922, 0.0320002, 0.00813658, 0.00174178 }; +// const Scalar phi_o[numComponents] = {0.0633375, 1.73469, 0.19746, 0.0147604, 0.000630321, 2.48063e-05, 7.74427e-07 }; +// const Scalar pRefOil = 1.5e+07; + + assert(0 <= phaseIdx && phaseIdx <= numPhases); + assert(0 <= compIdx && compIdx <= numComponents); + + params.update(phaseIdx); + switch (phaseIdx) { + case gPhaseIdx: + case oPhaseIdx: { + params.update(phaseIdx); + Scalar phi = PengRobinsonMixture::computeFugacityCoeff(params, + phaseIdx, + compIdx); + return phi; + } + case wPhaseIdx: + return + henryCoeffWater_(compIdx, params.temperature(wPhaseIdx)) + / + params.pressure(wPhaseIdx); + default: DUNE_THROW(Dune::InvalidStateException, "Unhandled phase index " << phaseIdx); + } + } + + /* + static Scalar evalTable_(const Scalar *y, Scalar p) + { + Scalar tmp = pIdx_(p); + int pIdx = static_cast<int>(tmp); + + Scalar p1 = (pIdx + 0) * (pMax_ - pMin_)/Scalar(numEntries_) + pMin_; + Scalar p2 = (pIdx + 1) * (pMax_ - pMin_)/Scalar(numEntries_) + pMin_; + +#if 0 + Scalar alpha = tmp - pIdx; + return y[pIdx]*(1 - alpha) + y[pIdx + 1]*alpha; +#else + Scalar pPrimeLeft = (y[pIdx + 1] - y[pIdx - 1])/( 2*(pMax_ - pMin_)/numEntries_ ); + Scalar pPrimeRight = (y[pIdx + 2] - y[pIdx] )/( 2*(pMax_ - pMin_)/numEntries_ ); + Spline<Scalar> sp(p1, p2, + y[pIdx], y[pIdx + 1], + pPrimeLeft, pPrimeRight); + return sp.eval(p); +#endif + } + */ + + /*! + * \brief Calculate the dynamic viscosity of a fluid phase [Pa*s] + */ + static Scalar computeViscosity(MutableParameters ¶ms, + int phaseIdx) + { + assert(0 <= phaseIdx && phaseIdx <= numPhases); + params.update(phaseIdx); + + switch (phaseIdx) { + case gPhaseIdx: { + // given by SPE-5 in table on page 64. we use a constant + // viscosity, though... + return 0.0170e-2 * 0.1; + } + case wPhaseIdx: + // given by SPE-5: 0.7 centi-Poise = 0.0007 Pa s + return 0.7e-2 * 0.1; + case oPhaseIdx: { + // given by SPE-5 in table on page 64. we use a constant + // viscosity, though... + return 0.208e-2 * 0.1; + } + default: DUNE_THROW(Dune::InvalidStateException, "Unhandled phase index " << phaseIdx); + } + }; + + /*! + * \brief Calculate the binary molecular diffusion coefficient for + * a component in a fluid phase [mol^2 * s / (kg*m^3)] + * + * Molecular diffusion of a compoent $\kappa$ is caused by a + * gradient of the chemical potential and follows the law + * + * \f[ J = - D \grad mu_\kappa \f] + * + * where \f$\mu_\kappa\$ is the component's chemical potential, + * \f$D\f$ is the diffusion coefficient and \f$J\f$ is the + * diffusive flux. \f$mu_\kappa\f$ is connected to the component's + * fugacity \f$f_\kappa\f$ by the relation + * + * \f[ \mu_\kappa = R T_\alpha \mathrm{ln} \frac{f_\kappa}{p_\alpha} \f] + * + * where \f$p_\alpha\f$ and \f$T_\alpha\f$ are the fluid phase' + * pressure and temperature. + */ + static Scalar computeDiffusionCoeff(MutableParameters ¶ms, + int phaseIdx, + int compIdx) + { + // TODO! + DUNE_THROW(Dune::NotImplemented, "Diffusion coefficients"); + }; + + /*! + * \brief Given a phase's composition, temperature and pressure, + * calculate its specific enthalpy [J/kg]. + */ + static Scalar computeEnthalpy(MutableParameters ¶ms, + int phaseIdx) + { + // TODO! + DUNE_THROW(Dune::NotImplemented, "Enthalpies"); + }; + + /*! + * \brief Given a phase's composition, temperature and pressure, + * calculate its specific internal energy [J/kg]. + */ + static Scalar internalEnergy(MutableParameters ¶ms, + int phaseIdx) + { + // TODO! + DUNE_THROW(Dune::NotImplemented, "Enthalpies"); + }; + +private: + static Scalar henryCoeffWater_(int compIdx, Scalar temperature) + { + // use henry's law for the solutes and the vapor pressure for + // the solvent. + switch (compIdx) { + case H2OIdx: return H2O::vaporPressure(temperature); + + // the values of the Henry constant for the solutes have + // been computed using the Peng-Robinson equation of state + // (-> slope of the component's fugacity function at + // almost 100% water content) + case C1Idx: return 5.57601e+09; + case C3Idx: return 1.89465e+10; + case C6Idx: return 5.58969e+12; + case C10Idx: return 4.31947e+17; + case C15Idx: return 4.27283e+28; + case C20Idx: return 3.39438e+36; + default: DUNE_THROW(Dune::InvalidStateException, "Unknown component index " << compIdx); + } + }; +}; + +} // end namepace + +#endif diff --git a/test/boxmodels/Makefile.am b/test/boxmodels/Makefile.am index 8c3650936ee7f1e0ea9876976400e1fd2b645f62..bcebc872c57a4ace2ab7ce7452f4161db679cfd5 100644 --- a/test/boxmodels/Makefile.am +++ b/test/boxmodels/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = 1p 1p2c 2p 2p2c 2p2cni 2pni richards +SUBDIRS = 1p 1p2c 2p 2p2c 2p2cni 2pni MpNc richards EXTRA_DIST = CMakeLists.txt diff --git a/test/boxmodels/MpNc/CMakeLists.txt b/test/boxmodels/MpNc/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ad33076f533a6d95cc564f69aa8f8df7f55defbc --- /dev/null +++ b/test/boxmodels/MpNc/CMakeLists.txt @@ -0,0 +1,18 @@ +add_definitions(-DYASPGRID -DENABLE_ALBERTA -DENABLE_ALUGRID -DENABLE_UG) + +# 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/MpNc/Makefile.am b/test/boxmodels/MpNc/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..5d397cf79edb9c4010fd0acfbc888648199d5c21 --- /dev/null +++ b/test/boxmodels/MpNc/Makefile.am @@ -0,0 +1,10 @@ +check_PROGRAMS = test_MpNc +noinst_HEADERS = *.hh + +EXTRA_DIST=grids/*.dgf +gridsdir=$(datadir)/dumux/grids +grids_DATA=grids/*.dgf + +test_MpNc_SOURCES = test_MpNc.cc + +include $(top_srcdir)/am/global-rules diff --git a/test/boxmodels/MpNc/obstacleproblem.hh b/test/boxmodels/MpNc/obstacleproblem.hh new file mode 100644 index 0000000000000000000000000000000000000000..7df0e1748df45a7a8ed352e111d8ed0937c879e1 --- /dev/null +++ b/test/boxmodels/MpNc/obstacleproblem.hh @@ -0,0 +1,564 @@ +/***************************************************************************** + * 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 * + * 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 + * \ingroup TwoPTwoNBoxProblems + * + * \brief Problem where water and gas is injected by means of a + * dirchlet condition on the lower right of the domain which have to go + * around an obstacle with \f$10^3\f$ lower permeability. + * \author Andreas Lauser, Klaus Mosthaf, Bernd Flemisch + */ +#ifndef DUMUX_OBSTACLEPROBLEM_HH +#define DUMUX_OBSTACLEPROBLEM_HH + +#define USE_2P2C 0 +#define OBSTACLE_FIND_CONVERGENCE_RADIUS 0 + +#include <dune/common/parametertreeparser.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> + +#if USE_2P2C +#include <dumux/boxmodels/2p2c/2p2cmodel.hh> +#else +#include <dumux/boxmodels/MpNc/MpNcmodel.hh> +#endif + +/* +#include <dumux/material/fluidsystems/simple_h2o_n2_system.hh> +#include <dumux/material/fluidsystems/simple_h2o_n2_tce_system.hh> +#include <dumux/material/fluidsystems/h2o_n2_system.hh> +#include <dumux/material/fluidsystems/h2o_n2_o2_system.hh> +#include <dumux/material/fluidsystems/h2o_h2_o2_system.hh> +#include <dumux/material/fluidsystems/h2o_h2_n2_o2_system.hh> +*/ + +#include <dumux/material/new_fluidsystems/h2on2fluidsystem.hh> + +#include "obstaclespatialparameters.hh" + +namespace Dumux +{ + +template <class TypeTag> +class ObstacleProblem; + +namespace Properties +{ +#if USE_2P2C +NEW_TYPE_TAG(ObstacleProblem, INHERITS_FROM(BoxTwoPTwoC, ObstacleSpatialParameters)); +#else +NEW_TYPE_TAG(ObstacleProblem, INHERITS_FROM(BoxMPNC, ObstacleSpatialParameters)); +#endif + +// Set the grid type +SET_TYPE_PROP(ObstacleProblem, Grid, Dune::YaspGrid<2>); + +// Set the problem property +SET_TYPE_PROP(ObstacleProblem, + Problem, + Dumux::ObstacleProblem<TypeTag>); + + +// Set fluid configuration +SET_PROP(ObstacleProblem, FluidSystem) +{ private: + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; +public: + typedef Dumux::H2ON2FluidSystem<Scalar> type; +}; +// Dumux::Simple_H2O_N2_TCE_System<TypeTag> ); +//Dumux::H2O_N2_System<TypeTag> ); + +#if ! USE_2P2C +// Enable smooth upwinding? +SET_BOOL_PROP(ObstacleProblem, EnableSmoothUpwinding, false); + +// Enable molecular diffusion of the components? +SET_BOOL_PROP(ObstacleProblem, EnableDiffusion, false); + +// Use the chopped Newton method? +SET_BOOL_PROP(ObstacleProblem, NewtonEnableChop, true); +#endif + +// Enable gravity +SET_BOOL_PROP(ObstacleProblem, EnableGravity, true); + +// Write Newton convergence to disk? +SET_BOOL_PROP(ObstacleProblem, NewtonWriteConvergence, false); + +// Use the line search strategy for the Newton update? +SET_BOOL_PROP(ObstacleProblem, NewtonUseLineSearch, false); + +// Enable the re-use of the jacobian matrix whenever possible? +SET_BOOL_PROP(ObstacleProblem, EnableJacobianRecycling, true); + +// Reassemble the jacobian matrix only where it changed? +SET_BOOL_PROP(ObstacleProblem, EnablePartialReassemble, true); + +// use forward diffferences to approximate the partial derivatives +SET_INT_PROP(ObstacleProblem, NumericDifferenceMethod, +1); +} + + +/*! + * \ingroup TwoPBoxProblems + * \brief Problem where liquid water is injected by means of a + * dirchlet condition on the lower right of the domain which have to go + * around an obstacle with \f$10^3\f$ lower permeability. + * + * The domain is sized 60m times 40m and consists of two media, a + * moderately permeable soil (\f$ K_0=10e-12 m^2\f$) and an obstacle + * at \f$[10; 20]m \times [0; 35]m \f$ with a lower permeablility of + * \f$ K_1=K_0/1000\f$. + * + * Initially the whole domain is filled by nitrogen, the temperature + * is \f$20\symbol{23}C\f$ at the whole domain. The gas pressure at + * the left side of the domain is 1 bar, at the right side it is 2 bar + * with a linear gradient in between. + * + * The boundary is no-flow except on the lower 10 meters of the left + * and the right boundary which is a Dirichlet condition with the same + * values as the initial condition. + */ +template <class TypeTag> +class ObstacleProblem +#if USE_2P2C + : public TwoPTwoCProblem<TypeTag> +#else + : public MPNCProblem<TypeTag> +#endif +{ + typedef ObstacleProblem<TypeTag> ThisType; +#if USE_2P2C + typedef TwoPTwoCProblem<TypeTag> ParentType; +#else + typedef MPNCProblem<TypeTag> ParentType; +#endif + + 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(BoundaryTypes)) BoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + + enum { + // Grid and world dimension + dim = GridView::dimension, + dimWorld = GridView::dimensionworld, + + numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)), + numComponents = GET_PROP_VALUE(TypeTag, PTAG(NumComponents)), + + gPhaseIdx = FluidSystem::gPhaseIdx, + //oPhaseIdx = FluidSystem::oPhaseIdx, + lPhaseIdx = FluidSystem::lPhaseIdx, + }; + + 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 Dune::FieldVector<typename GridView::Grid::ctype, dimWorld> GlobalPosition; + + typedef typename GET_PROP_TYPE(TypeTag, PTAG(TimeManager)) TimeManager; + +#if USE_2P2C + typedef typename GET_PROP_TYPE(TypeTag, PTAG(TwoPTwoCIndices)) Indices; + +#else // ! USE_2P2C + // copy some indices for convenience + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MPNCIndices)) Indices; + enum { + fug0Idx = Indices::fug0Idx, + S0Idx = Indices::S0Idx, + p0Idx = Indices::p0Idx, + }; +#endif // USE_2P2C +public: + ObstacleProblem(TimeManager &timeManager, const GridView &gridView) + : ParentType(timeManager, gridView) + { + temperature_ = 273.15 + 25; // -> 25°C + + // initialize the tables of the fluid system + Scalar Tmin = temperature_ - 1.0; + Scalar Tmax = temperature_ + 1.0; + int nT = 10; + Scalar pmin = 0.75 * 1e5; + Scalar pmax = 1.25 * 2e5; + int np = 1000; + FluidSystem::init(Tmin, Tmax, nT, pmin, pmax, np); + } + + /*! + * \brief Called by Dumux::TimeManager in order to do a time + * integration on the model. + */ + void timeIntegration() + { +#if !OBSTACLE_FIND_CONVERGENCE_RADIUS + if (GET_PROP_VALUE(TypeTag, PTAG(NewtonWriteConvergence))) + this->newtonController().setMaxSteps(40); + ParentType::timeIntegration(); +#else + std::cout << "Finding convergence radius\n"; + this->newtonController().setVerbose(false); + this->newtonController().setMaxSteps(40); + this->newtonController().setTargetSteps(20); + Scalar successDt = 0.0; + const int maxFails = 10; + for (int i = 0; true; ++i) { + std::cout << "Try dt of " << this->timeManager().timeStepSize() << "\n"; + std::cout.flush(); + + if (i == maxFails && this->gridView().comm().rank() == 0) + DUNE_THROW(Dune::MathError, + "Newton solver didn't converge after " + << maxFails + << " timestep divisions. dt=" + << this->timeManager().timeStepSize()); + + if (this->model().update(this->newtonMethod(), + this->newtonController())) + { + // sucessfull update. remember time step size + successDt = this->timeManager().timeStepSize(); + this->model().updateFailed(); + break; + } + + if (i > 0 && this->gridView().comm().rank() == 0) + std::cout << "Newton solver did not converge. Retrying with time step of " + << this->timeManager().timeStepSize() << "sec\n"; + + // update failed + Scalar dt = this->timeManager().timeStepSize(); + Scalar nextDt = dt / 2; + this->timeManager().setTimeStepSize(nextDt); + std::cout << "Failed for dt=" << dt << ". Try with half dt of " << nextDt << "\n"; + } + + // increase time step until the update fails + while (true) + { + if (!this->model().update(this->newtonMethod(), + this->newtonController())) + break; + + // sucessfull update. increase time step size + successDt = this->timeManager().timeStepSize(); + Scalar nextDt = successDt*1.25; + this->timeManager().setTimeStepSize(nextDt); + if (this->timeManager().timeStepSize() < nextDt) { + std::cout << "End of simulation reached!\n"; + break; + }; + std::cout << "Increase dt to " << nextDt << "\n"; + std::cout.flush(); + this->model().updateFailed(); + } + + // do a last update with the largest successful time step + this->newtonController().setVerbose(true); + this->timeManager().setTimeStepSize(successDt); + std::cout << "Convergence radius is " << successDt << "\n"; + this->model().update(this->newtonMethod(), this->newtonController()); +#endif + } + + /*! + * \brief Called directly after the time integration. + */ + void postTimeStep() + { + // Calculate storage terms of the individual phases + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + PrimaryVariables phaseStorage; + this->model().globalPhaseStorage(phaseStorage, phaseIdx); + + if (this->gridView().comm().rank() == 0) { + std::cout + <<"Storage in " + << FluidSystem::phaseName(phaseIdx) + << "Phase: [" + << phaseStorage + << "]" + << "\n"; + } + } + + // Calculate total storage terms + PrimaryVariables storage; + this->model().globalStorage(storage); + + // Write mass balance information for rank 0 + if (this->gridView().comm().rank() == 0) { + std::cout + <<"Storage total: [" << storage << "]" + << "\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 "obstacle"; } + + /*! + * \brief Returns the temperature [K] within the domain. + */ + Scalar temperature() 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 (onInlet_(globalPos) || onOutlet_(globalPos)) + 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. + * + * For this method, the \a values parameter stores the mass flux + * in normal direction of each component. Negative values mean + * influx. + */ + void neumann(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvElemGeom, + const Intersection &is, + int scvIdx, + int boundaryFaceIdx) const + { values = 0; } + + // \} + + /*! + * \name Volume terms + */ + // \{ + + /*! + * \brief Evaluate the source term for all phases within a given + * sub-control-volume. + * + * For this method, the \a values parameter stores the rate mass + * of a component is generated or annihilate per volume + * unit. Positive values mean that mass is created, negative ones + * mean that it vanishes. + */ + 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. + * + * 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); + Valgrind::CheckDefined(values); + } + + // \} + + /*! + * \brief Write a restart file? + */ + bool shouldWriteRestartFile() const + { + return ParentType::shouldWriteRestartFile(); + } + + +#if USE_2P2C + /*! + * \brief Return the initial phase state inside a control volume. + */ + int initialPhasePresence(const Vertex &vert, + int &globalIdx, + const GlobalPosition &globalPos) const + { + if (onInlet_(globalPos)) + return Indices::lPhaseOnly; + else + return Indices::gPhaseOnly; + }; +#endif + +private: + // the internal method for the initial condition + void initial_(PrimaryVariables &values, + const GlobalPosition &globalPos) const + { + Scalar pg; + if (onInlet_(globalPos)) + pg = 2e5; + else + pg = 1e5; + + +#if USE_2P2C + values[Indices::plIdx] = pg; + + if (onInlet_(globalPos)) + values[Indices::SgOrXIdx] = 0.0; // X^a_w + else + values[Indices::SgOrXIdx] = 0.0; // X^w_g +#else + if (onInlet_(globalPos)) { + // only liquid + Scalar S[numPhases]; + Scalar p[numPhases]; + Scalar xl[numComponents]; + Scalar beta[numComponents]; + + p[gPhaseIdx] = pg; + p[lPhaseIdx] = pg; + + S[lPhaseIdx] = 1.0; + S[gPhaseIdx] = 0.0; + + xl[FluidSystem::H2OIdx] = 1.0; + xl[FluidSystem::N2Idx] = 0.0; + beta[FluidSystem::H2OIdx] = FluidSystem::H2O::vaporPressure(temperature_); + beta[FluidSystem::N2Idx] = Dumux::BinaryCoeff::H2O_N2::henry(temperature_); + + // assign the primary variables + for (int i = 0; i < numComponents; ++i) + values[fug0Idx + i] = xl[i]*beta[i]; + + for (int i = 0; i < numPhases - 1; ++i) + values[S0Idx + i] = S[i]; + + values[p0Idx] = p[0]; + } + else { + // only gas + Scalar S[numPhases]; + Scalar xg[numComponents]; + Scalar p[numPhases]; + + S[lPhaseIdx] = 0.0; + S[gPhaseIdx] = 1.0; + + p[lPhaseIdx] = pg; + p[gPhaseIdx] = pg; + + + xg[FluidSystem::H2OIdx] = 0.01; + xg[FluidSystem::N2Idx] = 0.99; + + + // assign the primary variables + for (int i = 0; i < numComponents; ++i) + values[fug0Idx + i] = xg[i]*pg; + + for (int i = 0; i < numPhases - 1; ++i) + values[S0Idx + i] = S[i]; + + values[p0Idx] = p[0]; + } +#endif // USE_2P2C + } + + bool onInlet_(const GlobalPosition &globalPos) const + { + Scalar x = globalPos[0]; + Scalar y = globalPos[1]; + return x >= 60 - eps_ && y <= 10; + }; + + bool onOutlet_(const GlobalPosition &globalPos) const + { + Scalar x = globalPos[0]; + Scalar y = globalPos[1]; + return x < eps_ && y <= 10; + }; + + Scalar temperature_; + static constexpr Scalar eps_ = 1e-6; +}; +} //end namespace + +#endif diff --git a/test/boxmodels/MpNc/obstaclespatialparameters.hh b/test/boxmodels/MpNc/obstaclespatialparameters.hh new file mode 100644 index 0000000000000000000000000000000000000000..f48e9d3202e0671d18b70caba21c553c4b48a5e6 --- /dev/null +++ b/test/boxmodels/MpNc/obstaclespatialparameters.hh @@ -0,0 +1,302 @@ +/***************************************************************************** + * Copyright (C) 2008-2009 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/>. * + *****************************************************************************/ +#ifndef DUMUX_OBSTACLE_SPATIAL_PARAMS_HH +#define DUMUX_OBSTACLE_SPATIAL_PARAMS_HH + +#include <dumux/material/spatialparameters/boxspatialparameters.hh> +#include <dumux/material/fluidmatrixinteractions/2p/linearmaterial.hh> +#include <dumux/material/fluidmatrixinteractions/2p/regularizedlinearmaterial.hh> +#include <dumux/material/fluidmatrixinteractions/2p/regularizedbrookscorey.hh> +#include <dumux/material/fluidmatrixinteractions/2p/efftoabslaw.hh> + +#if USE_2P2C +#include <dumux/boxmodels/2p2c/2p2cmodel.hh> +#else +#include <dumux/boxmodels/MpNc/MpNcmodel.hh> +#include <dumux/material/fluidmatrixinteractions/Mp/Mplinearmaterial.hh> +#include <dumux/material/fluidmatrixinteractions/Mp/2padapter.hh> +#endif + +namespace Dumux +{ + +//forward declaration +template<class TypeTag> +class ObstacleSpatialParameters; + +namespace Properties +{ +// The spatial parameters TypeTag +NEW_TYPE_TAG(ObstacleSpatialParameters); + +// Set the spatial parameters +SET_TYPE_PROP(ObstacleSpatialParameters, SpatialParameters, Dumux::ObstacleSpatialParameters<TypeTag>); + +// Set the material Law +SET_PROP(ObstacleSpatialParameters, MaterialLaw) +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + enum { + lPhaseIdx = FluidSystem::lPhaseIdx, + gPhaseIdx = FluidSystem::gPhaseIdx, + }; + // define the material law + typedef typename GET_PROP_TYPE(TypeTag, PTAG(Scalar)) Scalar; + // typedef RegularizedBrooksCorey<Scalar> EffMaterialLaw; + typedef RegularizedLinearMaterial<Scalar> EffMaterialLaw; + typedef EffToAbsLaw<EffMaterialLaw> TwoPMaterialLaw; +public: +#if USE_2P2C + typedef TwoPMaterialLaw type; +#else + typedef TwoPAdapter<lPhaseIdx, TwoPMaterialLaw> type; +#endif +}; +} + +/** + * \brief Definition of the spatial params properties for the obstacle problem + * + */ +template<class TypeTag> +class ObstacleSpatialParameters : 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 GET_PROP_TYPE(TypeTag, PTAG(SolutionVector)) SolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(ElementVolumeVariables)) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluxVariables)) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FVElementGeometry)) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, PTAG(FluidSystem)) FluidSystem; + + typedef typename Grid::ctype CoordScalar; + enum { + dim=GridView::dimension, + dimWorld=GridView::dimensionworld, + numPhases = GET_PROP_VALUE(TypeTag, PTAG(NumPhases)), + }; + + enum { + lPhaseIdx = FluidSystem::lPhaseIdx, + gPhaseIdx = FluidSystem::gPhaseIdx, + }; + + typedef typename GridView::template Codim<0>::Entity Element; + typedef Dune::FieldVector<CoordScalar,dimWorld> GlobalPosition; + typedef Dune::FieldVector<Scalar,dimWorld> Vector; + +public: + typedef typename GET_PROP_TYPE(TypeTag, PTAG(MaterialLaw)) MaterialLaw; + typedef typename MaterialLaw::Params MaterialLawParams; + + ObstacleSpatialParameters(const GridView &gv) + : ParentType(gv) + { + // intrinsic permeabilities + coarseK_ = 1e-12; + fineK_ = 1e-15; + + // the porosity + porosity_ = 0.3; + + // residual saturations + fineMaterialParams_.setSwr(0.0); + fineMaterialParams_.setSnr(0.0); + coarseMaterialParams_.setSwr(0.0); + coarseMaterialParams_.setSnr(0.0); + + // parameters for the linear law, i.e. minimum and maximum + // pressures + fineMaterialParams_.setEntryPC(0.0); + coarseMaterialParams_.setEntryPC(0.0); + fineMaterialParams_.setMaxPC(0.0); + coarseMaterialParams_.setMaxPC(0.0); + + /* + // entry pressures for Brooks-Corey + fineMaterialParams_.setPe(5e3); + coarseMaterialParams_.setPe(1e3); + + // Brooks-Corey shape parameters + fineMaterialParams_.setLambda(2); + coarseMaterialParams_.setLambda(2); + */ + } + + ~ObstacleSpatialParameters() + {} + + /*! + * \brief Update the spatial parameters with the flow solution + * after a timestep. + * + * \TODO call parameters + */ + void update(const SolutionVector &globalSol) + { + }; + + /*! + * \brief Returns the intrinsic permeability tensor. + * + * \param element The current finite element + * \param fvElemGeom The current finite volume geometry of the element + * \param scvIdx The index sub-control volume where the + * intrinsic permeability is given. + */ + Scalar intrinsicPermeability(const Element &element, + const FVElementGeometry &fvElemGeom, + int scvIdx) const + { + if (isFineMaterial_(fvElemGeom.subContVol[scvIdx].global)) + return fineK_; + return coarseK_; + } + + /*! + * \brief Define the porosity \f$[-]\f$ of the soil + * + * \param vDat The data defined on the sub-control volume + * \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 + { + return porosity_; + } + + /*! + * \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)] + } + + /*! + * \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 result vector + * \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; // [W / (m K ) ] +// static const Scalar lGranite = 2.8; // [W / (m K ) ] +// +// // 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; +// } + + // return the brooks-corey context depending on the position + 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_; + } + + const Scalar & soilDensity(const Element &element, + const FVElementGeometry &fvElemGeom, + int scvIdx) const + { + return 2700. ; // density of granite [kg/m^3] + } + + const Scalar & soilThermalConductivity(const Element &element, + const FVElementGeometry &fvElemGeom, + int scvIdx) const + { + return 2.8; // conductivity of granite [W / (m K ) ] + } + +private: + /*! + * \brief Returns whether a given global position is in the + * fine-permeability region or not. + */ + static bool isFineMaterial_(const GlobalPosition &pos) + { + return + 10 <= pos[0] && pos[0] <= 20 && + 0 <= pos[1] && pos[1] <= 35; + }; + + Scalar coarseK_; + Scalar fineK_; + Scalar porosity_; + MaterialLawParams fineMaterialParams_; + MaterialLawParams coarseMaterialParams_; +}; + +} + +#endif diff --git a/test/boxmodels/MpNc/test_MpNc.cc b/test/boxmodels/MpNc/test_MpNc.cc new file mode 100644 index 0000000000000000000000000000000000000000..be92f454d02c69e44d879e9ebd64ad1b68fade2f --- /dev/null +++ b/test/boxmodels/MpNc/test_MpNc.cc @@ -0,0 +1,31 @@ +/***************************************************************************** + * 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/>. * + *****************************************************************************/ +#include "config.h" +#include "obstacleproblem.hh" + +#include <dumux/common/start.hh> + +int main(int argc, char** argv) +{ + typedef TTAG(ObstacleProblem) TypeTag; + int ret = Dumux::startFromDGF<TypeTag>(argc, argv); + Dumux::Parameters::print<TypeTag>(); + return ret; +}