https://mooseframework.inl.gov
INSFVRhieChowInterpolator.C
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 
11 #include "INSFVAttributes.h"
13 #include "GatherRCDataFaceThread.h"
14 #include "MooseMesh.h"
15 #include "SystemBase.h"
16 #include "NS.h"
17 #include "Assembly.h"
18 #include "INSFVVelocityVariable.h"
20 #include "VectorCompositeFunctor.h"
21 #include "FVElementalKernel.h"
22 #include "NSFVUtils.h"
23 #include "DisplacedProblem.h"
24 
25 #include "libmesh/mesh_base.h"
26 #include "libmesh/elem_range.h"
27 #include "libmesh/parallel_algebra.h"
28 #include "libmesh/remote_elem.h"
29 #include "metaphysicl/dualsemidynamicsparsenumberarray.h"
30 #include "metaphysicl/parallel_dualnumber.h"
31 #include "metaphysicl/parallel_dynamic_std_array_wrapper.h"
32 #include "metaphysicl/parallel_semidynamicsparsenumberarray.h"
33 #include "timpi/parallel_sync.h"
34 
35 using namespace libMesh;
36 
38 
41 {
42  auto params = emptyInputParameters();
43  params.addParam<bool>(
44  "pull_all_nonlocal_a",
45  false,
46  "Whether to pull all nonlocal 'a' coefficient data to our process. Note that 'nonlocal' "
47  "means elements that we have access to (this may not be all the elements in the mesh if the "
48  "mesh is distributed) but that we do not own.");
49  params.addParamNamesToGroup("pull_all_nonlocal_a", "Parallel Execution Tuning");
50 
51  params.addParam<bool>(
52  "correct_volumetric_force", false, "Flag to activate volume force corrections.");
53  MooseEnum volume_force_correction_method("force-consistent pressure-consistent",
54  "force-consistent");
55  params.addParam<MooseEnum>(
56  "volume_force_correction_method",
57  volume_force_correction_method,
58  "The method used for correcting the Rhie-Chow coefficients for a volume force.");
59  params.addParam<std::vector<MooseFunctorName>>(
60  "volumetric_force_functors", "The names of the functors with the volumetric force sources.");
61  return params;
62 }
63 
64 std::vector<std::string>
66 {
67  return {"pull_all_nonlocal_a",
68  "correct_volumetric_force",
69  "volume_force_correction_method",
70  "volumetric_force_functors"};
71 }
72 
75 {
78 
79  params.addClassDescription(
80  "Computes the Rhie-Chow velocity based on gathered 'a' coefficient data.");
81 
82  ExecFlagEnum & exec_enum = params.set<ExecFlagEnum>("execute_on", true);
84  exec_enum = {EXEC_PRE_KERNELS};
85  params.suppressParameter<ExecFlagEnum>("execute_on");
86 
87  params.addParam<MooseFunctorName>(
88  "a_u",
89  "For simulations in which the advecting velocities are aux variables, this parameter must be "
90  "supplied. It represents the on-diagonal coefficients for the 'x' component velocity, solved "
91  "via the Navier-Stokes equations.");
92  params.addParam<MooseFunctorName>(
93  "a_v",
94  "For simulations in which the advecting velocities are aux variables, this parameter must be "
95  "supplied when the mesh dimension is greater than 1. It represents the on-diagonal "
96  "coefficients for the 'y' component velocity, solved via the Navier-Stokes equations.");
97  params.addParam<MooseFunctorName>(
98  "a_w",
99  "For simulations in which the advecting velocities are aux variables, this parameter must be "
100  "supplied when the mesh dimension is greater than 2. It represents the on-diagonal "
101  "coefficients for the 'z' component velocity, solved via the Navier-Stokes equations.");
102  params.addParam<VariableName>("disp_x", "The x-component of displacement");
103  params.addParam<VariableName>("disp_y", "The y-component of displacement");
104  params.addParam<VariableName>("disp_z", "The z-component of displacement");
105  return params;
106 }
107 
109  : RhieChowInterpolatorBase(params),
110  _vel(libMesh::n_threads()),
111  _a(_moose_mesh, blockIDs(), "a", /*extrapolated_boundary*/ true),
112  _ax(_a, 0),
113  _ay(_a, 1),
114  _az(_a, 2),
115  _momentum_sys_number(_fe_problem.systemNumForVariable(getParam<VariableName>("u"))),
116  _example(0),
117  _a_data_provided(false),
118  _pull_all_nonlocal(getParam<bool>("pull_all_nonlocal_a")),
119  _bool_correct_vf(getParam<bool>("correct_volumetric_force")),
120  _volume_force_correction_method(getParam<MooseEnum>("volume_force_correction_method")),
121  _volumetric_force_functors(
122  isParamValid("volumetric_force_functors")
123  ? &getParam<std::vector<MooseFunctorName>>("volumetric_force_functors")
124  : nullptr)
125 {
126  auto process_displacement = [this](const auto & disp_name, auto & disp_container)
127  {
128  if (!_displaced)
129  paramError(disp_name,
130  "Displacement provided but we are not running on the displaced mesh. If you "
131  "really want this object to run on the displaced mesh, then set "
132  "'use_displaced_mesh = true', otherwise remove this displacement parameter");
133  disp_container.resize(libMesh::n_threads());
134  fillContainer(disp_name, disp_container);
135  checkBlocks(*disp_container[0]);
136  };
137 
138  if (isParamValid("disp_x"))
139  process_displacement("disp_x", _disp_xs);
140 
141  if (_dim >= 2)
142  {
143  if (isParamValid("disp_y"))
144  process_displacement("disp_y", _disp_ys);
145  else if (isParamValid("disp_x"))
146  paramError("disp_y", "If 'disp_x' is provided, then 'disp_y' must be as well");
147  }
148 
149  if (_dim >= 3)
150  {
151  if (isParamValid("disp_z"))
152  process_displacement("disp_z", _disp_zs);
153  else if (isParamValid("disp_x"))
154  paramError("disp_z", "If 'disp_x' is provided, then 'disp_z' must be as well");
155  }
156 
157  for (const auto tid : make_range(libMesh::n_threads()))
158  {
159  _vel[tid] = std::make_unique<PiecewiseByBlockLambdaFunctor<ADRealVectorValue>>(
160  name() + std::to_string(tid),
161  [this, tid](const auto & r, const auto & t) -> ADRealVectorValue
162  {
163  ADRealVectorValue velocity((*_us[tid])(r, t));
164  if (_dim >= 2)
165  velocity(1) = (*_vs[tid])(r, t);
166  if (_dim >= 3)
167  velocity(2) = (*_ws[tid])(r, t);
168  return velocity;
169  },
170  std::set<ExecFlagType>({EXEC_ALWAYS}),
171  _moose_mesh,
172  blockIDs());
173 
174  if (_disp_xs.size())
175  _disps.push_back(std::make_unique<Moose::VectorCompositeFunctor<ADReal>>(
176  name() + "_disp_" + std::to_string(tid),
177  *_disp_xs[tid],
178  _dim >= 2 ? static_cast<const Moose::FunctorBase<ADReal> &>(*_disp_ys[tid])
179  : static_cast<const Moose::FunctorBase<ADReal> &>(_zero_functor),
180  _dim >= 3 ? static_cast<const Moose::FunctorBase<ADReal> &>(*_disp_zs[tid])
181  : static_cast<const Moose::FunctorBase<ADReal> &>(_zero_functor)));
182  }
183 
185  paramError("a_u",
186  "Rhie Chow coefficients may not be specified for average velocity interpolation");
187 
189  fillARead();
190 
192  paramError("volumetric_force_functors",
193  "At least one volumetric force functor must be specified if "
194  "'correct_volumetric_force' is true.");
195 
196  // Volume correction related
197  if (_bool_correct_vf)
198  {
199  const unsigned int num_volume_forces = (*_volumetric_force_functors).size();
200  _volumetric_force.resize(num_volume_forces);
201  for (const auto i : make_range(num_volume_forces))
202  _volumetric_force[i] = &getFunctor<Real>((*_volumetric_force_functors)[i]);
203  }
204 }
205 
206 void
208 {
209  _a_read.resize(libMesh::n_threads());
210 
211  if (isParamValid("a_u"))
212  {
213  if (_dim > 1 && !isParamValid("a_v"))
214  mooseError("If a_u is provided, then a_v must be provided");
215 
216  if (_dim > 2 && !isParamValid("a_w"))
217  mooseError("If a_u is provided, then a_w must be provided");
218 
219  _a_data_provided = true;
220  _a_aux.resize(libMesh::n_threads());
221  }
222  else if (isParamValid("a_v"))
223  paramError("a_v", "If the a_v coefficients are provided, then a_u must be provided");
224  else if (isParamValid("a_w"))
225  paramError("a_w", "If the a_w coefficients are provided, then a_u must be provided");
226 
227  if (_a_data_provided)
228  {
229  for (const auto tid : make_range(libMesh::n_threads()))
230  {
231  const Moose::FunctorBase<ADReal> *v_comp, *w_comp;
232  if (_dim > 1)
234  deduceFunctorName("a_v"), tid, name(), true);
235  else
236  v_comp = &_zero_functor;
237  if (_dim > 2)
239  deduceFunctorName("a_w"), tid, name(), true);
240  else
241  w_comp = &_zero_functor;
242 
243  _a_aux[tid] = std::make_unique<Moose::VectorCompositeFunctor<ADReal>>(
244  "RC_a_coeffs",
246  *v_comp,
247  *w_comp);
248  _a_read[tid] = _a_aux[tid].get();
249  }
250  }
251  else
252  for (const auto tid : make_range(libMesh::n_threads()))
253  {
254  _a_read[tid] = &_a;
255 
256  // We are the fluid flow application, so we should make sure users have the ability to
257  // write 'a' out to aux variables for possible transfer to other applications
261  }
262 }
263 
264 void
266 {
267  insfvSetup();
268 
270  return;
271  for (const auto var_num : _var_numbers)
272  {
273  std::vector<MooseObject *> var_objects;
275  .query()
276  .template condition<AttribVar>(static_cast<int>(var_num))
277  .template condition<AttribResidualObject>(true)
278  .template condition<AttribSysNum>(_u->sys().number())
279  .queryInto(var_objects);
280  for (auto * const var_object : var_objects)
281  {
282  // Allow FVElementalKernel that are not INSFVMomentumResidualObject for now, refs #20695
283  if (!dynamic_cast<INSFVMomentumResidualObject *>(var_object) &&
284  !dynamic_cast<FVElementalKernel *>(var_object))
285  mooseError("Object ",
286  var_object->name(),
287  " is not a INSFVMomentumResidualObject. Make sure that all the objects applied "
288  "to the momentum equation are INSFV or derived objects.");
289  else if (!dynamic_cast<INSFVMomentumResidualObject *>(var_object) &&
290  dynamic_cast<FVElementalKernel *>(var_object))
291  mooseWarning(
292  "Elemental kernel ",
293  var_object->name(),
294  " is not a INSFVMomentumResidualObject. Make sure that all the objects applied "
295  "to the momentum equation are INSFV or derived objects.");
296  }
297 
298  if (var_objects.size() == 0 && !_a_data_provided)
299  mooseError("No INSFVKernels detected for the velocity variables. If you are trying to use "
300  "auxiliary variables for advection, please specify the a_u/v/w coefficients. If "
301  "not, please specify INSFVKernels for the momentum equations.");
302  }
303 
304  // Get baseline force if force-correction method is used for volumetric correction
305  if (_bool_correct_vf && _volume_force_correction_method == "force-consistent")
306  {
307  _baseline_volume_force = 1e10;
308  for (const auto & loc_elem : *_elem_range)
309  {
310  Real elem_value = 0.0;
311  for (const auto i : make_range(_volumetric_force.size()))
312  elem_value += (*_volumetric_force[i])(makeElemArg(loc_elem), determineState());
313 
314  if (std::abs(elem_value) < _baseline_volume_force)
315  _baseline_volume_force = std::abs(elem_value);
316  if (_baseline_volume_force == 0)
317  break;
318  }
320  }
321 }
322 
323 void
325 {
326  _elem_range =
327  std::make_unique<ConstElemRange>(_mesh.active_local_subdomain_set_elements_begin(blockIDs()),
328  _mesh.active_local_subdomain_set_elements_end(blockIDs()));
329 }
330 
331 void
333 {
334  insfvSetup();
335 
336  // If the mesh has been modified:
337  // - the boundary elements may have changed
338  // - some elements may have been refined
339  _elements_to_push_pull.clear();
340  _a.clear();
341 }
342 
343 void
345 {
346  if (!needAComputation())
347  return;
348 
349  // Reset map of coefficients to zero.
350  // The keys should not have changed unless the mesh has changed
351  // Dont reset if not in current system
352  // IDEA: clear them derivatives
354  for (auto & pair : _a)
355  pair.second = 0;
356  else
357  for (auto & pair : _a)
358  {
359  auto & a_val = pair.second;
360  a_val = MetaPhysicL::raw_value(a_val);
361  }
362 }
363 
364 void
366 {
367  // Either we provided the RC coefficients using aux-variable, or we are solving for another
368  // system than the momentum equations are in, in a multi-system setup for example
370  return;
371 
372  mooseAssert(!_a_data_provided,
373  "a-coefficient data should not be provided if the velocity variables are in the "
374  "nonlinear system and we are running kernels that compute said a-coefficients");
375  // One might think that we should do a similar assertion for
376  // (_velocity_interp_method == Moose::FV::InterpMethod::RhieChow). However, even if we are not
377  // using the generated a-coefficient data in that case, some kernels have been optimized to
378  // add their residuals into the global system during the generation of the a-coefficient data.
379  // Hence if we were to skip the kernel execution we would drop those residuals
380 
381  TIME_SECTION("execute", 1, "Computing Rhie-Chow coefficients");
382 
383  // A lot of RC data gathering leverages the automatic differentiation system, e.g. for linear
384  // operators we pull out the 'a' coefficients by querying the ADReal residual derivatives
385  // member at the element or neighbor dof locations. Consequently we need to enable derivative
386  // computation. We do this here outside the threaded regions
387  const auto saved_do_derivatives = ADReal::do_derivatives;
388  ADReal::do_derivatives = true;
389 
390  PARALLEL_TRY
391  {
393  Threads::parallel_reduce(*_elem_range, et);
394  }
395  PARALLEL_CATCH;
396 
397  PARALLEL_TRY
398  {
403  Threads::parallel_reduce(faces, fvr);
404  }
405  PARALLEL_CATCH;
406 
407  ADReal::do_derivatives = saved_do_derivatives;
408 }
409 
410 void
412 {
413  if (!needAComputation() || this->n_processors() == 1)
414  return;
415 
416  // If advecting with auxiliary variables, no need to synchronize data
417  // Same if not solving for the velocity variables at the moment
419  return;
420 
421  using Datum = std::pair<dof_id_type, VectorValue<ADReal>>;
422  std::unordered_map<processor_id_type, std::vector<Datum>> push_data;
423  std::unordered_map<processor_id_type, std::vector<dof_id_type>> pull_requests;
424  static const VectorValue<ADReal> example;
425 
426  // Create push data
427  for (const auto * const elem : _elements_to_push_pull)
428  {
429  const auto id = elem->id();
430  const auto pid = elem->processor_id();
431  auto it = _a.find(id);
432  mooseAssert(it != _a.end(), "We definitely should have found something");
433  push_data[pid].push_back(std::make_pair(id, it->second));
434  }
435 
436  // Create pull data
437  if (_pull_all_nonlocal)
438  {
439  for (const auto * const elem :
440  as_range(_mesh.active_not_local_elements_begin(), _mesh.active_not_local_elements_end()))
441  if (blockIDs().count(elem->subdomain_id()))
442  pull_requests[elem->processor_id()].push_back(elem->id());
443  }
444  else
445  {
446  for (const auto * const elem : _elements_to_push_pull)
447  pull_requests[elem->processor_id()].push_back(elem->id());
448  }
449 
450  // First push
451  {
452  auto action_functor =
453  [this](const processor_id_type libmesh_dbg_var(pid), const std::vector<Datum> & sent_data)
454  {
455  mooseAssert(pid != this->processor_id(), "We do not send messages to ourself here");
456  for (const auto & pr : sent_data)
457  _a[pr.first] += pr.second;
458  };
459  TIMPI::push_parallel_vector_data(_communicator, push_data, action_functor);
460  }
461 
462  // Then pull
463  {
464  auto gather_functor = [this](const processor_id_type libmesh_dbg_var(pid),
465  const std::vector<dof_id_type> & elem_ids,
466  std::vector<VectorValue<ADReal>> & data_to_fill)
467  {
468  mooseAssert(pid != this->processor_id(), "We shouldn't be gathering from ourselves.");
469  data_to_fill.resize(elem_ids.size());
470  for (const auto i : index_range(elem_ids))
471  {
472  const auto id = elem_ids[i];
473  auto it = _a.find(id);
474  mooseAssert(it != _a.end(), "We should hold the value for this locally");
475  data_to_fill[i] = it->second;
476  }
477  };
478 
479  auto action_functor = [this](const processor_id_type libmesh_dbg_var(pid),
480  const std::vector<dof_id_type> & elem_ids,
481  const std::vector<VectorValue<ADReal>> & filled_data)
482  {
483  mooseAssert(pid != this->processor_id(), "The request filler shouldn't have been ourselves");
484  mooseAssert(elem_ids.size() == filled_data.size(), "I think these should be the same size");
485  for (const auto i : index_range(elem_ids))
486  _a[elem_ids[i]] = filled_data[i];
487  };
489  _communicator, pull_requests, gather_functor, action_functor, &example);
490  }
491 }
492 
493 void
495 {
496  if (!needAComputation() || this->n_processors() == 1)
497  return;
498 
499  // Ghost a for the elements on the boundary
500  for (auto elem_id : _moose_mesh.getBoundaryActiveSemiLocalElemIds(boundary_id))
501  {
502  const auto & elem = _moose_mesh.elemPtr(elem_id);
503  // no need to ghost if locally owned or far from local process
504  if (elem->processor_id() != this->processor_id() && elem->is_semilocal(this->processor_id()))
505  // Adding to the a coefficient will make sure the final result gets communicated
506  addToA(elem, 0, 0);
507  }
508 
509  // Ghost a for the neighbors of the elements on the boundary
510  for (auto neighbor_id : _moose_mesh.getBoundaryActiveNeighborElemIds(boundary_id))
511  {
512  const auto & neighbor = _moose_mesh.queryElemPtr(neighbor_id);
513  // no need to ghost if locally owned or far from local process
514  if (neighbor->processor_id() != this->processor_id() &&
515  neighbor->is_semilocal(this->processor_id()))
516  // Adding to the a coefficient will make sure the final result gets communicated
517  addToA(neighbor, 0, 0);
518  }
519 }
520 
523  const FaceInfo & fi,
524  const Moose::StateArg & time,
525  const THREAD_ID tid,
526  const bool subtract_mesh_velocity) const
527 {
528  const Elem * const elem = &fi.elem();
529  const Elem * const neighbor = fi.neighborPtr();
530  auto & vel = *_vel[tid];
531  auto & p = *_ps[tid];
532  auto * const u = _us[tid];
533  MooseVariableFVReal * const v = _v ? _vs[tid] : nullptr;
534  MooseVariableFVReal * const w = _w ? _ws[tid] : nullptr;
535  // Check if skewness-correction is necessary
536  const bool correct_skewness = velocitySkewCorrection(tid);
537  auto incorporate_mesh_velocity =
538  [this, tid, subtract_mesh_velocity, &time](const auto & space, auto & velocity)
539  {
540  if (_disps.size() && subtract_mesh_velocity)
541  velocity -= _disps[tid]->dot(space, time);
542  };
543 
544  if (Moose::FV::onBoundary(*this, fi))
545  {
546  const Elem * const boundary_elem = hasBlocks(elem->subdomain_id()) ? elem : neighbor;
547  const Moose::FaceArg boundary_face{&fi,
549  true,
550  correct_skewness,
551  boundary_elem,
552  nullptr};
553  auto velocity = vel(boundary_face, time);
554  incorporate_mesh_velocity(boundary_face, velocity);
555 
556  // If not solving for velocity, clear derivatives
559  else
560  return velocity;
561  }
562 
564 
565  Moose::FaceArg face{
566  &fi, Moose::FV::LimiterType::CentralDifference, true, correct_skewness, nullptr, nullptr};
567  // Create the average face velocity (not corrected using RhieChow yet)
568  velocity(0) = (*u)(face, time);
569  if (v)
570  velocity(1) = (*v)(face, time);
571  if (w)
572  velocity(2) = (*w)(face, time);
573 
574  incorporate_mesh_velocity(face, velocity);
575 
576  // If not solving for velocity, clear derivatives
579 
580  // Return if Rhie-Chow was not requested or if we have a porosity jump
582  std::get<0>(NS::isPorosityJumpFace(epsilon(tid), fi, time)))
583  return velocity;
584 
585  // Rhie-Chow coefficients are not available on initial
587  {
588  mooseDoOnce(mooseWarning("Cannot compute Rhie Chow coefficients on initial. Returning linearly "
589  "interpolated velocities"););
590  return velocity;
591  }
592  if (!_fe_problem.shouldSolve())
593  {
594  mooseDoOnce(mooseWarning("Cannot compute Rhie Chow coefficients if not solving. Returning "
595  "linearly interpolated velocities"););
596  return velocity;
597  }
598 
599  mooseAssert(((m == Moose::FV::InterpMethod::RhieChow) &&
602  "The 'a' coefficients have not been generated or provided for "
603  "Rhie Chow velocity interpolation.");
604 
605  mooseAssert(neighbor && this->hasBlocks(neighbor->subdomain_id()),
606  "We should be on an internal face...");
607 
608  // Get pressure gradient. This is the uncorrected gradient plus a correction from cell
609  // centroid values on either side of the face
610  const auto correct_skewness_p = pressureSkewCorrection(tid);
611  const auto & grad_p = p.adGradSln(fi, time, correct_skewness_p);
612 
613  // Get uncorrected pressure gradient. This will use the element centroid gradient if we are
614  // along a boundary face
615  const auto & unc_grad_p = p.uncorrectedAdGradSln(fi, time, correct_skewness_p);
616 
617  // Volumetric Correction Method #1: pressure-based correction
618  // Function that allows us to mark the face for which the Rhie-Chow interpolation is
619  // inconsistent Normally, we should apply a reconstructed volume correction to the Rhie-Chow
620  // coefficients However, since the fluxes at the face are given by the volume force we will
621  // simply mark the face add the reverse pressure interpolation for these faces In brief, this
622  // function is just marking the faces where the Rhie-Chow interpolation is inconsistent
623  auto vf_indicator_pressure_based =
624  [this, &elem, &neighbor, &time, &fi, &correct_skewness](const Point & unit_basis_vector)
625  {
626  // Holders for the interpolated corrected and uncorrected volume force
627  Real interp_vf;
628  Real uncorrected_interp_vf;
629 
630  // Compute the corrected interpolated face value
631  Moose::FaceArg face{
632  &fi, Moose::FV::LimiterType::CentralDifference, true, correct_skewness, nullptr, nullptr};
633 
634  interp_vf = 0.0;
635  for (const auto i : make_range(_volumetric_force.size()))
636  interp_vf += (*this->_volumetric_force[i])(face, time);
637 
638  // Compute the uncorrected interpolated face value
639  // For it to be consistent with the pressure gradient interpolation `uncorrectedAdGradSln`
640  // the uncorrected volume force computation should follow the same Green-Gauss process
641 
642  Real elem_value = 0.0;
643  Real neigh_value = 0.0;
644 
645  // Uncorrected interpolation - Step 1: loop over the faces of the element to compute
646  // face-average cell value
647  Real coord_multiplier;
648  const auto coord_type = _fe_problem.getCoordSystem(elem->subdomain_id());
649  const unsigned int rz_radial_coord =
651 
652  for (const auto side : make_range(elem->n_sides()))
653  {
654  const Elem * const loc_neighbor = elem->neighbor_ptr(side);
655  const bool elem_has_fi = Moose::FV::elemHasFaceInfo(*elem, loc_neighbor);
656  const FaceInfo * const fi_loc =
657  _moose_mesh.faceInfo(elem_has_fi ? elem : loc_neighbor,
658  elem_has_fi ? side : loc_neighbor->which_neighbor_am_i(elem));
659 
660  Moose::FaceArg loc_face{
661  fi_loc, Moose::FV::LimiterType::CentralDifference, true, correct_skewness, elem, nullptr};
662 
664  elem->vertex_average(), coord_multiplier, coord_type, rz_radial_coord);
665 
666  Real face_volume_contribution = fi_loc->faceArea() *
667  (neighbor->vertex_average() - elem->vertex_average()).norm() *
668  coord_multiplier;
669 
670  for (const auto i : make_range(_volumetric_force.size()))
671  {
672  // Add which side (can be both, then we use a nullptr) of the face info the force is defined
673  // on
674  loc_face.face_side =
675  this->_volumetric_force[i]->hasFaceSide(*fi_loc, true)
676  ? (this->_volumetric_force[i]->hasFaceSide(*fi_loc, false) ? nullptr
677  : fi_loc->elemPtr())
678  : fi_loc->neighborPtr();
679  elem_value += (*this->_volumetric_force[i])(loc_face, time) * face_volume_contribution *
680  (fi_loc->normal() * unit_basis_vector);
681  }
682  }
683  elem_value = elem_value / elem->volume();
684 
685  // Uncorrected interpolation - Step 2: loop over the face of the neighbor to compute
686  // face-average cell value
687  for (const auto side : make_range(neighbor->n_sides()))
688  {
689  const Elem * const loc_elem = neighbor->neighbor_ptr(side);
690  const bool elem_has_fi = Moose::FV::elemHasFaceInfo(*neighbor, loc_elem);
691  const FaceInfo * const fi_loc =
692  _moose_mesh.faceInfo(elem_has_fi ? neighbor : loc_elem,
693  elem_has_fi ? side : loc_elem->which_neighbor_am_i(neighbor));
694 
695  Moose::FaceArg loc_face{
696  fi_loc, Moose::FV::LimiterType::CentralDifference, true, correct_skewness, elem, nullptr};
697 
699  neighbor->vertex_average(), coord_multiplier, coord_type, rz_radial_coord);
700 
701  Real face_volume_contribution = fi_loc->faceArea() *
702  (elem->vertex_average() - neighbor->vertex_average()).norm() *
703  coord_multiplier;
704 
705  for (const auto i : make_range(_volumetric_force.size()))
706  {
707  loc_face.face_side =
708  this->_volumetric_force[i]->hasFaceSide(*fi_loc, true)
709  ? (this->_volumetric_force[i]->hasFaceSide(*fi_loc, false) ? nullptr
710  : fi_loc->elemPtr())
711  : fi_loc->neighborPtr();
712  neigh_value += (*this->_volumetric_force[i])(loc_face, time) * face_volume_contribution *
713  (fi_loc->normal() * unit_basis_vector);
714  }
715  }
716  neigh_value = neigh_value / neighbor->volume();
717 
718  // Uncorrected interpolation - Step 3: interpolate element and neighbor reconstructed values
719  // to the face
721  fi.faceCentroid(), coord_multiplier, coord_type, rz_radial_coord);
722  interpolate(
723  Moose::FV::InterpMethod::Average, uncorrected_interp_vf, elem_value, neigh_value, fi, true);
724 
725  // Return the flag indicator on which face the volume force correction is inconsistent
726  return MooseUtils::relativeFuzzyEqual(interp_vf, uncorrected_interp_vf, 1e-10) ? 0.0 : 1.0;
727  };
728 
729  // Volumetric Correction Method #2: volume-based correction
730  // In thery, pressure and velocity cannot be decoupled when a body force is present
731  // Hence, we can de-activate the RC cofficient in faces that have a normal volume force
732  // In the method we mark the faces with a non-zero volume force with recpect to the baseline
733  auto vf_indicator_force_based = [this, &time, &fi, &correct_skewness](Point & face_normal)
734  {
735  Real value = 0.0;
736  Moose::FaceArg loc_face{
737  &fi, Moose::FV::LimiterType::CentralDifference, true, correct_skewness, nullptr, nullptr};
738 
739  for (const auto i : make_range(_volumetric_force.size()))
740  value += (*_volumetric_force[i])(loc_face, time) * (face_normal * fi.normal());
741  if ((std::abs(value) - _baseline_volume_force) > 0)
742  return 1.0;
743  else
744  return 0.0;
745  };
746 
747  const Point & elem_centroid = fi.elemCentroid();
748  const Point & neighbor_centroid = fi.neighborCentroid();
749  Real elem_volume = fi.elemVolume();
750  Real neighbor_volume = fi.neighborVolume();
751 
752  // Now we need to perform the computations of D
753  const auto elem_a = (*_a_read[tid])(makeElemArg(elem), time);
754 
755  mooseAssert(UserObject::_subproblem.getCoordSystem(elem->subdomain_id()) ==
756  UserObject::_subproblem.getCoordSystem(neighbor->subdomain_id()),
757  "Coordinate systems must be the same between the two elements");
758 
759  Real coord;
760  coordTransformFactor(UserObject::_subproblem, elem->subdomain_id(), elem_centroid, coord);
761 
762  elem_volume *= coord;
763 
764  VectorValue<ADReal> elem_D = 0;
765  for (const auto i : make_range(_dim))
766  {
767  mooseAssert(elem_a(i).value() != 0, "We should not be dividing by zero");
768  elem_D(i) = elem_volume / elem_a(i);
769  }
770 
771  VectorValue<ADReal> face_D;
772 
773  const auto neighbor_a = (*_a_read[tid])(makeElemArg(neighbor), time);
774 
775  coordTransformFactor(UserObject::_subproblem, neighbor->subdomain_id(), neighbor_centroid, coord);
776  neighbor_volume *= coord;
777 
778  VectorValue<ADReal> neighbor_D = 0;
779  for (const auto i : make_range(_dim))
780  {
781  mooseAssert(neighbor_a(i).value() != 0, "We should not be dividing by zero");
782  neighbor_D(i) = neighbor_volume / neighbor_a(i);
783  }
784 
785  // We require this to ensure that the correct interpolation weights are used.
786  // This will change once the traditional weights are replaced by the weights
787  // that are used by the skewness-correction.
788  Moose::FV::InterpMethod coeff_interp_method = correct_skewness
791  Moose::FV::interpolate(coeff_interp_method, face_D, elem_D, neighbor_D, fi, true);
792 
793  // evaluate face porosity, see (18) in Hanimann 2021 or (11) in Nordlund 2016
794  const auto face_eps = epsilon(tid)(face, time);
795 
796  // Perform the pressure correction. We don't use skewness-correction on the pressure since
797  // it only influences the averaged cell gradients which cancel out in the correction
798  // below.
799  for (const auto i : make_range(_dim))
800  {
801  // "Standard" pressure-based RC interpolation
802  velocity(i) -= face_D(i) * face_eps * (grad_p(i) - unc_grad_p(i));
803 
804  if (_bool_correct_vf)
805  {
806  // To solve the volume force incorrect interpolation, we add back the pressure gradient to the
807  // RC-inconsistent faces regarding the marking method
808  Point unit_basis_vector;
809  unit_basis_vector(i) = 1.0;
810 
811  // Get the value of the correction face indicator
812  Real correction_indicator;
813  if (_volume_force_correction_method == "force-consistent")
814  correction_indicator = vf_indicator_force_based(unit_basis_vector);
815  else
816  correction_indicator = vf_indicator_pressure_based(unit_basis_vector);
817 
818  // Correct back the velocity
819  velocity(i) += face_D(i) * face_eps * (grad_p(i) - unc_grad_p(i)) * correction_indicator;
820  }
821  }
822 
823  // If not solving for velocity, clear derivatives
826  else
827  return velocity;
828 }
A class that gathers body force data from elemental kernels contributing to the Navier-Stokes momentu...
unsigned int getAxisymmetricRadialCoord() const
bool shouldSolve() const
void pull_parallel_vector_data(const Communicator &comm, const MapToVectors &queries, GatherFunctor &gather_data, const ActionFunctor &act_on_data, const datum *example)
virtual void initialize() override
bool elemHasFaceInfo(const Elem &elem, const Elem *const neighbor)
bool needAComputation() const
Whether we need &#39;a&#39; coefficient computation.
std::vector< MooseVariableFVReal * > _ps
All the thread copies of the pressure variable.
std::vector< const Moose::Functor< Real > * > _volumetric_force
Values of the functors storing the volumetric forces.
CellCenteredMapFunctor< ADRealVectorValue, std::unordered_map< dof_id_type, ADRealVectorValue > > _a
A map from element IDs to &#39;a&#39; coefficient data.
unsigned int n_threads()
std::unordered_set< dof_id_type > getBoundaryActiveSemiLocalElemIds(BoundaryID bid) const
const Moose::Functor< T > & getFunctor(const std::string &name, const THREAD_ID tid, const std::string &requestor_name, bool requestor_is_ad)
const unsigned int invalid_uint
virtual void ghostADataOnBoundary(const BoundaryID boundary_id) override
makes sure coefficient data gets communicated on both sides of a given boundary
virtual Elem * elemPtr(const dof_id_type i)
MooseMesh & _moose_mesh
The MooseMesh that this user object operates on.
face_info_iterator ownedFaceInfoBegin()
virtual const Moose::FunctorBase< ADReal > & epsilon(THREAD_ID tid) const
A virtual method that allows us to only implement getVelocity once for free and porous flows...
INSFVVelocityVariable *const _u
The thread 0 copy of the x-velocity variable.
void coordTransformFactor(const P &point, C &factor, const Moose::CoordinateSystemType coord_type, const unsigned int rz_radial_coord=libMesh::invalid_uint)
INSFVVelocityVariable *const _v
The thread 0 copy of the y-velocity variable (null if the problem is 1D)
const ExecFlagType & getCurrentExecuteOnFlag() const
const Elem & elem() const
std::vector< MooseVariableField< Real > * > _disp_xs
All the thread copies of the x-displacement variable.
Moose::StateArg determineState() const
std::tuple< bool, ADReal, ADReal > isPorosityJumpFace(const Moose::Functor< ADReal > &porosity, const FaceInfo &fi, const Moose::StateArg &time)
Checks to see whether the porosity value jumps from one side to the other of the provided face...
Definition: NSFVUtils.C:58
void coordTransformFactor(const SubProblem &s, SubdomainID sub_id, const P &point, C &factor, SubdomainID neighbor_sub_id=libMesh::Elem::invalid_subdomain_id)
std::vector< MooseVariableField< Real > * > _disp_ys
All the thread copies of the y-displacement variable.
void addFunctor(const std::string &name, const Moose::FunctorBase< T > &functor, const THREAD_ID tid)
const Moose::ConstantFunctor< ADReal > _zero_functor
A zero functor potentially used in _a_read.
auto raw_value(const Eigen::Map< T > &in)
bool _pull_all_nonlocal
Whether we want to pull all nonlocal &#39;a&#39; coefficient data.
Moose::VectorComponentFunctor< ADReal > _az
The z-component of &#39;a&#39;.
const bool _displaced
Whether this object is operating on the displaced mesh.
void addAvailableFlags(const ExecFlagType &flag, Args... flags)
const std::vector< MooseFunctorName > * _volumetric_force_functors
Names of the functors storing the volumetric forces.
const Parallel::Communicator & _communicator
virtual const std::set< SubdomainID > & blockIDs() const
The following methods are specializations for using the Parallel::packed_range_* routines for a vecto...
const ExecFlagType EXEC_ALWAYS
Real faceArea() const
virtual const std::string & name() const
void mooseWarning(Args &&... args) const
InputParameters emptyInputParameters()
A class that gathers &#39;a&#39; coefficient data from flux kernels, boundary conditions, and interface kerne...
virtual Elem * queryElemPtr(const dof_id_type i)
SubProblem & _subproblem
bool isParamValid(const std::string &name) const
std::vector< MooseVariableFVReal * > _us
All the thread copies of the x-velocity variable.
Moose::ElemArg makeElemArg(const Elem *elem, bool correct_skewnewss=false) const
bool pressureSkewCorrection(THREAD_ID tid) const
Whether central differencing face interpolations of pressure should include a skewness correction...
void push_parallel_vector_data(const Communicator &comm, MapToVectors &&data, const ActionFunctor &act_on_data)
uint8_t processor_id_type
virtual VectorValue< ADReal > getVelocity(const Moose::FV::InterpMethod m, const FaceInfo &fi, const Moose::StateArg &time, const THREAD_ID tid, bool subtract_mesh_velocity) const override
Retrieve a face velocity.
const std::vector< const FaceInfo *> & faceInfo() const
processor_id_type n_processors() const
static InputParameters uniqueParams()
Parameters of this object that should be added to the NSFV action that are unique to this object...
registerMooseObject("NavierStokesApp", INSFVRhieChowInterpolator)
const Elem * neighborPtr() const
std::unordered_set< dof_id_type > getBoundaryActiveNeighborElemIds(BoundaryID bid) const
virtual void addToA(const libMesh::Elem *elem, unsigned int component, const ADReal &value) override
API that momentum residual objects that have on-diagonals for velocity call.
Moose::VectorComponentFunctor< ADReal > _ay
The y-component of &#39;a&#39;.
Real value(unsigned n, unsigned alpha, unsigned beta, Real x)
void min(const T &r, T &o, Request &req) const
TheWarehouse & theWarehouse() const
static InputParameters validParams()
unsigned int which_neighbor_am_i(const Elem *e) const
boundary_id_type BoundaryID
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
virtual unsigned int currentNlSysNum() const override
void insfvSetup()
perform the setup of this object
std::unordered_set< const Elem * > _elements_to_push_pull
Non-local elements that we should push and pull data for across processes.
const unsigned int _dim
The dimension of the mesh, e.g. 3 for hexes and tets, 2 for quads and tris.
const Point & normal() const
void paramError(const std::string &param, Args... args) const
unsigned int number() const
auto norm(const T &a) -> decltype(std::abs(a))
void fillARead()
Fills the _a_read data member at construction time with the appropriate functors. ...
Moose::FV::InterpMethod _velocity_interp_method
The interpolation method to use for the velocity.
void fillContainer(const std::string &var_name, Container &container)
Fill the passed-in variable container with the thread copies of var_name.
const libMesh::MeshBase & _mesh
The libMesh mesh that this object acts on.
const Elem * elemPtr() const
const MooseEnum _volume_force_correction_method
– Method used for computing the properties average
static std::vector< std::string > listOfCommonParams()
bool onBoundary(const SubdomainRestrictable &obj, const FaceInfo &fi)
void checkBlocks(const VarType &var) const
Check the block consistency between the passed in var and us.
virtual unsigned int n_sides() const=0
const Elem * neighbor_ptr(unsigned int i) const
const INSFVVelocityVariable * vel() const
std::vector< const Moose::FunctorBase< VectorValue< ADReal > > * > _a_read
A vector sized according to the number of threads that holds the &#39;a&#39; data we will read from when comp...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
static const std::string v
Definition: NS.h:84
virtual void meshChanged() override
subdomain_id_type subdomain_id() const
INSFVVelocityVariable *const _w
The thread 0 copy of the z-velocity variable (null if the problem is not 3D)
Real _baseline_volume_force
Minimum absolute RC force over the domain.
FEProblemBase & _fe_problem
bool relativeFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
std::vector< unsigned int > _var_numbers
The velocity variable numbers.
Query query()
std::unique_ptr< ConstElemRange > _elem_range
All the active and elements local to this process that exist on this object&#39;s subdomains.
virtual Real volume() const
IntRange< T > make_range(T beg, T end)
std::vector< std::unique_ptr< Moose::VectorCompositeFunctor< ADReal > > > _disps
A functor for computing the displacement.
std::vector< MooseVariableField< Real > * > _disp_zs
All the thread copies of the z-displacement variable.
const bool & _bool_correct_vf
Correct Rhie-Chow coefficients for volumetric force flag.
void mooseError(Args &&... args) const
virtual void initialSetup() override
std::vector< MooseVariableFVReal * > _ws
All the thread copies of the z-velocity variable.
This user-object gathers &#39;a&#39; (on-diagonal velocity coefficients) data.
Moose::CoordinateSystemType getCoordSystem(SubdomainID sid) const
static const std::string velocity
Definition: NS.h:45
const ExecFlagType EXEC_PRE_KERNELS
Moose::VectorComponentFunctor< ADReal > _ax
bool _a_data_provided
Whether &#39;a&#39; data has been provided by the user.
face_info_iterator ownedFaceInfoEnd()
bool velocitySkewCorrection(THREAD_ID tid) const
Whether central differencing face interpolations of velocity should include a skewness correction Als...
bool hasBlocks(const SubdomainName &name) const
virtual void finalize() override
processor_id_type processor_id() const
void interpolate(InterpMethod m, T &result, const T2 &value1, const T3 &value2, const FaceInfo &fi, const bool one_is_elem)
INSFVRhieChowInterpolator(const InputParameters &params)
std::vector< std::unique_ptr< Moose::FunctorBase< VectorValue< ADReal > > > > _a_aux
A vector sized according to the number of threads that holds vector composites of &#39;a&#39; component funct...
std::vector< MooseVariableFVReal * > _vs
All the thread copies of the y-velocity variable.
auto index_range(const T &sizable)
static InputParameters validParams()
Point vertex_average() const
static std::string deduceFunctorName(const std::string &name, const InputParameters &params)
std::vector< std::unique_ptr< PiecewiseByBlockLambdaFunctor< ADRealVectorValue > > > _vel
A functor for computing the (non-RC corrected) velocity.
unsigned int THREAD_ID
const unsigned int _momentum_sys_number
The number of the nonlinear system in which the monolithic momentum and continuity equations are loca...
const ExecFlagType EXEC_INITIAL