Loading [MathJax]/extensions/tex2jax.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
velocity.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 *****************************************************************************/
25#ifndef DUMUX_POROUSMEDIUMFLOW_VELOCITY_HH
26#define DUMUX_POROUSMEDIUMFLOW_VELOCITY_HH
27
28#include <vector>
29#include <type_traits>
30
31#include <dune/common/fvector.hh>
32#include <dune/common/float_cmp.hh>
33#include <dune/geometry/type.hh>
34#include <dune/geometry/referenceelements.hh>
35
39#include <dumux/flux/traits.hh>
40
41namespace Dumux {
42
43#ifndef DOXYGEN
44namespace Detail {
45// helper structs and functions detecting if the model is compositional
46
47template <typename T, typename ...Ts>
48using MoleFractionDetector = decltype(std::declval<T>().moleFraction(std::declval<Ts>()...));
49
50template<class T, typename ...Args>
51static constexpr bool hasMoleFraction()
52{ return Dune::Std::is_detected<MoleFractionDetector, T, Args...>::value; }
53
54template <typename T, typename ...Ts>
55using MassFractionDetector = decltype(std::declval<T>().massFraction(std::declval<Ts>()...));
56
57template<class T, typename ...Args>
58static constexpr bool hasMassFraction()
59{ return Dune::Std::is_detected<MassFractionDetector, T, Args...>::value; }
60
61} // end namespace Detail
62#endif // DOXYGEN
63
68template<class GridVariables, class FluxVariables>
70{
71 using GridGeometry = typename GridVariables::GridGeometry;
72 using FVElementGeometry = typename GridGeometry::LocalView;
73 using SubControlVolume = typename GridGeometry::SubControlVolume;
74 using SubControlVolumeFace = typename GridGeometry::SubControlVolumeFace;
75 using GridView = typename GridGeometry::GridView;
76 using Element = typename GridView::template Codim<0>::Entity;
77 using GridVolumeVariables = typename GridVariables::GridVolumeVariables;
78 using ElementFluxVarsCache = typename GridVariables::GridFluxVariablesCache::LocalView;
79 using VolumeVariables = typename GridVariables::VolumeVariables;
80 using ElementVolumeVariables = typename GridVolumeVariables::LocalView;
81 using FluidSystem = typename VolumeVariables::FluidSystem;
82 using Scalar = typename GridVariables::Scalar;
83 using FluxTraits = typename Dumux::FluxTraits<FluxVariables>;
84 using AdvectionType = typename FluxVariables::AdvectionType;
85
86 static constexpr int dim = GridView::dimension;
87 static constexpr int dimWorld = GridView::dimensionworld;
88 static constexpr bool isBox = GridGeometry::discMethod == DiscretizationMethod::box;
89 static constexpr int dofCodim = isBox ? dim : 0;
90 static constexpr bool stationaryVelocityField = FluxTraits::hasStationaryVelocityField();
91
92 using GlobalPosition = typename Element::Geometry::GlobalCoordinate;
93
94 using Problem = typename GridVolumeVariables::Problem;
95
96 static constexpr bool modelIsCompositional = Detail::hasMoleFraction<typename GridVolumeVariables::VolumeVariables, int, int>() ||
97 Detail::hasMassFraction<typename GridVolumeVariables::VolumeVariables, int, int>();
98
99 static_assert(VolumeVariables::numFluidPhases() >= 1, "Velocity output only makes sense for models with fluid phases.");
100
101public:
102 static constexpr int numFluidPhases = VolumeVariables::numFluidPhases();
103 using VelocityVector = std::vector<Dune::FieldVector<Scalar, dimWorld>>;
104
110 PorousMediumFlowVelocity(const GridVariables& gridVariables)
111 : problem_(gridVariables.curGridVolVars().problem())
112 , gridGeometry_(gridVariables.gridGeometry())
113 , gridVariables_(gridVariables)
114 {
115 // set the number of scvs the vertices are connected to
116 if constexpr (isBox && dim > 1)
117 {
118 // resize to the number of vertices of the grid
119 cellNum_.assign(gridGeometry_.gridView().size(dim), 0);
120
121 for (const auto& element : elements(gridGeometry_.gridView()))
122 for (unsigned int vIdx = 0; vIdx < element.subEntities(dim); ++vIdx)
123 ++cellNum_[gridGeometry_.vertexMapper().subIndex(element, vIdx, dim)];
124 }
125 }
126
130 const Element& element,
131 const FVElementGeometry& fvGeometry,
132 const ElementVolumeVariables& elemVolVars,
133 const ElementFluxVarsCache& elemFluxVarsCache,
134 int phaseIdx) const
135 {
136 using Velocity = typename VelocityVector::value_type;
137
138 const auto geometry = element.geometry();
139 const Dune::GeometryType geomType = geometry.type();
140
141 // the upwind term to be used for the volume flux evaluation
142 auto upwindTerm = [phaseIdx](const auto& volVars) { return volVars.mobility(phaseIdx); };
143
144 if constexpr (isBox && dim == 1)
145 {
146 Velocity tmpVelocity(0.0);
147 tmpVelocity = (geometry.corner(1) - geometry.corner(0));
148 tmpVelocity /= tmpVelocity.two_norm();
149
150 for (auto&& scvf : scvfs(fvGeometry))
151 {
152 if (scvf.boundary())
153 continue;
154
155 // insantiate the flux variables
156 FluxVariables fluxVars;
157 fluxVars.init(problem_, element, fvGeometry, elemVolVars, scvf, elemFluxVarsCache);
158
159 // get the volume flux divided by the area of the
160 // subcontrolvolume face in the reference element
161 Scalar localArea = scvfReferenceArea_(geomType, scvf.index());
162 Scalar flux = fluxVars.advectiveFlux(phaseIdx, upwindTerm) / localArea;
163 const auto& insideVolVars = elemVolVars[scvf.insideScvIdx()];
164 flux /= insideVolVars.extrusionFactor();
165 tmpVelocity *= flux;
166
167 const int eIdxGlobal = gridGeometry_.elementMapper().index(element);
168 velocity[eIdxGlobal] = tmpVelocity;
169 }
170 return;
171 }
172
173 // get the transposed Jacobian of the element mapping
174 const auto refElement = referenceElement(geometry);
175 const auto& localPos = refElement.position(0, 0);
176 const auto jacobianT2 = geometry.jacobianTransposed(localPos);
177
178 if constexpr (isBox)
179 {
180 using ScvVelocities = Dune::BlockVector<Velocity>;
181 ScvVelocities scvVelocities(fvGeometry.numScv());
182 scvVelocities = 0;
183
184 for (auto&& scvf : scvfs(fvGeometry))
185 {
186 if (scvf.boundary())
187 continue;
188
189 // local position of integration point
190 const auto localPosIP = geometry.local(scvf.ipGlobal());
191
192 // Transformation of the global normal vector to normal vector in the reference element
193 const auto jacobianT1 = geometry.jacobianTransposed(localPosIP);
194 const auto globalNormal = scvf.unitOuterNormal();
195 GlobalPosition localNormal(0);
196 jacobianT1.mv(globalNormal, localNormal);
197 localNormal /= localNormal.two_norm();
198
199 // instantiate the flux variables
200 FluxVariables fluxVars;
201 fluxVars.init(problem_, element, fvGeometry, elemVolVars, scvf, elemFluxVarsCache);
202
203 // get the volume flux divided by the area of the
204 // subcontrolvolume face in the reference element
205 Scalar localArea = scvfReferenceArea_(geomType, scvf.index());
206 Scalar flux = fluxVars.advectiveFlux(phaseIdx, upwindTerm) / localArea;
207 const auto& insideVolVars = elemVolVars[scvf.insideScvIdx()];
208 flux /= insideVolVars.extrusionFactor();
209
210 // transform the volume flux into a velocity vector
211 Velocity tmpVelocity = localNormal;
212 tmpVelocity *= flux;
213
214 scvVelocities[scvf.insideScvIdx()] += tmpVelocity;
215 scvVelocities[scvf.outsideScvIdx()] += tmpVelocity;
216 }
217
218 // transform vertex velocities from local to global coordinates
219 for (auto&& scv : scvs(fvGeometry))
220 {
221 int vIdxGlobal = scv.dofIndex();
222
223 // calculate the subcontrolvolume velocity by the Piola transformation
224 Velocity scvVelocity(0);
225
226 jacobianT2.mtv(scvVelocities[scv.indexInElement()], scvVelocity);
227 scvVelocity /= geometry.integrationElement(localPos)*cellNum_[vIdxGlobal];
228 // add up the wetting phase subcontrolvolume velocities for each vertex
229 velocity[vIdxGlobal] += scvVelocity;
230 }
231 }
232 else
233 {
234 // For the number of scvfs per facet (mpfa) we simply obtain the number of
235 // corners of the first facet as prisms/pyramids are not supported here anyway
236 // -> for prisms/pyramids the number of scvfs would differ from facet to facet
237 static constexpr bool isMpfa = GridGeometry::discMethod == DiscretizationMethod::ccmpfa;
238 const int numScvfsPerFace = isMpfa ? element.template subEntity<1>(0).geometry().corners() : 1;
239
240 if (fvGeometry.numScvf() != element.subEntities(1)*numScvfsPerFace)
241 DUNE_THROW(Dune::NotImplemented, "Velocity output for non-conforming grids");
242
243 if (!geomType.isCube() && !geomType.isSimplex())
244 DUNE_THROW(Dune::NotImplemented, "Velocity output for other geometry types than cube and simplex");
245
246 // first we extract the corner indices for each scv for the CIV method
247 // for network grids there might be multiple intersection with the same geometryInInside
248 // we identify those by the indexInInside for now (assumes conforming grids at branching facets)
249 // here we keep track of them
250 std::vector<bool> handledScvf;
251 if (dim < dimWorld)
252 handledScvf.resize(element.subEntities(1), false);
253
254 // find the local face indices of the scvfs (for conforming meshes)
255 std::vector<unsigned int> scvfIndexInInside(fvGeometry.numScvf());
256 int localScvfIdx = 0;
257 for (const auto& intersection : intersections(gridGeometry_.gridView(), element))
258 {
259 if (dim < dimWorld)
260 if (handledScvf[intersection.indexInInside()])
261 continue;
262
263 if (intersection.neighbor() || intersection.boundary())
264 {
265 for (int i = 0; i < numScvfsPerFace; ++i)
266 scvfIndexInInside[localScvfIdx++] = intersection.indexInInside();
267
268 // for surface and network grids mark that we handled this face
269 if (dim < dimWorld)
270 handledScvf[intersection.indexInInside()] = true;
271 }
272 }
273
274 std::vector<Scalar> scvfFluxes(element.subEntities(1), 0.0);
275 localScvfIdx = 0;
276 for (auto&& scvf : scvfs(fvGeometry))
277 {
278 const auto& insideVolVars = elemVolVars[scvf.insideScvIdx()];
279 if (!scvf.boundary())
280 {
281 FluxVariables fluxVars;
282 fluxVars.init(problem_, element, fvGeometry, elemVolVars, scvf, elemFluxVarsCache);
283 scvfFluxes[scvfIndexInInside[localScvfIdx]] += fluxVars.advectiveFlux(phaseIdx, upwindTerm)/insideVolVars.extrusionFactor();
284 }
285 else
286 {
287 auto bcTypes = problem_.boundaryTypes(element, scvf);
288 if (bcTypes.hasOnlyDirichlet())
289 {
290 FluxVariables fluxVars;
291 fluxVars.init(problem_, element, fvGeometry, elemVolVars, scvf, elemFluxVarsCache);
292 scvfFluxes[scvfIndexInInside[localScvfIdx]] += fluxVars.advectiveFlux(phaseIdx, upwindTerm)/insideVolVars.extrusionFactor();
293 }
294 }
295
296 // increment scvf counter
297 localScvfIdx++;
298 }
299
300 // Correct boundary fluxes in case of Neumann conditions.
301 // In this general setting, it would be very difficult to
302 // calculate correct phase, i.e., volume, fluxes from arbitrary
303 // Neumann conditions. We approximate the Neumann flux by the
304 // flux on the opposite face. For extremely distorted grids this can
305 // lead to unexpected results (but then TPFA also leads to unexpected results).
306 localScvfIdx = 0;
307 for (auto&& scvf : scvfs(fvGeometry))
308 {
309 if (scvf.boundary())
310 {
311 auto bcTypes = problem_.boundaryTypes(element, scvf);
312 if (bcTypes.hasNeumann())
313 {
314 // for stationary velocity fields we can easily compute the correct velocity
315 // this special treatment makes sure that the velocity field is also correct on Neumann boundaries
316 // of tracer problems where the velocity field is given.
317 // (For Dirichlet boundaries no special treatment is necessary.)
318 if (stationaryVelocityField)
319 {
320 const auto flux = AdvectionType::flux(problem_, element, fvGeometry, elemVolVars,
321 scvf, phaseIdx, elemFluxVarsCache);
322
323 const auto& insideVolVars = elemVolVars[scvf.insideScvIdx()];
324 scvfFluxes[scvfIndexInInside[localScvfIdx]] += flux / insideVolVars.extrusionFactor();
325 }
326 else
327 {
328 // check if we have Neumann no flow, we can just use 0
329 const auto neumannFlux = problem_.neumann(element, fvGeometry, elemVolVars, elemFluxVarsCache, scvf);
330 using NumEqVector = std::decay_t<decltype(neumannFlux)>;
331 if (Dune::FloatCmp::eq<NumEqVector, Dune::FloatCmp::CmpStyle::absolute>(neumannFlux, NumEqVector(0.0), 1e-30))
332 scvfFluxes[scvfIndexInInside[localScvfIdx]] = 0;
333
334 // otherwise, we try some reconstruction
335 // for cubes
336 else if (dim == 1 || geomType.isCube())
337 {
338 const auto fIdx = scvfIndexInInside[localScvfIdx];
339
340 if constexpr (!modelIsCompositional)
341 {
342 // We assume that the density at the face equals the one at the cell center and reconstruct a volume flux from the Neumann mass flux.
343 const auto& insideVolVars = elemVolVars[scvf.insideScvIdx()];
344 const auto eqIdx = VolumeVariables::Indices::conti0EqIdx + phaseIdx;
345 scvfFluxes[fIdx] += neumannFlux[eqIdx] / insideVolVars.density(phaseIdx) * scvf.area() * insideVolVars.extrusionFactor();
346 }
347 else
348 {
349 // For compositional models, we generally can't reconstruct the volume flow from the Neumann flux (which is a species flux rather
350 // than a phase flux here). Instead, we use the velocity of the opposing face.
351 const auto fIdxOpposite = fIdx%2 ? fIdx-1 : fIdx+1;
352 scvfFluxes[fIdx] = -scvfFluxes[fIdxOpposite];
353 }
354 }
355
356 // for simplices
357 else if (geomType.isSimplex())
358 scvfFluxes[scvfIndexInInside[localScvfIdx]] = 0;
359
360 else
361 DUNE_THROW(Dune::NotImplemented, "Velocity computation at Neumann boundaries for cell-centered and prism/pyramid");
362 }
363 }
364 }
365
366 // increment scvf counter
367 localScvfIdx++;
368 }
369
370
371 Velocity refVelocity;
372 // cubes: On the reference element simply average over opposite fluxes
373 // note that this is equal to a corner velocity interpolation method
374 if (dim == 1 || geomType.isCube())
375 {
376 for (int i = 0; i < dim; i++)
377 refVelocity[i] = 0.5 * (scvfFluxes[2*i + 1] - scvfFluxes[2*i]);
378 }
379 // simplices: Raviart-Thomas-0 interpolation evaluated at the cell center
380 else if (geomType.isSimplex())
381 {
382 for (int dimIdx = 0; dimIdx < dim; dimIdx++)
383 {
384 refVelocity[dimIdx] = -scvfFluxes[dim - 1 - dimIdx];
385 for (int fIdx = 0; fIdx < dim + 1; fIdx++)
386 refVelocity[dimIdx] += scvfFluxes[fIdx]/(dim + 1);
387 }
388 }
389 // 3D prism and pyramids
390 else
391 DUNE_THROW(Dune::NotImplemented, "Velocity computation for cell-centered and prism/pyramid");
392
393 Velocity scvVelocity(0);
394 jacobianT2.mtv(refVelocity, scvVelocity);
395
396 scvVelocity /= geometry.integrationElement(localPos);
397
398 int eIdxGlobal = gridGeometry_.elementMapper().index(element);
399
400 velocity[eIdxGlobal] = scvVelocity;
401
402 } // cell-centered
403 }
404
405private:
406 // The area of a subcontrolvolume face in a reference element.
407 // The 3d non-cube values have been calculated with quadrilateralArea3D
408 // of boxfvelementgeometry.hh.
409 static Scalar scvfReferenceArea_(Dune::GeometryType geomType, int fIdx)
410 {
411 if (dim == 1 || geomType.isCube())
412 {
413 return 1.0/(1 << (dim-1));
414 }
415 else if (geomType.isTriangle())
416 {
417 static const Scalar faceToArea[] = {0.372677996249965,
418 0.372677996249965,
419 0.235702260395516};
420 return faceToArea[fIdx];
421 }
422 else if (geomType.isTetrahedron())
423 {
424 static const Scalar faceToArea[] = {0.102062072615966,
425 0.102062072615966,
426 0.058925565098879,
427 0.102062072615966,
428 0.058925565098879,
429 0.058925565098879};
430 return faceToArea[fIdx];
431 }
432 else if (geomType.isPyramid())
433 {
434 static const Scalar faceToArea[] = {0.130437298687488,
435 0.130437298687488,
436 0.130437298687488,
437 0.130437298687488,
438 0.150923085635624,
439 0.1092906420717,
440 0.1092906420717,
441 0.0781735959970571};
442 return faceToArea[fIdx];
443 }
444 else if (geomType.isPrism())
445 {
446 static const Scalar faceToArea[] = {0.166666666666667,
447 0.166666666666667,
448 0.166666666666667,
449 0.186338998124982,
450 0.186338998124982,
451 0.117851130197758,
452 0.186338998124982,
453 0.186338998124982,
454 0.117851130197758};
455 return faceToArea[fIdx];
456 }
457 else {
458 DUNE_THROW(Dune::NotImplemented, "scvf area for unknown GeometryType");
459 }
460 }
461
462private:
463 const Problem& problem_;
464 const GridGeometry& gridGeometry_;
465 const GridVariables& gridVariables_;
466
467 std::vector<int> cellNum_;
468};
469
470} // end namespace Dumux
471
472#endif
The infrastructure to retrieve run-time parameters from Dune::ParameterTrees.
Element solution classes and factory functions.
The available discretization methods in Dumux.
typename NumEqVectorTraits< PrimaryVariables >::type NumEqVector
A vector with the same size as numbers of equations This is the default implementation and has to be ...
Definition: numeqvector.hh:46
Definition: adapt.hh:29
Traits of a flux variables type.
Definition: flux/traits.hh:44
static constexpr bool hasStationaryVelocityField()
Definition: flux/traits.hh:45
Velocity computation for implicit (porous media) models.
Definition: velocity.hh:70
void calculateVelocity(VelocityVector &velocity, const Element &element, const FVElementGeometry &fvGeometry, const ElementVolumeVariables &elemVolVars, const ElementFluxVarsCache &elemFluxVarsCache, int phaseIdx) const
Definition: velocity.hh:129
PorousMediumFlowVelocity(const GridVariables &gridVariables)
Constructor initializes the static data with the initial solution.
Definition: velocity.hh:110
std::vector< Dune::FieldVector< Scalar, dimWorld > > VelocityVector
Definition: velocity.hh:103
static constexpr int numFluidPhases
Definition: velocity.hh:102
Defines the flux traits.