227 enum { dimWorld = GridView::dimensionworld };
230 using Toolbox = MathToolbox<Evaluation>;
232 using DimVector = Dune::FieldVector<Scalar, dimWorld>;
233 using DimEvalVector = Dune::FieldVector<Evaluation, dimWorld>;
234 using DimMatrix = Dune::FieldMatrix<Scalar, dimWorld, dimWorld>;
235 using DimEvalMatrix = Dune::FieldMatrix<Evaluation, dimWorld, dimWorld>;
242 {
return ergunCoefficient_; }
251 {
return mobilityPassabilityRatio_[phaseIdx]; }
254 void calculateGradients_(
const ElementContext& elemCtx,
260 const auto focusDofIdx = elemCtx.focusDofIndex();
261 const unsigned i =
static_cast<unsigned>(this->interiorDofIdx_);
262 const unsigned j =
static_cast<unsigned>(this->exteriorDofIdx_);
263 const auto& intQuantsIn = elemCtx.intensiveQuantities(i, timeIdx);
264 const auto& intQuantsEx = elemCtx.intensiveQuantities(j, timeIdx);
269 for (
unsigned dimIdx = 0; dimIdx < dimWorld; ++dimIdx) {
270 sqrtK_[dimIdx] = std::sqrt(this->K_[dimIdx][dimIdx]);
274 if (focusDofIdx == i) {
276 (intQuantsIn.ergunCoefficient() +
277 getValue(intQuantsEx.ergunCoefficient())) / 2;
279 else if (focusDofIdx == j) {
281 (getValue(intQuantsIn.ergunCoefficient()) +
282 intQuantsEx.ergunCoefficient()) / 2;
286 (getValue(intQuantsIn.ergunCoefficient()) +
287 getValue(intQuantsEx.ergunCoefficient())) / 2;
291 for (
unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
292 if (!elemCtx.model().phaseIsConsidered(phaseIdx)) {
296 const unsigned upIdx =
static_cast<unsigned>(this->upstreamIndex_(phaseIdx));
297 const auto& up = elemCtx.intensiveQuantities(upIdx, timeIdx);
299 if (focusDofIdx == upIdx) {
300 density_[phaseIdx] = up.fluidState().density(phaseIdx);
301 mobilityPassabilityRatio_[phaseIdx] = up.mobilityPassabilityRatio(phaseIdx);
304 density_[phaseIdx] = getValue(up.fluidState().density(phaseIdx));
305 mobilityPassabilityRatio_[phaseIdx] = getValue(up.mobilityPassabilityRatio(phaseIdx));
310 template <
class Flu
idState>
311 void calculateBoundaryGradients_(
const ElementContext& elemCtx,
312 unsigned boundaryFaceIdx,
314 const FluidState& fluidState)
321 const auto focusDofIdx = elemCtx.focusDofIndex();
322 const unsigned i =
static_cast<unsigned>(this->interiorDofIdx_);
323 const auto& intQuantsIn = elemCtx.intensiveQuantities(i, timeIdx);
327 if (focusDofIdx == i) {
328 ergunCoefficient_ = intQuantsIn.ergunCoefficient();
331 ergunCoefficient_ = getValue(intQuantsIn.ergunCoefficient());
337 for (
unsigned dimIdx = 0; dimIdx < dimWorld; ++dimIdx) {
338 sqrtK_[dimIdx] = std::sqrt(this->K_[dimIdx][dimIdx]);
341 for (
unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
342 if (!elemCtx.model().phaseIsConsidered(phaseIdx)) {
346 if (focusDofIdx == i) {
347 density_[phaseIdx] = intQuantsIn.fluidState().density(phaseIdx);
348 mobilityPassabilityRatio_[phaseIdx] = intQuantsIn.mobilityPassabilityRatio(phaseIdx);
351 density_[phaseIdx] = getValue(intQuantsIn.fluidState().density(phaseIdx));
352 mobilityPassabilityRatio_[phaseIdx] =
353 getValue(intQuantsIn.mobilityPassabilityRatio(phaseIdx));
366 const auto focusDofIdx = elemCtx.focusDofIndex();
367 const auto i = asImp_().interiorIndex();
368 const auto j = asImp_().exteriorIndex();
369 const auto& intQuantsI = elemCtx.intensiveQuantities(i, timeIdx);
370 const auto& intQuantsJ = elemCtx.intensiveQuantities(j, timeIdx);
372 const auto& scvf = elemCtx.stencil(timeIdx).interiorFace(scvfIdx);
373 const auto& normal = scvf.normal();
374 Valgrind::CheckDefined(normal);
378 if (focusDofIdx == i) {
379 ergunCoefficient_ = (intQuantsI.ergunCoefficient() +
380 getValue(intQuantsJ.ergunCoefficient())) / 2;
382 else if (focusDofIdx == j) {
383 ergunCoefficient_ = (getValue(intQuantsI.ergunCoefficient()) +
384 intQuantsJ.ergunCoefficient()) / 2;
387 ergunCoefficient_ = (getValue(intQuantsI.ergunCoefficient()) +
388 getValue(intQuantsJ.ergunCoefficient())) / 2;
394 for (
unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
395 if (!elemCtx.model().phaseIsConsidered(phaseIdx)) {
396 this->filterVelocity_[phaseIdx] = 0.0;
397 this->volumeFlux_[phaseIdx] = 0.0;
401 calculateForchheimerFlux_(phaseIdx);
403 this->volumeFlux_[phaseIdx] = 0.0;
404 for (
unsigned dimIdx = 0; dimIdx < dimWorld; ++dimIdx) {
405 this->volumeFlux_[phaseIdx] +=
406 this->filterVelocity_[phaseIdx][dimIdx]*normal[dimIdx];
418 const auto& boundaryFace = elemCtx.stencil(timeIdx).boundaryFace(bfIdx);
419 const auto& normal = boundaryFace.normal();
424 for (
unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
425 if (!elemCtx.model().phaseIsConsidered(phaseIdx)) {
426 this->filterVelocity_[phaseIdx] = 0.0;
427 this->volumeFlux_[phaseIdx] = 0.0;
431 calculateForchheimerFlux_(phaseIdx);
433 this->volumeFlux_[phaseIdx] = 0.0;
434 for (
unsigned dimIdx = 0; dimIdx < dimWorld; ++dimIdx) {
435 this->volumeFlux_[phaseIdx] +=
436 this->filterVelocity_[phaseIdx][dimIdx]*normal[dimIdx];
441 void calculateForchheimerFlux_(
unsigned phaseIdx)
444 DimEvalVector& velocity = this->filterVelocity_[phaseIdx];
448 DimEvalVector deltaV(1e5);
451 DimEvalVector residual;
453 DimEvalMatrix gradResid;
456 unsigned newtonIter = 0;
457 while (deltaV.one_norm() > 1e-11) {
458 if (newtonIter >= 50) {
459 throw NumericalProblem(
"Could not determine Forchheimer velocity within "
460 + std::to_string(newtonIter) +
" iterations");
465 gradForchheimerResid_(residual, gradResid, phaseIdx);
468 gradResid.solve(deltaV, residual);
473 void forchheimerResid_(DimEvalVector& residual,
unsigned phaseIdx)
const
475 const DimEvalVector& velocity = this->filterVelocity_[phaseIdx];
478 const auto& mobility = this->mobility_[phaseIdx];
479 const auto& density = density_[phaseIdx];
480 const auto& mobPassabilityRatio = mobilityPassabilityRatio_[phaseIdx];
483 const auto& pGrad = this->potentialGrad_[phaseIdx];
491 for (
unsigned dimIdx = 0; dimIdx < dimWorld; ++ dimIdx) {
492 residual[dimIdx] += mobility*pGrad[dimIdx]*this->K_[dimIdx][dimIdx];
506 Evaluation absVel = 0.0;
507 for (
unsigned dimIdx = 0; dimIdx < dimWorld; ++dimIdx) {
508 absVel += velocity[dimIdx]*velocity[dimIdx];
516 absVel = Toolbox::sqrt(absVel);
518 const auto& alpha = density * mobPassabilityRatio * ergunCoefficient_ * absVel;
519 for (
unsigned dimIdx = 0; dimIdx < dimWorld; ++dimIdx) {
520 residual[dimIdx] += sqrtK_[dimIdx] * alpha * velocity[dimIdx];
522 Valgrind::CheckDefined(residual);
525 void gradForchheimerResid_(DimEvalVector& residual,
526 DimEvalMatrix& gradResid,
530 DimEvalVector& velocity = this->filterVelocity_[phaseIdx];
531 forchheimerResid_(residual, phaseIdx);
533 constexpr Scalar eps = 1e-11;
535 for (
unsigned i = 0; i < dimWorld; ++i) {
536 const Scalar coordEps = std::max(eps, Toolbox::scalarValue(velocity[i]) * (1 + eps));
537 velocity[i] += coordEps;
538 forchheimerResid_(tmp, phaseIdx);
542 velocity[i] -= coordEps;
555 for (
unsigned i = 0; i < dimWorld; i++) {
556 for (
unsigned j = 0; j < dimWorld; j++) {
561 if (std::abs(K[i][j]) > 1e-25) {
570 Implementation& asImp_()
571 {
return *
static_cast<Implementation *
>(
this); }
573 const Implementation& asImp_()
const
574 {
return *
static_cast<const Implementation *
>(
this); }
581 Evaluation ergunCoefficient_;
584 std::array<Evaluation, numPhases> mobilityPassabilityRatio_;
587 std::array<Evaluation, numPhases> density_;