3.1-git
DUNE for Multi-{Phase, Component, Scale, Physics, ...} flow and transport in porous media
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
30#include <dune/common/fvector.hh>
31#include <dune/common/float_cmp.hh>
32#include <dune/geometry/referenceelements.hh>
33
38
39namespace Dumux {
40
45template<class GridVariables, class FluxVariables>
47{
48 using GridGeometry = typename GridVariables::GridGeometry;
49 using FVElementGeometry = typename GridGeometry::LocalView;
50 using SubControlVolume = typename GridGeometry::SubControlVolume;
51 using SubControlVolumeFace = typename GridGeometry::SubControlVolumeFace;
52 using GridView = typename GridGeometry::GridView;
53 using Element = typename GridView::template Codim<0>::Entity;
54 using GridVolumeVariables = typename GridVariables::GridVolumeVariables;
55 using ElementFluxVarsCache = typename GridVariables::GridFluxVariablesCache::LocalView;
56 using VolumeVariables = typename GridVariables::VolumeVariables;
57 using ElementVolumeVariables = typename GridVolumeVariables::LocalView;
58 using FluidSystem = typename VolumeVariables::FluidSystem;
59 using Scalar = typename GridVariables::Scalar;
60
61 static constexpr int dim = GridView::dimension;
62 static constexpr int dimWorld = GridView::dimensionworld;
63 static constexpr bool isBox = GridGeometry::discMethod == DiscretizationMethod::box;
64 static constexpr int dofCodim = isBox ? dim : 0;
65
66 using GlobalPosition = typename Element::Geometry::GlobalCoordinate;
67 using ReferenceElements = Dune::ReferenceElements<typename GridView::ctype, dim>;
68
69 using Problem = typename GridVolumeVariables::Problem;
70 using BoundaryTypes = typename Problem::Traits::BoundaryTypes;
71
72public:
73 static constexpr int numFluidPhases = VolumeVariables::numFluidPhases();
74 using VelocityVector = std::vector<Dune::FieldVector<Scalar, dimWorld>>;
75
81 PorousMediumFlowVelocity(const GridVariables& gridVariables)
82 : problem_(gridVariables.curGridVolVars().problem())
83 , gridGeometry_(gridVariables.gridGeometry())
84 , gridVariables_(gridVariables)
85 {
86 // set the number of scvs the vertices are connected to
87 if (isBox && dim > 1)
88 {
89 // resize to the number of vertices of the grid
90 cellNum_.assign(gridGeometry_.gridView().size(dim), 0);
91
92 for (const auto& element : elements(gridGeometry_.gridView()))
93 for (unsigned int vIdx = 0; vIdx < element.subEntities(dim); ++vIdx)
94 ++cellNum_[gridGeometry_.vertexMapper().subIndex(element, vIdx, dim)];
95 }
96 }
97
101 const Element& element,
102 const FVElementGeometry& fvGeometry,
103 const ElementVolumeVariables& elemVolVars,
104 const ElementFluxVarsCache& elemFluxVarsCache,
105 int phaseIdx) const
106 {
107 using Velocity = typename VelocityVector::value_type;
108
109 const auto geometry = element.geometry();
110 const Dune::GeometryType geomType = geometry.type();
111
112 // the upwind term to be used for the volume flux evaluation
113 auto upwindTerm = [phaseIdx](const auto& volVars) { return volVars.mobility(phaseIdx); };
114
115 if(isBox && dim == 1)
116 {
117 Velocity tmpVelocity(0.0);
118 tmpVelocity = (geometry.corner(1) - geometry.corner(0));
119 tmpVelocity /= tmpVelocity.two_norm();
120
121 for (auto&& scvf : scvfs(fvGeometry))
122 {
123 if (scvf.boundary())
124 continue;
125
126 // insantiate the flux variables
127 FluxVariables fluxVars;
128 fluxVars.init(problem_, element, fvGeometry, elemVolVars, scvf, elemFluxVarsCache);
129
130 // get the volume flux divided by the area of the
131 // subcontrolvolume face in the reference element
132 Scalar localArea = scvfReferenceArea_(geomType, scvf.index());
133 Scalar flux = fluxVars.advectiveFlux(phaseIdx, upwindTerm) / localArea;
134 const auto& insideVolVars = elemVolVars[scvf.insideScvIdx()];
135 flux /= insideVolVars.extrusionFactor();
136 tmpVelocity *= flux;
137
138 const int eIdxGlobal = gridGeometry_.elementMapper().index(element);
139 velocity[eIdxGlobal] = tmpVelocity;
140 }
141 return;
142 }
143
144 // get the transposed Jacobian of the element mapping
145 const auto referenceElement = ReferenceElements::general(geomType);
146 const auto& localPos = referenceElement.position(0, 0);
147 const auto jacobianT2 = geometry.jacobianTransposed(localPos);
148
149 if(isBox)
150 {
151 using ScvVelocities = Dune::BlockVector<Velocity>;
152 ScvVelocities scvVelocities(fvGeometry.numScv());
153 scvVelocities = 0;
154
155 for (auto&& scvf : scvfs(fvGeometry))
156 {
157 if (scvf.boundary())
158 continue;
159
160 // local position of integration point
161 const auto localPosIP = geometry.local(scvf.ipGlobal());
162
163 // Transformation of the global normal vector to normal vector in the reference element
164 const auto jacobianT1 = geometry.jacobianTransposed(localPosIP);
165 const auto globalNormal = scvf.unitOuterNormal();
166 GlobalPosition localNormal(0);
167 jacobianT1.mv(globalNormal, localNormal);
168 localNormal /= localNormal.two_norm();
169
170 // instantiate the flux variables
171 FluxVariables fluxVars;
172 fluxVars.init(problem_, element, fvGeometry, elemVolVars, scvf, elemFluxVarsCache);
173
174 // get the volume flux divided by the area of the
175 // subcontrolvolume face in the reference element
176 Scalar localArea = scvfReferenceArea_(geomType, scvf.index());
177 Scalar flux = fluxVars.advectiveFlux(phaseIdx, upwindTerm) / localArea;
178 const auto& insideVolVars = elemVolVars[scvf.insideScvIdx()];
179 flux /= insideVolVars.extrusionFactor();
180
181 // transform the volume flux into a velocity vector
182 Velocity tmpVelocity = localNormal;
183 tmpVelocity *= flux;
184
185 scvVelocities[scvf.insideScvIdx()] += tmpVelocity;
186 scvVelocities[scvf.outsideScvIdx()] += tmpVelocity;
187 }
188
189 // transform vertex velocities from local to global coordinates
190 for (auto&& scv : scvs(fvGeometry))
191 {
192 int vIdxGlobal = scv.dofIndex();
193
194 // calculate the subcontrolvolume velocity by the Piola transformation
195 Velocity scvVelocity(0);
196
197 jacobianT2.mtv(scvVelocities[scv.indexInElement()], scvVelocity);
198 scvVelocity /= geometry.integrationElement(localPos)*cellNum_[vIdxGlobal];
199 // add up the wetting phase subcontrolvolume velocities for each vertex
200 velocity[vIdxGlobal] += scvVelocity;
201 }
202 }
203 else
204 {
205 // For the number of scvfs per facet (mpfa) we simply obtain the number of
206 // corners of the first facet as prisms/pyramids are not supported here anyway
207 // -> for prisms/pyramids the number of scvfs would differ from facet to facet
208 static constexpr bool isMpfa = GridGeometry::discMethod == DiscretizationMethod::ccmpfa;
209 const int numScvfsPerFace = isMpfa ? element.template subEntity<1>(0).geometry().corners() : 1;
210
211 if (fvGeometry.numScvf() != element.subEntities(1)*numScvfsPerFace)
212 DUNE_THROW(Dune::NotImplemented, "Velocity output for non-conforming grids");
213
214 if (!geomType.isCube() && !geomType.isSimplex())
215 DUNE_THROW(Dune::NotImplemented, "Velocity output for other geometry types than cube and simplex");
216
217 // first we extract the corner indices for each scv for the CIV method
218 // for network grids there might be multiple intersection with the same geometryInInside
219 // we identify those by the indexInInside for now (assumes conforming grids at branching facets)
220 // here we keep track of them
221 std::vector<bool> handledScvf;
222 if (dim < dimWorld)
223 handledScvf.resize(element.subEntities(1), false);
224
225 // find the local face indices of the scvfs (for conforming meshes)
226 std::vector<unsigned int> scvfIndexInInside(fvGeometry.numScvf());
227 int localScvfIdx = 0;
228 for (const auto& intersection : intersections(gridGeometry_.gridView(), element))
229 {
230 if (dim < dimWorld)
231 if (handledScvf[intersection.indexInInside()])
232 continue;
233
234 if (intersection.neighbor() || intersection.boundary())
235 {
236 for (int i = 0; i < numScvfsPerFace; ++i)
237 scvfIndexInInside[localScvfIdx++] = intersection.indexInInside();
238
239 // for surface and network grids mark that we handled this face
240 if (dim < dimWorld)
241 handledScvf[intersection.indexInInside()] = true;
242 }
243 }
244
245 std::vector<Scalar> scvfFluxes(element.subEntities(1), 0.0);
246 localScvfIdx = 0;
247 for (auto&& scvf : scvfs(fvGeometry))
248 {
249 const auto& insideVolVars = elemVolVars[scvf.insideScvIdx()];
250 if (!scvf.boundary())
251 {
252 FluxVariables fluxVars;
253 fluxVars.init(problem_, element, fvGeometry, elemVolVars, scvf, elemFluxVarsCache);
254 scvfFluxes[scvfIndexInInside[localScvfIdx]] += fluxVars.advectiveFlux(phaseIdx, upwindTerm)/insideVolVars.extrusionFactor();
255 }
256 else
257 {
258 auto bcTypes = problemBoundaryTypes_(element, scvf);
259 if (bcTypes.hasOnlyDirichlet())
260 {
261 FluxVariables fluxVars;
262 fluxVars.init(problem_, element, fvGeometry, elemVolVars, scvf, elemFluxVarsCache);
263 scvfFluxes[scvfIndexInInside[localScvfIdx]] += fluxVars.advectiveFlux(phaseIdx, upwindTerm)/insideVolVars.extrusionFactor();
264 }
265 }
266
267 // increment scvf counter
268 localScvfIdx++;
269 }
270
271 // Correct boundary fluxes in case of Neumann conditions.
272 // In this general setting, it would be very difficult to
273 // calculate correct phase, i.e., volume, fluxes from arbitrary
274 // Neumann conditions. We approximate the Neumann flux by the
275 // flux on the opposite face. For extremely distorted grids this can
276 // lead to unexpected results (but then TPFA also leads to unexpected results).
277 localScvfIdx = 0;
278 for (auto&& scvf : scvfs(fvGeometry))
279 {
280 if (scvf.boundary())
281 {
282 auto bcTypes = problemBoundaryTypes_(element, scvf);
283 if (bcTypes.hasNeumann())
284 {
285 // check if we have Neumann no flow, we can just use 0
286 const auto neumannFlux = Deprecated::neumann(problem_, element, fvGeometry, elemVolVars, elemFluxVarsCache, scvf);
287 using NumEqVector = std::decay_t<decltype(neumannFlux)>;
288 if (Dune::FloatCmp::eq<NumEqVector, Dune::FloatCmp::CmpStyle::absolute>(neumannFlux, NumEqVector(0.0), 1e-30))
289 scvfFluxes[scvfIndexInInside[localScvfIdx]] = 0;
290 // cubes
291 else if (dim == 1 || geomType.isCube())
292 {
293 const auto fIdx = scvfIndexInInside[localScvfIdx];
294 const auto fIdxOpposite = fIdx%2 ? fIdx-1 : fIdx+1;
295 scvfFluxes[fIdx] = -scvfFluxes[fIdxOpposite];
296 }
297 // simplices
298 else if (geomType.isSimplex())
299 scvfFluxes[scvfIndexInInside[localScvfIdx]] = 0;
300 }
301 }
302
303 // increment scvf counter
304 localScvfIdx++;
305 }
306
307 Velocity refVelocity;
308 // cubes: On the reference element simply average over opposite fluxes
309 // note that this is equal to a corner velocity interpolation method
310 if (dim == 1 || geomType.isCube())
311 {
312 for (int i = 0; i < dim; i++)
313 refVelocity[i] = 0.5 * (scvfFluxes[2*i + 1] - scvfFluxes[2*i]);
314 }
315 // simplices: Raviart-Thomas-0 interpolation evaluated at the cell center
316 else if (geomType.isSimplex())
317 {
318 for (int dimIdx = 0; dimIdx < dim; dimIdx++)
319 {
320 refVelocity[dimIdx] = -scvfFluxes[dim - 1 - dimIdx];
321 for (int fIdx = 0; fIdx < dim + 1; fIdx++)
322 refVelocity[dimIdx] += scvfFluxes[fIdx]/(dim + 1);
323 }
324 }
325 // 3D prism and pyramids
326 else
327 DUNE_THROW(Dune::NotImplemented, "velocity output for cell-centered and prism/pyramid");
328
329 Velocity scvVelocity(0);
330 jacobianT2.mtv(refVelocity, scvVelocity);
331
332 scvVelocity /= geometry.integrationElement(localPos);
333
334 int eIdxGlobal = gridGeometry_.elementMapper().index(element);
335
336 velocity[eIdxGlobal] = scvVelocity;
337
338 } // cell-centered
339 }
340
341private:
342 // The area of a subcontrolvolume face in a reference element.
343 // The 3d non-cube values have been calculated with quadrilateralArea3D
344 // of boxfvelementgeometry.hh.
345 static Scalar scvfReferenceArea_(Dune::GeometryType geomType, int fIdx)
346 {
347 if (dim == 1 || geomType.isCube())
348 {
349 return 1.0/(1 << (dim-1));
350 }
351 else if (geomType.isTriangle())
352 {
353 static const Scalar faceToArea[] = {0.372677996249965,
354 0.372677996249965,
355 0.235702260395516};
356 return faceToArea[fIdx];
357 }
358 else if (geomType.isTetrahedron())
359 {
360 static const Scalar faceToArea[] = {0.102062072615966,
361 0.102062072615966,
362 0.058925565098879,
363 0.102062072615966,
364 0.058925565098879,
365 0.058925565098879};
366 return faceToArea[fIdx];
367 }
368 else if (geomType.isPyramid())
369 {
370 static const Scalar faceToArea[] = {0.130437298687488,
371 0.130437298687488,
372 0.130437298687488,
373 0.130437298687488,
374 0.150923085635624,
375 0.1092906420717,
376 0.1092906420717,
377 0.0781735959970571};
378 return faceToArea[fIdx];
379 }
380 else if (geomType.isPrism())
381 {
382 static const Scalar faceToArea[] = {0.166666666666667,
383 0.166666666666667,
384 0.166666666666667,
385 0.186338998124982,
386 0.186338998124982,
387 0.117851130197758,
388 0.186338998124982,
389 0.186338998124982,
390 0.117851130197758};
391 return faceToArea[fIdx];
392 }
393 else {
394 DUNE_THROW(Dune::NotImplemented, "scvf area for unknown GeometryType");
395 }
396 }
397
398private:
399 // The following SFINAE enable_if usage allows compilation, even if only a
400 //
401 // boundaryTypes(const Element&, const scv&)
402 //
403 // is provided in the problem file. In that case, the compiler cannot detect
404 // (without additional measures like "using...") the signature
405 //
406 // boundaryTypes(const Element&, const scvf&)
407 //
408 // in the problem base class. Therefore, calls to this method trigger a
409 // compiler error. However, that call is needed for calculating velocities
410 // if the cell-centered discretization is used. By proceeding as in the
411 // following lines, that call will only be compiled if cell-centered
412 // actually is used.
413 template <bool enable = isBox, typename std::enable_if_t<!enable, int> = 0>
414 BoundaryTypes problemBoundaryTypes_(const Element& element, const SubControlVolumeFace& scvf) const
415 { return problem_.boundaryTypes(element, scvf); }
416
418 template <bool enable = isBox, typename std::enable_if_t<enable, int> = 0>
419 BoundaryTypes problemBoundaryTypes_(const Element& element, const SubControlVolumeFace& scvf) const
420 { return BoundaryTypes(); }
421
422 const Problem& problem_;
423 const GridGeometry& gridGeometry_;
424 const GridVariables& gridVariables_;
425
426 std::vector<int> cellNum_;
427};
428
429} // end namespace Dumux
430
431#endif
Helpers for deprecation.
The infrastructure to retrieve run-time parameters from Dune::ParameterTrees.
Element solution classes and factory functions.
The available discretization methods in Dumux.
Dune::IteratorRange< typename MultiDomainGlue< DomainGridView, TargetGridView, DomainMapper, TargetMapper >::Intersections::const_iterator > intersections(const MultiDomainGlue< DomainGridView, TargetGridView, DomainMapper, TargetMapper > &glue)
Range generator to iterate with range-based for loops over all intersections as follows: for (const a...
Definition: glue.hh:62
make the local view function available whenever we use the grid geometry
Definition: adapt.hh:29
Property tag Velocity
The type velocity reconstruction.
Definition: porousmediumflow/sequential/properties.hh:67
Velocity computation for implicit (porous media) models.
Definition: velocity.hh:47
void calculateVelocity(VelocityVector &velocity, const Element &element, const FVElementGeometry &fvGeometry, const ElementVolumeVariables &elemVolVars, const ElementFluxVarsCache &elemFluxVarsCache, int phaseIdx) const
Definition: velocity.hh:100
PorousMediumFlowVelocity(const GridVariables &gridVariables)
Constructor initializes the static data with the initial solution.
Definition: velocity.hh:81
std::vector< Dune::FieldVector< Scalar, dimWorld > > VelocityVector
Definition: velocity.hh:74
static constexpr int numFluidPhases
Definition: velocity.hh:73