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