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