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