https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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"
14#include "MooseMesh.h"
15#include "SystemBase.h"
16#include "NS.h"
17#include "Assembly.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
40using 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
69std::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}),
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
189 if (_velocity_interp_method == Moose::FV::InterpMethod::Average && isParamValid("a_u"))
190 paramError("a_u",
191 "Rhie Chow coefficients may not be specified for average velocity interpolation");
192
193 if (_velocity_interp_method != Moose::FV::InterpMethod::Average)
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
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
211void
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
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
269void
271{
272 insfvSetup();
273
274 if (_velocity_interp_method == Moose::FV::InterpMethod::Average)
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))
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 {
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
328void
330{
332 std::make_unique<ConstElemRange>(_mesh.active_local_subdomain_set_elements_begin(blockIDs()),
333 _mesh.active_local_subdomain_set_elements_end(blockIDs()));
334}
335
336void
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
345 _a.clear();
346}
347
348void
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
369void
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 {
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
415void
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
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
498void
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,
553 Moose::FV::LimiterType::CentralDifference,
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
563 return MetaPhysicL::raw_value(velocity);
564 else
565 return velocity;
566 }
567
568 VectorValue<ADReal> velocity;
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
583 velocity = MetaPhysicL::raw_value(velocity);
584
585 // Return if Rhie-Chow was not requested or if we have a porosity jump
586 if (m == Moose::FV::InterpMethod::Average ||
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 }
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) &&
605 (_velocity_interp_method == 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()) ==
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
794 ? Moose::FV::InterpMethod::SkewCorrectedAverage
795 : Moose::FV::InterpMethod::Average;
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
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
830 return MetaPhysicL::raw_value(velocity);
831 else
832 return velocity;
833}
DualNumber< Real, DNDerivativeType, true > ADReal
void coordTransformFactor(const SubProblem &s, SubdomainID sub_id, const P &point, C &factor, SubdomainID neighbor_sub_id=libMesh::Elem::invalid_subdomain_id)
boundary_id_type BoundaryID
const Real p
const double v
registerMooseObject("NavierStokesApp", INSFVRhieChowInterpolator)
InputParameters emptyInputParameters()
unsigned int THREAD_ID
const ExecFlagType EXEC_ALWAYS
const ExecFlagType EXEC_INITIAL
const ExecFlagType EXEC_PRE_KERNELS
bool hasBlocks(const SubdomainName &name) const
virtual const std::set< SubdomainID > & blockIDs() const
void addAvailableFlags(const ExecFlagType &flag, Args... flags)
bool shouldSolve() const
const ExecFlagType & getCurrentExecuteOnFlag() const
virtual unsigned int currentNlSysNum() const override
TheWarehouse & theWarehouse() const
Real neighborVolume() const
const Point & normal() const
Real elemVolume() const
const Elem & elem() const
const Elem * neighborPtr() const
Real faceArea() const
const Elem * elemPtr() const
const Point & neighborCentroid() const
const Point & elemCentroid() const
const Point & faceCentroid() const
static std::string deduceFunctorName(const std::string &name, const InputParameters &params)
Moose::ElemArg makeElemArg(const Elem *elem, bool correct_skewnewss=false) const
A class that gathers body force data from elemental kernels contributing to the Navier-Stokes momentu...
A class that gathers 'a' coefficient data from flux kernels, boundary conditions, and interface kerne...
All objects that contribute to pressure-based (e.g.
This user-object gathers 'a' (on-diagonal velocity coefficients) data.
std::vector< std::unique_ptr< PiecewiseByBlockLambdaFunctor< ADRealVectorValue > > > _vel
A functor for computing the (non-RC corrected) velocity.
INSFVRhieChowInterpolator(const InputParameters &params)
virtual void ghostADataOnBoundary(const BoundaryID boundary_id) override
makes sure coefficient data gets communicated on both sides of a given boundary
bool pressureSkewCorrection(THREAD_ID tid) const
Whether central differencing face interpolations of pressure should include a skewness correction.
virtual void initialSetup() override
std::unordered_set< const Elem * > _elements_to_push_pull
Non-local elements that we should push and pull data for across processes.
std::vector< std::unique_ptr< Moose::VectorCompositeFunctor< ADReal > > > _disps
A functor for computing the displacement.
Moose::VectorComponentFunctor< ADReal > _ax
std::vector< MooseVariableField< Real > * > _disp_zs
All the thread copies of the z-displacement variable.
std::vector< MooseVariableField< Real > * > _disp_ys
All the thread copies of the y-displacement variable.
std::unique_ptr< ConstElemRange > _elem_range
All the active and elements local to this process that exist on this object's subdomains.
std::vector< MooseVariableField< Real > * > _disp_xs
All the thread copies of the x-displacement variable.
static InputParameters validParams()
const MooseEnum _volume_force_correction_method
– Method used for computing the properties average
const bool & _bool_correct_vf
Correct Rhie-Chow coefficients for volumetric force flag.
const unsigned int _momentum_sys_number
The number of the nonlinear system in which the monolithic momentum and continuity equations are loca...
bool velocitySkewCorrection(THREAD_ID tid) const
Whether central differencing face interpolations of velocity should include a skewness correction Als...
bool _a_data_provided
Whether 'a' data has been provided by the user.
Moose::VectorComponentFunctor< ADReal > _ay
The y-component of 'a'.
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 'a' component funct...
static InputParameters uniqueParams()
Parameters of this object that should be added to the NSFV action that are unique to this object.
Moose::VectorComponentFunctor< ADReal > _az
The z-component of 'a'.
bool needAComputation() const
Whether we need 'a' coefficient computation.
std::vector< const Moose::Functor< Real > * > _volumetric_force
Values of the functors storing the volumetric forces.
virtual void meshChanged() override
const std::vector< MooseFunctorName > * _volumetric_force_functors
Names of the functors storing the volumetric forces.
std::vector< const Moose::FunctorBase< VectorValue< ADReal > > * > _a_read
A vector sized according to the number of threads that holds the 'a' data we will read from when comp...
CellCenteredMapFunctor< ADRealVectorValue, std::unordered_map< dof_id_type, ADRealVectorValue > > _a
A map from element IDs to 'a' coefficient data.
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.
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.
Real _baseline_volume_force
Minimum absolute RC force over the domain.
void insfvSetup()
perform the setup of this object
bool _pull_all_nonlocal
Whether we want to pull all nonlocal 'a' coefficient data.
void fillARead()
Fills the _a_read data member at construction time with the appropriate functors.
static std::vector< std::string > listOfCommonParams()
const Moose::ConstantFunctor< ADReal > _zero_functor
A zero functor potentially used in _a_read.
const std::string & name() const
void paramError(const std::string &param, Args... args) const
void mooseError(Args &&... args) const
void mooseWarning(Args &&... args) const
bool isParamValid(const std::string &name) const
std::unordered_set< dof_id_type > getBoundaryActiveSemiLocalElemIds(BoundaryID bid) const
face_info_iterator ownedFaceInfoEnd()
virtual Elem * elemPtr(const dof_id_type i)
face_info_iterator ownedFaceInfoBegin()
const std::vector< const FaceInfo * > & faceInfo() const
virtual Elem * queryElemPtr(const dof_id_type i)
std::unordered_set< dof_id_type > getBoundaryActiveNeighborElemIds(BoundaryID bid) const
SystemBase & sys()
const bool _displaced
Whether this object is operating on the displaced mesh.
Moose::FV::InterpMethod _velocity_interp_method
The interpolation method to use for the velocity.
INSFVVelocityVariable *const _w
The thread 0 copy of the z-velocity variable (null if the problem is not 3D)
std::vector< MooseVariableFVReal * > _ws
All the thread copies of the z-velocity variable.
void fillContainer(const std::string &var_name, Container &container)
Fill the passed-in variable container with the thread copies of var_name.
const INSFVVelocityVariable * vel() const
MooseMesh & _moose_mesh
The MooseMesh that this user object operates on.
const libMesh::MeshBase & _mesh
The libMesh mesh that this object acts on.
std::vector< unsigned int > _var_numbers
The velocity variable numbers.
std::vector< MooseVariableFVReal * > _vs
All the thread copies of the y-velocity variable.
static InputParameters validParams()
void checkBlocks(const VarType &var) const
Check the block consistency between the passed in var and us.
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 _v
The thread 0 copy of the y-velocity variable (null if the problem is 1D)
std::vector< MooseVariableFVReal * > _us
All the thread copies of the x-velocity variable.
INSFVVelocityVariable *const _u
The thread 0 copy of the x-velocity variable.
const unsigned int _dim
The dimension of the mesh, e.g. 3 for hexes and tets, 2 for quads and tris.
std::vector< MooseVariableFVReal * > _ps
All the thread copies of the pressure variable.
const Moose::Functor< T > & getFunctor(const std::string &name, const THREAD_ID tid, const std::string &requestor_name, bool requestor_is_ad)
unsigned int getAxisymmetricRadialCoord() const
Moose::CoordinateSystemType getCoordSystem(SubdomainID sid) const
void addFunctor(const std::string &name, const Moose::FunctorBase< T > &functor, const THREAD_ID tid)
unsigned int number() const
void min(const T &r, T &o, Request &req) const
Query query()
Moose::StateArg determineState() const
SubProblem & _subproblem
FEProblemBase & _fe_problem
unsigned int which_neighbor_am_i(const Elem *e) const
subdomain_id_type subdomain_id() const
virtual Real volume() const
virtual unsigned int n_sides() const=0
const Elem * neighbor_ptr(unsigned int i) const
Point vertex_average() const
const Parallel::Communicator & _communicator
processor_id_type processor_id() const
processor_id_type n_processors() const
auto raw_value(const Eigen::Map< T > &in)
void coordTransformFactor(const P &point, C &factor, const Moose::CoordinateSystemType coord_type, const unsigned int rz_radial_coord=libMesh::invalid_uint)
void interpolate(InterpMethod m, T &result, const T2 &value1, const T3 &value2, const FaceInfo &fi, const bool one_is_elem)
bool elemHasFaceInfo(const Elem &elem, const Elem *const neighbor)
bool onBoundary(const SubdomainRestrictable &obj, const FaceInfo &fi)
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:119
void pull_parallel_vector_data(const Communicator &comm, const MapToVectors &queries, GatherFunctor &gather_data, const ActionFunctor &act_on_data, const datum *example)
void push_parallel_vector_data(const Communicator &comm, MapToVectors &&data, const ActionFunctor &act_on_data)
void parallel_reduce(const Range &range, Body &body, unsigned int n_threads=libMesh::n_threads())
The following methods are specializations for using the Parallel::packed_range_* routines for a vecto...
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
auto index_range(const T &sizable)
const unsigned int invalid_uint
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
uint8_t processor_id_type
IntRange< T > make_range(T beg, T end)
unsigned int n_threads()