forchheimerfluxmodule.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 This file is part of the Open Porous Media project (OPM).
5
6 OPM 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 2 of the License, or
9 (at your option) any later version.
10
11 OPM 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 OPM. If not, see <http://www.gnu.org/licenses/>.
18
19 Consult the COPYING file in the top-level source directory of this
20 module for the precise wording of the license and the list of
21 copyright holders.
22*/
30#ifndef EWOMS_FORCHHEIMER_FLUX_MODULE_HH
31#define EWOMS_FORCHHEIMER_FLUX_MODULE_HH
32
33#include <opm/common/Exceptions.hpp>
34
35#include <opm/material/common/Valgrind.hpp>
36
39
40#include <dune/common/fvector.hh>
41#include <dune/common/fmatrix.hh>
42
43#include <algorithm>
44#include <array>
45#include <cassert>
46#include <cmath>
47#include <stdexcept>
48#include <string>
49
50namespace Opm {
51template <class TypeTag>
52class ForchheimerIntensiveQuantities;
53
54template <class TypeTag>
55class ForchheimerExtensiveQuantities;
56
57template <class TypeTag>
58class ForchheimerBaseProblem;
59
64template <class TypeTag>
66{
70
74 static void registerParameters()
75 {}
76};
77
83template <class TypeTag>
85{
88
89public:
99 template <class Context>
100 Scalar ergunCoefficient(const Context&,
101 unsigned,
102 unsigned) const
103 {
104 throw std::logic_error("Not implemented: Problem::ergunCoefficient()");
105 }
106
118 template <class Context>
119 Evaluation mobilityPassabilityRatio(Context& context,
120 unsigned spaceIdx,
121 unsigned timeIdx,
122 unsigned phaseIdx) const
123 {
124 return 1.0 / context.intensiveQuantities(spaceIdx, timeIdx).fluidState().viscosity(phaseIdx);
125 }
126};
127
132template <class TypeTag>
134{
138
139 enum { numPhases = getPropValue<TypeTag, Properties::NumPhases>() };
140
141public:
149 const Evaluation& ergunCoefficient() const
150 { return ergunCoefficient_; }
151
155 const Evaluation& mobilityPassabilityRatio(unsigned phaseIdx) const
156 { return mobilityPassabilityRatio_[phaseIdx]; }
157
158protected:
159 void update_(const ElementContext& elemCtx, unsigned dofIdx, unsigned timeIdx)
160 {
161 const auto& problem = elemCtx.problem();
162 ergunCoefficient_ = problem.ergunCoefficient(elemCtx, dofIdx, timeIdx);
163
164 for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
165 mobilityPassabilityRatio_[phaseIdx] =
166 problem.mobilityPassabilityRatio(elemCtx,
167 dofIdx,
168 timeIdx,
169 phaseIdx);
170 }
171 }
172
173private:
174 Evaluation ergunCoefficient_;
175 std::array<Evaluation, numPhases> mobilityPassabilityRatio_;
176};
177
217template <class TypeTag>
219 : public DarcyExtensiveQuantities<TypeTag>
220{
229
230 enum { dimWorld = GridView::dimensionworld };
231 enum { numPhases = getPropValue<TypeTag, Properties::NumPhases>() };
232
233 using Toolbox = MathToolbox<Evaluation>;
234
235 using DimVector = Dune::FieldVector<Scalar, dimWorld>;
236 using DimEvalVector = Dune::FieldVector<Evaluation, dimWorld>;
237 using DimMatrix = Dune::FieldMatrix<Scalar, dimWorld, dimWorld>;
238 using DimEvalMatrix = Dune::FieldMatrix<Evaluation, dimWorld, dimWorld>;
239
240public:
244 const Evaluation& ergunCoefficient() const
245 { return ergunCoefficient_; }
246
253 Evaluation& mobilityPassabilityRatio(unsigned phaseIdx) const
254 { return mobilityPassabilityRatio_[phaseIdx]; }
255
256protected:
257 void calculateGradients_(const ElementContext& elemCtx,
258 unsigned faceIdx,
259 unsigned timeIdx)
260 {
261 DarcyExtQuants::calculateGradients_(elemCtx, faceIdx, timeIdx);
262
263 const auto focusDofIdx = elemCtx.focusDofIndex();
264 const unsigned i = static_cast<unsigned>(this->interiorDofIdx_);
265 const unsigned j = static_cast<unsigned>(this->exteriorDofIdx_);
266 const auto& intQuantsIn = elemCtx.intensiveQuantities(i, timeIdx);
267 const auto& intQuantsEx = elemCtx.intensiveQuantities(j, timeIdx);
268
269 // calculate the square root of the intrinsic permeability
270 assert(isDiagonal_(this->K_));
271 sqrtK_ = 0.0;
272 for (unsigned dimIdx = 0; dimIdx < dimWorld; ++dimIdx) {
273 sqrtK_[dimIdx] = std::sqrt(this->K_[dimIdx][dimIdx]);
274 }
275
276 // obtain the Ergun coefficient. Lacking better ideas, we use its the arithmetic mean.
277 if (focusDofIdx == i) {
279 (intQuantsIn.ergunCoefficient() +
280 getValue(intQuantsEx.ergunCoefficient())) / 2;
281 }
282 else if (focusDofIdx == j) {
284 (getValue(intQuantsIn.ergunCoefficient()) +
285 intQuantsEx.ergunCoefficient()) / 2;
286 }
287 else {
289 (getValue(intQuantsIn.ergunCoefficient()) +
290 getValue(intQuantsEx.ergunCoefficient())) / 2;
291 }
292
293 // obtain the mobility to passability ratio for each phase.
294 for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
295 if (!elemCtx.model().phaseIsConsidered(phaseIdx)) {
296 continue;
297 }
298
299 const unsigned upIdx = static_cast<unsigned>(this->upstreamIndex_(phaseIdx));
300 const auto& up = elemCtx.intensiveQuantities(upIdx, timeIdx);
301
302 if (focusDofIdx == upIdx) {
303 density_[phaseIdx] = up.fluidState().density(phaseIdx);
304 mobilityPassabilityRatio_[phaseIdx] = up.mobilityPassabilityRatio(phaseIdx);
305 }
306 else {
307 density_[phaseIdx] = getValue(up.fluidState().density(phaseIdx));
308 mobilityPassabilityRatio_[phaseIdx] = getValue(up.mobilityPassabilityRatio(phaseIdx));
309 }
310 }
311 }
312
313 template <class FluidState>
314 void calculateBoundaryGradients_(const ElementContext& elemCtx,
315 unsigned boundaryFaceIdx,
316 unsigned timeIdx,
317 const FluidState& fluidState)
318 {
320 boundaryFaceIdx,
321 timeIdx,
322 fluidState);
323
324 const auto focusDofIdx = elemCtx.focusDofIndex();
325 const unsigned i = static_cast<unsigned>(this->interiorDofIdx_);
326 const auto& intQuantsIn = elemCtx.intensiveQuantities(i, timeIdx);
327
328 // obtain the Ergun coefficient. Because we are on the boundary here, we will
329 // take the Ergun coefficient of the interior
330 if (focusDofIdx == i) {
331 ergunCoefficient_ = intQuantsIn.ergunCoefficient();
332 }
333 else {
334 ergunCoefficient_ = getValue(intQuantsIn.ergunCoefficient());
335 }
336
337 // calculate the square root of the intrinsic permeability
338 assert(isDiagonal_(this->K_));
339 sqrtK_ = 0.0;
340 for (unsigned dimIdx = 0; dimIdx < dimWorld; ++dimIdx) {
341 sqrtK_[dimIdx] = std::sqrt(this->K_[dimIdx][dimIdx]);
342 }
343
344 for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
345 if (!elemCtx.model().phaseIsConsidered(phaseIdx)) {
346 continue;
347 }
348
349 if (focusDofIdx == i) {
350 density_[phaseIdx] = intQuantsIn.fluidState().density(phaseIdx);
351 mobilityPassabilityRatio_[phaseIdx] = intQuantsIn.mobilityPassabilityRatio(phaseIdx);
352 }
353 else {
354 density_[phaseIdx] = getValue(intQuantsIn.fluidState().density(phaseIdx));
355 mobilityPassabilityRatio_[phaseIdx] =
356 getValue(intQuantsIn.mobilityPassabilityRatio(phaseIdx));
357 }
358 }
359 }
360
367 void calculateFluxes_(const ElementContext& elemCtx, unsigned scvfIdx, unsigned timeIdx)
368 {
369 const auto focusDofIdx = elemCtx.focusDofIndex();
370 const auto i = asImp_().interiorIndex();
371 const auto j = asImp_().exteriorIndex();
372 const auto& intQuantsI = elemCtx.intensiveQuantities(i, timeIdx);
373 const auto& intQuantsJ = elemCtx.intensiveQuantities(j, timeIdx);
374
375 const auto& scvf = elemCtx.stencil(timeIdx).interiorFace(scvfIdx);
376 const auto& normal = scvf.normal();
377 Valgrind::CheckDefined(normal);
378
379 // obtain the Ergun coefficient from the intensive quantity object. Until a
380 // better method comes along, we use arithmetic averaging.
381 if (focusDofIdx == i) {
382 ergunCoefficient_ = (intQuantsI.ergunCoefficient() +
383 getValue(intQuantsJ.ergunCoefficient())) / 2;
384 }
385 else if (focusDofIdx == j) {
386 ergunCoefficient_ = (getValue(intQuantsI.ergunCoefficient()) +
387 intQuantsJ.ergunCoefficient()) / 2;
388 }
389 else {
390 ergunCoefficient_ = (getValue(intQuantsI.ergunCoefficient()) +
391 getValue(intQuantsJ.ergunCoefficient())) / 2;
392 }
393
395 // calculate the weights of the upstream and the downstream control volumes
397 for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
398 if (!elemCtx.model().phaseIsConsidered(phaseIdx)) {
399 this->filterVelocity_[phaseIdx] = 0.0;
400 this->volumeFlux_[phaseIdx] = 0.0;
401 continue;
402 }
403
405
406 this->volumeFlux_[phaseIdx] = 0.0;
407 for (unsigned dimIdx = 0; dimIdx < dimWorld; ++dimIdx) {
408 this->volumeFlux_[phaseIdx] +=
409 this->filterVelocity_[phaseIdx][dimIdx]*normal[dimIdx];
410 }
411 }
412 }
413
417 void calculateBoundaryFluxes_(const ElementContext& elemCtx,
418 unsigned bfIdx,
419 unsigned timeIdx)
420 {
421 const auto& boundaryFace = elemCtx.stencil(timeIdx).boundaryFace(bfIdx);
422 const auto& normal = boundaryFace.normal();
423
425 // calculate the weights of the upstream and the downstream degrees of freedom
427 for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
428 if (!elemCtx.model().phaseIsConsidered(phaseIdx)) {
429 this->filterVelocity_[phaseIdx] = 0.0;
430 this->volumeFlux_[phaseIdx] = 0.0;
431 continue;
432 }
433
435
436 this->volumeFlux_[phaseIdx] = 0.0;
437 for (unsigned dimIdx = 0; dimIdx < dimWorld; ++dimIdx) {
438 this->volumeFlux_[phaseIdx] +=
439 this->filterVelocity_[phaseIdx][dimIdx]*normal[dimIdx];
440 }
441 }
442 }
443
444 void calculateForchheimerFlux_(unsigned phaseIdx)
445 {
446 // initial guess: filter velocity is zero
447 DimEvalVector& velocity = this->filterVelocity_[phaseIdx];
448 velocity = 0.0;
449
450 // the change of velocity between two consecutive Newton iterations
451 DimEvalVector deltaV(1e5);
452 // the function value that is to be minimized of the equation that is to be
453 // fulfilled
454 DimEvalVector residual;
455 // derivative of equation that is to be solved
456 DimEvalMatrix gradResid;
457
458 // search by means of the Newton method for a root of Forchheimer equation
459 unsigned newtonIter = 0;
460 while (deltaV.one_norm() > 1e-11) {
461 if (newtonIter >= 50) {
462 throw NumericalProblem("Could not determine Forchheimer velocity within "
463 + std::to_string(newtonIter) + " iterations");
464 }
465 ++newtonIter;
466
467 // calculate the residual and its Jacobian matrix
468 gradForchheimerResid_(residual, gradResid, phaseIdx);
469
470 // newton method
471 gradResid.solve(deltaV, residual);
472 velocity -= deltaV;
473 }
474 }
475
476 void forchheimerResid_(DimEvalVector& residual, unsigned phaseIdx) const
477 {
478 const DimEvalVector& velocity = this->filterVelocity_[phaseIdx];
479
480 // Obtaining the upstreamed quantities
481 const auto& mobility = this->mobility_[phaseIdx];
482 const auto& density = density_[phaseIdx];
483 const auto& mobPassabilityRatio = mobilityPassabilityRatio_[phaseIdx];
484
485 // optain the quantites for the integration point
486 const auto& pGrad = this->potentialGrad_[phaseIdx];
487
488 // residual = v_\alpha
489 residual = velocity;
490
491 // residual += mobility_\alpha K(\grad p_\alpha - \rho_\alpha g)
492 // -> this->K_.usmv(mobility, pGrad, residual);
493 assert(isDiagonal_(this->K_));
494 for (unsigned dimIdx = 0; dimIdx < dimWorld; ++ dimIdx) {
495 residual[dimIdx] += mobility*pGrad[dimIdx]*this->K_[dimIdx][dimIdx];
496 }
497
498 // Forchheimer turbulence correction:
499 //
500 // residual +=
501 // \rho_\alpha
502 // * mobility_\alpha
503 // * C_E / \eta_{r,\alpha}
504 // * abs(v_\alpha) * sqrt(K)*v_\alpha
505 //
506 // -> sqrtK_.usmv(density*mobilityPassabilityRatio*ergunCoefficient_*velocity.two_norm(),
507 // velocity,
508 // residual);
509 Evaluation absVel = 0.0;
510 for (unsigned dimIdx = 0; dimIdx < dimWorld; ++dimIdx) {
511 absVel += velocity[dimIdx]*velocity[dimIdx];
512 }
513 // the derivatives of the square root of 0 are undefined, so we must guard
514 // against this case
515 if (absVel <= 0.0) {
516 absVel = 0.0;
517 }
518 else {
519 absVel = Toolbox::sqrt(absVel);
520 }
521 const auto& alpha = density * mobPassabilityRatio * ergunCoefficient_ * absVel;
522 for (unsigned dimIdx = 0; dimIdx < dimWorld; ++dimIdx) {
523 residual[dimIdx] += sqrtK_[dimIdx] * alpha * velocity[dimIdx];
524 }
525 Valgrind::CheckDefined(residual);
526 }
527
528 void gradForchheimerResid_(DimEvalVector& residual,
529 DimEvalMatrix& gradResid,
530 unsigned phaseIdx)
531 {
532 // TODO (?) use AD for this.
533 DimEvalVector& velocity = this->filterVelocity_[phaseIdx];
534 forchheimerResid_(residual, phaseIdx);
535
536 constexpr Scalar eps = 1e-11;
537 DimEvalVector tmp;
538 for (unsigned i = 0; i < dimWorld; ++i) {
539 const Scalar coordEps = std::max(eps, Toolbox::scalarValue(velocity[i]) * (1 + eps));
540 velocity[i] += coordEps;
541 forchheimerResid_(tmp, phaseIdx);
542 tmp -= residual;
543 tmp /= coordEps;
544 gradResid[i] = tmp;
545 velocity[i] -= coordEps;
546 }
547 }
548
556 bool isDiagonal_(const DimMatrix& K) const
557 {
558 for (unsigned i = 0; i < dimWorld; i++) {
559 for (unsigned j = 0; j < dimWorld; j++) {
560 if (i == j) {
561 continue;
562 }
563
564 if (std::abs(K[i][j]) > 1e-25) {
565 return false;
566 }
567 }
568 }
569 return true;
570 }
571
572private:
573 Implementation& asImp_()
574 { return *static_cast<Implementation *>(this); }
575
576 const Implementation& asImp_() const
577 { return *static_cast<const Implementation *>(this); }
578
579protected:
580 // intrinsic permeability tensor and its square root
581 DimVector sqrtK_;
582
583 // Ergun coefficient of all phases at the integration point
585
586 // Passability of all phases at the integration point
587 std::array<Evaluation, numPhases> mobilityPassabilityRatio_;
588
589 // Density of all phases at the integration point
590 std::array<Evaluation, numPhases> density_;
591};
592
593} // namespace Opm
594
595#endif
Provides the Darcy flux module.
Definition: darcyfluxmodule.hh:122
std::array< EvalDimVector, numPhases > filterVelocity_
Definition: darcyfluxmodule.hh:566
std::array< EvalDimVector, numPhases > potentialGrad_
Definition: darcyfluxmodule.hh:573
void calculateBoundaryGradients_(const ElementContext &elemCtx, unsigned boundaryFaceIdx, unsigned timeIdx, const FluidState &fluidState)
Calculate the gradients at the grid boundary which are required to determine the volumetric fluxes.
Definition: darcyfluxmodule.hh:343
short interiorDofIdx_
Definition: darcyfluxmodule.hh:578
std::array< Evaluation, numPhases > volumeFlux_
Definition: darcyfluxmodule.hh:570
std::array< Evaluation, numPhases > mobility_
Definition: darcyfluxmodule.hh:563
short upstreamIndex_(unsigned phaseIdx) const
Definition: darcyfluxmodule.hh:179
void calculateGradients_(const ElementContext &elemCtx, unsigned faceIdx, unsigned timeIdx)
Calculate the gradients which are required to determine the volumetric fluxes.
Definition: darcyfluxmodule.hh:190
short exteriorDofIdx_
Definition: darcyfluxmodule.hh:579
DimMatrix K_
Definition: darcyfluxmodule.hh:560
Provides the defaults for the parameters required by the Forchheimer velocity approach.
Definition: forchheimerfluxmodule.hh:85
Evaluation mobilityPassabilityRatio(Context &context, unsigned spaceIdx, unsigned timeIdx, unsigned phaseIdx) const
Returns the ratio between the phase mobility and its passability for a given fluid phase .
Definition: forchheimerfluxmodule.hh:119
Scalar ergunCoefficient(const Context &, unsigned, unsigned) const
Returns the Ergun coefficient.
Definition: forchheimerfluxmodule.hh:100
Provides the Forchheimer flux module.
Definition: forchheimerfluxmodule.hh:220
void calculateGradients_(const ElementContext &elemCtx, unsigned faceIdx, unsigned timeIdx)
Definition: forchheimerfluxmodule.hh:257
void calculateForchheimerFlux_(unsigned phaseIdx)
Definition: forchheimerfluxmodule.hh:444
const Evaluation & ergunCoefficient() const
Return the Ergun coefficent at the face's integration point.
Definition: forchheimerfluxmodule.hh:244
void calculateBoundaryGradients_(const ElementContext &elemCtx, unsigned boundaryFaceIdx, unsigned timeIdx, const FluidState &fluidState)
Definition: forchheimerfluxmodule.hh:314
void forchheimerResid_(DimEvalVector &residual, unsigned phaseIdx) const
Definition: forchheimerfluxmodule.hh:476
std::array< Evaluation, numPhases > mobilityPassabilityRatio_
Definition: forchheimerfluxmodule.hh:587
DimVector sqrtK_
Definition: forchheimerfluxmodule.hh:581
void gradForchheimerResid_(DimEvalVector &residual, DimEvalMatrix &gradResid, unsigned phaseIdx)
Definition: forchheimerfluxmodule.hh:528
void calculateBoundaryFluxes_(const ElementContext &elemCtx, unsigned bfIdx, unsigned timeIdx)
Calculate the volumetric flux rates of all phases at the domain boundary.
Definition: forchheimerfluxmodule.hh:417
Evaluation ergunCoefficient_
Definition: forchheimerfluxmodule.hh:584
void calculateFluxes_(const ElementContext &elemCtx, unsigned scvfIdx, unsigned timeIdx)
Calculate the volumetric fluxes of all phases.
Definition: forchheimerfluxmodule.hh:367
std::array< Evaluation, numPhases > density_
Definition: forchheimerfluxmodule.hh:590
bool isDiagonal_(const DimMatrix &K) const
Check whether all off-diagonal entries of a tensor are zero.
Definition: forchheimerfluxmodule.hh:556
Evaluation & mobilityPassabilityRatio(unsigned phaseIdx) const
Return the ratio of the mobility divided by the passability at the face's integration point for a giv...
Definition: forchheimerfluxmodule.hh:253
Provides the intensive quantities for the Forchheimer module.
Definition: forchheimerfluxmodule.hh:134
const Evaluation & ergunCoefficient() const
Returns the Ergun coefficient.
Definition: forchheimerfluxmodule.hh:149
void update_(const ElementContext &elemCtx, unsigned dofIdx, unsigned timeIdx)
Definition: forchheimerfluxmodule.hh:159
const Evaluation & mobilityPassabilityRatio(unsigned phaseIdx) const
Returns the passability of a phase.
Definition: forchheimerfluxmodule.hh:155
This file contains the necessary classes to calculate the volumetric fluxes out of a pressure potenti...
Declare the properties used by the infrastructure code of the finite volume discretizations.
Definition: blackoilboundaryratevector.hh:39
typename Properties::Detail::GetPropImpl< TypeTag, Property >::type::type GetPropType
get the type alias defined in the property (equivalent to old macro GET_PROP_TYPE(....
Definition: propertysystem.hh:233
std::string to_string(const ConvergenceReport::ReservoirFailure::Type t)
Specifies a flux module which uses the Forchheimer relation.
Definition: forchheimerfluxmodule.hh:66
static void registerParameters()
Register all run-time parameters for the flux module.
Definition: forchheimerfluxmodule.hh:74