https://mooseframework.inl.gov
Loading...
Searching...
No Matches
RhieChowMassFlux.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
10// MOOSE includes
11#include "RhieChowMassFlux.h"
12#include "SubProblem.h"
13#include "MooseMesh.h"
14#include "NS.h"
16#include "PIMPLE.h"
17#include "SIMPLE.h"
18#include "MathFVUtils.h"
19#include "FVUtils.h"
20#include "PetscVectorReader.h"
21#include "LinearSystem.h"
29#include "MooseUtils.h"
30
31// libMesh includes
32#include "libmesh/mesh_base.h"
33#include "libmesh/elem.h"
34#include "libmesh/elem_range.h"
35#include "libmesh/petsc_matrix.h"
36
37#include <cmath>
38
40
43{
46
47 params.addClassDescription("Computes H/A and 1/A together with face mass fluxes for segregated "
48 "momentum-pressure equations using linear systems.");
49
50 params.addRequiredParam<VariableName>(NS::pressure, "The pressure variable.");
51 params.addRequiredParam<VariableName>("u", "The x-component of velocity");
52 params.addParam<VariableName>("v", "The y-component of velocity");
53 params.addParam<VariableName>("w", "The z-component of velocity");
54 params.addRequiredParam<std::string>(
55 "p_diffusion_kernel",
56 "The LinearFVPressureCorrectionDiffusion kernel acting on the pressure.");
57
58 params.addRequiredParam<MooseFunctorName>(NS::density, "Density functor");
59
60 // We disable the execution of this, should only provide functions
61 // for the SIMPLE executioner
62 ExecFlagEnum & exec_enum = params.set<ExecFlagEnum>("execute_on", true);
64 exec_enum = {EXEC_NONE};
65 params.suppressParameter<ExecFlagEnum>("execute_on");
66
67 // Pressure projection
68 params.addParam<MooseEnum>("pressure_projection_method",
69 MooseEnum("standard consistent", "standard"),
70 "The method to use in the pressure projection for Ainv - "
71 "standard (SIMPLE) or consistent (SIMPLEC)");
72 params.addParam<MooseEnum>(
73 "pressure_diffusion_interpolation",
74 MooseEnum("average harmonic", "average"),
75 "The face interpolation method for Ainv in the pressure correction diffusion term.");
76 return params;
77}
78
82 _moose_mesh(UserObject::_subproblem.mesh()),
83 _mesh(_moose_mesh.getMesh()),
84 _dim(blocksMaxDimension()),
85 _p(dynamic_cast<MooseLinearVariableFVReal *>(
86 &UserObject::_subproblem.getVariable(0, getParam<VariableName>(NS::pressure)))),
87 _vel(_dim, nullptr),
88 _HbyA_flux(_moose_mesh, blockIDs(), "HbyA_flux"),
89 _Ainv(_moose_mesh, blockIDs(), "Ainv"),
90 _face_mass_flux(
91 declareRestartableData<FaceCenteredMapFunctor<Real, std::unordered_map<dof_id_type, Real>>>(
92 "face_flux", _moose_mesh, blockIDs(), "face_values")),
93 _rho(getFunctor<Real>(NS::density)),
94 _pressure_system(nullptr),
95 _pressure_gradient_field(nullptr),
96 _base_pressure_gradient_field(nullptr),
97 _global_pressure_system_number(0),
98 _pressure_projection_method(getParam<MooseEnum>("pressure_projection_method")),
99 _pressure_diffusion_interp_method(getParam<MooseEnum>("pressure_diffusion_interpolation") ==
100 "harmonic"
101 ? Moose::FV::InterpMethod::HarmonicAverage
102 : Moose::FV::InterpMethod::Average)
103{
104 if (!_p)
105 paramError(NS::pressure, "the pressure must be a MooseLinearVariableFVReal.");
106 checkBlocks(*_p);
107
108 std::vector<std::string> vel_names = {"u", "v", "w"};
109 for (const auto i : index_range(_vel))
110 {
111 _vel[i] = dynamic_cast<MooseLinearVariableFVReal *>(
112 &UserObject::_subproblem.getVariable(0, getParam<VariableName>(vel_names[i])));
113
114 if (!_vel[i])
115 paramError(vel_names[i], "the velocity must be a MOOSELinearVariableFVReal.");
116 checkBlocks(*_vel[i]);
117 }
118
119 // Register the elemental/face functors which will be queried in the pressure equation
120 for (const auto tid : make_range(UserObject::_subproblem.numThreads()))
121 {
124 }
125
126 if (!dynamic_cast<SIMPLE *>(getMooseApp().getExecutioner()) &&
127 !dynamic_cast<PIMPLE *>(getMooseApp().getExecutioner()))
128 mooseError(this->name(),
129 " should only be used with a linear segregated thermal-hydraulics solver!");
130}
131
132void
134 const std::vector<LinearSystem *> & momentum_systems,
135 LinearSystem & pressure_system,
136 const std::vector<unsigned int> & momentum_system_numbers)
137{
138 _momentum_systems = momentum_systems;
139 _momentum_system_numbers = momentum_system_numbers;
140 _pressure_system = &pressure_system;
142
143 auto * const pressure_var =
145 if (!pressure_var)
146 mooseError("The pressure variable in system '",
148 "' must be a MooseLinearVariableFVReal.");
149
150 // For each momentum system, find the LinearFVMomentumPressure kernels applicable to this RC
151 // object, i.e. those whose blocks overlap the blocks this RC object handles. Kernels entirely
152 // outside our blocks are irrelevant and ignored; multiple applicable kernels per system are
153 // allowed as long as they partition our blocks without overlapping (checked below).
154 std::vector<std::vector<const LinearFVMomentumPressure *>> applicable_kernels(
155 momentum_systems.size());
156 for (const auto system_index : index_range(momentum_systems))
157 {
158 std::vector<LinearFVElementalKernel *> kernels;
160 .query()
161 .template condition<AttribThread>(_tid)
162 .template condition<AttribSystem>("LinearFVElementalKernel")
163 .template condition<AttribSysNum>(momentum_system_numbers[system_index])
164 .queryInto(kernels);
165
166 for (const auto * const kernel : kernels)
167 {
168 const auto * const candidate = dynamic_cast<const LinearFVMomentumPressure *>(kernel);
169 if (candidate && MooseUtils::setsIntersect(candidate->blockIDs(), blockIDs()))
170 applicable_kernels[system_index].push_back(candidate);
171 }
172 }
173
174 for (const auto system_index : index_range(momentum_systems))
175 {
176 const auto & kernels = applicable_kernels[system_index];
177 const auto & system_name = momentum_systems[system_index]->name();
178
179 // Coverage: the applicable kernels must together cover every block this RC object handles.
180 std::set<SubdomainID> covered_blocks;
181 for (const auto * const kernel : kernels)
182 covered_blocks.insert(kernel->blockIDs().begin(), kernel->blockIDs().end());
183
184 std::set<SubdomainID> missing_blocks;
185 std::set_difference(blockIDs().begin(),
186 blockIDs().end(),
187 covered_blocks.begin(),
188 covered_blocks.end(),
189 std::inserter(missing_blocks, missing_blocks.end()));
190
191 if (!missing_blocks.empty())
192 mooseError("RhieChowMassFlux '",
193 name(),
194 "' found no applicable LinearFVMomentumPressure kernel for momentum system '",
195 system_name,
196 "' on block(s) ",
197 Moose::stringify(missing_blocks),
198 ".");
199
200 // Component and pairwise block-overlap checks among the applicable kernels for this system.
201 for (const auto i : index_range(kernels))
202 {
203 if (kernels[i]->momentumComponent() != system_index)
204 mooseError("LinearFVMomentumPressure kernel '",
205 kernels[i]->name(),
206 "' declares momentum component ",
207 kernels[i]->momentumComponent(),
208 " but is linked to momentum system '",
209 system_name,
210 "' (component ",
211 system_index,
212 ") through RhieChowMassFlux '",
213 name(),
214 "'.");
215
216 for (const auto j : make_range(i + 1, kernels.size()))
217 {
218 std::set<SubdomainID> overlap;
219 for (const auto block : kernels[i]->blockIDs())
220 if (hasBlocks(block) && kernels[j]->blockIDs().count(block))
221 overlap.insert(block);
222 if (!overlap.empty())
223 mooseError("Momentum system '",
224 system_name,
225 "' has more than one LinearFVMomentumPressure kernel contributing a "
226 "pressure source on the same block(s) for RhieChowMassFlux '",
227 name(),
228 "': '",
229 kernels[i]->name(),
230 "' and '",
231 kernels[j]->name(),
232 "' overlap on block(s) ",
233 Moose::stringify(overlap),
234 ".");
235 }
236 }
237 }
238
239 // Flatten every system's applicable kernels into one list spanning the whole RC object. Even
240 // kernels on disjoint blocks/components must share one coupling-gradient field, because H/A
241 // has a single field source for this RC object.
242 std::vector<const LinearFVMomentumPressure *> momentum_pressure_kernels;
243 for (auto & kernels : applicable_kernels)
244 momentum_pressure_kernels.insert(
245 momentum_pressure_kernels.end(), kernels.begin(), kernels.end());
246
247 const auto * const coupling_kernel = momentum_pressure_kernels.front();
248 const auto & coupling_reader = coupling_kernel->pressureGradientField();
249
250 if (dynamic_cast<const FVReconstructedPressureGradient *>(&coupling_reader.method()) &&
251 coupling_kernel->pressureVariableNumber() != _p->number())
252 mooseError("FVReconstructedPressureGradient can only be used for the pressure variable "
253 "registered on RhieChowMassFlux '",
254 name(),
255 "'.");
256
257 for (const auto * const kernel : momentum_pressure_kernels)
258 {
259 const auto & reader = kernel->pressureGradientField();
260
261 if (&reader.system() != &coupling_reader.system() ||
262 reader.systemNumber() != coupling_reader.systemNumber())
263 mooseError("RhieChowMassFlux '",
264 name(),
265 "': momentum pressure kernels '",
266 coupling_kernel->name(),
267 "' (block(s) ",
268 Moose::stringify(coupling_kernel->blockIDs()),
269 ") and '",
270 kernel->name(),
271 "' (block(s) ",
272 Moose::stringify(kernel->blockIDs()),
273 ") must use pressure gradients from the same system.");
274
275 if (reader.variableNumber() != coupling_reader.variableNumber())
276 mooseError("RhieChowMassFlux '",
277 name(),
278 "': momentum pressure kernels '",
279 coupling_kernel->name(),
280 "' (pressure variable number ",
281 coupling_reader.variableNumber(),
282 ") and '",
283 kernel->name(),
284 "' (pressure variable number ",
285 reader.variableNumber(),
286 ") must use the same pressure variable.");
287
288 if (&reader.method() != &coupling_reader.method())
289 mooseError("RhieChowMassFlux '",
290 name(),
291 "': momentum pressure kernels '",
292 coupling_kernel->name(),
293 "' and '",
294 kernel->name(),
295 "' must use the same pressure gradient method.");
296
297 if (&reader.components() != &coupling_reader.components())
298 mooseError("RhieChowMassFlux '",
299 name(),
300 "': momentum pressure kernels '",
301 coupling_kernel->name(),
302 "' and '",
303 kernel->name(),
304 "' must share the same registered pressure gradient field.");
305 }
306
307 _pressure_gradient_field = &coupling_reader;
308
310 {
311 reconstructedGradientMethod().linkFlowSystem(*this, coupling_reader);
312 const auto & base_reader =
313 pressure_var->requestCellGradients(reconstructedGradientMethod().baseGradientMethodName());
314 _base_pressure_gradient_field = &base_reader;
315
317 "Reconstructed and base pressure gradient readers must be distinct when "
318 "FVReconstructedPressureGradient is active.");
319 }
320 else
322
325 for (auto & system : _momentum_systems)
326 {
327 _global_momentum_system_numbers.push_back(system->number());
328 _momentum_implicit_systems.push_back(dynamic_cast<LinearImplicitSystem *>(&system->system()));
329 }
330
332 {
335 _pressure_projection_method != "consistent")
336 paramError("pressure_projection_method",
337 "FVReconstructedPressureGradient with nonorthogonal pressure diffusion requires "
338 "the consistent pressure projection.");
339 }
340
342
343 std::set<BoundaryID> velocity_boundary_ids;
344 for (const auto dim_i : make_range(_dim))
345 for (const auto & [boundary_id, _] : _vel[dim_i]->getBoundaryConditionMap())
346 velocity_boundary_ids.insert(boundary_id);
347
348 const auto is_dirichlet =
349 [](const MooseLinearVariableFVReal & variable, const BoundaryID boundary_id)
350 {
351 return dynamic_cast<const LinearFVAdvectionDiffusionFunctorDirichletBC *>(
352 variable.getBoundaryCondition(boundary_id)) != nullptr;
353 };
354
355 // The legacy boundary HbyA reconstruction uses the x-velocity BC as a proxy for every velocity
356 // component, so all components must have the same Dirichlet classification on those boundaries.
357 for (const auto boundary_id : velocity_boundary_ids)
358 {
359 const auto * const pressure_bc = _p->getBoundaryCondition(boundary_id);
360 if (dynamic_cast<const LinearFVPressureFluxBC *>(pressure_bc))
361 continue;
362
363 const bool velocity_is_dirichlet = is_dirichlet(*_vel[0], boundary_id);
364 for (const auto dim_i : make_range(_dim))
365 if (is_dirichlet(*_vel[dim_i], boundary_id) != velocity_is_dirichlet)
366 mooseError("All velocity components must either have Dirichlet boundary conditions or "
367 "non-Dirichlet boundary conditions on boundary '",
368 _moose_mesh.getBoundaryName(boundary_id),
369 "' when the pressure boundary condition is not a LinearFVPressureFluxBC.");
370 }
371}
372
373void
375{
376 _HbyA_flux.clear();
377 _Ainv.clear();
378 _face_mass_flux.clear();
379 _grad_p_current.clear();
381}
382
383void
385{
386 // Nothing to reset when momentum hasn't been linked (e.g. should_solve_momentum = false, used
387 // by the restart-freeze pattern).
389 return;
390
391 _grad_p_current.clear();
392
394 return;
395
396 // Seed the next attempt from the most recently accepted coupling gradient. The linear FV
397 // gradient manager restores that state after rejection and checkpoints it for restart/recovery.
399}
400
401void
403{
404 mooseAssert(_pressure_gradient_field,
405 "The pressure gradient field must be linked before preparing momentum.");
406
407 _grad_p_current.clear();
408 for (const auto & component : pressureGradientComponents())
409 _grad_p_current.push_back(component->clone());
410}
411
412void
418
419void
421{
423 return;
424
425 mooseAssert(!_grad_p_current.empty(),
426 "A momentum predictor must be prepared before another PISO corrector.");
427
428 for (const auto component : make_range(dimension()))
431}
432
433void
435{
437 return;
438
439 // The current pressure solution and corrected face flux are both unrelaxed here. Form the
440 // conservative candidate before pressure relaxation replaces that solution.
442 updateCellVelocity(reconstructedGradientMethod().reconstructedCandidate(*this));
443}
444
445void
447{
449 {
450 // Refresh every ordinary pressure gradient while leaving the old coupling field untouched
451 // until the current reconstructed candidate has been relaxed into it.
456 }
457 else
458 {
461 }
462}
463
464void
469
470void
472{
473 // We fetch the pressure diffusion kernel to ensure that the face flux correction
474 // is consistent with the pressure discretization in the Poisson equation.
475 std::vector<LinearFVFluxKernel *> flux_kernel;
476 auto base_query = _fe_problem.theWarehouse()
477 .query()
478 .template condition<AttribThread>(_tid)
479 .template condition<AttribSysNum>(_p->sys().number())
480 .template condition<AttribSystem>("LinearFVFluxKernel")
481 .template condition<AttribName>(getParam<std::string>("p_diffusion_kernel"))
482 .queryInto(flux_kernel);
483 if (flux_kernel.size() != 1)
485 "p_diffusion_kernel",
486 "The kernel with the given name could not be found or multiple instances were identified.");
487 _p_diffusion_kernel = dynamic_cast<LinearFVPressureCorrectionDiffusion *>(flux_kernel[0]);
489 paramError("p_diffusion_kernel",
490 "The provided diffusion kernel must be of type "
491 "LinearFVPressureCorrectionDiffusion.");
492}
493
494void
496{
497 // We cache the cell volumes into a petsc vector for corrections here so we can use
498 // the optimized petsc operations for the normalization
500 for (const auto & elem_info : _fe_problem.mesh().elemInfoVector())
501 // We have to check this because the variable might not be defined on the given
502 // block
503 if (hasBlocks(elem_info->subdomain_id()))
504 {
505 const auto elem_dof = elem_info->dofIndices()[_global_pressure_system_number][0];
506 _cell_fluid_volumes->set(elem_dof, elem_info->volume() * elem_info->coordFactor());
507 }
508
509 _cell_fluid_volumes->close();
510
511 _flow_face_info.clear();
512 for (auto & fi : _fe_problem.mesh().faceInfo())
513 if (hasBlocks(fi->elemPtr()->subdomain_id()) ||
514 (fi->neighborPtr() && hasBlocks(fi->neighborPtr()->subdomain_id())))
515 _flow_face_info.push_back(fi);
516}
517
518template <typename GradientComponent>
519void
520RhieChowMassFlux::updateCellVelocity(const std::vector<GradientComponent> & pressure_gradient)
521{
522 // u_C = -(H/A)_C - (1/A)_C * grad(p)_C.
523 for (const auto system_i : index_range(_momentum_implicit_systems))
524 {
525 auto working_vector = _Ainv_raw[system_i]->clone();
526 working_vector->pointwise_mult(*working_vector, *pressure_gradient[system_i]);
527 working_vector->add(*_HbyA_raw[system_i]);
528 working_vector->scale(-1.0);
529 (*_momentum_implicit_systems[system_i]->solution) = *working_vector;
530
531#ifndef NDEBUG
532 {
533 // Proof that the assignment above satisfies the momentum-coupling identity
534 // u_C + (H/A)_C + Ainv_C * grad(p)_C = 0. The solution was just assigned directly from
535 // this formula, so the residual is pure floating-point round-off from the
536 // clone/scale/add sequence, not solver error; 1e-10 is a loose absolute bound with
537 // headroom over that round-off.
538 auto defect = _Ainv_raw[system_i]->clone();
539 defect->pointwise_mult(*defect, *pressure_gradient[system_i]);
540 defect->add(*_HbyA_raw[system_i]);
541 defect->add(*_momentum_implicit_systems[system_i]->solution);
542 mooseAssert(MooseUtils::absoluteFuzzyEqual(defect->linfty_norm(), 0.0, 1e-10),
543 "RhieChowMassFlux: cell-velocity update violates the momentum-coupling "
544 "identity u + H/A + Ainv*grad(p) = 0.");
545 }
546#endif
547
548 _momentum_implicit_systems[system_i]->update();
549 _momentum_systems[system_i]->setSolution(
550 *_momentum_implicit_systems[system_i]->current_local_solution);
551 }
552}
553
554void
556{
557 for (const auto & pair : _HbyA_flux)
558 _HbyA_flux[pair.first] = 0;
559
560 for (const auto & pair : _Ainv)
561 _Ainv[pair.first] = 0;
562}
563
564void
566{
567 using namespace Moose::FV;
568
569 const auto time_arg = Moose::currentState();
570
571 // We loop through the faces and compute the resulting face fluxes from the
572 // initial conditions for velocity
573 for (auto & fi : _flow_face_info)
574 {
575 RealVectorValue density_times_velocity;
576 // On internal face we do a regular interpolation with geometric weights
577 if (_vel[0]->isInternalFace(*fi))
578 {
579 const auto & elem_info = *fi->elemInfo();
580 const auto & neighbor_info = *fi->neighborInfo();
581
582 Real elem_rho = _rho(makeElemArg(fi->elemPtr()), time_arg);
583 Real neighbor_rho = _rho(makeElemArg(fi->neighborPtr()), time_arg);
584
585 for (const auto dim_i : index_range(_vel))
586 interpolate(InterpMethod::Average,
587 density_times_velocity(dim_i),
588 _vel[dim_i]->getElemValue(elem_info, time_arg) * elem_rho,
589 _vel[dim_i]->getElemValue(neighbor_info, time_arg) * neighbor_rho,
590 *fi,
591 true);
592 }
593 // On the boundary, we just take the boundary values
594 else
595 {
596 const bool elem_is_fluid = hasBlocks(fi->elemPtr()->subdomain_id());
597 const Elem * const boundary_elem = elem_is_fluid ? fi->elemPtr() : fi->neighborPtr();
598
599 // We need this multiplier in case the face is an internal face and
600 const Real boundary_normal_multiplier = elem_is_fluid ? 1.0 : -1.0;
601 const Moose::FaceArg boundary_face{
602 fi, Moose::FV::LimiterType::CentralDifference, true, false, boundary_elem, nullptr};
603
604 const Real face_rho = _rho(boundary_face, time_arg);
605 for (const auto dim_i : index_range(_vel))
606 density_times_velocity(dim_i) = boundary_normal_multiplier * face_rho *
607 velocityBoundaryValue(dim_i, *fi, boundary_face);
608 }
609
610 _face_mass_flux[fi->id()] = density_times_velocity * fi->normal();
611 }
612}
613
614Real
616{
617 return _face_mass_flux.evaluate(&fi);
618}
619
620Real
622{
623 const std::string neighbor_cell_id =
624 fi.neighborPtr() ? std::to_string(fi.neighborPtr()->id()) : "none";
625 const auto face_flux = _face_mass_flux.find(fi.id());
626 if (face_flux == _face_mass_flux.end())
627 mooseError("RhieChowMassFlux '",
628 name(),
629 "' has no corrected mass flux for face ID ",
630 fi.id(),
631 " adjacent to cell ID ",
632 fi.elem().id(),
633 " and neighbor cell ID ",
634 neighbor_cell_id,
635 ".");
636
637 if (!std::isfinite(face_flux->second))
638 mooseError("RhieChowMassFlux '",
639 name(),
640 "' has non-finite corrected mass flux ",
641 face_flux->second,
642 " on face ID ",
643 fi.id(),
644 " adjacent to cell ID ",
645 fi.elem().id(),
646 " and neighbor cell ID ",
647 neighbor_cell_id,
648 ".");
649
650 // On a face shared with a non-flow block, the FaceInfo element may be the non-flow cell, so
651 // the density must be evaluated on the flow side.
652 const Elem * face_side = fi.elemPtr();
653 if (!_vel[0]->isInternalFace(fi) && !hasBlocks(fi.elemPtr()->subdomain_id()))
654 face_side = fi.neighborPtr();
655
656 const Moose::FaceArg face_arg{&fi,
657 /*limiter_type=*/Moose::FV::LimiterType::CentralDifference,
658 /*elem_is_upwind=*/true,
659 /*correct_skewness=*/false,
660 face_side,
661 /*state_limiter*/ nullptr};
662 const Real face_rho = _rho(face_arg, Moose::currentState());
663 if (!std::isfinite(face_rho) || face_rho <= 0.0)
664 mooseError("RhieChowMassFlux '",
665 name(),
666 "' requires finite, positive density for corrected face flux reconstruction, but "
667 "density is ",
668 face_rho,
669 " on face ID ",
670 fi.id(),
671 " adjacent to cell ID ",
672 fi.elem().id(),
673 " and neighbor cell ID ",
674 neighbor_cell_id,
675 ".");
676
677 const Real volumetric_flux = face_flux->second / face_rho;
678 if (!std::isfinite(volumetric_flux))
679 mooseError("RhieChowMassFlux '",
680 name(),
681 "' computed non-finite volumetric flux ",
682 volumetric_flux,
683 " on face ID ",
684 fi.id(),
685 " adjacent to cell ID ",
686 fi.elem().id(),
687 " and neighbor cell ID ",
688 neighbor_cell_id,
689 ".");
690
691 return volumetric_flux;
692}
693
696{
699 "The pressure gradient field has not been registered for RhieChowMassFlux '", name(), "'.");
700
702}
703
706{
708 mooseError("The base pressure gradient field has not been registered for RhieChowMassFlux '",
709 name(),
710 "'.");
711
713}
714
715const std::vector<std::unique_ptr<NumericVector<Number>>> &
717{
718 mooseAssert(!_HbyA_raw.empty(), "HbyA data is not ready.");
719
720 return _HbyA_raw;
721}
722
723const std::vector<std::unique_ptr<NumericVector<Number>>> &
725{
726 mooseAssert(!_Ainv_raw.empty(), "Ainv data is not ready.");
727
728 return _Ainv_raw;
729}
730
731const std::vector<NumericVector<Number> *> &
736
737unsigned int
739{
740 return _p->number();
741}
742
743Real
745 const FaceInfo & fi,
746 const Moose::StateArg & time,
747 const THREAD_ID /*tid*/,
748 bool libmesh_dbg_var(subtract_mesh_velocity)) const
749{
750 mooseAssert(!subtract_mesh_velocity, "RhieChowMassFlux does not support moving meshes yet!");
751
752 if (m != Moose::FV::InterpMethod::RhieChow)
753 mooseError("Interpolation methods other than Rhie-Chow are not supported!");
754 if (time.state != Moose::currentState().state)
755 mooseError("Older interpolation times are not supported!");
756
757 return getVolumetricFaceFlux(fi);
758}
759
760void
762{
763 using namespace Moose::FV;
764
765 // Petsc vector reader to make the repeated reading from the vector faster
766 PetscVectorReader p_reader(*_pressure_system->system().current_local_solution);
767
768 // We loop through the faces and compute the face fluxes using the pressure gradient
769 // and the momentum matrix/right hand side
770 for (auto & fi : _flow_face_info)
771 {
772 const Real p_grad_flux = computeFacePressureGradientFlux(*fi, p_reader);
773 // Compute the new face flux
774 const Real face_flux = -_HbyA_flux[fi->id()] + p_grad_flux;
775 _face_mass_flux[fi->id()] = face_flux;
776 }
777
779}
780
781Real
782RhieChowMassFlux::velocityBoundaryValue(const unsigned int component,
783 const FaceInfo & fi,
784 const Moose::FaceArg & boundary_face) const
785{
786 const auto face_type = fi.faceType(
787 std::make_pair(_vel[component]->number(), _global_momentum_system_numbers[component]));
788
789 LinearFVBoundaryCondition * dirichlet_bc = nullptr;
790 for (const auto bnd_id : fi.boundaryIDs())
791 if (auto * const bc_pointer = _vel[component]->getBoundaryCondition(bnd_id))
792 if (dynamic_cast<LinearFVAdvectionDiffusionFunctorDirichletBC *>(bc_pointer))
793 {
794 // Two Dirichlet conditions on one face would prescribe conflicting velocities.
795 mooseAssert(!dirichlet_bc,
796 "Face " + std::to_string(fi.id()) +
797 " carries more than one Dirichlet velocity boundary condition.");
798 dirichlet_bc = bc_pointer;
799 }
800
801 if (dirichlet_bc)
802 {
803 dirichlet_bc->setupFaceData(&fi, face_type);
804 return dirichlet_bc->computeBoundaryValue();
805 }
806
807 return MetaPhysicL::raw_value((*_vel[component])(boundary_face, Moose::currentState()));
808}
809
810Real
812{
813 // Making sure the kernel knows which face we are on
815
816 // We are setting this to 1.0 because we don't want to multiply the kernel contributions
817 // with the surface area yet. The surface area will be factored in in the advection kernels.
819
820 Real p_grad_flux = 0.0;
821 if (_p->isInternalFace(fi))
822 {
823 const auto & elem_info = *fi.elemInfo();
824 const auto & neighbor_info = *fi.neighborInfo();
825
826 // Fetching the dof indices for the pressure variable
827 const auto elem_dof = elem_info.dofIndices()[_global_pressure_system_number][0];
828 const auto neighbor_dof = neighbor_info.dofIndices()[_global_pressure_system_number][0];
829
830 // Fetching the values of the pressure for the element and the neighbor
831 const auto p_elem_value = p_reader(elem_dof);
832 const auto p_neighbor_value = p_reader(neighbor_dof);
833
834 // Compute the elem matrix contributions for the face
835 const auto elem_matrix_contribution = _p_diffusion_kernel->computeElemMatrixContribution();
836 const auto neighbor_matrix_contribution =
838 const auto elem_rhs_contribution = _p_diffusion_kernel->computeElemRightHandSideContribution();
839
840 // Compute the face flux from the matrix and right hand side contributions
841 p_grad_flux = (p_neighbor_value * neighbor_matrix_contribution +
842 p_elem_value * elem_matrix_contribution) -
843 elem_rhs_contribution;
844 }
845 else if (auto * bc_pointer = _p->getBoundaryCondition(*fi.boundaryIDs().begin()))
846 {
847 mooseAssert(fi.boundaryIDs().size() == 1, "We should only have one boundary on every face.");
848 bc_pointer->setupFaceData(
849 &fi, fi.faceType(std::make_pair(_p->number(), _global_pressure_system_number)));
850
851 const auto time_arg = Moose::currentState();
852 const ElemInfo & elem_info =
853 hasBlocks(fi.elemPtr()->subdomain_id()) ? *fi.elemInfo() : *fi.neighborInfo();
854 const auto p_elem_value = _p->getElemValue(elem_info, time_arg);
855 const auto matrix_contribution =
857 const auto rhs_contribution = _p_diffusion_kernel->computeBoundaryRHSContribution(*bc_pointer);
858
859 // On the boundary, only the element side has a contribution
860 p_grad_flux = p_elem_value * matrix_contribution - rhs_contribution;
861 }
862
863 return p_grad_flux;
864}
865
866void
868{
869 // We loop through the faces and populate the coupling fields (face H/A and 1/H)
870 // with 0s for now. Pressure corrector solves will always come after the
871 // momentum source so we expect these fields to change before the actual solve.
872 for (auto & fi : _fe_problem.mesh().faceInfo())
873 {
874 _Ainv[fi->id()];
875 _HbyA_flux[fi->id()];
876 }
877}
878
879void
881 const std::vector<std::unique_ptr<NumericVector<Number>>> & raw_hbya,
882 const std::vector<std::unique_ptr<NumericVector<Number>>> & raw_Ainv)
883{
884 // We have the raw H/A and 1/A vectors in a petsc format. This function
885 // will create face functors from them
886 using namespace Moose::FV;
887 const auto time_arg = Moose::currentState();
888
889 // Create the petsc vector readers for faster repeated access
890 std::vector<PetscVectorReader> hbya_reader;
891 for (const auto dim_i : index_range(raw_hbya))
892 hbya_reader.emplace_back(*raw_hbya[dim_i]);
893
894 std::vector<PetscVectorReader> ainv_reader;
895 for (const auto dim_i : index_range(raw_Ainv))
896 ainv_reader.emplace_back(*raw_Ainv[dim_i]);
897
898 // We loop through the faces and populate the coupling fields (face H/A and 1/A)
899 for (auto & fi : _flow_face_info)
900 {
901 Real face_rho = 0;
902 RealVectorValue face_hbya;
903
904 // We do the lookup in advance
905 auto & Ainv = _Ainv[fi->id()];
906
907 // If it is internal, we just interpolate (using geometric weights) to the face
908 if (_vel[0]->isInternalFace(*fi))
909 {
910 // Get the dof indices for the element and the neighbor
911 const auto & elem_info = *fi->elemInfo();
912 const auto & neighbor_info = *fi->neighborInfo();
913 const auto elem_dof = elem_info.dofIndices()[_global_momentum_system_numbers[0]][0];
914 const auto neighbor_dof = neighbor_info.dofIndices()[_global_momentum_system_numbers[0]][0];
915
916 // Get the density values for the element and neighbor. We need this multiplication to make
917 // the coupling fields mass fluxes.
918 const Real elem_rho = _rho(makeElemArg(fi->elemPtr()), time_arg);
919 const Real neighbor_rho = _rho(makeElemArg(fi->neighborPtr()), time_arg);
920
921 // Now we do the interpolation to the face
922 interpolate(Moose::FV::InterpMethod::Average, face_rho, elem_rho, neighbor_rho, *fi, true);
923 for (const auto dim_i : index_range(raw_hbya))
924 {
925 interpolate(Moose::FV::InterpMethod::Average,
926 face_hbya(dim_i),
927 hbya_reader[dim_i](elem_dof),
928 hbya_reader[dim_i](neighbor_dof),
929 *fi,
930 true);
932 Ainv(dim_i),
933 elem_rho * ainv_reader[dim_i](elem_dof),
934 neighbor_rho * ainv_reader[dim_i](neighbor_dof),
935 *fi,
936 true);
937 }
938 }
939 else
940 {
941 const bool elem_is_fluid = hasBlocks(fi->elemPtr()->subdomain_id());
942
943 // We need this multiplier in case the face is an internal face and
944 const Real boundary_normal_multiplier = elem_is_fluid ? 1.0 : -1.0;
945
946 const ElemInfo & elem_info = elem_is_fluid ? *fi->elemInfo() : *fi->neighborInfo();
947 const auto elem_dof = elem_info.dofIndices()[_global_momentum_system_numbers[0]][0];
948
949 mooseAssert(fi->boundaryIDs().size() == 1, "We should only have one boundary on every face.");
950 const auto * pressure_bc = _p->getBoundaryCondition(*fi->boundaryIDs().begin());
951
952 // For the legacy Dirichlet-velocity plus extrapolated-pressure path, we still need a
953 // special boundary HbyA reconstruction to keep the face flux consistent. When the pressure
954 // BC itself is a LinearFVPressureFluxBC, that object already enforces the prescribed
955 // boundary mass flux, so we use the standard one-term boundary expansion instead.
956 if (_vel[0]->isDirichletBoundaryFace(*fi) &&
957 !dynamic_cast<const LinearFVPressureFluxBC *>(pressure_bc))
958 {
959 const Moose::FaceArg boundary_face{
960 fi, Moose::FV::LimiterType::CentralDifference, true, false, elem_info.elem(), nullptr};
961 face_rho = _rho(boundary_face, Moose::currentState());
962
963 for (const auto dim_i : make_range(_dim))
964 {
965
966 face_hbya(dim_i) =
967 -MetaPhysicL::raw_value((*_vel[dim_i])(boundary_face, Moose::currentState()));
968 face_hbya(dim_i) *= boundary_normal_multiplier;
969 }
970 }
971 // Otherwise we just do a one-term expansion (so we just use the element value)
972 else
973 {
974 const auto elem_dof = elem_info.dofIndices()[_global_momentum_system_numbers[0]][0];
975
976 face_rho = _rho(makeElemArg(elem_info.elem()), time_arg);
977 for (const auto dim_i : make_range(_dim))
978 face_hbya(dim_i) = boundary_normal_multiplier * hbya_reader[dim_i](elem_dof);
979 }
980
981 // We just do a one-term expansion for 1/A no matter what
982 const Real elem_rho = _rho(makeElemArg(elem_info.elem()), time_arg);
983 for (const auto dim_i : index_range(raw_Ainv))
984 Ainv(dim_i) = elem_rho * ainv_reader[dim_i](elem_dof);
985 }
986 // Lastly, we populate the face flux resulted by H/A
987 _HbyA_flux[fi->id()] = face_hbya * fi->normal() * face_rho;
988 }
989}
990
991void
993{
994 if (verbose)
995 {
996 _console << "************************************" << std::endl;
997 _console << "Computing HbyA" << std::endl;
998 _console << "************************************" << std::endl;
999 }
1001 "The momentum system shall be linked before calling this function!");
1002
1003 mooseAssert(!_grad_p_current.empty(),
1004 "A coupling pressure-gradient snapshot must exist before computing H/A.");
1005 mooseAssert(_grad_p_current.size() == _momentum_implicit_systems.size(),
1006 "The coupling pressure-gradient snapshot must have one component per momentum "
1007 "system.");
1008
1009#ifndef NDEBUG
1010 for (const auto system_i : index_range(_momentum_implicit_systems))
1011 {
1012 const auto & snapshot = *_grad_p_current[system_i];
1013 const auto & momentum_layout = *_momentum_implicit_systems[system_i]->current_local_solution;
1014 mooseAssert(snapshot.size() == momentum_layout.size() &&
1015 snapshot.local_size() == momentum_layout.local_size() &&
1016 snapshot.first_local_index() == momentum_layout.first_local_index() &&
1017 snapshot.last_local_index() == momentum_layout.last_local_index(),
1018 "The coupling pressure-gradient snapshot layout must match its momentum system.");
1019 }
1020#endif
1021
1022 const auto & coupling_pressure_gradient = _grad_p_current;
1023
1024 _HbyA_raw.clear();
1025 _Ainv_raw.clear();
1026
1027 for (auto system_i : index_range(_momentum_systems))
1028 {
1029 LinearImplicitSystem * momentum_system = _momentum_implicit_systems[system_i];
1030
1031 NumericVector<Number> & rhs = *(momentum_system->rhs);
1032 NumericVector<Number> & current_local_solution = *(momentum_system->current_local_solution);
1033 NumericVector<Number> & solution = *(momentum_system->solution);
1034 PetscMatrix<Number> * mmat = dynamic_cast<PetscMatrix<Number> *>(momentum_system->matrix);
1035 mooseAssert(mmat,
1036 "The matrices used in the segregated INSFVRhieChow objects need to be convertable "
1037 "to PetscMatrix!");
1038
1039 if (verbose)
1040 {
1041 _console << "Matrix in rc object" << std::endl;
1042 mmat->print();
1043 }
1044
1045 // First, we extract the diagonal and we will hold on to it for a little while
1046 _Ainv_raw.push_back(current_local_solution.zero_clone());
1047 NumericVector<Number> & Ainv = *(_Ainv_raw.back());
1048
1049 mmat->get_diagonal(Ainv);
1050
1051 if (verbose)
1052 {
1053 _console << "Velocity solution in H(u)" << std::endl;
1054 solution.print();
1055 }
1056
1057 // Time to create H(u) = M_{offdiag} * u - b_{nonpressure}
1058 _HbyA_raw.push_back(current_local_solution.zero_clone());
1059 NumericVector<Number> & HbyA = *(_HbyA_raw.back());
1060
1061 // We start with the matrix product part, we will do
1062 // M*u - A*u for 2 reasons:
1063 // 1, We assume A*u petsc operation is faster than setting the matrix diagonal to 0
1064 // 2, In PISO loops we need to reuse the matrix so we can't just set the diagonals to 0
1065
1066 // We create a working vector to ease some of the operations, we initialize its values
1067 // with the current solution values to have something for the A*u term
1068 auto working_vector = momentum_system->current_local_solution->zero_clone();
1069 PetscVector<Number> * working_vector_petsc =
1070 dynamic_cast<PetscVector<Number> *>(working_vector.get());
1071 mooseAssert(working_vector_petsc,
1072 "The vectors used in the RhieChowMassFlux objects need to be convertable "
1073 "to PetscVectors!");
1074
1075 mmat->vector_mult(HbyA, solution);
1076 working_vector_petsc->pointwise_mult(Ainv, solution);
1077 HbyA.add(-1.0, *working_vector_petsc);
1078
1079 if (verbose)
1080 {
1081 _console << " H(u)" << std::endl;
1082 HbyA.print();
1083 }
1084
1085 // We continue by adding the momentum right hand side contributions
1086 HbyA.add(-1.0, rhs);
1087
1088 // Unfortunately, the pressure forces are included in the momentum RHS
1089 // so we have to correct them back using the same coupling gradient that
1090 // assembled the momentum pressure source.
1091 working_vector_petsc->pointwise_mult(*coupling_pressure_gradient[system_i],
1093 HbyA.add(-1.0, *working_vector_petsc);
1094
1095 if (verbose)
1096 {
1097 _console << "total RHS" << std::endl;
1098 rhs.print();
1099 _console << "pressure RHS" << std::endl;
1100 coupling_pressure_gradient[system_i]->print();
1101 _console << " H(u)-rhs-relaxation_source" << std::endl;
1102 HbyA.print();
1103 }
1104
1105 // It is time to create element-wise 1/A-s based on the the diagonal of the momentum matrix
1106 *working_vector_petsc = 1.0;
1107 Ainv.pointwise_divide(*working_vector_petsc, Ainv);
1108
1109 // Create 1/A*(H(u)-RHS)
1110 HbyA.pointwise_mult(HbyA, Ainv);
1111
1112 if (verbose)
1113 {
1114 _console << " (H(u)-rhs)/A" << std::endl;
1115 HbyA.print();
1116 }
1117
1118 if (_pressure_projection_method == "consistent")
1119 {
1120
1121 // Consistent Corrections to SIMPLE
1122 // 1. Ainv_old = 1/a_p <- Ainv = 1/(a_p + \sum_n a_n)
1123 // 2. H(u) <- H(u*) + H(u') = H(u*) - (Ainv - Ainv_old) * grad(p) * Vc
1124
1125 if (verbose)
1126 _console << "Performing SIMPLEC projection." << std::endl;
1127
1128 // Lambda function to calculate the sum of diagonal and neighbor coefficients
1129 auto get_row_sum = [mmat](NumericVector<Number> & sum_vector)
1130 {
1131 // Ensure the sum_vector is zeroed out
1132 sum_vector.zero();
1133
1134 // Local row size
1135 const auto local_size = mmat->local_m();
1136
1137 for (const auto row_i : make_range(local_size))
1138 {
1139 // Get all non-zero components of the row of the matrix
1140 const auto global_index = mmat->row_start() + row_i;
1141 std::vector<numeric_index_type> indices;
1142 std::vector<Real> values;
1143 mmat->get_row(global_index, indices, values);
1144
1145 // Sum row elements (no absolute values)
1146 const Real row_sum = std::accumulate(values.cbegin(), values.cend(), 0.0);
1147
1148 // Add the sum of diagonal and elements to the sum_vector
1149 sum_vector.add(global_index, row_sum);
1150 }
1151 sum_vector.close();
1152 };
1153
1154 // Create a temporary vector to store the sum of diagonal and neighbor coefficients
1155 auto row_sum = current_local_solution.zero_clone();
1156 get_row_sum(*row_sum);
1157
1158 // Create vector with new inverse projection matrix
1159 auto Ainv_full = current_local_solution.zero_clone();
1160 *working_vector_petsc = 1.0;
1161 Ainv_full->pointwise_divide(*working_vector_petsc, *row_sum);
1162 const auto Ainv_full_old = Ainv_full->clone();
1163
1164 // Correct HbyA
1165 Ainv_full->add(-1.0, Ainv);
1166 working_vector_petsc->pointwise_mult(*Ainv_full, *coupling_pressure_gradient[system_i]);
1167 working_vector_petsc->pointwise_mult(*working_vector_petsc, *_cell_fluid_volumes);
1168 HbyA.add(-1.0, *working_vector_petsc);
1169
1170 // Correct Ainv
1171 Ainv = *Ainv_full_old;
1172 }
1173
1174 Ainv.pointwise_mult(Ainv, *_cell_fluid_volumes);
1175
1176 if (verbose)
1177 {
1178 _console << " 1/A" << std::endl;
1179 Ainv.print();
1180 }
1181 }
1182
1183 // We fill the 1/A and H/A functors
1185
1186 if (verbose)
1187 {
1188 _console << "************************************" << std::endl;
1189 _console << "DONE Computing HbyA " << std::endl;
1190 _console << "************************************" << std::endl;
1191 }
1192}
1193
1194const std::vector<NumericVector<Number> *> &
1199
1200bool
1205
1208{
1210 "reconstructedGradientMethod() should only be called when reconstructed "
1211 "pressure gradients are active.");
1212
1213 return dynamic_cast<const FVReconstructedPressureGradient &>(pressureGradientField().method());
1214}
1215
1218{
1219 auto & method = _fe_problem.getFVGradientMethod(pressureGradientField().method().name(), _tid);
1220 mooseAssert(&method == &pressureGradientField().method(),
1221 "The registered writable gradient method must match the pressure-gradient reader.");
1222 return dynamic_cast<FVReconstructedPressureGradient &>(method);
1223}
1224
1225void
1227{
1230 mooseError("RhieChowMassFlux '",
1231 name(),
1232 "' is missing a required pressure system, pressure-gradient field, base-gradient "
1233 "field, or pressure-correction diffusion kernel for reconstructed pressure "
1234 "gradients.");
1235
1237
1238 if (_momentum_systems.size() != _dim || _momentum_implicit_systems.size() != _dim ||
1240 mooseError("RhieChowMassFlux '",
1241 name(),
1242 "' requires one linked momentum system per spatial component for reconstructed "
1243 "pressure gradients.");
1244
1245 if (_pressure_system->nVariables() != 1)
1246 mooseError(
1247 "FVReconstructedPressureGradient assumes the pressure and momentum systems each have "
1248 "exactly one variable so their DOF indices can be used in vector operations. Pressure "
1249 "system '",
1251 "' has variables: ",
1253
1254 for (const auto system_i : index_range(_momentum_systems))
1255 {
1256 const auto * const momentum_system = _momentum_systems[system_i];
1257 if (!momentum_system || !_momentum_implicit_systems[system_i])
1258 mooseError("RhieChowMassFlux '",
1259 name(),
1260 "' has an invalid momentum-system layout for component ",
1261 system_i,
1262 ".");
1263
1264 if (momentum_system->nVariables() != 1)
1265 mooseError("FVReconstructedPressureGradient assumes the pressure and momentum systems each "
1266 "have exactly one variable so their DOF indices can be used in vector operations. "
1267 "Momentum system '",
1268 momentum_system->name(),
1269 "' has variables: ",
1270 Moose::stringify(momentum_system->getVariableNames()));
1271 }
1272}
boundary_id_type BoundaryID
unsigned int THREAD_ID
const ExecFlagType EXEC_NONE
std::array< Real, 2 > values
registerMooseObject("NavierStokesApp", RhieChowMassFlux)
bool hasBlocks(const SubdomainName &name) const
virtual const std::set< SubdomainID > & blockIDs() const
const ConsoleStream _console
const Elem * elem() const
const std::vector< std::vector< dof_id_type > > & dofIndices() const
void addAvailableFlags(const ExecFlagType &flag, Args... flags)
FVGradientMethod & getFVGradientMethod(const GradientMethodName &name, const THREAD_ID tid=0)
virtual MooseMesh & mesh() override
TheWarehouse & theWarehouse() const
Gradient method that reconstructs and relaxes the pressure gradient used for Rhie-Chow momentum-press...
void finalizeCouplingPressureGradient(const RhieChowMassFlux &rc, const GradientView &base_gradient)
Relax and publish the current candidate as the coupling pressure gradient.
void validateSetup(const RhieChowMassFlux &rc) const
Validate the one-time Rhie-Chow linkage and field layouts required by reconstruction.
void linkFlowSystem(RhieChowMassFlux &rc, const LinearFVGradientReader &pressure_gradient)
Link this stateful method to one Rhie-Chow flow-system configuration.
void saveLaggedVelocityGradient(RhieChowMassFlux &rc)
Save the lagged cell velocity gradient used by the reconstruction.
void computeCandidateFromCorrectedFlux(const RhieChowMassFlux &rc)
Reconstruct the conservative pressure-gradient candidate from the corrected face flux.
void resetForTimeStep(const RhieChowMassFlux &rc)
Prepare solver-iteration state for a new time-step attempt.
A functor whose evaluation relies on querying a map where the keys are face info ids and the values c...
ValueType evaluate(const FaceInfo *const fi) const
Evaluate the face functor using a FaceInfo argument.
const Point & normal() const
VarFaceNeighbors faceType(const std::pair< unsigned int, unsigned int > &var_sys) const
const std::set< BoundaryID > & boundaryIDs() const
const Elem & elem() const
const Elem * neighborPtr() const
const ElemInfo * elemInfo() const
const ElemInfo * neighborInfo() const
dof_id_type id() const
const Elem * elemPtr() const
Moose::ElemArg makeElemArg(const Elem *elem, bool correct_skewnewss=false) const
virtual Real computeBoundaryRHSContribution(const LinearFVBoundaryCondition &bc) override
bool useNonorthogonalCorrection() const
virtual Real computeElemMatrixContribution() override
virtual Real computeNeighborMatrixContribution() override
virtual Real computeElemRightHandSideContribution() override
virtual Real computeBoundaryMatrixContribution(const LinearFVBoundaryCondition &bc) override
void setupFaceData(const FaceInfo *face_info, const FaceInfo::VarFaceNeighbors face_type)
virtual Real computeBoundaryValue() const=0
virtual void setupFaceData(const FaceInfo *face_info)
void setCurrentFaceArea(const Real area)
const GradientContainer & components() const
const FVGradientMethod & method() const
Kernel that adds the component of the pressure gradient in the momentum equations to the right hand s...
Pressure correction diffusion kernel for the linear finite volume SIMPLE algorithm.
Class implementing a flux boundary condition for linear finite volume pressure variables used in the ...
void computeGradientsExcept(const LinearFVGradientReader &excluded_reader)
void updateFVGradient(const LinearFVGradientReader &reader)
virtual System & system() override
void computeGradients()
Executioner * getExecutioner() const
const std::string & name() const
void paramError(const std::string &param, Args... args) const
void mooseError(Args &&... args) const
MooseApp & getMooseApp() const
const LinearFVGradientReader & requestCellGradients(unsigned int oldest_state=0)
Real getElemValue(const ElemInfo &elem_info, const StateArg &state) const
LinearFVBoundaryCondition * getBoundaryCondition(const BoundaryID bd_id) const
const std::string & getBoundaryName(const BoundaryID boundary_id) const
const std::vector< const FaceInfo * > & faceInfo() const
const std::vector< const ElemInfo * > & elemInfoVector() const
SystemBase & sys()
unsigned int number() const
bool isInternalFace(const FaceInfo &) const
static InputParameters validParams()
Executioner set up to solve a transient thermal-hydraulics problem using the PIMPLE algorithm.
Definition PIMPLE.h:21
static InputParameters validParams()
User object responsible for determining the face fluxes using the Rhie-Chow interpolation in a segreg...
const MooseMesh & _moose_mesh
The MooseMesh that this user object operates on.
void updateCellVelocity(const std::vector< GradientComponent > &pressure_gradient)
Update cell velocity from the supplied momentum-coupling pressure gradient.
const Moose::FV::InterpMethod _pressure_diffusion_interp_method
Interpolation method used for the pressure diffusion coefficient on faces.
MooseLinearVariableFVReal & velocityVariable(unsigned int component)
Velocity variable for one spatial component.
const LinearFVGradientReader & basePressureGradientField() const
Get the base pressure gradient field used to seed reconstructed gradient updates.
std::vector< std::unique_ptr< NumericVector< Number > > > _grad_p_current
Coupling pressure gradient captured before the current momentum predictor is assembled.
virtual void computeHbyA(bool verbose)
Computes the inverse of the diagonal (1/A) of the system matrix plus the H/A components for the press...
const LinearFVGradientReader * _pressure_gradient_field
Registered pressure gradient field used by Rhie-Chow and compatible momentum pressure kernels.
virtual void linkMomentumPressureSystems(const std::vector< LinearSystem * > &momentum_systems, LinearSystem &pressure_system, const std::vector< unsigned int > &momentum_system_numbers)
Update the momentum system-related information.
virtual void initFaceMassFlux()
Initialize the container for face velocities.
LinearFVPressureCorrectionDiffusion * _p_diffusion_kernel
Pointer to the pressure diffusion term in the pressure Poisson equation.
const std::vector< NumericVector< Number > * > & pressureGradientComponents() const
Get the registered pressure gradient component vectors.
Real velocityBoundaryValue(unsigned int component, const FaceInfo &fi, const Moose::FaceArg &boundary_face) const
Get the prescribed velocity value on a Dirichlet boundary face.
std::vector< unsigned int > _momentum_system_numbers
Numbers of the momentum system(s)
virtual void initialize() override
virtual void timestepSetup() override
Prepare reconstructed-gradient state once per attempted time step.
unsigned int _global_pressure_system_number
Global number of the pressure system.
const MooseLinearVariableFVReal *const _p
The thread 0 copy of the pressure variable.
Real getMassFlux(const FaceInfo &fi) const
Get the face velocity times density (used in advection terms)
std::vector< LinearSystem * > _momentum_systems
Pointers to the linear system(s) in moose corresponding to the momentum equation(s)
void finalizePressureCorrector()
After pressure relaxation, refresh every registered pressure gradient and publish relaxed reconstruct...
void preparePISOCorrector()
Capture the lagged velocity gradient for another corrector using the current predictor.
virtual void meshChanged() override
void populateCouplingFunctors(const std::vector< std::unique_ptr< NumericVector< Number > > > &raw_hbya, const std::vector< std::unique_ptr< NumericVector< Number > > > &raw_Ainv)
Populate the face values of the H/A and 1/A fields.
virtual void initialSetup() override
Real getVolumetricFaceFlux(const FaceInfo &fi) const
Get the volumetric face flux (used in advection terms)
std::vector< MooseLinearVariableFVReal * > _vel
Thread 0 copies of the velocity variables; non-const to allow writing cell velocities in updateCellVe...
void checkReconstructedPressureGradientCompatibility() const
Check the single-variable system layout assumed by reconstructed pressure-gradient vector ops.
void finalizeMomentumPredictor()
Capture the lagged velocity gradients computed while assembling the momentum predictor.
LinearSystem * _pressure_system
Pointer to the pressure system.
dof_id_type _face_mass_flux_generation
Producer counter for the conservative face mass flux: incremented every time computeFaceMassFlux() ru...
virtual void computeFaceMassFlux()
Update the values of the face velocities in the containers.
std::vector< std::unique_ptr< NumericVector< Number > > > _Ainv_raw
We hold on to the cell-based 1/A vectors so that we can easily reconstruct the cell velocities as wel...
const std::vector< std::unique_ptr< NumericVector< Number > > > & HbyAComponents() const
Get the momentum-layout H/A component vectors.
std::vector< const FaceInfo * > _flow_face_info
The subset of the FaceInfo objects that actually cover the subdomains which the flow field is defined...
void initPressureGradient()
Compute every registered pressure gradient once, before the first momentum predictor of a SIMPLE iter...
std::vector< std::unique_ptr< NumericVector< Number > > > _HbyA_raw
We hold on to the cell-based HbyA vectors so that we can easily reconstruct the cell velocities as we...
void checkBlocks(const VarType &var) const
Check the block consistency between the passed in var and us.
LinearSystem & momentumSystem(unsigned int component)
Momentum linear system for one spatial component.
FVReconstructedPressureGradient & reconstructedGradientMethod()
Access the reconstructed gradient method when it is selected.
void setupMeshInformation()
Compute the cell volumes on the mesh.
const Moose::Functor< Real > & _rho
Functor describing the density of the fluid.
std::vector< libMesh::LinearImplicitSystem * > _momentum_implicit_systems
Pointers to the momentum equation implicit system(s) from libmesh.
unsigned int pressureVariableNumber() const
Variable number of the pressure variable reconstructed by this object.
virtual void initCouplingField()
Initialize the coupling fields (HbyA and Ainv)
void preparePressureRelaxation()
Before pressure relaxation, form the conservative reconstructed candidate from the corrected face flu...
const std::vector< NumericVector< Number > * > & basePressureGradientComponents() const
Get the base-pressure gradient component vectors used to seed reconstructed updates.
FaceCenteredMapFunctor< RealVectorValue, std::unordered_map< dof_id_type, RealVectorValue > > _Ainv
A map functor from faces to $(1/A)_f$.
bool usingReconstructedPressureGradientMethod() const
Whether the registered pressure gradient field is produced by the reconstructed method.
const LinearFVGradientReader * _base_pressure_gradient_field
Base pressure gradient field used to seed reconstructed gradient updates.
void prepareMomentumPredictor()
Snapshot the fields read by the next momentum predictor.
const unsigned int _dim
The dimension of the mesh, e.g. 3 for hexes and tets, 2 for quads and tris.
RhieChowMassFlux(const InputParameters &params)
const std::vector< std::unique_ptr< NumericVector< Number > > > & AinvComponents() const
Get the momentum-layout 1/A component vectors.
FaceCenteredMapFunctor< Real, std::unordered_map< dof_id_type, Real > > & _face_mass_flux
A map functor from faces to mass fluxes which are used in the advection terms.
const LinearFVGradientReader & pressureGradientField() const
Get the registered pressure gradient field used by compatible momentum pressure kernels.
static InputParameters validParams()
FaceCenteredMapFunctor< Real, std::unordered_map< dof_id_type, Real > > _HbyA_flux
A map functor from faces to $HbyA_{ij} = (A_{offdiag}*\mathrm{(predicted~velocity)} - \mathrm{Source}...
std::unique_ptr< NumericVector< Number > > _cell_fluid_volumes
Fluid volume of each cell, used to rapidly scale the pressure-gradient force in H/A and 1/A.
unsigned int dimension() const
Number of spatial dimensions of the coupled momentum/pressure systems.
const MooseEnum _pressure_projection_method
Enumerator for the method used for pressure projection.
std::vector< unsigned int > _global_momentum_system_numbers
Global numbers of the momentum system(s)
Real computeFacePressureGradientFlux(const FaceInfo &fi, PetscVectorReader &p_reader)
Compute the pressure-gradient flux contribution for a single face.
Executioner set up to solve a thermal-hydraulics problem using the SIMPLE algorithm.
Definition SIMPLE.h:20
virtual const NumericVector< Number > *const & currentSolution() const override final
virtual const MooseVariableFieldBase & getVariable(const THREAD_ID tid, const std::string &var_name, Moose::VarKindType expected_var_type=Moose::VarKindType::VAR_ANY, Moose::VarFieldType expected_var_field_type=Moose::VarFieldType::VAR_FIELD_ANY) const=0
void addFunctor(const std::string &name, const Moose::FunctorBase< T > &functor, const THREAD_ID tid)
virtual unsigned int nVariables() const
MooseVariableFieldBase & getVariable(THREAD_ID tid, const std::string &var_name) const
unsigned int number() const
const std::vector< VariableName > & getVariableNames() const
virtual const std::string & name() const
Query query()
SubProblem & _subproblem
FEProblemBase & _fe_problem
const THREAD_ID _tid
virtual std::unique_ptr< NumericVector< T > > zero_clone() const=0
MeshBase & mesh
auto raw_value(const Eigen::Map< T > &in)
std::string stringify(const T &t)
StateArg currentState()
static const std::string density
Definition NS.h:34
static const std::string pressure
Definition NS.h:57
unsigned int state