47 const bool two_term_boundary_expansion,
49 const bool force_green_gauss =
false)
51 mooseAssert(elem_arg.
elem,
"This should be non-null");
53 const auto rz_radial_coord =
mesh.getAxisymmetricRadialCoord();
55 const T elem_value = functor(elem_arg, state_arg);
58 unsigned int num_ebfs = 0;
67 bool volume_set =
false;
86 std::vector<libMesh::VectorValue<Real>> ebf_grad_coeffs;
89 std::vector<const T *> ebf_b;
92 std::vector<libMesh::VectorValue<Real>> grad_ebf_coeffs;
96 auto action_functor = [&volume_set,
107 two_term_boundary_expansion,
109 rz_radial_coord](
const Elem & libmesh_dbg_var(functor_elem),
112 const Point & surface_vector,
114 const bool elem_has_info)
116 mooseAssert(fi,
"We need a FaceInfo for this action_functor");
118 elem_arg.elem == &functor_elem,
119 "Just a sanity check that the element being passed in is the one we passed out.");
121 if (functor.isExtrapolatedBoundaryFace(*fi, elem_arg.elem, state_arg))
123 if (two_term_boundary_expansion)
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);
134 grad_ebf_coeffs.push_back(-surface_vector);
141 grad_b += surface_vector * elem_value;
160 fi->elemCentroid(), coord, coord_type, rz_radial_coord);
161 volume = fi->elemVolume() * coord;
166 fi->neighborCentroid(), coord, coord_type, rz_radial_coord);
167 volume = fi->neighborVolume() * coord;
175 *elem_arg.elem,
mesh, action_functor, coord_type, rz_radial_coord);
177 mooseAssert(volume_set && volume > 0,
"We should have set the volume");
183 grad_b(rz_radial_coord) -= elem_value / elem_arg.elem->vertex_average()(rz_radial_coord);
188 "We have not yet implemented the correct translation from gradient to divergence for "
189 "spherical coordinates yet.");
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);
210 for (
const auto j : make_range(num_ebfs))
211 A(i,
Moose::dim + j) = grad_ebf_coeffs[j](i) / volume;
218 for (
const auto j : make_range(num_ebfs))
224 for (
const auto i : make_range(
unsigned(
Moose::dim)))
240 catch (std::exception & e)
245 if (!strstr(e.what(),
"singular"))
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: ",
286 const size_t num_faces = std::distance(ptr_range.begin(), ptr_range.end());
289 std::vector<Point> delta_x_list;
291 delta_x_list.reserve(num_faces);
295 delta_phi_list.reserve(num_faces);
298 std::vector<Point> delta_x_ebf_list;
299 delta_phi_list.reserve(num_faces);
301 std::vector<VectorValue<Real>>
303 delta_phi_list.reserve(num_faces);
305 std::vector<const T *> ebf_b;
306 delta_phi_list.reserve(num_faces);
308 unsigned int num_ebfs = 0;
311 auto action_functor = [&elem_value,
321 two_term_boundary_expansion](
const Elem & libmesh_dbg_var(loc_elem),
322 const Elem * loc_neigh,
326 const bool elem_has_info)
328 mooseAssert(fi,
"We need a FaceInfo for this action_functor");
330 elem_arg.elem == &loc_elem,
331 "Just a sanity check that the element being passed in is the one we passed out.");
333 if (functor.isExtrapolatedBoundaryFace(*fi, elem_arg.elem, state_arg))
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);
342 if (two_term_boundary_expansion)
348 ebf_grad_coeffs.push_back(-delta_x);
350 ebf_b.push_back(&elem_value);
352 delta_phi = -elem_value;
353 delta_x_ebf_list.push_back(delta_x);
360 delta_phi_list.push_back(delta_phi);
367 if (functor.isInternalFace(*fi))
370 delta_x = loc_neigh->vertex_average() - elem_arg.elem->vertex_average();
373 neighbor_value = functor(neighbor_arg, state_arg);
378 delta_x = elem_has_info ? (fi->faceCentroid() - fi->elemCentroid())
379 : (fi->faceCentroid() - fi->neighborCentroid());
389 delta_x_list.push_back(delta_x);
390 delta_phi_list.push_back(neighbor_value - elem_value);
396 *elem_arg.elem,
mesh, action_functor, coord_type, rz_radial_coord);
399 const unsigned int n = delta_x_list.size();
400 mooseAssert(n >=
dim,
"Not enough neighbors to perform least squares");
402 DenseMatrix<T> ATA(
dim,
dim);
403 DenseVector<T> ATb(
dim);
411 for (
unsigned int i = 0; i < n; ++i)
413 const Point & delta_x = delta_x_list[i];
414 const T & delta_phi = delta_phi_list[i];
416 for (
unsigned int d1 = 0; d1 <
dim; ++d1)
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;
422 for (
unsigned int d2 = 0; d2 <
dim; ++d2)
424 const Real dx_d2_norm = delta_x(d2) / dx_norm;
425 ATA(d1, d2) += dx_d1_norm * dx_d2_norm;
428 ATb(d1) += dx_d1_norm * delta_phi / dx_norm;
434 for (
unsigned int d = 0; d <
dim; ++d)
436 ATA(d, d) += epsilon;
442 DenseVector<T> grad_ls(
dim);
443 ATA.lu_solve(ATb, grad_ls);
446 for (
unsigned int d = 0; d <
dim; ++d)
448 grad(d) = grad_ls(d);
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);
461 for (
unsigned int d1 = 0; d1 <
dim; ++d1)
464 for (
unsigned int d2 = 0; d2 <
dim; ++d2)
465 A(d1, d2) = ATA(d1, d2);
472 for (
const auto i : make_range(num_ebfs))
474 const Point & delta_x = delta_x_ebf_list[i];
475 for (
unsigned int d1 = 0; d1 <
dim; ++d1)
477 const Real dx_d1 = delta_x(d1);
478 A(d1,
Moose::dim + i) -= dx_d1 / delta_x.norm() / delta_x.norm();
483 for (
const auto j : make_range(num_ebfs))
489 for (
const auto i : make_range(
unsigned(
Moose::dim)))
510 "We have not yet implemented the correct translation from gradient to divergence for "
511 "spherical coordinates yet.");
513 catch (std::exception & e)
518 if (!strstr(e.what(),
"singular"))
523 "Singular matrix encountered in least squares gradient computation. Falling back "
524 "to Green-Gauss gradient.");
527 elem_arg, state_arg, functor,
false,
mesh,
true);