problem.hh 9.86 KB
Newer Older
1
2
3
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
// vi: set et ts=4 sw=4 sts=4:
/*****************************************************************************
4
 *   See the file COPYING for full copying permissions.                      *
5
6
7
8
9
10
11
12
 *                                                                           *
 *   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          *
13
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
14
15
16
17
18
19
20
 *   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
Kai Wendel's avatar
Kai Wendel committed
21
 * \brief Isothermal NAPL infiltration problem: L- or DNAPL contaminates
22
23
 *        the unsaturated and the saturated groundwater zone.
 */
24
25
#ifndef DUMUX_NAPLINFILTRATIONPROBLEM_3P_HH
#define DUMUX_NAPLINFILTRATIONPROBLEM_3P_HH
26

27
28
#include <dumux/common/properties.hh>
#include <dumux/common/parameters.hh>
29
#include <dumux/common/boundarytypes.hh>
30
#include <dumux/common/numeqvector.hh>
Kai Wendel's avatar
Kai Wendel committed
31
#include <dumux/porousmediumflow/problem.hh>
32
33
34
35
36
37
38
39
40

namespace Dumux
{
/*!
 * \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
41
 * upper part is the vadose zone.
42
43
44
45
46
47
48
49
50
51
52
53
 * 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.
 *
Kai Wendel's avatar
Kai Wendel committed
54
 * This problem uses the \ref ThreePModel.
55
56
57
 *
 * This problem should typically be simulated for 30 days.
 * A good choice for the initial time step size is 60 s.
Kai Wendel's avatar
Kai Wendel committed
58
 * To adjust the simulation time it is necessary to edit the file naplinfiltrationexercise.input
59
60
 *
 * To run the simulation execute the following line in shell:
Kai Wendel's avatar
Kai Wendel committed
61
 * <tt>./naplinfiltrationexercise -parameterFile naplinfiltrationexercise.input</tt>
62
63
 *  */
template <class TypeTag >
Kai Wendel's avatar
Kai Wendel committed
64
class InfiltrationThreePProblem : public PorousMediumFlowProblem<TypeTag>
65
{
Kai Wendel's avatar
Kai Wendel committed
66
    using ParentType = PorousMediumFlowProblem<TypeTag>;
67

68
    using Scalar = GetPropType<TypeTag, Properties::Scalar>;
69
    using GridView = typename GetPropType<TypeTag, Properties::GridGeometry>::GridView;
70
    using Indices = typename GetPropType<TypeTag, Properties::ModelTraits>::Indices;
71
72
73

    enum {
        pressureIdx = Indices::pressureIdx,
Kai Wendel's avatar
Kai Wendel committed
74
75
        swIdx = Indices::swIdx,
        snIdx = Indices::snIdx,
76

Kai Wendel's avatar
Kai Wendel committed
77
        // world dimension
78
79
80
        dimWorld = GridView::dimensionworld
    };

81
    using PrimaryVariables = GetPropType<TypeTag, Properties::PrimaryVariables>;
82
    using NumEqVector = Dumux::NumEqVector<PrimaryVariables>;
83
    using BoundaryTypes = Dumux::BoundaryTypes<GetPropType<TypeTag, Properties::ModelTraits>::numEq()>;
84
    using FluidSystem = GetPropType<TypeTag, Properties::FluidSystem>;
85
    using FVGridGeometry = GetPropType<TypeTag, Properties::GridGeometry>;
86

Kai Wendel's avatar
Kai Wendel committed
87
88
    using Element = typename GridView::template Codim<0>::Entity;
    using GlobalPosition = typename Element::Geometry::GlobalCoordinate;
89
90
91
92
93
94

public:
    /*!
     * \brief The constructor
     *
     */
Kai Wendel's avatar
Kai Wendel committed
95
96
    InfiltrationThreePProblem(std::shared_ptr<const FVGridGeometry> fvGridGeometry)
    : ParentType(fvGridGeometry)
97
98
    {
        temperature_ = 273.15 + 10.0; // -> 10 degrees Celsius
Kai Wendel's avatar
Kai Wendel committed
99
        FluidSystem::init(282.15, 284.15, 3, 8e4, 3e5, 200);
100

Kai Wendel's avatar
Kai Wendel committed
101
        name_ = getParam<std::string>("Problem.Name");
102

Kai Wendel's avatar
Kai Wendel committed
103
104
        this->spatialParams().plotMaterialLaw();
        time_ = 0.0;
105
106
107
108
109
110
    }

    /*!
     * \name Problem parameters
     */
    // \{
Kai Wendel's avatar
Kai Wendel committed
111
112
    void setTime(Scalar time)
    { time_ = time; }
113
114
115
116
117
118

    /*!
     * \brief The problem name.
     *
     * This is used as a prefix for files generated by the simulation.
     */
Kai Wendel's avatar
Kai Wendel committed
119
120
    const std::string& name() const
    { return name_; }
121
122
123
124

    /*!
     * \brief Returns the temperature within the domain.
     *
Kai Wendel's avatar
Kai Wendel committed
125
126
     * \param globalPos The position
     *
127
128
     * This problem assumes a temperature of 10 degrees Celsius.
     */
Kai Wendel's avatar
Kai Wendel committed
129
    Scalar temperatureAtPos(const GlobalPosition &globalPos) const
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
    {
        return temperature_;
    }

    // \}

    /*!
     * \name Boundary conditions
     */
    // \{

    /*!
     * \brief Specifies which kind of boundary condition should be
     *        used for which equation on a given boundary segment.
     *
Kai Wendel's avatar
Kai Wendel committed
145
     * \param globalPos The position for which the bc type should be evaluated
146
     */
Kai Wendel's avatar
Kai Wendel committed
147
    BoundaryTypes boundaryTypesAtPos(const GlobalPosition &globalPos) const
148
    {
Kai Wendel's avatar
Kai Wendel committed
149
150
        BoundaryTypes values;

151
152
        if(globalPos[0] > this->gridGeometry().bBoxMax()[0] - eps_
           || globalPos[0] < this->gridGeometry().bBoxMin()[0] + eps_)
153
154
155
            values.setAllDirichlet();
        else
            values.setAllNeumann();
Kai Wendel's avatar
Kai Wendel committed
156
157

        return values;
158
159
160
161
    }

    /*!
     * \brief Evaluate the boundary conditions for a dirichlet
Kai Wendel's avatar
Kai Wendel committed
162
     *        boundary segment.
163
164
     *
     * \param values The dirichlet values for the primary variables
Kai Wendel's avatar
Kai Wendel committed
165
     * \param globalPos The position for which the bc type should be evaluated
166
167
168
     *
     * For this method, the \a values parameter stores primary variables.
     */
Kai Wendel's avatar
Kai Wendel committed
169
    PrimaryVariables dirichletAtPos(const GlobalPosition &globalPos) const
170
    {
Kai Wendel's avatar
Kai Wendel committed
171
        return initial_(globalPos);
172
173
174
175
176
    }
    /*!
     * \brief Evaluate the boundary conditions for a neumann
     *        boundary segment.
     *
Kai Wendel's avatar
Kai Wendel committed
177
     * \param globalPos The position of the integration point of the boundary segment.
178
179
180
181
     *
     * For this method, the \a values parameter stores the mass flux
     * in normal direction of each phase. Negative values mean influx.
     */
Kai Wendel's avatar
Kai Wendel committed
182
    NumEqVector neumannAtPos(const GlobalPosition &globalPos) const
183
    {
Kai Wendel's avatar
Kai Wendel committed
184
        NumEqVector values(0.0);
185
186

        // negative values for injection
Kai Wendel's avatar
Kai Wendel committed
187
        if (time_ < 2592000.0 - eps_)
188
        {
Kai Wendel's avatar
Kai Wendel committed
189
            if ((globalPos[0] < 175.0 + eps_) && (globalPos[0] > 150.0 - eps_)
190
                 && (globalPos[1] > this->gridGeometry().bBoxMax()[1] - eps_))
191
            {
Kai Wendel's avatar
Kai Wendel committed
192
193
                // mol fluxes, convert with M(Mesit.)=0,120 kg/mol --> 1.2e-4  kg/(sm)
                values[Indices::conti0EqIdx + FluidSystem::nCompIdx] = -0.001*0.12;
194
            }
195
        }
Kai Wendel's avatar
Kai Wendel committed
196
197

        return values;
198
199
200
201
202
203
204
205
206
207
208
209
    }

    // \}

    /*!
     * \name Volume terms
     */
    // \{

    /*!
     * \brief Evaluate the initial value for a control volume.
     *
Kai Wendel's avatar
Kai Wendel committed
210
     * \param globalPos The position for which the initial condition should be evaluated
211
     *
Kai Wendel's avatar
Kai Wendel committed
212
213
     * For this method, the \a values parameter stores primary
     * variables.
214
     */
Kai Wendel's avatar
Kai Wendel committed
215
    PrimaryVariables initialAtPos(const GlobalPosition &globalPos) const
216
    {
Kai Wendel's avatar
Kai Wendel committed
217
        return initial_(globalPos);
218
219
    }

Kai Wendel's avatar
Kai Wendel committed
220
221
222
223
224
225
226
227
    /*!
     * \brief Returns the temperature within the domain.
     *
     * This problem assumes a uniform temperature of 10 degrees Celsius.
     */
    Scalar temperature() const
    { return temperature_; }

228

229
230
231
232

private:
    // internal method for the initial condition (reused for the
    // dirichlet conditions!)
Kai Wendel's avatar
Kai Wendel committed
233
    PrimaryVariables initial_(const GlobalPosition &globalPos) const
234
    {
Kai Wendel's avatar
Kai Wendel committed
235
        PrimaryVariables values(0.0);
236
        Scalar swr=0.12, sgr=0.03;
Kai Wendel's avatar
Kai Wendel committed
237
238
239

        Scalar y = globalPos[1];
        Scalar x = globalPos[0];
240

Kai Wendel's avatar
Kai Wendel committed
241
        if(y > (-1e-3*x+5) - eps_)
242
        {
Kai Wendel's avatar
Kai Wendel committed
243
244
            Scalar pc = 9.81 * 1000.0 * (y - (-5e-4*x+5));
            if (pc < 0.0) pc = 0.0;
245

246
            Scalar sw = invertPcgw_(pc, this->spatialParams().fluidMatrixInteractionAtPos(globalPos));
Kai Wendel's avatar
Kai Wendel committed
247
248
249
250
251
252
            if(sw < swr) sw = swr;
            if(sw > 1.-sgr) sw = 1.-sgr;

            values[pressureIdx] = 1e5;
            values[swIdx] = sw;
            values[snIdx] = 0.;
253
254
        }
        else
255
        {
Kai Wendel's avatar
Kai Wendel committed
256
257
258
            values[pressureIdx] = 1e5 + 9.81 * 1000.0 * ((-5e-4*x+5) - y);
            values[swIdx] = 1.-sgr;
            values[snIdx] = 0.;
259
        }
Kai Wendel's avatar
Kai Wendel committed
260
        return values;
261
262
    }

Kai Wendel's avatar
Kai Wendel committed
263
    // small solver inverting the pc curve
264
265
    template<class FMInteraction>
    static Scalar invertPcgw_(Scalar pcIn, const FMInteraction& fluidMatrixInteraction)
266
    {
267
268
        Scalar lower(0.0);
        Scalar upper(1.0);
Kai Wendel's avatar
Kai Wendel committed
269
        const unsigned int maxIter = 25;
270
271
        const Scalar bisLimit = 1.0;

Kai Wendel's avatar
Kai Wendel committed
272
273
        Scalar sw, pcgw;
        for (unsigned int k = 1; k <= maxIter; k++)
274
        {
275
            sw = 0.5*(upper + lower);
276
            pcgw = fluidMatrixInteraction.pcgw(sw, 0.0);
Kai Wendel's avatar
Kai Wendel committed
277
            const Scalar delta = std::abs(pcgw - pcIn);
278
279
280
            if (delta < bisLimit)
                return sw;

Kai Wendel's avatar
Kai Wendel committed
281
            if (k == maxIter)
282
283
                return sw;

Kai Wendel's avatar
Kai Wendel committed
284
            if (pcgw > pcIn)
285
286
287
                lower = sw;
            else
                upper = sw;
288
        }
289
        return sw;
290
291
292
    }

    Scalar temperature_;
Kai Wendel's avatar
Kai Wendel committed
293
294
295
    static constexpr Scalar eps_ = 1e-6;
    std::string name_;
    Scalar time_;
296
};
Kai Wendel's avatar
Kai Wendel committed
297
298

} // end namespace Dumux
299
300

#endif