version 3.8
fluxhelper.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//
7
13#ifndef DUMUX_NAVIERSTOKES_MOMENTUM_BOUNDARY_FLUXHELPER_HH
14#define DUMUX_NAVIERSTOKES_MOMENTUM_BOUNDARY_FLUXHELPER_HH
15
16#include <dumux/common/math.hh>
20
21namespace Dumux {
22
28{
43 template<class Problem, class FVElementGeometry, class ElementVolumeVariables, class ElementFluxVariablesCache, class Scalar>
44 static auto fixedPressureMomentumFlux(const Problem& problem,
45 const FVElementGeometry& fvGeometry,
46 const typename FVElementGeometry::SubControlVolumeFace& scvf,
47 const ElementVolumeVariables& elemVolVars,
48 const ElementFluxVariablesCache& elemFluxVarsCache,
49 const Scalar pressure,
50 const bool zeroNormalVelocityGradient = true)
51 {
52 // TODO density upwinding?
53 static_assert(FVElementGeometry::GridGeometry::discMethod == DiscretizationMethods::fcstaggered);
54 using MomentumFlux = typename Problem::MomentumFluxType;
55 constexpr std::size_t dim = static_cast<std::size_t>(FVElementGeometry::GridGeometry::GridView::dimension);
56 static_assert(
57 MomentumFlux::size() == dim,
58 "Expects momentum flux to have as many entries as dimensions."
59 );
60
61 const auto& element = fvGeometry.element();
62
63 static_assert(
64 std::decay_t<decltype(problem.dirichlet(element, scvf))>::size() == dim,
65 "Expects problem.dirichlet to return an array with as many entries as dimensions."
66 );
67
68 if (scvf.isFrontal())
69 {
70 MomentumFlux flux(0.0);
71
72 // pressure contribution
73 flux[scvf.normalAxis()] = (pressure - problem.referencePressure(element, fvGeometry, scvf)) * scvf.directionSign();
74
75 // advective terms
76 if (problem.enableInertiaTerms())
77 {
78 const auto v = elemVolVars[scvf.insideScvIdx()].velocity();
79 flux[scvf.normalAxis()] += v*v * problem.density(element, fvGeometry, scvf) * scvf.directionSign();
80 }
81
82 return flux;
83 }
84 else if (scvf.isLateral())
85 {
86 // if no zero velocity gradient is desired the lateral flux is zero
87 if (!zeroNormalVelocityGradient)
88 return MomentumFlux(0.0);
89
90 // otherwise, make sure the flow does not diverge by accounting for the off-diagonal entries of the stress tensor
91
92 // if the lateral scvf is adjacent to a slip boundary, call the helper function for this special case
93 const auto& scv = fvGeometry.scv(scvf.insideScvIdx());
94 if (scv.boundary() && problem.onSlipBoundary(fvGeometry, fvGeometry.frontalScvfOnBoundary(scv)))
95 return slipVelocityMomentumFlux(problem, fvGeometry, scvf, elemVolVars, elemFluxVarsCache);
96
97 // viscous terms
98 MomentumFlux flux(0.0);
99 const Scalar mu = problem.effectiveViscosity(element, fvGeometry, scvf);
100
101 // lateral face normal to boundary (integration point touches boundary)
102 if (scv.boundary())
103 flux[scv.dofAxis()] -= mu * StaggeredVelocityGradients::velocityGradIJ(fvGeometry, scvf, elemVolVars)
104 * scvf.directionSign();
105 // lateral face coinciding with boundary
106 else if (scvf.boundary())
107 flux[scv.dofAxis()] -= mu * StaggeredVelocityGradients::velocityGradJI(fvGeometry, scvf, elemVolVars)
108 * scvf.directionSign();
109
110 // advective terms
111 if (problem.enableInertiaTerms()) // TODO revise advection terms! Take care with upwinding!
112 {
113 const auto transportingVelocity = [&]()
114 {
115 const auto& orthogonalScvf = fvGeometry.lateralOrthogonalScvf(scvf);
116 const auto innerTransportingVelocity = elemVolVars[orthogonalScvf.insideScvIdx()].velocity();
117
118 static const bool useOldScheme = getParam<bool>("FreeFlow.UseOldTransportingVelocity", true); // TODO how to deprecate?
119
120 if (useOldScheme)
121 {
122 if (scvf.boundary())
123 {
124 if (problem.boundaryTypes(element, scvf).isDirichlet(scvf.normalAxis()))
125 return problem.dirichlet(element, scvf)[scvf.normalAxis()];
126 }
127 else
128 return innerTransportingVelocity;
129 }
130
131 // use the Dirichlet velocity as transporting velocity if the lateral face is on a Dirichlet boundary
132 if (scvf.boundary())
133 {
134 if (problem.boundaryTypes(element, scvf).isDirichlet(scvf.normalAxis()))
135 return 0.5*(problem.dirichlet(element, scvf)[scvf.normalAxis()] + innerTransportingVelocity);
136 }
137
138 if (orthogonalScvf.boundary())
139 {
140 if (problem.boundaryTypes(element, orthogonalScvf).isDirichlet(scvf.normalAxis()))
141 return 0.5*(problem.dirichlet(element, scvf)[scvf.normalAxis()] + innerTransportingVelocity);
142 else
143 return innerTransportingVelocity; // fallback value, should actually never be called
144 }
145
146 // average the transporting velocity by weighting with the scv volumes
147 const auto insideVolume = fvGeometry.scv(orthogonalScvf.insideScvIdx()).volume();
148 const auto outsideVolume = fvGeometry.scv(orthogonalScvf.outsideScvIdx()).volume();
149 const auto outerTransportingVelocity = elemVolVars[orthogonalScvf.outsideScvIdx()].velocity();
150 return (insideVolume*innerTransportingVelocity + outsideVolume*outerTransportingVelocity) / (insideVolume + outsideVolume);
151 }();
152
153 // lateral face normal to boundary (integration point touches boundary)
154 if (scv.boundary())
155 {
156 const auto innerVelocity = elemVolVars[scvf.insideScvIdx()].velocity();
157 const auto outerVelocity = elemVolVars[scvf.outsideScvIdx()].velocity();
158
159 const auto rho = problem.insideAndOutsideDensity(element, fvGeometry, scvf);
160 const bool selfIsUpstream = scvf.directionSign() == sign(transportingVelocity);
161
162 const auto insideMomentum = innerVelocity * rho.first;
163 const auto outsideMomentum = outerVelocity * rho.second;
164
165 static const auto upwindWeight = getParamFromGroup<Scalar>(problem.paramGroup(), "Flux.UpwindWeight");
166
167 const auto transportedMomentum = selfIsUpstream ? (upwindWeight * insideMomentum + (1.0 - upwindWeight) * outsideMomentum)
168 : (upwindWeight * outsideMomentum + (1.0 - upwindWeight) * insideMomentum);
169
170 flux[scv.dofAxis()] += transportingVelocity * transportedMomentum * scvf.directionSign();
171 }
172
173 // lateral face coinciding with boundary
174 else if (scvf.boundary())
175 {
176 const auto insideDensity = problem.density(element, fvGeometry.scv(scvf.insideScvIdx()));
177 const auto innerVelocity = elemVolVars[scvf.insideScvIdx()].velocity();
178 flux[scv.dofAxis()] += innerVelocity * transportingVelocity * insideDensity * scvf.directionSign();
179 }
180 }
181
182 return flux;
183 }
184
185 DUNE_THROW(Dune::InvalidStateException, "Face is neither lateral nor frontal.");
186 }
187
198 template<class Problem, class FVElementGeometry, class ElementVolumeVariables, class ElementFluxVariablesCache, class TangentialVelocityGradient = double>
199 static auto slipVelocityMomentumFlux(const Problem& problem,
200 const FVElementGeometry& fvGeometry,
201 const typename FVElementGeometry::SubControlVolumeFace& scvf,
202 const ElementVolumeVariables& elemVolVars,
203 const ElementFluxVariablesCache& elemFluxVarsCache,
204 const TangentialVelocityGradient& tangentialVelocityGradient = TangentialVelocityGradient(0.0))
205 {
206 static_assert(FVElementGeometry::GridGeometry::discMethod == DiscretizationMethods::fcstaggered);
207 using MomentumFlux = typename Problem::MomentumFluxType;
208 constexpr std::size_t dim = static_cast<std::size_t>(FVElementGeometry::GridGeometry::GridView::dimension);
209 static_assert(
210 MomentumFlux::size() == dim,
211 "Expects momentum flux to have as many entries as dimensions."
212 );
213
214 MomentumFlux flux(0.0);
215
216 // slip velocity momentum contribution only makes sense for lateral scvfs
217 if (scvf.isFrontal())
218 return flux;
219
220 using Scalar = std::decay_t<decltype(flux[0])>;
221 const auto& scv = fvGeometry.scv(scvf.insideScvIdx());
222 const auto& orthogonalScvf = fvGeometry.lateralOrthogonalScvf(scvf);
223 const auto& orthogonalScv = fvGeometry.scv(orthogonalScvf.insideScvIdx());
224
225 if (scvf.boundary() && problem.onSlipBoundary(fvGeometry, fvGeometry.frontalScvfOnBoundary(orthogonalScv)))
226 {
227 /*
228 * ---------#######::::::::: x dof position ***** porous boundary at bottom
229 * slip | || | ::
230 * gradient | || | velI :: -- element
231 * -------> | || scv x~~~~> ::
232 * ------> | || | :: O position at which gradient is evaluated (integration point)
233 * -----> | velJ ^ | :^
234 * ----> -------|-######O::::::::| <----This velocity dof (outer velJ) does not exist if the scv itself lies on a
235 * ***************~~~>****** non-Dirichlet boundary. In that case, use the given tangentialVelocityGradient.
236 * frontal scvf v_slip
237 * on porous || and # staggered half-control-volume (own element)
238 * boundary
239 * :: staggered half-control-volume (neighbor element)
240 *
241 */
242 const Scalar velI = elemVolVars[scvf.insideScvIdx()].velocity();
243
244 // viscous terms
245 const Scalar mu = problem.effectiveViscosity(fvGeometry.element(), fvGeometry, scvf);
246 const Scalar distance = (scv.dofPosition()- scvf.ipGlobal()).two_norm();
247 const Scalar velGradJI = [&]
248 {
249 if (elemVolVars.hasVolVars(orthogonalScvf.outsideScvIdx()))
250 return StaggeredVelocityGradients::velocityGradJI(fvGeometry, scvf, elemVolVars);
251 else
252 return tangentialVelocityGradient;
253 }();
254
255 const Scalar slipVelocity = problem.beaversJosephVelocity(fvGeometry, scvf, elemVolVars, velGradJI)[scv.dofAxis()]; // TODO rename to slipVelocity
256 const Scalar velGradIJ = (slipVelocity - velI) / distance * scvf.directionSign();
257
258 flux[scv.dofAxis()] -= (mu * (velGradIJ + velGradJI))*scvf.directionSign();
259
260 // advective terms
261 if (problem.enableInertiaTerms())
262 {
263 // transporting velocity corresponds to velJ
264 const auto transportingVelocity = [&]()
265 {
266 const auto innerTransportingVelocity = elemVolVars[orthogonalScvf.insideScvIdx()].velocity();
267
268 if (!elemVolVars.hasVolVars(orthogonalScvf.outsideScvIdx()))
269 return innerTransportingVelocity; // fallback
270
271 const auto outerTransportingVelocity = elemVolVars[orthogonalScvf.outsideScvIdx()].velocity();
272
273 // if the orthogonal scvf lies on a boundary and if there are outside volvars, we assume that these come from a Dirichlet condition
274 if (orthogonalScvf.boundary())
275 return outerTransportingVelocity;
276
277 static const bool useOldScheme = getParam<bool>("FreeFlow.UseOldTransportingVelocity", true); // TODO how to deprecate?
278 if (useOldScheme)
279 return innerTransportingVelocity;
280 else
281 {
282 // average the transporting velocity by weighting with the scv volumes
283 const auto insideVolume = fvGeometry.scv(orthogonalScvf.insideScvIdx()).volume();
284 const auto outsideVolume = fvGeometry.scv(orthogonalScvf.outsideScvIdx()).volume();
285 const auto outerTransportingVelocity = elemVolVars[orthogonalScvf.outsideScvIdx()].velocity();
286 return (insideVolume*innerTransportingVelocity + outsideVolume*outerTransportingVelocity) / (insideVolume + outsideVolume);
287 }
288 }();
289
290 // Do not use upwinding here but directly take the slip velocity located on the boundary. Upwinding with a weight of 0.5
291 // would actually prevent second order grid convergence.
292 const auto rho = problem.insideAndOutsideDensity(fvGeometry.element(), fvGeometry, scvf);
293 const auto transportedMomentum = slipVelocity * rho.second;
294
295 flux[scv.dofAxis()] += transportingVelocity * transportedMomentum * scvf.directionSign();
296 }
297 }
298 else if (scv.boundary() && problem.onSlipBoundary(fvGeometry, fvGeometry.frontalScvfOnBoundary(scv)))
299 {
300 /* *
301 * ---------#######* x dof position ***** porous boundary at right side
302 * | || |*
303 * | || |* velI -- element
304 * | || scv x~~~~>
305 * | || |* O position at which gradient is evaluated (integration point)
306 * | velJ ^ |*^
307 * -------|-######O*| v_slip || and # staggered half-control-volume (own element)
308 * | |*
309 * | neighbor |*
310 * | element ~~~~> velI outside (Does not exist if lower later lateral scvf lies on non-Dirichlet
311 * | |* boundary. In that case, use the given tangentialVelocityGradient.)
312 * | |*
313 * ----------------* :: staggered half-control-volume (neighbor element)
314 * ^
315 * | ^
316 * slip gradient | | ^
317 * | | |
318 *
319 */
320 const Scalar velJ = elemVolVars[orthogonalScvf.insideScvIdx()].velocity();
321
322 // viscous terms
323 const Scalar mu = problem.effectiveViscosity(fvGeometry.element(), fvGeometry, scvf);
324 const Scalar distance = (fvGeometry.scv(orthogonalScvf.insideScvIdx()).dofPosition()- scvf.ipGlobal()).two_norm();
325
326 const Scalar velGradIJ = [&]
327 {
328 if (elemVolVars.hasVolVars(scvf.outsideScvIdx()))
329 return StaggeredVelocityGradients::velocityGradIJ(fvGeometry, scvf, elemVolVars);
330 else
331 return tangentialVelocityGradient;
332 }();
333
334 const Scalar slipVelocity = problem.beaversJosephVelocity(fvGeometry, orthogonalScvf, elemVolVars, velGradIJ)[scvf.normalAxis()]; // TODO rename to slipVelocity
335 const Scalar velGradJI = (slipVelocity - velJ) / distance * orthogonalScvf.directionSign();
336
337 flux[scv.dofAxis()] -= (mu * (velGradIJ + velGradJI))*scvf.directionSign();
338
339 // advective terms
340 if (problem.enableInertiaTerms())
341 {
342 // transporting velocity corresponds to velJ
343 const auto transportingVelocity = slipVelocity;
344
345 // if the scvf lies on a boundary and if there are outside volvars, we assume that these come from a Dirichlet condition
346 if (scvf.boundary() && elemVolVars.hasVolVars(scvf.outsideScvIdx()))
347 {
348 flux[scv.dofAxis()] += problem.density(fvGeometry.element(), scv)
349 * elemVolVars[scvf.outsideScvIdx()].velocity() * transportingVelocity * scvf.directionSign(); // TODO revise density
350 return flux;
351 }
352
353 const auto innerVelocity = elemVolVars[scvf.insideScvIdx()].velocity();
354 const auto outerVelocity = [&]
355 {
356 if (!elemVolVars.hasVolVars(scvf.outsideScvIdx()))
357 return innerVelocity; // fallback
358 else
359 return elemVolVars[scvf.outsideScvIdx()].velocity();
360 }();
361
362 const auto rho = problem.insideAndOutsideDensity(fvGeometry.element(), fvGeometry, scvf);
363 const bool selfIsUpstream = scvf.directionSign() == sign(transportingVelocity);
364
365 const auto insideMomentum = innerVelocity * rho.first;
366 const auto outsideMomentum = outerVelocity * rho.second;
367
368 static const auto upwindWeight = getParamFromGroup<Scalar>(problem.paramGroup(), "Flux.UpwindWeight");
369
370 const auto transportedMomentum = selfIsUpstream ? (upwindWeight * insideMomentum + (1.0 - upwindWeight) * outsideMomentum)
371 : (upwindWeight * outsideMomentum + (1.0 - upwindWeight) * insideMomentum);
372
373 flux[scv.dofAxis()] += transportingVelocity * transportedMomentum * scvf.directionSign();
374 }
375 }
376
377 return flux;
378 }
379};
380
381} // end namespace Dumux
382
383#endif
static auto velocityGradJI(const FVElementGeometry &fvGeometry, const typename FVElementGeometry::SubControlVolumeFace &scvf, const ElemVolVars &elemVolVars)
Returns the velocity gradient in line with our current scvf.
Definition: momentum/velocitygradients.hh:222
static auto velocityGradIJ(const FVElementGeometry &fvGeometry, const typename FVElementGeometry::SubControlVolumeFace &scvf, const ElemVolVars &elemVolVars)
Returns the velocity gradient perpendicular to the orientation of our current scvf.
Definition: momentum/velocitygradients.hh:181
constexpr int sign(const ValueType &value) noexcept
Sign or signum function.
Definition: math.hh:629
static ctype distance(const Dune::FieldVector< ctype, dimWorld > &a, const Dune::FieldVector< ctype, dimWorld > &b)
Compute the shortest distance between two points.
Definition: distance.hh:282
auto volume(const Geometry &geo, unsigned int integrationOrder=4)
The volume of a given geometry.
Definition: volume.hh:159
Define some often used mathematical functions.
The available discretization methods in Dumux.
constexpr FCStaggered fcstaggered
Definition: method.hh:151
std::string pressure(int phaseIdx) noexcept
I/O name of pressure for multiphase systems.
Definition: name.hh:22
Definition: adapt.hh:17
The infrastructure to retrieve run-time parameters from Dune::ParameterTrees.
Struct containing flux helper functions to be used in the momentum problem's Neumann function.
Definition: fluxhelper.hh:28
static auto slipVelocityMomentumFlux(const Problem &problem, const FVElementGeometry &fvGeometry, const typename FVElementGeometry::SubControlVolumeFace &scvf, const ElementVolumeVariables &elemVolVars, const ElementFluxVariablesCache &elemFluxVarsCache, const TangentialVelocityGradient &tangentialVelocityGradient=TangentialVelocityGradient(0.0))
Returns the momentum flux for a boundary with a slip condition.
Definition: fluxhelper.hh:199
static auto fixedPressureMomentumFlux(const Problem &problem, const FVElementGeometry &fvGeometry, const typename FVElementGeometry::SubControlVolumeFace &scvf, const ElementVolumeVariables &elemVolVars, const ElementFluxVariablesCache &elemFluxVarsCache, const Scalar pressure, const bool zeroNormalVelocityGradient=true)
Returns the momentum flux a fixed-pressure boundary.
Definition: fluxhelper.hh:44