forked from idaholab/moose
/
MathFVUtils.h
662 lines (610 loc) · 25 KB
/
MathFVUtils.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
//* This file is part of the MOOSE framework
//* https://www.mooseframework.org
//*
//* All rights reserved, see COPYRIGHT for full restrictions
//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
//*
//* Licensed under LGPL 2.1, please see LICENSE for details
//* https://www.gnu.org/licenses/lgpl-2.1.html
#pragma once
#include "MooseError.h"
#include "FaceInfo.h"
#include "Limiter.h"
#include "MathUtils.h"
#include "MooseFunctor.h"
#include "libmesh/compare_types.h"
#include "libmesh/elem.h"
#include <tuple>
template <typename>
class MooseVariableFV;
namespace Moose
{
namespace FV
{
/**
* This creates a structure with info on : an element, \p FaceInfo, skewness correction and
* subdomain ID. The element returned will correspond to the method argument. The \p FaceInfo part
* of the structure will simply correspond to the current \p _face_info. The subdomain ID part of
* the structure will correspond to the subdomain ID of the method's element argument except in the
* case in which that subdomain ID does not correspond to a subdomain ID that the \p obj is defined
* on. In that case the subdomain ID of the structure will correspond to the subdomain ID of the
* element across the face, on which the \p obj *is* defined
*/
template <typename SubdomainRestrictable>
ElemFromFaceArg
makeSidedFace(const SubdomainRestrictable & obj,
const Elem * const elem,
const FaceInfo & fi,
const bool correct_skewness = false)
{
if (elem && obj.hasBlocks(elem->subdomain_id()))
return {elem, &fi, correct_skewness, correct_skewness, elem->subdomain_id()};
else
{
const Elem * const elem_across = (elem == &fi.elem()) ? fi.neighborPtr() : &fi.elem();
mooseAssert(elem_across && obj.hasBlocks(elem_across->subdomain_id()),
"How are there no elements with subs on here!");
return {elem, &fi, correct_skewness, correct_skewness, elem_across->subdomain_id()};
}
}
/**
* @return the value of \p makeSidedFace called with the face info element
*/
template <typename SubdomainRestrictable>
ElemFromFaceArg
elemFromFace(const SubdomainRestrictable & obj,
const FaceInfo & fi,
const bool correct_skewness = false)
{
return makeSidedFace(obj, &fi.elem(), fi, correct_skewness);
}
/**
* @return the value of \p makeSidedFace called with the face info neighbor
*/
template <typename SubdomainRestrictable>
ElemFromFaceArg
neighborFromFace(const SubdomainRestrictable & obj,
const FaceInfo & fi,
const bool correct_skewness = false)
{
return makeSidedFace(obj, fi.neighborPtr(), fi, correct_skewness);
}
/**
* Determine the subdomain ID pair that should be used when creating a face argument for a
* functor. As explained in the doxygen for \p makeSidedFace these
* subdomain IDs do not simply correspond to the subdomain IDs of the face information element pair;
* they must respect the block restriction of the \p obj
*/
template <typename SubdomainRestrictable>
std::pair<SubdomainID, SubdomainID>
faceArgSubdomains(const SubdomainRestrictable & obj, const FaceInfo & fi)
{
return std::make_pair(makeSidedFace(obj, &fi.elem(), fi).sub_id,
makeSidedFace(obj, fi.neighborPtr(), fi).sub_id);
}
inline FaceArg
makeFace(const FaceInfo & fi,
const LimiterType limiter_type,
const bool elem_is_upwind,
const std::pair<SubdomainID, SubdomainID> & subs,
const bool skewness_correction = false,
const bool apply_gradient_correction = false)
{
return {&fi,
limiter_type,
elem_is_upwind,
skewness_correction,
apply_gradient_correction,
subs.first,
subs.second};
}
/**
* Make a functor face argument with a central differencing limiter, e.g. compose a face argument
* that will tell functors to perform (possibly skew-corrected) linear interpolations from cell
* center values to faces
* @param fi the face information
* @param subs the two subdomains that should go into the face argument. The first member of this
* pair will be the "element" subdomain id and the second member of the pair will be the "neighbor"
* subdomain id
* @param skewness_correction whether to apply skew correction weights
* @param Whether to apply the face gradient when computing a skew corrected face value. A true
* value for this parameter in conjunction with a false value for \p skewness_correction parameter
* does not make sense. A false value for this parameter in conjunction with a true value for \p
* skewness_correction should only be set by someone who really knows what they're doing
* @return a face argument for functors
*/
inline FaceArg
makeCDFace(const FaceInfo & fi,
const std::pair<SubdomainID, SubdomainID> & subs,
const bool skewness_correction = false,
const bool apply_gradient_correction = false)
{
return makeFace(fi,
LimiterType::CentralDifference,
true,
subs,
skewness_correction,
apply_gradient_correction);
}
/**
* Make a functor face argument with a central differencing limiter, e.g. compose a face argument
* that will tell functors to perform (possibly skew-corrected) linear interpolations from cell
* center values to faces. The subdomain ids for the face argument will be created from the face
* information object's \p elem() and \p neighbor() subdomain ids. If the latter is null, then an
* invalid subdomain ID will be used
* @param fi the face information
* @param skewness_correction whether to apply skew correction weights
* @param Whether to apply the face gradient when computing a skew corrected face value. A true
* value for this parameter in conjunction with a false value for \p skewness_correction parameter
* does not make sense. A false value for this parameter in conjunction with a true value for \p
* skewness_correction should only be set by someone who really knows what they're doing
* @return a face argument for functors
*/
inline FaceArg
makeCDFace(const FaceInfo & fi,
const bool skewness_correction = false,
const bool apply_gradient_correction = false)
{
return makeCDFace(
fi,
std::make_pair(fi.elem().subdomain_id(),
fi.neighborPtr() ? fi.neighbor().subdomain_id() : Moose::INVALID_BLOCK_ID),
skewness_correction,
apply_gradient_correction);
}
/// This codifies a set of available ways to interpolate with elem+neighbor
/// solution information to calculate values (e.g. solution, material
/// properties, etc.) at the face (centroid). These methods are used in the
/// class's interpolate functions. Some interpolation methods are only meant
/// to be used with advective terms (e.g. upwind), others are more
/// generically applicable.
enum class InterpMethod
{
/// (elem+neighbor)/2
Average,
/// (gc*elem+(1-gc)*neighbor)+gradient*(rf-rf')
SkewCorrectedAverage,
/// Extended stencil using the vertex values
VertexBased,
/// weighted
Upwind,
// Rhie-Chow
RhieChow,
VanLeer,
MinMod,
SOU,
QUICK
};
/**
* Produce the interpolation coefficients in the equation:
*
* \phi_f = c_1 * \phi_{F1} + c_2 * \phi_{F2}
*
* A couple of examples: if we are doing an average interpolation with
* an orthogonal regular grid, then the pair will be (0.5, 0.5). If we are doing an
* upwind interpolation with the velocity facing outward from the F1 element,
* then the pair will be (1.0, 0.0).
*
* @param m The interpolation method
* @param fi The face information
* @param one_is_elem Whether fi.elem() == F1
* @param advector The advecting velocity. Not relevant for an Average interpolation
* @return a pair where the first Real is c_1 and the second Real is c_2
*/
template <typename Vector = RealVectorValue>
std::pair<Real, Real>
interpCoeffs(const InterpMethod m,
const FaceInfo & fi,
const bool one_is_elem,
const Vector advector = Vector())
{
switch (m)
{
case InterpMethod::Average:
{
if (one_is_elem)
return std::make_pair(fi.gC(), 1. - fi.gC());
else
return std::make_pair(1. - fi.gC(), fi.gC());
}
case InterpMethod::SkewCorrectedAverage:
{
if (one_is_elem)
return std::make_pair(fi.gCSkewed(), 1. - fi.gCSkewed());
else
return std::make_pair(1. - fi.gCSkewed(), fi.gCSkewed());
}
case InterpMethod::Upwind:
{
if ((advector * fi.normal() > 0) == one_is_elem)
return std::make_pair(1., 0.);
else
return std::make_pair(0., 1.);
}
default:
mooseError("Unrecognized interpolation method");
}
}
/**
* A simple linear interpolation of values between cell centers to a cell face. The \p one_is_elem
* parameter indicates whether value1 corresponds to the FaceInfo elem value; else it corresponds to
* the FaceInfo neighbor value
*/
template <typename T, typename T2>
typename libMesh::CompareTypes<T, T2>::supertype
linearInterpolation(const T & value1,
const T2 & value2,
const FaceInfo & fi,
const bool one_is_elem,
const InterpMethod interp_method = InterpMethod::Average)
{
mooseAssert(interp_method == InterpMethod::Average ||
interp_method == InterpMethod::SkewCorrectedAverage,
"The selected interpolation function only works with average or skewness-corrected "
"average options!");
const auto coeffs = interpCoeffs(interp_method, fi, one_is_elem);
return coeffs.first * value1 + coeffs.second * value2;
}
/**
* Linear interpolation with skewness correction using the face gradient.
* See more info in Moukalled Chapter 9. The correction involves a first order
* Taylor expansion around the intersection of the cell face and the line
* connecting the two cell centers.
*/
template <typename T, typename T2, typename T3>
typename libMesh::CompareTypes<T, T2>::supertype
skewCorrectedLinearInterpolation(const T & value1,
const T2 & value2,
const T3 & face_gradient,
const FaceInfo & fi,
const bool one_is_elem)
{
const auto coeffs = interpCoeffs(InterpMethod::SkewCorrectedAverage, fi, one_is_elem);
auto value = (coeffs.first * value1 + coeffs.second * value2) +
face_gradient * (fi.faceCentroid() - fi.rIntersection());
return value;
}
/// Provides interpolation of face values for non-advection-specific purposes (although it can/will
/// still be used by advective kernels sometimes). The interpolated value is stored in result.
/// This should be called when a face value needs to be computed from two neighboring
/// cells/elements. value1 and value2 represent the cell property/values from which to compute the
/// face value. The \p one_is_elem parameter indicates whether value1 corresponds to the FaceInfo
/// elem value; else it corresponds to the FaceInfo neighbor value
template <typename T, typename T2, typename T3>
void
interpolate(InterpMethod m,
T & result,
const T2 & value1,
const T3 & value2,
const FaceInfo & fi,
const bool one_is_elem)
{
switch (m)
{
case InterpMethod::Average:
result = linearInterpolation(value1, value2, fi, one_is_elem);
break;
case InterpMethod::SkewCorrectedAverage:
{
// We create a zero gradient to ensure that the skewness-corrected
// weights are used, but no correction is applied. This will change when the
// old weights are replaced by the ones used with skewness-correction
typename TensorTools::IncrementRank<T2>::type surface_gradient;
result = skewCorrectedLinearInterpolation(value1, value2, surface_gradient, fi, one_is_elem);
break;
}
default:
mooseError("unsupported interpolation method for FVFaceInterface::interpolate");
}
}
/**
* perform a possibly skew-corrected linear interpolation by evaluating the supplied functor with
* the provided functor face argument
*/
template <typename T>
T
linearInterpolation(const FunctorBase<T> & functor, const FaceArg & face)
{
mooseAssert(face.limiter_type == LimiterType::CentralDifference,
"this interpolation method is meant for linear interpolations");
mooseAssert(face.fi,
"We must have a non-null face_info in order to prepare our ElemFromFace tuples");
const auto elem_from_face = face.elemFromFace();
const auto neighbor_from_face = face.neighborFromFace();
if (face.correct_skewness)
{
typedef typename TensorTools::IncrementRank<T>::type GradientType;
// This condition ensures that the recursive algorithm (face_center->
// face_gradient -> cell_gradient -> face_center -> ...) terminates after
// one loop. It is hardcoded to one loop at this point since it yields
// 2nd order accuracy on skewed meshes with the minimum additional effort.
FaceArg new_face(face);
new_face.apply_gradient_to_skewness = false;
const auto surface_gradient =
face.apply_gradient_to_skewness ? functor.gradient(new_face) : GradientType(0);
return skewCorrectedLinearInterpolation(
functor(elem_from_face), functor(neighbor_from_face), surface_gradient, *face.fi, true);
}
else
return linearInterpolation(
functor(elem_from_face), functor(neighbor_from_face), *face.fi, true);
}
/**
* Computes the product of the advected and the advector based on the given interpolation method
*/
template <typename T1,
typename T2,
typename T3,
template <typename>
class Vector1,
template <typename>
class Vector2>
void
interpolate(InterpMethod m,
Vector1<T1> & result,
const T2 & fi_elem_advected,
const T2 & fi_neighbor_advected,
const Vector2<T3> & fi_elem_advector,
const Vector2<T3> & fi_neighbor_advector,
const FaceInfo & fi)
{
switch (m)
{
case InterpMethod::Average:
result = linearInterpolation(fi_elem_advected * fi_elem_advector,
fi_neighbor_advected * fi_neighbor_advector,
fi,
true);
break;
case InterpMethod::Upwind:
{
const auto face_advector = linearInterpolation(MetaPhysicL::raw_value(fi_elem_advector),
MetaPhysicL::raw_value(fi_neighbor_advector),
fi,
true);
Real elem_coeff, neighbor_coeff;
if (face_advector * fi.normal() > 0)
elem_coeff = 1, neighbor_coeff = 0;
else
elem_coeff = 0, neighbor_coeff = 1;
result = elem_coeff * fi_elem_advected * fi_elem_advector +
neighbor_coeff * fi_neighbor_advected * fi_neighbor_advector;
break;
}
default:
mooseError("unsupported interpolation method for FVFaceInterface::interpolate");
}
}
/// Provides interpolation of face values for advective flux kernels. This should be called by
/// advective kernels when a face value is needed from two neighboring cells/elements. The
/// interpolated value is stored in result. value1 and value2 represent the two neighboring advected
/// cell property/values. advector represents the vector quantity at the face that is doing the
/// advecting (e.g. the flow velocity at the face); this value often will have been computed using a
/// call to the non-advective interpolate function. The \p one_is_elem parameter indicates whether
/// value1 corresponds to the FaceInfo elem value; else it corresponds to the FaceInfo neighbor
/// value
template <typename T, typename T2, typename T3, typename Vector>
void
interpolate(InterpMethod m,
T & result,
const T2 & value1,
const T3 & value2,
const Vector & advector,
const FaceInfo & fi,
const bool one_is_elem)
{
const auto coeffs = interpCoeffs(m, fi, one_is_elem, advector);
result = coeffs.first * value1 + coeffs.second * value2;
}
/// Calculates and returns "grad_u dot normal" on the face to be used for
/// diffusive terms. If using any cross-diffusion corrections, etc. all
/// those calculations should be handled appropriately by this function.
template <typename T, typename T2>
ADReal gradUDotNormal(const T & elem_value,
const T2 & neighbor_value,
const FaceInfo & face_info,
const MooseVariableFV<Real> & fv_var,
bool correct_skewness = false);
/**
* From Moukalled 12.30
*
* r_f = (phiC - phiU) / (phiD - phiC)
*
* However, this formula is only clear on grids where upwind locations can be readily determined,
* which is not the case for unstructured meshes. So we leverage a virtual upwind location and
* Moukalled 12.65
*
* phiD - phiU = 2 * grad(phi)_C * dCD ->
* phiU = phiD - 2 * grad(phi)_C * dCD
*
* Combining the two equations and performing some algebraic manipulation yields this equation for
* r_f:
*
* r_f = 2 * grad(phi)_C * dCD / (phiD - phiC) - 1
*
* This equation is clearly asymmetric considering the face between C and D because of the
* subscript on grad(phi). Hence this method can be thought of as constructing an r associated with
* the C side of the face
*/
template <typename Scalar, typename Vector>
Scalar
rF(const Scalar & phiC, const Scalar & phiD, const Vector & gradC, const RealVectorValue & dCD)
{
if ((phiD - phiC) == 0)
return 1e6 * MathUtils::sign(gradC * dCD) + 0 * (gradC * dCD + phiD + phiC);
return 2. * gradC * dCD / (phiD - phiC) - 1.;
}
/**
* Produce the interpolation coefficients in the equation:
*
* \phi_f = c_upwind * \phi_{upwind} + c_downwind * \phi_{downwind}
*
* A couple of examples: if we are doing an average interpolation with
* an orthogonal regular grid, then the pair will be (0.5, 0.5). If we are doing an
* upwind interpolation then the pair will be (1.0, 0.0).
*
* @return a pair where the first Real is c_upwind and the second Real is c_downwind
*/
template <typename T>
std::pair<T, T>
interpCoeffs(const Limiter<T> & limiter,
const T & phi_upwind,
const T & phi_downwind,
const VectorValue<T> * const grad_phi_upwind,
const FaceInfo & fi,
const bool fi_elem_is_upwind)
{
// Using beta, w_f, g nomenclature from Greenshields
const auto beta = limiter(phi_upwind,
phi_downwind,
grad_phi_upwind,
fi_elem_is_upwind ? fi.dCF() : RealVectorValue(-fi.dCF()));
const auto w_f = fi_elem_is_upwind ? fi.gC() : (1. - fi.gC());
const auto g = beta * (1. - w_f);
return std::make_pair(1. - g, g);
}
/**
* Interpolates with a limiter
*/
template <typename Scalar,
typename Vector,
typename Enable = typename std::enable_if<ScalarTraits<Scalar>::value>::type>
Scalar
interpolate(const Limiter<Scalar> & limiter,
const Scalar & phi_upwind,
const Scalar & phi_downwind,
const Vector * const grad_phi_upwind,
const FaceInfo & fi,
const bool fi_elem_is_upwind)
{
auto pr = interpCoeffs(limiter, phi_upwind, phi_downwind, grad_phi_upwind, fi, fi_elem_is_upwind);
return pr.first * phi_upwind + pr.second * phi_downwind;
}
/**
* Vector overload
*/
template <typename Limiter, typename T, typename Tensor>
VectorValue<T>
interpolate(const Limiter & limiter,
const TypeVector<T> & phi_upwind,
const TypeVector<T> & phi_downwind,
const Tensor * const /*grad_phi_upwind*/,
const FaceInfo & fi,
const bool fi_elem_is_upwind)
{
mooseAssert(limiter.constant(),
"Non-constant limiters are not currently supported in the vector overload of the "
"limited interpolate method");
static const VectorValue<T> example_gradient(0);
VectorValue<T> ret;
for (const auto i : make_range(unsigned(LIBMESH_DIM)))
ret(i) = interpolate(
limiter, phi_upwind(i), phi_downwind(i), &example_gradient, fi, fi_elem_is_upwind);
return ret;
}
/**
* Interpolates with a limiter and a face argument
* @return a pair of pairs. The first pair corresponds to the interpolation coefficients with the
* first corresponding to the face information element and the second corresponding to the face
* information neighbor. This pair should sum to unity. The second pair corresponds to the face
* information functor element value and neighbor
*/
template <typename T, typename Enable = typename std::enable_if<ScalarTraits<T>::value>::type>
std::pair<std::pair<T, T>, std::pair<T, T>>
interpCoeffsAndAdvected(const FunctorBase<T> & functor, const FaceArg & face)
{
typedef typename FunctorBase<T>::GradientType GradientType;
static const GradientType zero(0);
mooseAssert(face.fi, "this must be non-null");
const auto limiter = Limiter<typename LimiterValueType<T>::value_type>::build(face.limiter_type);
const auto upwind_arg = face.elem_is_upwind ? face.elemFromFace() : face.neighborFromFace();
const auto downwind_arg = face.elem_is_upwind ? face.neighborFromFace() : face.elemFromFace();
auto phi_upwind = functor(upwind_arg);
auto phi_downwind = functor(downwind_arg);
std::pair<T, T> interp_coeffs;
if (face.limiter_type == LimiterType::Upwind ||
face.limiter_type == LimiterType::CentralDifference)
interp_coeffs =
interpCoeffs(*limiter, phi_upwind, phi_downwind, &zero, *face.fi, face.elem_is_upwind);
else
{
const auto grad_phi_upwind = functor.gradient(upwind_arg);
interp_coeffs = interpCoeffs(
*limiter, phi_upwind, phi_downwind, &grad_phi_upwind, *face.fi, face.elem_is_upwind);
}
if (face.elem_is_upwind)
return std::make_pair(std::move(interp_coeffs),
std::make_pair(std::move(phi_upwind), std::move(phi_downwind)));
else
return std::make_pair(
std::make_pair(std::move(interp_coeffs.second), std::move(interp_coeffs.first)),
std::make_pair(std::move(phi_downwind), std::move(phi_upwind)));
}
template <typename T, typename Enable = typename std::enable_if<ScalarTraits<T>::value>::type>
T
interpolate(const FunctorBase<T> & functor, const FaceArg & face)
{
const auto [interp_coeffs, advected] = interpCoeffsAndAdvected(functor, face);
return interp_coeffs.first * advected.first + interp_coeffs.second * advected.second;
}
template <typename T>
VectorValue<T>
interpolate(const FunctorBase<VectorValue<T>> & functor, const FaceArg & face)
{
static const VectorValue<T> grad_zero(0);
mooseAssert(face.fi, "this must be non-null");
const auto limiter = Limiter<typename LimiterValueType<T>::value_type>::build(face.limiter_type);
const auto upwind_arg = face.elem_is_upwind ? face.elemFromFace() : face.neighborFromFace();
const auto downwind_arg = face.elem_is_upwind ? face.neighborFromFace() : face.elemFromFace();
auto phi_upwind = functor(upwind_arg);
auto phi_downwind = functor(downwind_arg);
VectorValue<T> ret;
T coeff_upwind, coeff_downwind;
if (face.limiter_type == LimiterType::Upwind ||
face.limiter_type == LimiterType::CentralDifference)
{
for (unsigned int i = 0; i < LIBMESH_DIM; ++i)
{
const auto &component_upwind = phi_upwind(i), component_downwind = phi_downwind(i);
std::tie(coeff_upwind, coeff_downwind) = interpCoeffs(*limiter,
component_upwind,
component_downwind,
&grad_zero,
*face.fi,
face.elem_is_upwind);
ret(i) = coeff_upwind * component_upwind + coeff_downwind * component_downwind;
}
}
else
{
const auto grad_phi_upwind = functor.gradient(upwind_arg);
for (unsigned int i = 0; i < LIBMESH_DIM; ++i)
{
const auto &component_upwind = phi_upwind(i), component_downwind = phi_downwind(i);
const VectorValue<T> grad = grad_phi_upwind.row(i);
std::tie(coeff_upwind, coeff_downwind) = interpCoeffs(
*limiter, component_upwind, component_downwind, &grad, *face.fi, face.elem_is_upwind);
ret(i) = coeff_upwind * component_upwind + coeff_downwind * component_downwind;
}
}
return ret;
}
/**
* Return whether the supplied face is on a boundary of the \p object's execution
*/
template <typename SubdomainRestrictable>
bool
onBoundary(const SubdomainRestrictable & obj, const FaceInfo & fi)
{
const bool internal = fi.neighborPtr() && obj.hasBlocks(fi.elemSubdomainID()) &&
obj.hasBlocks(fi.neighborSubdomainID());
return !internal;
}
/**
* Determine whether the passed-in face is on the boundary of an object that lives on the provided
* subdomains. Note that if the subdomain set is empty we consider that to mean that the object has
* no block restriction and lives on the entire mesh
*/
bool onBoundary(const std::set<SubdomainID> & subs, const FaceInfo & fi);
}
}