Loading [MathJax]/jax/output/HTML-CSS/config.js
3.4
DUNE for Multi-{Phase, Component, Scale, Physics, ...} flow and transport in porous media
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
cctpfa/forchheimerslaw.hh
Go to the documentation of this file.
1// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2// vi: set et ts=4 sw=4 sts=4:
3/*****************************************************************************
4 * See the file COPYING for full copying permissions. *
5 * *
6 * This program is free software: you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation, either version 3 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
18 *****************************************************************************/
24#ifndef DUMUX_DISCRETIZATION_CC_TPFA_FORCHHEIMERS_LAW_HH
25#define DUMUX_DISCRETIZATION_CC_TPFA_FORCHHEIMERS_LAW_HH
26
27#include <dune/common/fvector.hh>
28#include <dune/common/fmatrix.hh>
29
30#include <dumux/common/math.hh>
34
38
39namespace Dumux {
40
41// forward declarations
42template<class TypeTag, DiscretizationMethod discMethod>
43class ForchheimersLawImplementation;
44
53template<class Scalar, class GridGeometry, class UpwindScheme, bool isNetwork>
55
61template <class TypeTag>
63: public CCTpfaForchheimersLaw<GetPropType<TypeTag, Properties::Scalar>,
64 GetPropType<TypeTag, Properties::GridGeometry>,
65 typename GetPropType<TypeTag, Properties::FluxVariables>::UpwindScheme,
66 (GetPropType<TypeTag, Properties::GridGeometry>::GridView::dimension < GetPropType<TypeTag, Properties::GridGeometry>::GridView::dimensionworld)>
67{};
68
73template<class GridGeometry>
74class TpfaForchheimersLawCacheFiller
75{
76 using FVElementGeometry = typename GridGeometry::LocalView;
77 using SubControlVolumeFace = typename GridGeometry::SubControlVolumeFace;
78 using Element = typename GridGeometry::GridView::template Codim<0>::Entity;
79
80public:
84 template<class FluxVariablesCache, class Problem, class ElementVolumeVariables, class FluxVariablesCacheFiller>
85 static void fill(FluxVariablesCache& scvfFluxVarsCache,
86 const Problem& problem,
87 const Element& element,
88 const FVElementGeometry& fvGeometry,
89 const ElementVolumeVariables& elemVolVars,
90 const SubControlVolumeFace& scvf,
91 const FluxVariablesCacheFiller& fluxVarsCacheFiller)
92 {
93 scvfFluxVarsCache.updateAdvection(problem, element, fvGeometry, elemVolVars, scvf);
94 }
95};
96
101template<class AdvectionType, class GridGeometry>
102class TpfaForchheimersLawCache
103{
104 using Scalar = typename AdvectionType::Scalar;
105 using FVElementGeometry = typename GridGeometry::LocalView;
106 using SubControlVolumeFace = typename GridGeometry::SubControlVolumeFace;
107 using Element = typename GridGeometry::GridView::template Codim<0>::Entity;
108 static constexpr int dimWorld = GridGeometry::GridView::dimensionworld;
109 using DimWorldMatrix = Dune::FieldMatrix<Scalar, dimWorld, dimWorld>;
110
111public:
112 using Filler = TpfaForchheimersLawCacheFiller<GridGeometry>;
113
114 template<class Problem, class ElementVolumeVariables>
115 void updateAdvection(const Problem& problem,
116 const Element& element,
117 const FVElementGeometry& fvGeometry,
118 const ElementVolumeVariables& elemVolVars,
119 const SubControlVolumeFace &scvf)
120 {
121 tij_ = AdvectionType::calculateTransmissibility(problem, element, fvGeometry, elemVolVars, scvf);
122 harmonicMeanSqrtK_ = AdvectionType::calculateHarmonicMeanSqrtPermeability(problem, elemVolVars, scvf);
123 }
124
125 const Scalar& advectionTij() const
126 { return tij_; }
127
128 const DimWorldMatrix& harmonicMeanSqrtPermeability() const
129 { return harmonicMeanSqrtK_; }
130
131private:
132 Scalar tij_;
133 DimWorldMatrix harmonicMeanSqrtK_;
134};
135
140template<class ScalarType, class GridGeometry, class UpwindScheme>
141class CCTpfaForchheimersLaw<ScalarType, GridGeometry, UpwindScheme, /*isNetwork*/ false>
142{
143 using ThisType = CCTpfaForchheimersLaw<ScalarType, GridGeometry, UpwindScheme, /*isNetwork*/ false>;
144 using FVElementGeometry = typename GridGeometry::LocalView;
145 using SubControlVolume = typename GridGeometry::SubControlVolume;
146 using SubControlVolumeFace = typename GridGeometry::SubControlVolumeFace;
147 using Extrusion = Extrusion_t<GridGeometry>;
148 using GridView = typename GridGeometry::GridView;
149 using Element = typename GridView::template Codim<0>::Entity;
150
151 static constexpr int dim = GridView::dimension;
152 static constexpr int dimWorld = GridView::dimensionworld;
153
154 using GlobalPosition = typename Element::Geometry::GlobalCoordinate;
155 using DimWorldVector = Dune::FieldVector<ScalarType, dimWorld>;
156 using DimWorldMatrix = Dune::FieldMatrix<ScalarType, dimWorld, dimWorld>;
157
158 using DarcysLaw = CCTpfaDarcysLaw<ScalarType, GridGeometry, /*isNetwork*/ false>;
159
160 public:
162 using Scalar = ScalarType;
163
165 static const DiscretizationMethod discMethod = DiscretizationMethod::cctpfa;
166
168 using Cache = TpfaForchheimersLawCache<ThisType, GridGeometry>;
169
232 template<class Problem, class ElementVolumeVariables, class ElementFluxVarsCache>
233 static Scalar flux(const Problem& problem,
234 const Element& element,
235 const FVElementGeometry& fvGeometry,
236 const ElementVolumeVariables& elemVolVars,
237 const SubControlVolumeFace& scvf,
238 int phaseIdx,
239 const ElementFluxVarsCache& elemFluxVarsCache)
240 {
241 const auto velocity = velocity_(problem, element, fvGeometry, elemVolVars, scvf, phaseIdx, elemFluxVarsCache);
242 Scalar flux = velocity * scvf.unitOuterNormal();
243 flux *= Extrusion::area(scvf);
244 return flux;
245 }
246
249 template<class Problem, class ElementVolumeVariables>
250 static Scalar calculateTransmissibility(const Problem& problem,
251 const Element& element,
252 const FVElementGeometry& fvGeometry,
253 const ElementVolumeVariables& elemVolVars,
254 const SubControlVolumeFace& scvf)
255 {
256 return DarcysLaw::calculateTransmissibility(problem, element, fvGeometry, elemVolVars, scvf);
257 }
258
263 template<class Problem, class ElementVolumeVariables,
264 bool scalarPerm = std::is_same<typename Problem::SpatialParams::PermeabilityType, Scalar>::value,
265 std::enable_if_t<scalarPerm, int> = 0>
266 static DimWorldMatrix calculateHarmonicMeanSqrtPermeability(const Problem& problem,
267 const ElementVolumeVariables& elemVolVars,
268 const SubControlVolumeFace& scvf)
269 {
270 using std::sqrt;
271 Scalar harmonicMeanSqrtK(0.0);
272
273 const auto insideScvIdx = scvf.insideScvIdx();
274 const auto& insideVolVars = elemVolVars[insideScvIdx];
275 const Scalar Ki = getPermeability_(problem, insideVolVars, scvf.ipGlobal());
276 const Scalar sqrtKi = sqrt(Ki);
277
278 if (!scvf.boundary())
279 {
280 const auto outsideScvIdx = scvf.outsideScvIdx();
281 const auto& outsideVolVars = elemVolVars[outsideScvIdx];
282 const Scalar Kj = getPermeability_(problem, outsideVolVars, scvf.ipGlobal());
283 const Scalar sqrtKj = sqrt(Kj);
284 harmonicMeanSqrtK = problem.spatialParams().harmonicMean(sqrtKi, sqrtKj, scvf.unitOuterNormal());
285 }
286 else
287 harmonicMeanSqrtK = sqrtKi;
288
289 // Convert to tensor
290 DimWorldMatrix result(0.0);
291 for (int i = 0; i < dimWorld; ++i)
292 result[i][i] = harmonicMeanSqrtK;
293
294 return result;
295 }
296
301 template<class Problem, class ElementVolumeVariables,
302 bool scalarPerm = std::is_same<typename Problem::SpatialParams::PermeabilityType, Scalar>::value,
303 std::enable_if_t<!scalarPerm, int> = 0>
304 static DimWorldMatrix calculateHarmonicMeanSqrtPermeability(const Problem& problem,
305 const ElementVolumeVariables& elemVolVars,
306 const SubControlVolumeFace& scvf)
307 {
308 using std::sqrt;
309 DimWorldMatrix sqrtKi(0.0);
310 DimWorldMatrix sqrtKj(0.0);
311 DimWorldMatrix harmonicMeanSqrtK(0.0);
312
313 const auto insideScvIdx = scvf.insideScvIdx();
314 const auto& insideVolVars = elemVolVars[insideScvIdx];
315 const auto& Ki = getPermeability_(problem, insideVolVars, scvf.ipGlobal());
316 // Make sure the permeability matrix does not have off-diagonal entries
317 // TODO implement method using eigenvalues and eigenvectors for general tensors
318 if (!isDiagonal_(Ki))
319 DUNE_THROW(Dune::NotImplemented, "Only diagonal permeability tensors are supported.");
320
321 for (int i = 0; i < dim; ++i)
322 sqrtKi[i][i] = sqrt(Ki[i][i]);
323
324 if (!scvf.boundary())
325 {
326 const auto outsideScvIdx = scvf.outsideScvIdx();
327 const auto& outsideVolVars = elemVolVars[outsideScvIdx];
328 const auto& Kj = getPermeability_(problem, outsideVolVars, scvf.ipGlobal());
329 // Make sure the permeability matrix does not have off-diagonal entries
330 if (!isDiagonal_(Kj))
331 DUNE_THROW(Dune::NotImplemented, "Only diagonal permeability tensors are supported.");
332
333 for (int i = 0; i < dim; ++i)
334 sqrtKj[i][i] = sqrt(Kj[i][i]);
335
336 harmonicMeanSqrtK = problem.spatialParams().harmonicMean(sqrtKi, sqrtKj, scvf.unitOuterNormal());
337 }
338 else
339 harmonicMeanSqrtK = sqrtKi;
340
341 return harmonicMeanSqrtK;
342 }
343
344private:
345
347 template<class Problem, class ElementVolumeVariables, class ElementFluxVarsCache>
348 static DimWorldVector velocity_(const Problem& problem,
349 const Element& element,
350 const FVElementGeometry& fvGeometry,
351 const ElementVolumeVariables& elemVolVars,
352 const SubControlVolumeFace& scvf,
353 int phaseIdx,
354 const ElementFluxVarsCache& elemFluxVarsCache)
355 {
356 // Get the volume flux based on Darcy's law. The value returned by this method needs to be multiplied with the
357 // mobility (upwinding).
358 const auto flux = DarcysLaw::flux(problem, element, fvGeometry, elemVolVars, scvf, phaseIdx, elemFluxVarsCache);
359 auto upwindTerm = [phaseIdx](const auto& volVars){ return volVars.mobility(phaseIdx); };
360 const auto darcyFlux = UpwindScheme::apply(elemVolVars, scvf, upwindTerm, flux, phaseIdx);
361
362 // Convert to Darcy velocity
363 DimWorldVector darcyVelocity = scvf.unitOuterNormal();
364 darcyVelocity *= darcyFlux / Extrusion::area(scvf);
365
366 // Get the harmonic mean of the square root of permeability
367 const auto& fluxVarsCache = elemFluxVarsCache[scvf];
368 const auto& sqrtK = fluxVarsCache.harmonicMeanSqrtPermeability();
369
370 // Obtain the Forchheimer coefficient from the spatial parameters
371 const Scalar forchCoeff = problem.spatialParams().forchCoeff(scvf);
372
373 // Initial guess of velocity: Darcy relation
374 DimWorldVector velocity = darcyVelocity;
375
376 DimWorldVector deltaV(0.0); // the change in velocity between Newton iterations
377 DimWorldVector residual(10e10); // the residual (function value that is to be minimized)
378 DimWorldMatrix gradF(0.0); // slope of equation that is to be solved
379
380 // Search by means of the Newton method for a root of Forchheimer equation
381 static const Scalar epsilon = getParamFromGroup<Scalar>(problem.paramGroup(), "Forchheimer.NewtonTolerance", 1e-12);
382 static const std::size_t maxNumIter = getParamFromGroup<std::size_t>(problem.paramGroup(), "Forchheimer.MaxIterations", 30);
383 for (int k = 0; residual.two_norm() > epsilon ; ++k)
384 {
385 if (k >= maxNumIter)
386 DUNE_THROW(NumericalProblem, "could not determine forchheimer velocity within "
387 << k << " iterations");
388
389 // calculate current value of Forchheimer equation
390 forchheimerResidual_(residual,
391 forchCoeff,
392 sqrtK,
393 darcyVelocity,
394 velocity,
395 elemVolVars,
396 scvf,
397 phaseIdx);
398
399 // newton's method: slope (gradF) and current value (residual) of function is known,
400 forchheimerDerivative_(gradF,
401 forchCoeff,
402 sqrtK,
403 velocity,
404 elemVolVars,
405 scvf,
406 phaseIdx);
407
408 // solve for change in velocity ("x-Axis intercept")
409 gradF.solve(deltaV, residual);
410 velocity -= deltaV;
411 }
412
413 // The fluxvariables expect a value on which an upwinding of the mobility is performed.
414 // We therefore divide by the mobility here.
415 const auto forchheimerUpwindMobility = UpwindScheme::multiplier(elemVolVars, scvf, upwindTerm, velocity * scvf.unitOuterNormal(), phaseIdx);
416
417 // Do not divide by zero. If the mobility is zero, the resulting flux with upwinding will be zero anyway.
418 if (forchheimerUpwindMobility > 0.0)
419 velocity /= forchheimerUpwindMobility;
420
421 return velocity;
422 }
423
425 template<class ElementVolumeVariables>
426 static void forchheimerResidual_(DimWorldVector& residual,
427 const Scalar forchCoeff,
428 const DimWorldMatrix& sqrtK,
429 const DimWorldVector& darcyVelocity,
430 const DimWorldVector& oldVelocity,
431 const ElementVolumeVariables& elemVolVars,
432 const SubControlVolumeFace& scvf,
433 const int phaseIdx)
434 {
435 residual = oldVelocity;
436
437 // residual += k_r/mu K grad p
438 residual -= darcyVelocity;
439
440 // residual += c_F rho / mu abs(v) sqrt(K) v
441 auto upwindTerm = [phaseIdx](const auto& volVars){ return volVars.density(phaseIdx) / volVars.viscosity(phaseIdx); };
442 const Scalar rhoOverMu = UpwindScheme::multiplier(elemVolVars, scvf, upwindTerm, oldVelocity * scvf.unitOuterNormal(), phaseIdx);
443 sqrtK.usmv(forchCoeff * rhoOverMu * oldVelocity.two_norm(), oldVelocity, residual);
444 }
445
447 template<class ElementVolumeVariables>
448 static void forchheimerDerivative_(DimWorldMatrix& derivative,
449 const Scalar forchCoeff,
450 const DimWorldMatrix& sqrtK,
451 const DimWorldVector& velocity,
452 const ElementVolumeVariables& elemVolVars,
453 const SubControlVolumeFace& scvf,
454 const int phaseIdx)
455 {
456
457
458 // Initialize because for low velocities, we add and do not set the entries.
459 derivative = 0.0;
460
461 auto upwindTerm = [phaseIdx](const auto& volVars){ return volVars.density(phaseIdx) / volVars.viscosity(phaseIdx); };
462
463 const Scalar absV = velocity.two_norm() ;
464 // This part of the derivative is only calculated if the velocity is sufficiently small:
465 // do not divide by zero!
466 // This should not be very bad because the derivative is only intended to give an
467 // approximation of the gradient of the
468 // function that goes into the Newton scheme.
469 // This is important in the case of a one-phase region in two-phase flow. The non-existing
470 // phase has a velocity of zero (kr=0).
471 // f = sqrtK * vTimesV (this is a matrix)
472 // f *= forchCoeff density / |velocity| / viscosity (multiply all entries with scalars)
473 const Scalar rhoOverMu = UpwindScheme::multiplier(elemVolVars, scvf, upwindTerm, velocity * scvf.unitOuterNormal(), phaseIdx);
474 if (absV > 1e-20)
475 {
476 for (int i = 0; i < dim; i++)
477 {
478 for (int k = 0; k <= i; k++)
479 {
480 derivative[i][k] = sqrtK[i][i] * velocity[i] * velocity[k] * forchCoeff
481 / absV * rhoOverMu;
482
483 if (k < i)
484 derivative[k][i] = derivative[i][k];
485 }
486 }
487 }
488
489 // add on the main diagonal:
490 // 1 + sqrtK_i forchCoeff density |v| / viscosity
491 for (int i = 0; i < dim; i++)
492 derivative[i][i] += (1.0 + (sqrtK[i][i]*forchCoeff * absV * rhoOverMu));
493 }
494
503 static bool isDiagonal_(const DimWorldMatrix & K)
504 {
505 using std::abs;
506 for (int i = 0; i < dim; i++) {
507 for (int k = 0; k < dim; k++) {
508 if ((i != k) && (abs(K[i][k]) >= 1e-25)) {
509 return false;
510 }
511 }
512 }
513 return true;
514 }
515
516 template<class Problem, class VolumeVariables,
517 std::enable_if_t<!Problem::SpatialParams::evaluatePermeabilityAtScvfIP(), int> = 0>
518 static decltype(auto) getPermeability_(const Problem& problem,
519 const VolumeVariables& volVars,
520 const GlobalPosition& scvfIpGlobal)
521 { return volVars.permeability(); }
522
523 template<class Problem, class VolumeVariables,
524 std::enable_if_t<Problem::SpatialParams::evaluatePermeabilityAtScvfIP(), int> = 0>
525 static decltype(auto) getPermeability_(const Problem& problem,
526 const VolumeVariables& volVars,
527 const GlobalPosition& scvfIpGlobal)
528 { return problem.spatialParams().permeabilityAtPos(scvfIpGlobal); }
529};
530
535template<class ScalarType, class GridGeometry, class UpwindScheme>
536class CCTpfaForchheimersLaw<ScalarType, GridGeometry, UpwindScheme, /*isNetwork*/ true>
537{
538 static_assert(AlwaysFalse<ScalarType>::value, "Forchheimer not implemented for network grids");
539};
540
541} // end namespace Dumux
542
543#endif
Define some often used mathematical functions.
The infrastructure to retrieve run-time parameters from Dune::ParameterTrees.
The available discretization methods in Dumux.
Helper classes to compute the integration elements.
DiscretizationMethod
The available discretization methods in Dumux.
Definition: method.hh:37
Definition: adapt.hh:29
forward declare
Definition: forchheimerslaw.hh:38
Forchheimer's law for cell-centered finite volume schemes with two-point flux approximation.
Definition: cctpfa/forchheimerslaw.hh:54
Declares all properties used in Dumux.
Darcy's law for cell-centered finite volume schemes with two-point flux approximation.