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 // C++
21 #include <cstring> // for "Jacobian" exception test
22 
23 namespace Moose
24 {
25 namespace FV
26 {
41 template <typename T,
42  typename Enable = typename std::enable_if<libMesh::ScalarTraits<T>::value>::type>
44 greenGaussGradient(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 
180  if (coord_type == Moose::CoordinateSystemType::COORD_RZ)
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)
250  mooseError(
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
522  mooseWarning(
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 
560 template <typename T,
561  typename Enable = typename std::enable_if<libMesh::ScalarTraits<T>::value>::type>
563 greenGaussGradient(const FaceArg & face_arg,
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 
619 template <typename T>
620 TensorValue<T>
621 greenGaussGradient(const ElemArg & elem_arg,
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 
640 template <typename T>
641 TensorValue<T>
642 greenGaussGradient(const FaceArg & face_arg,
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 
661 template <typename T>
662 typename Moose::FunctorBase<std::vector<T>>::GradientType
663 greenGaussGradient(const ElemArg & elem_arg,
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 
686 template <typename T>
687 typename Moose::FunctorBase<std::vector<T>>::GradientType
688 greenGaussGradient(const FaceArg & face_arg,
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 
711 template <typename T, std::size_t N>
712 typename Moose::FunctorBase<std::array<T, N>>::GradientType
713 greenGaussGradient(const ElemArg & elem_arg,
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
726  ArrayComponentFunctor<T, FunctorBase<std::array<T, N>>> scalar_functor(functor, i);
727  ret[i] =
728  greenGaussGradient(elem_arg, state_arg, scalar_functor, two_term_boundary_expansion, mesh);
729  }
730 
731  return ret;
732 }
733 
734 template <typename T, std::size_t N>
735 typename Moose::FunctorBase<std::array<T, N>>::GradientType
736 greenGaussGradient(const FaceArg & face_arg,
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
749  ArrayComponentFunctor<T, FunctorBase<std::array<T, N>>> scalar_functor(functor, 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 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
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:323
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:357
MeshBase & mesh
auto raw_value(const Eigen::Map< T > &in)
Definition: EigenADReal.h:100
static constexpr std::size_t dim
This is the dimension of all vector and tensor datastructures used in MOOSE.
Definition: Moose.h:162
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:92
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)