https://mooseframework.inl.gov
Loading...
Searching...
No Matches
GreenGaussGradient.h
Go to the documentation of this file.
1//* This file is part of the MOOSE framework
2//* https://mooseframework.inl.gov
3//*
4//* All rights reserved, see COPYRIGHT for full restrictions
5//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6//*
7//* Licensed under LGPL 2.1, please see LICENSE for details
8//* https://www.gnu.org/licenses/lgpl-2.1.html
9
10#pragma once
11
12#include "MooseFunctor.h"
13#include "MathFVUtils.h"
14#include "FVUtils.h"
15#include "MooseMeshUtils.h"
18#include "libmesh/elem.h"
19
20// C++
21#include <cstring> // for "Jacobian" exception test
22
23namespace Moose
24{
25namespace FV
26{
41template <typename T,
42 typename Enable = typename std::enable_if<libMesh::ScalarTraits<T>::value>::type>
44greenGaussGradient(const ElemArg & elem_arg,
45 const StateArg & state_arg,
46 const FunctorBase<T> & functor,
47 const bool two_term_boundary_expansion,
48 const MooseMesh & mesh,
49 const bool force_green_gauss = false)
50{
51 mooseAssert(elem_arg.elem, "This should be non-null");
52 const auto coord_type = mesh.getCoordSystem(elem_arg.elem->subdomain_id());
53 const auto rz_radial_coord = mesh.getAxisymmetricRadialCoord();
54
55 const T elem_value = functor(elem_arg, state_arg);
56
57 // We'll count the number of extrapolated boundary faces (ebfs)
58 unsigned int num_ebfs = 0;
59
60 // Gradient to be returned
61 VectorValue<T> grad;
62
63 if (!elem_arg.correct_skewness || force_green_gauss) // Do Green-Gauss
64 {
65 try
66 {
67 bool volume_set = false;
68 Real volume = 0;
69
70 // If we are performing a two term Taylor expansion for extrapolated boundary faces (faces on
71 // boundaries that do not have associated Dirichlet conditions), then the element gradient
72 // depends on the boundary face value and the boundary face value depends on the element
73 // gradient, so we have a system of equations to solve. Here is the system:
74 //
75 // \nabla \phi_C - \frac{1}{V} \sum_{ebf} \phi_{ebf} \vec{S_f} =
76 // \frac{1}{V} \sum_{of} \phi_{of} \vec{S_f} eqn. 1
77 //
78 // \phi_{ebf} - \vec{d_{Cf}} \cdot \nabla \phi_C = \phi_C eqn. 2
79 //
80 // where $C$ refers to the cell centroid, $ebf$ refers to an extrapolated boundary face, $of$
81 // refers to "other faces", e.g. non-ebf faces, and $f$ is a general face. $d_{Cf}$ is the
82 // vector drawn from the element centroid to the face centroid, and $\vec{S_f}$ is the surface
83 // vector, e.g. the face area times the outward facing normal
84
85 // ebf eqns: element gradient coefficients, e.g. eqn. 2, LHS term 2 coefficient
86 std::vector<libMesh::VectorValue<Real>> ebf_grad_coeffs;
87 // ebf eqns: rhs b values. These will actually correspond to the elem_value so we can use a
88 // pointer and avoid copying. This is the RHS of eqn. 2
89 std::vector<const T *> ebf_b;
90
91 // elem grad eqns: ebf coefficients, e.g. eqn. 1, LHS term 2 coefficients
92 std::vector<libMesh::VectorValue<Real>> grad_ebf_coeffs;
93 // elem grad eqns: rhs b value, e.g. eqn. 1 RHS
94 libMesh::VectorValue<T> grad_b = 0;
95
96 auto action_functor = [&volume_set,
97 &volume,
98 &elem_value,
99 &elem_arg,
100 &num_ebfs,
101 &ebf_grad_coeffs,
102 &ebf_b,
103 &grad_ebf_coeffs,
104 &grad_b,
105 &state_arg,
106 &functor,
107 two_term_boundary_expansion,
108 coord_type,
109 rz_radial_coord](const Elem & libmesh_dbg_var(functor_elem),
110 const Elem *,
111 const FaceInfo * const fi,
112 const Point & surface_vector,
113 Real coord,
114 const bool elem_has_info)
115 {
116 mooseAssert(fi, "We need a FaceInfo for this action_functor");
117 mooseAssert(
118 elem_arg.elem == &functor_elem,
119 "Just a sanity check that the element being passed in is the one we passed out.");
120
121 if (functor.isExtrapolatedBoundaryFace(*fi, elem_arg.elem, state_arg))
122 {
123 if (two_term_boundary_expansion)
124 {
125 num_ebfs += 1;
126
127 // eqn. 2
128 ebf_grad_coeffs.push_back(-1. * (elem_has_info
129 ? (fi->faceCentroid() - fi->elemCentroid())
130 : (fi->faceCentroid() - fi->neighborCentroid())));
131 ebf_b.push_back(&elem_value);
132
133 // eqn. 1
134 grad_ebf_coeffs.push_back(-surface_vector);
135 }
136 else
137 // We are doing a one-term expansion for the extrapolated boundary faces, in which case
138 // we have no eqn. 2 and we have no second term in the LHS of eqn. 1. Instead we apply
139 // the element centroid value as the face value (one-term expansion) in the RHS of eqn.
140 // 1
141 grad_b += surface_vector * elem_value;
142 }
143 else
144 grad_b +=
145 surface_vector * functor(Moose::FaceArg{fi,
147 true,
148 elem_arg.correct_skewness,
149 elem_arg.elem,
150 nullptr},
151 state_arg);
152
153 if (!volume_set)
154 {
155 // We use the FaceInfo volumes because those values have been pre-computed and cached.
156 // An explicit call to elem->volume() here would incur unnecessary expense
157 if (elem_has_info)
158 {
160 fi->elemCentroid(), coord, coord_type, rz_radial_coord);
161 volume = fi->elemVolume() * coord;
162 }
163 else
164 {
166 fi->neighborCentroid(), coord, coord_type, rz_radial_coord);
167 volume = fi->neighborVolume() * coord;
168 }
169
170 volume_set = true;
171 }
172 };
173
175 *elem_arg.elem, mesh, action_functor, coord_type, rz_radial_coord);
176
177 mooseAssert(volume_set && volume > 0, "We should have set the volume");
178 grad_b /= volume;
179
181 {
182 mooseAssert(rz_radial_coord != libMesh::invalid_uint, "rz_radial_coord must be set");
183 grad_b(rz_radial_coord) -= elem_value / elem_arg.elem->vertex_average()(rz_radial_coord);
184 }
185
186 mooseAssert(
188 "We have not yet implemented the correct translation from gradient to divergence for "
189 "spherical coordinates yet.");
190
191 // test for simple case
192 if (num_ebfs == 0)
193 grad = grad_b;
194 else
195 {
196 // We have to solve a system
197 const unsigned int sys_dim = Moose::dim + num_ebfs;
198 DenseVector<T> x(sys_dim), b(sys_dim);
199 DenseMatrix<T> A(sys_dim, sys_dim);
200
201 // Let's make i refer to Moose::dim indices, and j refer to num_ebfs indices
202
203 // eqn. 1
204 for (const auto i : make_range(Moose::dim))
205 {
206 // LHS term 1 coeffs
207 A(i, i) = 1;
208
209 // LHS term 2 coeffs
210 for (const auto j : make_range(num_ebfs))
211 A(i, Moose::dim + j) = grad_ebf_coeffs[j](i) / volume;
212
213 // RHS
214 b(i) = grad_b(i);
215 }
216
217 // eqn. 2
218 for (const auto j : make_range(num_ebfs))
219 {
220 // LHS term 1 coeffs
221 A(Moose::dim + j, Moose::dim + j) = 1;
222
223 // LHS term 2 coeffs
224 for (const auto i : make_range(unsigned(Moose::dim)))
225 A(Moose::dim + j, i) = ebf_grad_coeffs[j](i);
226
227 // RHS
228 b(Moose::dim + j) = *ebf_b[j];
229 }
230
231 A.lu_solve(b, x);
232 // libMesh is generous about what it considers nonsingular. Let's check a little more
233 // strictly
234 if (MooseUtils::absoluteFuzzyEqual(MetaPhysicL::raw_value(A(sys_dim - 1, sys_dim - 1)), 0))
235 throw libMesh::LogicError("Matrix A is singular!");
236 for (const auto i : make_range(Moose::dim))
237 grad(i) = x(i);
238 }
239 }
240 catch (std::exception & e)
241 {
242 // Don't ignore any *real* errors; we only handle matrix
243 // singularity (LogicError in older libMesh, DegenerateMap in
244 // newer) here
245 if (!strstr(e.what(), "singular"))
246 throw;
247
248 // Retry without two-term
249 if (!two_term_boundary_expansion)
251 "I believe we should only get singular systems when two-term boundary expansion is "
252 "being used. The error thrown during the computation of the gradient: ",
253 e.what());
254
255 grad = greenGaussGradient(elem_arg, state_arg, functor, false, mesh, true);
256 }
257 }
258 else // Do Least-Squares
259 {
260 try
261 {
262
263 // The least squares method aims to find the gradient at the element centroid by minimizing
264 // the difference between the estimated and actual value differences across neighboring cells.
265 // The least squares formulation is:
266 //
267 // Minimize J = \sum_{n} [ w_n ((\nabla \phi_C \cdot \delta \vec{x}_n) - \delta \phi_n) ]^2
268 //
269 // where:
270 // - \‍(\nabla \phi_C\‍) is the gradient at the element centroid C (unknown).
271 // - \‍(\delta \vec{x}_n = \vec{x}_n - \vec{x}_C\‍) is the vector from the element centroid to
272 // neighbor \‍(n\‍).
273 // - \‍(\delta \phi_n = \phi_n - \phi_C\‍) is the difference in the scalar value between
274 // neighbor \‍(n\‍) and element \‍(C\‍).
275 // - w_n = 1/||\vec{x}_n|| is a vector of weights that is used to handle large aspect ratio
276 // cells
277 //
278 // To handle extrapolated boundary faces (faces on boundaries without Dirichlet conditions),
279 // we include additional unknowns and equations in the least squares system.
280 // For ebfs, we may not know the value \‍(\phi_n\‍), so we include it as an unknown.
281 // This results in an augmented system that simultaneously solves for the gradient and the ebf
282 // values.
283
284 // Get estimated number of faces in a cell
285 const auto ptr_range = elem_arg.elem->neighbor_ptr_range();
286 const size_t num_faces = std::distance(ptr_range.begin(), ptr_range.end());
287
288 // Lists to store position differences and value differences for least squares computation
289 std::vector<Point> delta_x_list; // List of position differences between neighbor centroids
290 // and element centroid
291 delta_x_list.reserve(num_faces);
292
293 std::vector<T>
294 delta_phi_list; // List of value differences between neighbor values and element value
295 delta_phi_list.reserve(num_faces);
296
297 // Variables to handle extrapolated boundary faces (ebfs) in the least squares method
298 std::vector<Point> delta_x_ebf_list; // Position differences for ebfs
299 delta_phi_list.reserve(num_faces);
300
301 std::vector<VectorValue<Real>>
302 ebf_grad_coeffs; // Coefficients for the gradient in ebf equations
303 delta_phi_list.reserve(num_faces);
304
305 std::vector<const T *> ebf_b; // RHS values for ebf equations (pointers to avoid copying)
306 delta_phi_list.reserve(num_faces);
307
308 unsigned int num_ebfs = 0; // Number of extrapolated boundary faces
309
310 // Action functor to collect data from each face of the element
311 auto action_functor = [&elem_value,
312 &elem_arg,
313 &num_ebfs,
314 &ebf_grad_coeffs,
315 &ebf_b,
316 &delta_x_list,
317 &delta_phi_list,
318 &delta_x_ebf_list,
319 &state_arg,
320 &functor,
321 two_term_boundary_expansion](const Elem & libmesh_dbg_var(loc_elem),
322 const Elem * loc_neigh,
323 const FaceInfo * const fi,
324 const Point & /*surface_vector*/,
325 Real /*coord*/,
326 const bool elem_has_info)
327 {
328 mooseAssert(fi, "We need a FaceInfo for this action_functor");
329 mooseAssert(
330 elem_arg.elem == &loc_elem,
331 "Just a sanity check that the element being passed in is the one we passed out.");
332
333 if (functor.isExtrapolatedBoundaryFace(*fi, elem_arg.elem, state_arg))
334 {
335 // Extrapolated Boundary Face (ebf)
336 const Point delta_x = elem_has_info ? (fi->faceCentroid() - fi->elemCentroid())
337 : (fi->faceCentroid() - fi->neighborCentroid());
338 delta_x_list.push_back(delta_x);
339
340 T delta_phi;
341
342 if (two_term_boundary_expansion)
343 {
344 // Two-term expansion: include ebf values as unknowns in the augmented system
345 num_ebfs += 1;
346
347 // Coefficient for the gradient in the ebf equation
348 ebf_grad_coeffs.push_back(-delta_x);
349 // RHS value for the ebf equation
350 ebf_b.push_back(&elem_value);
351
352 delta_phi = -elem_value;
353 delta_x_ebf_list.push_back(delta_x);
354 }
355 else
356 {
357 // One-term expansion: assume zero difference across the boundary (\delta \phi = 0)
358 delta_phi = T(0);
359 }
360 delta_phi_list.push_back(delta_phi);
361 }
362 else
363 {
364 // Internal Face or Boundary Face with Dirichlet condition
365 Point delta_x;
366 T neighbor_value;
367 if (functor.isInternalFace(*fi))
368 {
369 // Internal face with a neighboring element
370 delta_x = loc_neigh->vertex_average() - elem_arg.elem->vertex_average();
371
372 const ElemArg neighbor_arg = {loc_neigh, elem_arg.correct_skewness};
373 neighbor_value = functor(neighbor_arg, state_arg);
374 }
375 else
376 {
377 // Boundary face with Dirichlet condition
378 delta_x = elem_has_info ? (fi->faceCentroid() - fi->elemCentroid())
379 : (fi->faceCentroid() - fi->neighborCentroid());
380 neighbor_value = functor(Moose::FaceArg{fi,
382 true,
383 elem_arg.correct_skewness,
384 elem_arg.elem,
385 nullptr},
386 state_arg);
387 }
388
389 delta_x_list.push_back(delta_x);
390 delta_phi_list.push_back(neighbor_value - elem_value);
391 }
392 };
393
394 // Loop over element faces and apply the action_functor
396 *elem_arg.elem, mesh, action_functor, coord_type, rz_radial_coord);
397
398 // Compute Least Squares gradient
399 const unsigned int n = delta_x_list.size();
400 mooseAssert(n >= dim, "Not enough neighbors to perform least squares");
401
402 DenseMatrix<T> ATA(dim, dim);
403 DenseVector<T> ATb(dim);
404
405 // Assemble the normal equations for the least squares method
406 // ATA = \sum_n (\delta \vec{x}_n^T * \delta \vec{x}_n)
407 // ATb = \sum_n (\delta \vec{x}_n^T * \delta \phi_n)
408 ATA.zero();
409 ATb.zero();
410
411 for (unsigned int i = 0; i < n; ++i)
412 {
413 const Point & delta_x = delta_x_list[i];
414 const T & delta_phi = delta_phi_list[i];
415
416 for (unsigned int d1 = 0; d1 < dim; ++d1)
417 {
418 const Real dx_d1 = delta_x(d1);
419 const Real dx_norm = delta_x.norm();
420 const Real dx_d1_norm = dx_d1 / dx_norm;
421
422 for (unsigned int d2 = 0; d2 < dim; ++d2)
423 {
424 const Real dx_d2_norm = delta_x(d2) / dx_norm;
425 ATA(d1, d2) += dx_d1_norm * dx_d2_norm;
426 }
427
428 ATb(d1) += dx_d1_norm * delta_phi / dx_norm;
429 }
430 }
431
432 // Add regularization to ATA to prevent singularity
433 T epsilon = 1e-12; // Small regularization parameter
434 for (unsigned int d = 0; d < dim; ++d)
435 {
436 ATA(d, d) += epsilon;
437 }
438
439 if (num_ebfs == 0)
440 {
441 // Solve and store the least squares gradient
442 DenseVector<T> grad_ls(dim);
443 ATA.lu_solve(ATb, grad_ls);
444
445 // Store the least squares gradient
446 for (unsigned int d = 0; d < dim; ++d)
447 {
448 grad(d) = grad_ls(d);
449 }
450 }
451 else
452 {
453 // We have to solve a system of equations
454 const unsigned int sys_dim = Moose::dim + num_ebfs;
455 DenseVector<T> x(sys_dim), b(sys_dim);
456 DenseMatrix<T> A(sys_dim, sys_dim);
457
458 // Let's make i refer to Moose::dim indices, and j refer to num_ebfs indices
459
460 // eqn. 1: Element gradient equations
461 for (unsigned int d1 = 0; d1 < dim; ++d1)
462 {
463 // LHS term 1 coefficients (gradient components)
464 for (unsigned int d2 = 0; d2 < dim; ++d2)
465 A(d1, d2) = ATA(d1, d2);
466
467 // RHS
468 b(d1) = ATb(d1);
469 }
470
471 // LHS term 2 coefficients (ebf contributions)
472 for (const auto i : make_range(num_ebfs))
473 {
474 const Point & delta_x = delta_x_ebf_list[i];
475 for (unsigned int d1 = 0; d1 < dim; ++d1)
476 {
477 const Real dx_d1 = delta_x(d1);
478 A(d1, Moose::dim + i) -= dx_d1 / delta_x.norm() / delta_x.norm();
479 }
480 }
481
482 // eqn. 2: Extrapolated boundary face equations
483 for (const auto j : make_range(num_ebfs))
484 {
485 // LHS term 1 coefficients (ebf values)
486 A(Moose::dim + j, Moose::dim + j) = 1;
487
488 // LHS term 2 coefficients (gradient components)
489 for (const auto i : make_range(unsigned(Moose::dim)))
490 A(Moose::dim + j, i) = ebf_grad_coeffs[j](i);
491
492 // RHS
493 b(Moose::dim + j) = *ebf_b[j];
494 }
495
496 // Solve the system
497 A.lu_solve(b, x);
498
499 // Check for singularity
500 if (MooseUtils::absoluteFuzzyEqual(MetaPhysicL::raw_value(A(sys_dim - 1, sys_dim - 1)), 0))
501 throw libMesh::LogicError("Matrix A is singular!");
502
503 // Extract the gradient components
504 for (const auto i : make_range(Moose::dim))
505 grad(i) = x(i);
506 }
507
508 mooseAssert(
510 "We have not yet implemented the correct translation from gradient to divergence for "
511 "spherical coordinates yet.");
512 }
513 catch (std::exception & e)
514 {
515 // Don't ignore any *real* errors; we only handle matrix
516 // singularity (LogicError in older libMesh, DegenerateMap in
517 // newer) here
518 if (!strstr(e.what(), "singular"))
519 throw;
520
521 // Log warning and default to simple green Gauss
523 "Singular matrix encountered in least squares gradient computation. Falling back "
524 "to Green-Gauss gradient.");
525
526 grad = greenGaussGradient(
527 elem_arg, state_arg, functor, false, mesh, /* force_green_gauss */ true);
528 }
529 }
530
531 return grad;
532
533 // Notes to future developer:
534 // Note 1:
535 // For the least squares gradient, the LS matrix could be precomputed and stored for every cell
536 // I tried doing this on October 2024, but the element lookup for these matrices is too slow
537 // and seems better to compute weights on the fly.
538 // Consider building a map from elem_id to these matrices and speed up lookup with octree
539 // Note 2:
540 // In the future one would like to have a hybrid gradient scheme, where:
541 // \nabla \phi = \beta (\nabla \phi)_{LS} + (1 - \beta) (\nabla \phi)_{GG}
542 // Then optize \beta based on mesh heuristics
543}
544
560template <typename T,
561 typename Enable = typename std::enable_if<libMesh::ScalarTraits<T>::value>::type>
564 const StateArg & state_arg,
565 const FunctorBase<T> & functor,
566 const bool two_term_boundary_expansion,
567 const MooseMesh & mesh)
568{
569 mooseAssert(face_arg.fi, "We should have a face info to compute a face gradient");
570 const auto & fi = *(face_arg.fi);
571 const auto & elem_arg = face_arg.makeElem();
572 const auto & neighbor_arg = face_arg.makeNeighbor();
573 const bool defined_on_elem = functor.hasBlocks(fi.elemSubdomainID());
574 const bool defined_on_neighbor = fi.neighborPtr() && functor.hasBlocks(fi.neighborSubdomainID());
575
576 if (defined_on_elem && defined_on_neighbor)
577 {
578 const auto & value_elem = functor(elem_arg, state_arg);
579 const auto & value_neighbor = functor(neighbor_arg, state_arg);
580
581 // This is the component of the gradient which is parallel to the line connecting
582 // the cell centers. Therefore, we can use our second order, central difference
583 // scheme to approximate it.
584 libMesh::VectorValue<T> face_gradient = (value_neighbor - value_elem) / fi.dCNMag() * fi.eCN();
585
586 // We only need nonorthogonal correctors in 2+ dimensions
587 if (mesh.dimension() > 1)
588 {
589 // We are using an orthogonal approach for the non-orthogonal correction, for more information
590 // see Hrvoje Jasak's PhD Thesis (Imperial College, 1996)
591 libMesh::VectorValue<T> interpolated_gradient;
592
593 // Compute the gradients in the two cells on both sides of the face
594 const auto & grad_elem =
595 greenGaussGradient(elem_arg, state_arg, functor, two_term_boundary_expansion, mesh);
596 const auto & grad_neighbor =
597 greenGaussGradient(neighbor_arg, state_arg, functor, two_term_boundary_expansion, mesh);
598
600 interpolated_gradient,
601 grad_elem,
602 grad_neighbor,
603 fi,
604 true);
605
606 face_gradient += interpolated_gradient - (interpolated_gradient * fi.eCN()) * fi.eCN();
607 }
608
609 return face_gradient;
610 }
611 else if (defined_on_elem)
612 return functor.gradient(elem_arg, state_arg);
613 else if (defined_on_neighbor)
614 return functor.gradient(neighbor_arg, state_arg);
615 else
616 mooseError("The functor must be defined on one of the sides");
617}
618
619template <typename T>
620TensorValue<T>
622 const StateArg & state_arg,
624 const bool two_term_boundary_expansion,
625 const MooseMesh & mesh)
626{
627 TensorValue<T> ret;
628 for (const auto i : make_range(Moose::dim))
629 {
630 VectorComponentFunctor<T> scalar_functor(functor, i);
631 const auto row_gradient =
632 greenGaussGradient(elem_arg, state_arg, scalar_functor, two_term_boundary_expansion, mesh);
633 for (const auto j : make_range(unsigned(Moose::dim)))
634 ret(i, j) = row_gradient(j);
635 }
636
637 return ret;
638}
639
640template <typename T>
641TensorValue<T>
643 const StateArg & state_arg,
645 const bool two_term_boundary_expansion,
646 const MooseMesh & mesh)
647{
648 TensorValue<T> ret;
649 for (const auto i : make_range(unsigned(Moose::dim)))
650 {
651 VectorComponentFunctor<T> scalar_functor(functor, i);
652 const auto row_gradient =
653 greenGaussGradient(face_arg, state_arg, scalar_functor, two_term_boundary_expansion, mesh);
654 for (const auto j : make_range(unsigned(Moose::dim)))
655 ret(i, j) = row_gradient(j);
656 }
657
658 return ret;
659}
660
661template <typename T>
662typename Moose::FunctorBase<std::vector<T>>::GradientType
664 const StateArg & state_arg,
665 const Moose::FunctorBase<std::vector<T>> & functor,
666 const bool two_term_boundary_expansion,
667 const MooseMesh & mesh)
668{
669 // Determine the size of the container
670 const auto vals = functor(elem_arg, state_arg);
671 typedef typename Moose::FunctorBase<std::vector<T>>::GradientType GradientType;
672 GradientType ret(vals.size());
673 for (const auto i : index_range(ret))
674 {
675 // Note that this can be very inefficient. Within the scalar greenGaussGradient routine we're
676 // going to do value type evaluations of the array functor from scalar_functor and we will be
677 // discarding all the value type evaluations other than the one corresponding to i
678 ArrayComponentFunctor<T, FunctorBase<std::vector<T>>> scalar_functor(functor, i);
679 ret[i] =
680 greenGaussGradient(elem_arg, state_arg, scalar_functor, two_term_boundary_expansion, mesh);
681 }
682
683 return ret;
684}
685
686template <typename T>
687typename Moose::FunctorBase<std::vector<T>>::GradientType
689 const StateArg & state_arg,
690 const Moose::FunctorBase<std::vector<T>> & functor,
691 const bool two_term_boundary_expansion,
692 const MooseMesh & mesh)
693{
694 // Determine the size of the container
695 const auto vals = functor(face_arg, state_arg);
696 typedef typename Moose::FunctorBase<std::vector<T>>::GradientType GradientType;
697 GradientType ret(vals.size());
698 for (const auto i : index_range(ret))
699 {
700 // Note that this can be very inefficient. Within the scalar greenGaussGradient routine we're
701 // going to do value type evaluations of the array functor from scalar_functor and we will be
702 // discarding all the value type evaluations other than the one corresponding to i
703 ArrayComponentFunctor<T, FunctorBase<std::vector<T>>> scalar_functor(functor, i);
704 ret[i] =
705 greenGaussGradient(face_arg, state_arg, scalar_functor, two_term_boundary_expansion, mesh);
706 }
707
708 return ret;
709}
710
711template <typename T, std::size_t N>
712typename Moose::FunctorBase<std::array<T, N>>::GradientType
714 const StateArg & state_arg,
715 const Moose::FunctorBase<std::array<T, N>> & functor,
716 const bool two_term_boundary_expansion,
717 const MooseMesh & mesh)
718{
719 typedef typename Moose::FunctorBase<std::array<T, N>>::GradientType GradientType;
720 GradientType ret;
721 for (const auto i : make_range(N))
722 {
723 // Note that this can be very inefficient. Within the scalar greenGaussGradient routine we're
724 // going to do value type evaluations of the array functor from scalar_functor and we will be
725 // discarding all the value type evaluations other than the one corresponding to i
727 ret[i] =
728 greenGaussGradient(elem_arg, state_arg, scalar_functor, two_term_boundary_expansion, mesh);
729 }
730
731 return ret;
732}
733
734template <typename T, std::size_t N>
735typename Moose::FunctorBase<std::array<T, N>>::GradientType
737 const StateArg & state_arg,
738 const Moose::FunctorBase<std::array<T, N>> & functor,
739 const bool two_term_boundary_expansion,
740 const MooseMesh & mesh)
741{
742 typedef typename Moose::FunctorBase<std::array<T, N>>::GradientType GradientType;
743 GradientType ret;
744 for (const auto i : make_range(N))
745 {
746 // Note that this can be very inefficient. Within the scalar greenGaussGradient routine we're
747 // going to do value type evaluations of the array functor from scalar_functor and we will be
748 // discarding all the value type evaluations other than the one corresponding to i
750 ret[i] =
751 greenGaussGradient(face_arg, state_arg, scalar_functor, two_term_boundary_expansion, mesh);
752 }
753
754 return ret;
755}
756}
757}
void mooseWarning(Args &&... args)
Emit a warning message with the given stringified, concatenated args.
Definition MooseError.h:345
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
This data structure is used to store geometric and variable related metadata about each cell face in ...
Definition FaceInfo.h:38
MooseMesh wraps a libMesh::Mesh object and enhances its capabilities by caching additional data and s...
Definition MooseMesh.h:95
This is essentially a forwarding functor that forwards the spatial and temporal evaluation arguments ...
Base class template for functor objects.
virtual bool hasBlocks(SubdomainID) const
Returns whether the functor is defined on this block.
GradientType gradient(const ElemArg &elem, const StateArg &state) const
Same as their evaluateGradient overloads with the same arguments but allows for caching implementatio...
This is essentially a forwarding functor that forwards the spatial and temporal evaluation arguments ...
SimpleRange< NeighborPtrIter > neighbor_ptr_range()
subdomain_id_type subdomain_id() const
MeshBase & mesh
auto raw_value(const Eigen::Map< T > &in)
void coordTransformFactor(const P &point, C &factor, const Moose::CoordinateSystemType coord_type, const unsigned int rz_radial_coord=libMesh::invalid_uint)
Compute a coordinate transformation volume integration factor.
void interpolate(InterpMethod m, T &result, const T2 &value1, const T3 &value2, const FaceInfo &fi, const bool one_is_elem)
Provides interpolation of face values for non-advection-specific purposes (although it can/will still...
void loopOverElemFaceInfo(const Elem &elem, const MooseMesh &mesh, ActionFunctor &act, const Moose::CoordinateSystemType coord_type, const unsigned int rz_radial_coord=libMesh::invalid_uint)
Definition FVUtils.h:42
@ Average
gc*elem+(1-gc)*neighbor
libMesh::VectorValue< T > greenGaussGradient(const ElemArg &elem_arg, const StateArg &state_arg, const FunctorBase< T > &functor, const bool two_term_boundary_expansion, const MooseMesh &mesh, const bool force_green_gauss=false)
Compute a cell gradient using the method of Green-Gauss.
MOOSE now contains C++17 code, so give a reasonable error message stating what the user can do to add...
static constexpr std::size_t dim
This is the dimension of all vector and tensor datastructures used in MOOSE.
Definition Moose.h:165
@ COORD_RZ
Definition MooseTypes.h:866
@ COORD_RSPHERICAL
Definition MooseTypes.h:867
const unsigned int invalid_uint
A structure that is used to evaluate Moose functors logically at an element/cell center.
const libMesh::Elem * elem
A structure defining a "face" evaluation calling argument for Moose functors.
ElemArg makeNeighbor() const
Make a ElemArg from our data using the face information neighbor.
ElemArg makeElem() const
Make a ElemArg from our data using the face information element.
bool correct_skewness
Whether to perform skew correction.
const FaceInfo * fi
a face information object which defines our location in space
State argument for evaluating functors.