Commit 93b6db81 authored by Timo Koch's avatar Timo Koch
Browse files

[bugfix][napl] Use mass fractions for 3p test version

parent edeea605
......@@ -2,7 +2,7 @@
DtInitial = 60 # [s]
TEnd = 31536000 # [s]
#TEnd = 315360000000 # [s]
EpisodeLength = 3153600 # [s]
EpisodeLength = 86400 # [s]
[Grid]
UpperRight = 500 10 # coordinates of upper right grid corner [m]
......
......@@ -251,8 +251,8 @@ public:
&& globalPos[1] > this->bBoxMax()[1] - eps_) // upper boundary
{
values[contiWEqIdx] = 0.0;
// /* mole flux, conversion via M(mesit.) = 0,120 kg/mol --> 1.2e-4 kg/(s*m)
values[contiNEqIdx] = -0.001;
// mole flux conversion via M(mesit.) = 0,120 kg/mol --> 1.2e-4 kg/(s*m)
values[contiNEqIdx] = -0.001*0.12; // 3p needs a mass fraction
values[contiGEqIdx] = 0.0;
}
}
......
......@@ -2,7 +2,7 @@
DtInitial = 60 # [s]
TEnd = 31536000 # [s]
#TEnd = 315360000000 # [s]
EpisodeLength = 3153600 # [s]
EpisodeLength = 86400 # [s]
[Grid]
UpperRight = 500 10 # coordinates of upper right grid corner [m]
......
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
// vi: set et ts=4 sw=4 sts=4:
/*****************************************************************************
* See the file COPYING for full copying permissions. *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*****************************************************************************/
/*!
* \file
*
* \brief Isothermal NAPL infiltration problem: LNAPL contaminates
* the unsaturated and the saturated groundwater zone.
*/
#ifndef DUMUX_NAPLINFILTRATIONPROBLEM_HH
#define DUMUX_NAPLINFILTRATIONPROBLEM_HH
#include <dumux/porousmediumflow/3p/implicit/propertydefaults.hh>
#include <dumux/porousmediumflow/3p3c/implicit/propertydefaults.hh>
#include <dumux/porousmediumflow/implicit/problem.hh>
#include "h2oairnaplfluidsystem.hh"
#include "naplinfiltrationspatialparams.hh"
namespace Dumux
{
template <class TypeTag>
class InfiltrationProblem;
namespace Properties
{
NEW_TYPE_TAG(InfiltrationProblem, INHERITS_FROM(InfiltrationSpatialParams));
NEW_TYPE_TAG(InfiltrationProblemThreeP, INHERITS_FROM(BoxThreeP, InfiltrationProblem));
NEW_TYPE_TAG(InfiltrationProblemThreePThreeC, INHERITS_FROM(BoxThreePThreeC, InfiltrationProblem));
// Set the grid type
SET_TYPE_PROP(InfiltrationProblem, Grid, Dune::YaspGrid<2>);
// Set the problem property
SET_TYPE_PROP(InfiltrationProblem, Problem, Dumux::InfiltrationProblem<TypeTag>);
// Set the fluid system
SET_TYPE_PROP(InfiltrationProblem,
FluidSystem,
Dumux::FluidSystems::H2OAirNAPL<TypeTag, typename GET_PROP_TYPE(TypeTag, Scalar)>);
// Enable gravity?
SET_BOOL_PROP(InfiltrationProblem, ProblemEnableGravity, true);
// Write newton convergence?
SET_BOOL_PROP(InfiltrationProblem, NewtonWriteConvergence, false);
// Maximum tolerated relative error in the Newton method
SET_SCALAR_PROP(InfiltrationProblem, NewtonMaxRelativeShift, 1e-4);
// -1 backward differences, 0: central differences, +1: forward differences
SET_INT_PROP(InfiltrationProblem, ImplicitNumericDifferenceMethod, 1);
}
/*!
* \ingroup ThreePThreeCBoxModel
* \ingroup BoxTestProblems
* \brief Isothermal NAPL infiltration problem: LNAPL contaminates
* the unsaturated and the saturated groundwater zone.
*
* The 2D domain of this test problem is 500 m long and 10 m deep, where
* the lower part represents a slightly inclined groundwater table, and the
* upper part is the vadose zone.
* A LNAPL (Non-Aqueous Phase Liquid which is lighter than water) infiltrates
* (modelled with a Neumann boundary condition) into the vadose zone. Upon
* reaching the water table, it spreads (since lighter than water) and migrates
* on top of the water table in the direction of the slope.
* On its way through the vadose zone, it leaves a trace of residually trapped
* immobile NAPL, which can in the following dissolve and evaporate slowly,
* and eventually be transported by advection and diffusion.
*
* Left and right boundaries are constant head boundaries (Dirichlet),
* Top and bottom are Neumann boundaries, all no-flow except for the small
* infiltration zone in the upper left part.
*
* This problem uses the \ref ThreePThreeCModel or the \ref ThreePModel.
*
* This problem should typically be simulated for 30 days.
* A good choice for the initial time step size is 60 s.
* To adjust the simulation time it is necessary to edit the file test_naplfiltrationexercise.input
*
* To run the simulation execute the following line in shell:
* <tt>./test_naplfiltrationexercise -ParameterFile test_naplfiltrationexercise.input</tt>
* */
template <class TypeTag >
class InfiltrationProblem : public ImplicitPorousMediaProblem<TypeTag>
{
typedef ImplicitPorousMediaProblem<TypeTag> ParentType;
typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar;
typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView;
// copy some indices for convenience
typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices;
enum {
pressureIdx = Indices::pressureIdx,
#if USE_THREE_P_MODEL
switch1Idx = Indices::swIdx,
switch2Idx = Indices::snIdx,
#else
switch1Idx = Indices::switch1Idx,
switch2Idx = Indices::switch2Idx,
wgPhaseOnly = Indices::wgPhaseOnly,
#endif
contiWEqIdx = Indices::contiWEqIdx,
contiNEqIdx = Indices::contiNEqIdx,
contiGEqIdx = Indices::contiGEqIdx,
// Grid and world dimension
dim = GridView::dimension,
dimWorld = GridView::dimensionworld
};
typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables;
typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes;
typedef typename GET_PROP_TYPE(TypeTag, TimeManager) TimeManager;
typedef typename GridView::template Codim<0>::Entity Element;
typedef typename GridView::template Codim<dim>::Entity Vertex;
typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry;
typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem;
typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw;
typedef typename GET_PROP_TYPE(TypeTag, MaterialLawParams) MaterialLawParams;
typedef Dune::FieldVector<Scalar, dimWorld> GlobalPosition;
public:
/*!
* \brief The constructor
*
* \param timeManager The time manager
* \param gridView The grid view
*/
InfiltrationProblem(TimeManager &timeManager, const GridView &gridView)
: ParentType(timeManager, gridView), eps_(1e-5)
{
temperature_ = 273.15 + 10.0; // -> 10 degrees Celsius
episodeLength_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, TimeManager, EpisodeLength);
this->timeManager().startNextEpisode(episodeLength_);
FluidSystem::init(/*tempMin=*/temperature_ - 1,
/*tempMax=*/temperature_ + 1,
/*nTemp=*/3,
/*pressMin=*/0.8*1e5,
/*pressMax=*/3*1e5,
/*nPress=*/200);
}
/*!
* \name Problem parameters
*/
// \{
/*!
* \brief The problem name.
*
* This is used as a prefix for files generated by the simulation.
*/
const char *name() const
{ return "infiltration"; }
/*!
* \brief Returns the temperature within the domain.
*
* This problem assumes a temperature of 10 degrees Celsius.
*/
Scalar temperature() const
{
return temperature_;
};
void sourceAtPos(PrimaryVariables &values,
const GlobalPosition &globalPos) const
{
values = 0;
}
// \}
/*!
* \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 globalPos The position of the finite volume in global coordinates
*/
void boundaryTypesAtPos(BoundaryTypes &values,
const GlobalPosition &globalPos) const
{
if(globalPos[0] > this->bBoxMax()[0] - eps_)
values.setAllDirichlet();
else if(globalPos[0] < this->bBoxMin()[0] + eps_)
values.setAllDirichlet();
else
values.setAllNeumann();
}
/*!
* \brief Evaluate the boundary conditions for a dirichlet
* control volume.
*
* \param values The dirichlet values for the primary variables
* \param globalPos The position of the center of the finite volume
* for which the dirichlet condition ought to be
* set in global coordinates
*
* For this method, the \a values parameter stores primary variables.
*/
void dirichletAtPos(PrimaryVariables &values,
const GlobalPosition &globalPos) const
{
initial_(values, globalPos);
}
/*!
* \brief Evaluate the boundary conditions for a neumann
* boundary segment.
*
* \param values The neumann values for the conservation equations in units of
* \f$ [ \textnormal{unit of conserved quantity} / (m^2 \cdot s )] \f$
* \param globalPos The position of the boundary face's integration point in global coordinates
*
* For this method, the \a values parameter stores the mass flux
* in normal direction of each phase. Negative values mean influx.
*/
void neumannAtPos(PrimaryVariables &values,
const GlobalPosition &globalPos) const
{
values = 0;
// negative values for injection
if (this->timeManager().time() < 2592000)
{
if (globalPos[0] < 175.0 + eps_
&& globalPos[0] > 150.0 + eps_
&& globalPos[1] > this->bBoxMax()[1] - eps_) // upper boundary
{
values[contiWEqIdx] = 0.0;
// /* mole flux, conversion via M(mesit.) = 0,120 kg/mol --> 1.2e-4 kg/(s*m)
values[contiNEqIdx] = -0.001;
values[contiGEqIdx] = 0.0;
}
}
}
// \}
/*!
* \name Volume terms
*/
// \{
/*!
* \brief Evaluate the initial value for a control volume.
*
* \param values The dirichlet values for the primary variables
* \param globalPos The position of the center of the finite volume
* for which the initial values ought to be
* set (in global coordinates)
*
* For this method, the \a values parameter stores primary variables.
*/
void initialAtPos(PrimaryVariables &values,
const GlobalPosition &globalPos) const
{
initial_(values, globalPos);
}
#if !USE_THREE_P_MODEL
/*!
* \brief Return the initial phase state inside a control volume.
*
* \param vert The vertex
* \param globalIdx The index of the global vertex
* \param globalPos The global position
*/
int initialPhasePresence(const Vertex &vertex,
int globalIdx,
const GlobalPosition &globalPos) const
{
return Indices::wgPhaseOnly;
}
#endif
bool shouldWriteOutput() const
{
return this->timeManager().timeStepIndex() == 0 ||
this->timeManager().episodeWillBeOver() ||
this->timeManager().willBeFinished();
}
void episodeEnd()
{
this->timeManager().startNextEpisode(episodeLength_);
}
private:
// internal method for the initial condition (reused for the
// dirichlet conditions!)
void initial_(PrimaryVariables &values,
const GlobalPosition &globalPos) const
{
const auto& materialLawParams = this->spatialParams().materialLawParams();
const Scalar swr = materialLawParams.swr();
const Scalar sgr = materialLawParams.sgr();
if(globalPos[1] >= -1e-3*globalPos[0] + 5)
{
const Scalar pc = std::max(0.0, 9.81*1000.0*(globalPos[1] + 5e-4*globalPos[0] - 5));
const Scalar sw = std::min(1.0-sgr, std::max(swr, invertPcGW_(pc, materialLawParams)));
values[pressureIdx] = 1.0e5;
values[switch1Idx] = sw;
values[switch2Idx] = 0.0;
}
else
{
values[pressureIdx] = 1.0e5 + 9.81*1000.0*(5 - 5.0e-4*globalPos[0] - globalPos[1]);
values[switch1Idx] = 1.0 - sgr;
values[switch2Idx] = 0.0;
}
}
static Scalar invertPcGW_(const Scalar pcIn,
const MaterialLawParams &pcParams)
{
Scalar lower(0.0);
Scalar upper(1.0);
const unsigned int maxIterations = 25;
const Scalar bisLimit = 1.0;
Scalar sw, pcGW;
for (unsigned int k = 1; k <= maxIterations; k++)
{
sw = 0.5*(upper + lower);
pcGW = MaterialLaw::pcgw(pcParams, sw);
const Scalar delta = std::abs(pcGW - pcIn);
if (delta < bisLimit)
return sw;
if (k == maxIterations)
return sw;
if (pcGW > pcIn)
lower = sw;
else
upper = sw;
}
return sw;
}
Scalar temperature_;
const Scalar eps_;
Scalar episodeLength_;
};
} //end namespace
#endif
......@@ -253,8 +253,8 @@ public:
&& globalPos[1] > this->bBoxMax()[1] - eps_) // upper boundary
{
values[contiWEqIdx] = 0.0;
// /* mole flux, conversion via M(mesit.) = 0,120 kg/mol --> 1.2e-4 kg/(s*m)
values[contiNEqIdx] = -0.001;
// mole flux conversion via M(mesit.) = 0,120 kg/mol --> 1.2e-4 kg/(s*m)
values[contiNEqIdx] = -0.001; // 3p3c needs a mole fraction
values[contiGEqIdx] = 0.0;
}
}
......
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment