version 3.10-dev
forchheimervelocity.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// SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
5// SPDX-License-Identifier: GPL-3.0-or-later
6//
14#ifndef DUMUX_FLUX_FORCHHEIMER_VELOCITY_HH
15#define DUMUX_FLUX_FORCHHEIMER_VELOCITY_HH
16
17#include <dune/common/fvector.hh>
18#include <dune/common/fmatrix.hh>
19
20#include <dumux/common/math.hh>
24
26
27namespace Dumux {
28
37template<class Scalar, class GridGeometry, class FluxVariables>
39{
40 using FVElementGeometry = typename GridGeometry::LocalView;
41 using SubControlVolumeFace = typename GridGeometry::SubControlVolumeFace;
42 using GridView = typename GridGeometry::GridView;
43 using Element = typename GridView::template Codim<0>::Entity;
44
45 static constexpr int dim = GridView::dimension;
46 static constexpr int dimWorld = GridView::dimensionworld;
47 using GlobalPosition = typename Element::Geometry::GlobalCoordinate;
48 public:
50 using DimWorldVector = Dune::FieldVector<Scalar, dimWorld>;
51 using DimWorldMatrix = Dune::FieldMatrix<Scalar, dimWorld, dimWorld>;
52
53
54 template<class ElementVolumeVariables>
55 static DimWorldVector velocity(const FVElementGeometry& fvGeometry,
56 const ElementVolumeVariables& elemVolVars,
57 const SubControlVolumeFace& scvf,
58 const int phaseIdx,
59 const DimWorldMatrix sqrtK,
60 const DimWorldVector darcyVelocity)
61 {
62 const auto& problem = elemVolVars.gridVolVars().problem();
63
64 // Obtain the Forchheimer coefficient from the spatial parameters
65 const Scalar forchCoeff = problem.spatialParams().forchCoeff(scvf);
66
67 // Initial guess of velocity: Darcy relation
68 DimWorldVector velocity = darcyVelocity;
69
70 DimWorldVector deltaV(0.0); // the change in velocity between Newton iterations
71 DimWorldVector residual(10e10); // the residual (function value that is to be minimized)
72 DimWorldMatrix gradF(0.0); // slope of equation that is to be solved
73
74 // Search by means of the Newton method for a root of Forchheimer equation
75 static const Scalar epsilon = getParamFromGroup<Scalar>(problem.paramGroup(), "Forchheimer.NewtonTolerance", 1e-12);
76 static const std::size_t maxNumIter = getParamFromGroup<std::size_t>(problem.paramGroup(), "Forchheimer.MaxIterations", 30);
77 for (int k = 0; residual.two_norm() > epsilon ; ++k)
78 {
79 if (k >= maxNumIter)
80 DUNE_THROW(NumericalProblem, "could not determine forchheimer velocity within "
81 << k << " iterations");
82
83 // calculate current value of Forchheimer equation
84 forchheimerResidual_(residual,
85 forchCoeff,
86 sqrtK,
87 darcyVelocity,
89 elemVolVars,
90 scvf,
91 phaseIdx);
92
93 // newton's method: slope (gradF) and current value (residual) of function is known,
94 forchheimerDerivative_(gradF,
95 forchCoeff,
96 sqrtK,
98 elemVolVars,
99 scvf,
100 phaseIdx);
101
102 // solve for change in velocity ("x-Axis intercept")
103 gradF.solve(deltaV, residual);
104 velocity -= deltaV;
105 }
106
107 // The fluxvariables expect a value on which an upwinding of the mobility is performed.
108 // We therefore divide by the mobility here.
109 auto upwindTerm = [phaseIdx](const auto& volVars){ return volVars.mobility(phaseIdx); };
110 const Scalar forchheimerUpwindMobility = UpwindScheme::multiplier(elemVolVars, scvf, upwindTerm, velocity * scvf.unitOuterNormal(), phaseIdx);
111
112 // Do not divide by zero. If the mobility is zero, the resulting flux with upwinding will be zero anyway.
113 if (forchheimerUpwindMobility > 0.0)
114 velocity /= forchheimerUpwindMobility;
115
116 return velocity;
117 }
118
123 template<class Problem, class ElementVolumeVariables,
124 bool scalarPerm = std::is_same<typename Problem::SpatialParams::PermeabilityType, Scalar>::value,
125 std::enable_if_t<scalarPerm, int> = 0>
127 const ElementVolumeVariables& elemVolVars,
128 const SubControlVolumeFace& scvf)
129 {
130 using std::sqrt;
131 Scalar harmonicMeanSqrtK(0.0);
132
133 const auto insideScvIdx = scvf.insideScvIdx();
134 const auto& insideVolVars = elemVolVars[insideScvIdx];
135 const Scalar Ki = getPermeability_(problem, insideVolVars, scvf.ipGlobal());
136 const Scalar sqrtKi = sqrt(Ki);
137
138 if (!scvf.boundary())
139 {
140 const auto outsideScvIdx = scvf.outsideScvIdx();
141 const auto& outsideVolVars = elemVolVars[outsideScvIdx];
142 const Scalar Kj = getPermeability_(problem, outsideVolVars, scvf.ipGlobal());
143 const Scalar sqrtKj = sqrt(Kj);
144 harmonicMeanSqrtK = faceTensorAverage(sqrtKi, sqrtKj, scvf.unitOuterNormal());
145 }
146 else
147 harmonicMeanSqrtK = sqrtKi;
148
149 // Convert to tensor
150 DimWorldMatrix result(0.0);
151 for (int i = 0; i < dimWorld; ++i)
152 result[i][i] = harmonicMeanSqrtK;
153
154 return result;
155 }
156
161 template<class Problem, class ElementVolumeVariables,
162 bool scalarPerm = std::is_same<typename Problem::SpatialParams::PermeabilityType, Scalar>::value,
163 std::enable_if_t<!scalarPerm, int> = 0>
165 const ElementVolumeVariables& elemVolVars,
166 const SubControlVolumeFace& scvf)
167 {
168 using std::sqrt;
169 DimWorldMatrix sqrtKi(0.0);
170 DimWorldMatrix sqrtKj(0.0);
171 DimWorldMatrix harmonicMeanSqrtK(0.0);
172
173 const auto insideScvIdx = scvf.insideScvIdx();
174 const auto& insideVolVars = elemVolVars[insideScvIdx];
175 const auto& Ki = getPermeability_(problem, insideVolVars, scvf.ipGlobal());
176 // Make sure the permeability matrix does not have off-diagonal entries
177 // TODO implement method using eigenvalues and eigenvectors for general tensors
178 if (!isDiagonal_(Ki))
179 DUNE_THROW(Dune::NotImplemented, "Only diagonal permeability tensors are supported.");
180
181 for (int i = 0; i < dim; ++i)
182 sqrtKi[i][i] = sqrt(Ki[i][i]);
183
184 if (!scvf.boundary())
185 {
186 const auto outsideScvIdx = scvf.outsideScvIdx();
187 const auto& outsideVolVars = elemVolVars[outsideScvIdx];
188 const auto& Kj = getPermeability_(problem, outsideVolVars, scvf.ipGlobal());
189 // Make sure the permeability matrix does not have off-diagonal entries
190 if (!isDiagonal_(Kj))
191 DUNE_THROW(Dune::NotImplemented, "Only diagonal permeability tensors are supported.");
192
193 for (int i = 0; i < dim; ++i)
194 sqrtKj[i][i] = sqrt(Kj[i][i]);
195
196 harmonicMeanSqrtK = faceTensorAverage(sqrtKi, sqrtKj, scvf.unitOuterNormal());
197 }
198 else
199 harmonicMeanSqrtK = sqrtKi;
200
201 return harmonicMeanSqrtK;
202 }
203
204private:
205
207 template<class ElementVolumeVariables>
208 static void forchheimerResidual_(DimWorldVector& residual,
209 const Scalar forchCoeff,
210 const DimWorldMatrix& sqrtK,
211 const DimWorldVector& darcyVelocity,
212 const DimWorldVector& oldVelocity,
213 const ElementVolumeVariables& elemVolVars,
214 const SubControlVolumeFace& scvf,
215 const int phaseIdx)
216 {
217 residual = oldVelocity;
218 // residual += k_r/mu K grad p
219 residual -= darcyVelocity;
220 // residual += c_F rho / mu abs(v) sqrt(K) v
221 auto upwindTerm = [phaseIdx](const auto& volVars){ return volVars.density(phaseIdx) / volVars.viscosity(phaseIdx); };
222 const Scalar rhoOverMu = UpwindScheme::multiplier(elemVolVars, scvf, upwindTerm, oldVelocity * scvf.unitOuterNormal(), phaseIdx);
223 sqrtK.usmv(forchCoeff * rhoOverMu * oldVelocity.two_norm(), oldVelocity, residual);
224 }
225
227 template<class ElementVolumeVariables>
228 static void forchheimerDerivative_(DimWorldMatrix& derivative,
229 const Scalar forchCoeff,
230 const DimWorldMatrix& sqrtK,
232 const ElementVolumeVariables& elemVolVars,
233 const SubControlVolumeFace& scvf,
234 const int phaseIdx)
235 {
236 // Initialize because for low velocities, we add and do not set the entries.
237 derivative = 0.0;
238
239 auto upwindTerm = [phaseIdx](const auto& volVars){ return volVars.density(phaseIdx) / volVars.viscosity(phaseIdx); };
240
241 const Scalar absV = velocity.two_norm();
242 // This part of the derivative is only calculated if the velocity is sufficiently small:
243 // do not divide by zero!
244 // This should not be very bad because the derivative is only intended to give an
245 // approximation of the gradient of the
246 // function that goes into the Newton scheme.
247 // This is important in the case of a one-phase region in two-phase flow. The non-existing
248 // phase has a velocity of zero (kr=0).
249 // f = sqrtK * vTimesV (this is a matrix)
250 // f *= forchCoeff density / |velocity| / viscosity (multiply all entries with scalars)
251 const Scalar rhoOverMu = UpwindScheme::multiplier(elemVolVars, scvf, upwindTerm, velocity * scvf.unitOuterNormal(), phaseIdx);
252 if (absV > 1e-20)
253 {
254 for (int i = 0; i < dim; i++)
255 {
256 for (int k = 0; k <= i; k++)
257 {
258 derivative[i][k] = sqrtK[i][i] * velocity[i] * velocity[k] * forchCoeff
259 / absV * rhoOverMu;
260
261 if (k < i)
262 derivative[k][i] = derivative[i][k];
263 }
264 }
265 }
266
267 // add on the main diagonal:
268 // 1 + sqrtK_i forchCoeff density |v| / viscosity
269 for (int i = 0; i < dim; i++)
270 derivative[i][i] += (1.0 + (sqrtK[i][i]*forchCoeff * absV * rhoOverMu));
271 }
272
281 static bool isDiagonal_(const DimWorldMatrix & K)
282 {
283 using std::abs;
284 for (int i = 0; i < dim; i++)
285 for (int k = 0; k < dim; k++)
286 if ((i != k) && (abs(K[i][k]) >= 1e-25))
287 return false;
288 return true;
289 }
290
291 template<class Problem, class VolumeVariables>
292 static decltype(auto) getPermeability_(const Problem& problem,
293 const VolumeVariables& volVars,
294 const GlobalPosition& scvfIpGlobal)
295 {
296 if constexpr (Problem::SpatialParams::evaluatePermeabilityAtScvfIP())
297 return problem.spatialParams().permeabilityAtPos(scvfIpGlobal);
298 else
299 return volVars.permeability();
300 }
301};
302
303} // end namespace Dumux
304
305#endif
Forchheimer's law For a detailed description see dumux/flow/forchheimerslaw.hh.
Definition: forchheimervelocity.hh:39
typename FluxVariables::UpwindScheme UpwindScheme
Definition: forchheimervelocity.hh:49
static DimWorldMatrix calculateHarmonicMeanSqrtPermeability(const Problem &problem, const ElementVolumeVariables &elemVolVars, const SubControlVolumeFace &scvf)
Returns the harmonic mean of and .
Definition: forchheimervelocity.hh:126
Dune::FieldVector< Scalar, dimWorld > DimWorldVector
Definition: forchheimervelocity.hh:50
static DimWorldVector velocity(const FVElementGeometry &fvGeometry, const ElementVolumeVariables &elemVolVars, const SubControlVolumeFace &scvf, const int phaseIdx, const DimWorldMatrix sqrtK, const DimWorldVector darcyVelocity)
Definition: forchheimervelocity.hh:55
Dune::FieldMatrix< Scalar, dimWorld, dimWorld > DimWorldMatrix
Definition: forchheimervelocity.hh:51
Exception thrown if a fixable numerical problem occurs.
Definition: exceptions.hh:27
Defines all properties used in Dumux.
Type traits.
A free function to average a Tensor at an interface.
UpwindSchemeImpl< GridGeometry, typename GridGeometry::DiscretizationMethod > UpwindScheme
The upwind scheme used for the advective fluxes. This depends on the chosen discretization method.
Definition: flux/upwindscheme.hh:30
Define some often used mathematical functions.
Definition: adapt.hh:17
Scalar faceTensorAverage(const Scalar T1, const Scalar T2, const Dune::FieldVector< Scalar, dim > &normal)
Average of a discontinuous scalar field at discontinuity interface (for compatibility reasons with th...
Definition: facetensoraverage.hh:29
The infrastructure to retrieve run-time parameters from Dune::ParameterTrees.