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 "PetscVectorReader.h"
19#include "LinearSystem.h"
27#include "FVUtils.h"
28#include "MooseUtils.h"
29
30// libMesh includes
31#include "libmesh/mesh_base.h"
32#include "libmesh/elem.h"
33#include "libmesh/elem_range.h"
34#include "libmesh/petsc_matrix.h"
35
36#include <cmath>
37
39
42{
45
46 params.addClassDescription("Computes H/A and 1/A together with face mass fluxes for segregated "
47 "momentum-pressure equations using linear systems.");
48
49 params.addRequiredParam<VariableName>(NS::pressure, "The pressure variable.");
50 params.addRequiredParam<VariableName>("u", "The x-component of velocity");
51 params.addParam<VariableName>("v", "The y-component of velocity");
52 params.addParam<VariableName>("w", "The z-component of velocity");
53 params.addRequiredParam<std::string>(
54 "p_diffusion_kernel",
55 "The LinearFVPressureCorrectionDiffusion kernel acting on the pressure.");
56
57 params.addRequiredParam<MooseFunctorName>(NS::density, "Density functor");
58
59 // We disable the execution of this, should only provide functions
60 // for the SIMPLE executioner
61 ExecFlagEnum & exec_enum = params.set<ExecFlagEnum>("execute_on", true);
63 exec_enum = {EXEC_NONE};
64 params.suppressParameter<ExecFlagEnum>("execute_on");
65
66 // Pressure projection
67 params.addParam<MooseEnum>("pressure_projection_method",
68 MooseEnum("standard consistent", "standard"),
69 "The method to use in the pressure projection for Ainv - "
70 "standard (SIMPLE) or consistent (SIMPLEC)");
71 params.addParam<MooseEnum>(
72 "pressure_diffusion_interpolation",
73 MooseEnum("average harmonic", "average"),
74 "The face interpolation method for Ainv in the pressure correction diffusion term.");
75 return params;
76}
77
81 _moose_mesh(UserObject::_subproblem.mesh()),
82 _mesh(_moose_mesh.getMesh()),
83 _dim(blocksMaxDimension()),
84 _p(dynamic_cast<MooseLinearVariableFVReal *>(
85 &UserObject::_subproblem.getVariable(0, getParam<VariableName>(NS::pressure)))),
86 _vel(_dim, nullptr),
87 _HbyA_flux(_moose_mesh, blockIDs(), "HbyA_flux"),
88 _Ainv(_moose_mesh, blockIDs(), "Ainv"),
89 _face_mass_flux(
90 declareRestartableData<FaceCenteredMapFunctor<Real, std::unordered_map<dof_id_type, Real>>>(
91 "face_flux", _moose_mesh, blockIDs(), "face_values")),
92 _rho(getFunctor<Real>(NS::density)),
93 _pressure_system(nullptr),
94 _pressure_gradient_field(nullptr),
95 _base_pressure_gradient_field(nullptr),
96 _global_pressure_system_number(0),
97 _pressure_projection_method(getParam<MooseEnum>("pressure_projection_method")),
98 _pressure_diffusion_interp_method(getParam<MooseEnum>("pressure_diffusion_interpolation") ==
99 "harmonic"
100 ? Moose::FV::InterpMethod::HarmonicAverage
101 : Moose::FV::InterpMethod::Average)
102{
103 if (!_p)
104 paramError(NS::pressure, "the pressure must be a MooseLinearVariableFVReal.");
105 checkBlocks(*_p);
106
107 std::vector<std::string> vel_names = {"u", "v", "w"};
108 for (const auto i : index_range(_vel))
109 {
110 _vel[i] = dynamic_cast<MooseLinearVariableFVReal *>(
111 &UserObject::_subproblem.getVariable(0, getParam<VariableName>(vel_names[i])));
112
113 if (!_vel[i])
114 paramError(vel_names[i], "the velocity must be a MOOSELinearVariableFVReal.");
115 checkBlocks(*_vel[i]);
116 }
117
118 // Register the elemental/face functors which will be queried in the pressure equation
119 for (const auto tid : make_range(UserObject::_subproblem.numThreads()))
120 {
123 }
124
125 if (!dynamic_cast<SIMPLE *>(getMooseApp().getExecutioner()) &&
126 !dynamic_cast<PIMPLE *>(getMooseApp().getExecutioner()))
127 mooseError(this->name(),
128 " should only be used with a linear segregated thermal-hydraulics solver!");
129}
130
131void
133 const std::vector<LinearSystem *> & momentum_systems,
134 LinearSystem & pressure_system,
135 const std::vector<unsigned int> & momentum_system_numbers)
136{
137 _momentum_systems = momentum_systems;
138 _momentum_system_numbers = momentum_system_numbers;
139 _pressure_system = &pressure_system;
141
142 auto * const pressure_var =
144 if (!pressure_var)
145 mooseError("The pressure variable in system '",
147 "' must be a MooseLinearVariableFVReal.");
148
149 // For each momentum system, find the LinearFVMomentumPressure kernels applicable to this RC
150 // object, i.e. those whose blocks overlap the blocks this RC object handles. Kernels entirely
151 // outside our blocks are irrelevant and ignored; multiple applicable kernels per system are
152 // allowed as long as they partition our blocks without overlapping (checked below).
153 std::vector<std::vector<const LinearFVMomentumPressure *>> applicable_kernels(
154 momentum_systems.size());
155 for (const auto system_index : index_range(momentum_systems))
156 {
157 std::vector<LinearFVElementalKernel *> kernels;
159 .query()
160 .template condition<AttribThread>(_tid)
161 .template condition<AttribSystem>("LinearFVElementalKernel")
162 .template condition<AttribSysNum>(momentum_system_numbers[system_index])
163 .queryInto(kernels);
164
165 for (const auto * const kernel : kernels)
166 {
167 const auto * const candidate = dynamic_cast<const LinearFVMomentumPressure *>(kernel);
168 if (candidate && MooseUtils::setsIntersect(candidate->blockIDs(), blockIDs()))
169 applicable_kernels[system_index].push_back(candidate);
170 }
171 }
172
173 for (const auto system_index : index_range(momentum_systems))
174 {
175 const auto & kernels = applicable_kernels[system_index];
176 const auto & system_name = momentum_systems[system_index]->name();
177
178 // Coverage: the applicable kernels must together cover every block this RC object handles.
179 std::set<SubdomainID> covered_blocks;
180 for (const auto * const kernel : kernels)
181 covered_blocks.insert(kernel->blockIDs().begin(), kernel->blockIDs().end());
182
183 std::set<SubdomainID> missing_blocks;
184 std::set_difference(blockIDs().begin(),
185 blockIDs().end(),
186 covered_blocks.begin(),
187 covered_blocks.end(),
188 std::inserter(missing_blocks, missing_blocks.end()));
189
190 if (!missing_blocks.empty())
191 mooseError("RhieChowMassFlux '",
192 name(),
193 "' found no applicable LinearFVMomentumPressure kernel for momentum system '",
194 system_name,
195 "' on block(s) ",
196 Moose::stringify(missing_blocks),
197 ".");
198
199 // Component and pairwise block-overlap checks among the applicable kernels for this system.
200 for (const auto i : index_range(kernels))
201 {
202 if (kernels[i]->momentumComponent() != system_index)
203 mooseError("LinearFVMomentumPressure kernel '",
204 kernels[i]->name(),
205 "' declares momentum component ",
206 kernels[i]->momentumComponent(),
207 " but is linked to momentum system '",
208 system_name,
209 "' (component ",
210 system_index,
211 ") through RhieChowMassFlux '",
212 name(),
213 "'.");
214
215 for (const auto j : make_range(i + 1, kernels.size()))
216 {
217 std::set<SubdomainID> overlap;
218 for (const auto block : kernels[i]->blockIDs())
219 if (hasBlocks(block) && kernels[j]->blockIDs().count(block))
220 overlap.insert(block);
221 if (!overlap.empty())
222 mooseError("Momentum system '",
223 system_name,
224 "' has more than one LinearFVMomentumPressure kernel contributing a "
225 "pressure source on the same block(s) for RhieChowMassFlux '",
226 name(),
227 "': '",
228 kernels[i]->name(),
229 "' and '",
230 kernels[j]->name(),
231 "' overlap on block(s) ",
232 Moose::stringify(overlap),
233 ".");
234 }
235 }
236 }
237
238 // Flatten every system's applicable kernels into one list spanning the whole RC object. Even
239 // kernels on disjoint blocks/components must share one coupling-gradient field, because H/A
240 // has a single field source for this RC object.
241 std::vector<const LinearFVMomentumPressure *> momentum_pressure_kernels;
242 for (auto & kernels : applicable_kernels)
243 momentum_pressure_kernels.insert(
244 momentum_pressure_kernels.end(), kernels.begin(), kernels.end());
245
246 const auto * const coupling_kernel = momentum_pressure_kernels.front();
247 const auto & coupling_reader = coupling_kernel->pressureGradientField();
248
249 if (dynamic_cast<const FVReconstructedPressureGradient *>(&coupling_reader.method()) &&
250 coupling_kernel->pressureVariableNumber() != _p->number())
251 mooseError("FVReconstructedPressureGradient can only be used for the pressure variable "
252 "registered on RhieChowMassFlux '",
253 name(),
254 "'.");
255
256 for (const auto * const kernel : momentum_pressure_kernels)
257 {
258 const auto & reader = kernel->pressureGradientField();
259
260 if (&reader.system() != &coupling_reader.system() ||
261 reader.systemNumber() != coupling_reader.systemNumber())
262 mooseError("RhieChowMassFlux '",
263 name(),
264 "': momentum pressure kernels '",
265 coupling_kernel->name(),
266 "' (block(s) ",
267 Moose::stringify(coupling_kernel->blockIDs()),
268 ") and '",
269 kernel->name(),
270 "' (block(s) ",
271 Moose::stringify(kernel->blockIDs()),
272 ") must use pressure gradients from the same system.");
273
274 if (reader.variableNumber() != coupling_reader.variableNumber())
275 mooseError("RhieChowMassFlux '",
276 name(),
277 "': momentum pressure kernels '",
278 coupling_kernel->name(),
279 "' (pressure variable number ",
280 coupling_reader.variableNumber(),
281 ") and '",
282 kernel->name(),
283 "' (pressure variable number ",
284 reader.variableNumber(),
285 ") must use the same pressure variable.");
286
287 if (&reader.method() != &coupling_reader.method())
288 mooseError("RhieChowMassFlux '",
289 name(),
290 "': momentum pressure kernels '",
291 coupling_kernel->name(),
292 "' and '",
293 kernel->name(),
294 "' must use the same pressure gradient method.");
295
296 if (&reader.components() != &coupling_reader.components())
297 mooseError("RhieChowMassFlux '",
298 name(),
299 "': momentum pressure kernels '",
300 coupling_kernel->name(),
301 "' and '",
302 kernel->name(),
303 "' must share the same registered pressure gradient field.");
304 }
305
306 _pressure_gradient_field = &coupling_reader;
307
309 {
310 reconstructedGradientMethod().linkFlowSystem(*this, coupling_reader);
311 const auto & base_reader =
312 pressure_var->requestCellGradients(reconstructedGradientMethod().baseGradientMethodName());
313 _base_pressure_gradient_field = &base_reader;
314
316 "Reconstructed and base pressure gradient readers must be distinct when "
317 "FVReconstructedPressureGradient is active.");
318 }
319 else
321
324 for (auto & system : _momentum_systems)
325 {
326 _global_momentum_system_numbers.push_back(system->number());
327 _momentum_implicit_systems.push_back(dynamic_cast<LinearImplicitSystem *>(&system->system()));
328 }
329
331 {
334 _pressure_projection_method != "consistent")
335 paramError("pressure_projection_method",
336 "FVReconstructedPressureGradient with nonorthogonal pressure diffusion requires "
337 "the consistent pressure projection.");
338 }
339
341
342 std::set<BoundaryID> velocity_boundary_ids;
343 for (const auto dim_i : make_range(_dim))
344 for (const auto & [boundary_id, _] : _vel[dim_i]->getBoundaryConditionMap())
345 velocity_boundary_ids.insert(boundary_id);
346
347 const auto is_dirichlet =
348 [](const MooseLinearVariableFVReal & variable, const BoundaryID boundary_id)
349 {
350 return dynamic_cast<const LinearFVAdvectionDiffusionFunctorDirichletBC *>(
351 variable.getBoundaryCondition(boundary_id)) != nullptr;
352 };
353
354 // The legacy boundary HbyA reconstruction uses the x-velocity BC as a proxy for every velocity
355 // component, so all components must have the same Dirichlet classification on those boundaries.
356 for (const auto boundary_id : velocity_boundary_ids)
357 {
358 const auto * const pressure_bc = _p->getBoundaryCondition(boundary_id);
359 if (dynamic_cast<const LinearFVPressureFluxBC *>(pressure_bc))
360 continue;
361
362 const bool velocity_is_dirichlet = is_dirichlet(*_vel[0], boundary_id);
363 for (const auto dim_i : make_range(_dim))
364 if (is_dirichlet(*_vel[dim_i], boundary_id) != velocity_is_dirichlet)
365 mooseError("All velocity components must either have Dirichlet boundary conditions or "
366 "non-Dirichlet boundary conditions on boundary '",
367 _moose_mesh.getBoundaryName(boundary_id),
368 "' when the pressure boundary condition is not a LinearFVPressureFluxBC.");
369 }
370}
371
372void
374{
375 _HbyA_flux.clear();
376 _Ainv.clear();
377 _face_mass_flux.clear();
378 _grad_p_current.clear();
380}
381
382void
384{
385 // Nothing to reset when momentum hasn't been linked (e.g. should_solve_momentum = false, used
386 // by the restart-freeze pattern).
388 return;
389
390 _grad_p_current.clear();
391
393 return;
394
395 // Seed the next attempt from the most recently accepted coupling gradient. The linear FV
396 // gradient manager restores that state after rejection and checkpoints it for restart/recovery.
398}
399
400void
402{
403 mooseAssert(_pressure_gradient_field,
404 "The pressure gradient field must be linked before preparing momentum.");
405
406 _grad_p_current.clear();
407 for (const auto & component : pressureGradientComponents())
408 _grad_p_current.push_back(component->clone());
409}
410
411void
417
418void
420{
422 return;
423
424 mooseAssert(!_grad_p_current.empty(),
425 "A momentum predictor must be prepared before another PISO corrector.");
426
427 for (const auto component : make_range(dimension()))
430}
431
432void
434{
436 return;
437
438 // The current pressure solution and corrected face flux are both unrelaxed here. Form the
439 // conservative candidate before pressure relaxation replaces that solution.
441 updateCellVelocity(reconstructedGradientMethod().reconstructedCandidate(*this));
442}
443
444void
446{
448 {
449 // Refresh every ordinary pressure gradient while leaving the old coupling field untouched
450 // until the current reconstructed candidate has been relaxed into it.
455 }
456 else
457 {
460 }
461}
462
463void
468
469void
471{
472 // We fetch the pressure diffusion kernel to ensure that the face flux correction
473 // is consistent with the pressure discretization in the Poisson equation.
474 std::vector<LinearFVFluxKernel *> flux_kernel;
475 auto base_query = _fe_problem.theWarehouse()
476 .query()
477 .template condition<AttribThread>(_tid)
478 .template condition<AttribSysNum>(_p->sys().number())
479 .template condition<AttribSystem>("LinearFVFluxKernel")
480 .template condition<AttribName>(getParam<std::string>("p_diffusion_kernel"))
481 .queryInto(flux_kernel);
482 if (flux_kernel.size() != 1)
484 "p_diffusion_kernel",
485 "The kernel with the given name could not be found or multiple instances were identified.");
486 _p_diffusion_kernel = dynamic_cast<LinearFVPressureCorrectionDiffusion *>(flux_kernel[0]);
488 paramError("p_diffusion_kernel",
489 "The provided diffusion kernel must be of type "
490 "LinearFVPressureCorrectionDiffusion.");
491}
492
493void
495{
496 // We cache the cell volumes into a petsc vector for corrections here so we can use
497 // the optimized petsc operations for the normalization
499 for (const auto & elem_info : _fe_problem.mesh().elemInfoVector())
500 // We have to check this because the variable might not be defined on the given
501 // block
502 if (hasBlocks(elem_info->subdomain_id()))
503 {
504 const auto elem_dof = elem_info->dofIndices()[_global_pressure_system_number][0];
505 _cell_volumes->set(elem_dof, elem_info->volume() * elem_info->coordFactor());
506 }
507
508 _cell_volumes->close();
509
510 _flow_face_info.clear();
511 for (auto & fi : _fe_problem.mesh().faceInfo())
512 if (hasBlocks(fi->elemPtr()->subdomain_id()) ||
513 (fi->neighborPtr() && hasBlocks(fi->neighborPtr()->subdomain_id())))
514 _flow_face_info.push_back(fi);
515}
516
517template <typename GradientComponent>
518void
519RhieChowMassFlux::updateCellVelocity(const std::vector<GradientComponent> & pressure_gradient)
520{
521 // u_C = -(H/A)_C - (1/A)_C * grad(p)_C.
522 for (const auto system_i : index_range(_momentum_implicit_systems))
523 {
524 auto working_vector = _Ainv_raw[system_i]->clone();
525 working_vector->pointwise_mult(*working_vector, *pressure_gradient[system_i]);
526 working_vector->add(*_HbyA_raw[system_i]);
527 working_vector->scale(-1.0);
528 (*_momentum_implicit_systems[system_i]->solution) = *working_vector;
529
530#ifndef NDEBUG
531 {
532 // Proof that the assignment above satisfies the momentum-coupling identity
533 // u_C + (H/A)_C + Ainv_C * grad(p)_C = 0. The solution was just assigned directly from
534 // this formula, so the residual is pure floating-point round-off from the
535 // clone/scale/add sequence, not solver error; 1e-10 is a loose absolute bound with
536 // headroom over that round-off.
537 auto defect = _Ainv_raw[system_i]->clone();
538 defect->pointwise_mult(*defect, *pressure_gradient[system_i]);
539 defect->add(*_HbyA_raw[system_i]);
540 defect->add(*_momentum_implicit_systems[system_i]->solution);
541 mooseAssert(MooseUtils::absoluteFuzzyEqual(defect->linfty_norm(), 0.0, 1e-10),
542 "RhieChowMassFlux: cell-velocity update violates the momentum-coupling "
543 "identity u + H/A + Ainv*grad(p) = 0.");
544 }
545#endif
546
547 _momentum_implicit_systems[system_i]->update();
548 _momentum_systems[system_i]->setSolution(
549 *_momentum_implicit_systems[system_i]->current_local_solution);
550 }
551}
552
553void
555{
556 for (const auto & pair : _HbyA_flux)
557 _HbyA_flux[pair.first] = 0;
558
559 for (const auto & pair : _Ainv)
560 _Ainv[pair.first] = 0;
561}
562
563void
565{
566 using namespace Moose::FV;
567
568 const auto time_arg = Moose::currentState();
569
570 // We loop through the faces and compute the resulting face fluxes from the
571 // initial conditions for velocity
572 for (auto & fi : _flow_face_info)
573 {
574 RealVectorValue density_times_velocity;
575
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 raw_value((*_vel[dim_i])(boundary_face, time_arg));
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 const Moose::FaceArg face_arg{&fi,
651 /*limiter_type=*/Moose::FV::LimiterType::CentralDifference,
652 /*elem_is_upwind=*/true,
653 /*correct_skewness=*/false,
654 &fi.elem(),
655 /*state_limiter*/ nullptr};
656 const Real face_rho = _rho(face_arg, Moose::currentState());
657 if (!std::isfinite(face_rho) || face_rho <= 0.0)
658 mooseError("RhieChowMassFlux '",
659 name(),
660 "' requires finite, positive density for corrected face flux reconstruction, but "
661 "density is ",
662 face_rho,
663 " on face ID ",
664 fi.id(),
665 " adjacent to cell ID ",
666 fi.elem().id(),
667 " and neighbor cell ID ",
668 neighbor_cell_id,
669 ".");
670
671 const Real volumetric_flux = face_flux->second / face_rho;
672 if (!std::isfinite(volumetric_flux))
673 mooseError("RhieChowMassFlux '",
674 name(),
675 "' computed non-finite volumetric flux ",
676 volumetric_flux,
677 " on face ID ",
678 fi.id(),
679 " adjacent to cell ID ",
680 fi.elem().id(),
681 " and neighbor cell ID ",
682 neighbor_cell_id,
683 ".");
684
685 return volumetric_flux;
686}
687
690{
693 "The pressure gradient field has not been registered for RhieChowMassFlux '", name(), "'.");
694
696}
697
700{
702 mooseError("The base pressure gradient field has not been registered for RhieChowMassFlux '",
703 name(),
704 "'.");
705
707}
708
709const std::vector<std::unique_ptr<NumericVector<Number>>> &
711{
712 mooseAssert(!_HbyA_raw.empty(), "HbyA data is not ready.");
713
714 return _HbyA_raw;
715}
716
717const std::vector<std::unique_ptr<NumericVector<Number>>> &
719{
720 mooseAssert(!_Ainv_raw.empty(), "Ainv data is not ready.");
721
722 return _Ainv_raw;
723}
724
725const std::vector<NumericVector<Number> *> &
730
731unsigned int
733{
734 return _p->number();
735}
736
737Real
739 const FaceInfo & fi,
740 const Moose::StateArg & time,
741 const THREAD_ID /*tid*/,
742 bool libmesh_dbg_var(subtract_mesh_velocity)) const
743{
744 mooseAssert(!subtract_mesh_velocity, "RhieChowMassFlux does not support moving meshes yet!");
745
746 if (m != Moose::FV::InterpMethod::RhieChow)
747 mooseError("Interpolation methods other than Rhie-Chow are not supported!");
748 if (time.state != Moose::currentState().state)
749 mooseError("Older interpolation times are not supported!");
750
751 return getVolumetricFaceFlux(fi);
752}
753
754void
756{
757 using namespace Moose::FV;
758
759 const auto time_arg = Moose::currentState();
760
761 // Petsc vector reader to make the repeated reading from the vector faster
762 PetscVectorReader p_reader(*_pressure_system->system().current_local_solution);
763
764 // We loop through the faces and compute the face fluxes using the pressure gradient
765 // and the momentum matrix/right hand side
766 for (auto & fi : _flow_face_info)
767 {
768 // Making sure the kernel knows which face we are on
770
771 // We are setting this to 1.0 because we don't want to multiply the kernel contributions
772 // with the surface area yet. The surface area will be factored in in the advection kernels.
774
775 Real p_grad_flux = 0.0;
776 if (_p->isInternalFace(*fi))
777 {
778 const auto & elem_info = *fi->elemInfo();
779 const auto & neighbor_info = *fi->neighborInfo();
780
781 // Fetching the dof indices for the pressure variable
782 const auto elem_dof = elem_info.dofIndices()[_global_pressure_system_number][0];
783 const auto neighbor_dof = neighbor_info.dofIndices()[_global_pressure_system_number][0];
784
785 // Fetching the values of the pressure for the element and the neighbor
786 const auto p_elem_value = p_reader(elem_dof);
787 const auto p_neighbor_value = p_reader(neighbor_dof);
788
789 // Compute the elem matrix contributions for the face
790 const auto elem_matrix_contribution = _p_diffusion_kernel->computeElemMatrixContribution();
791 const auto neighbor_matrix_contribution =
793 const auto elem_rhs_contribution =
795
796 // Compute the face flux from the matrix and right hand side contributions
797 p_grad_flux = (p_neighbor_value * neighbor_matrix_contribution +
798 p_elem_value * elem_matrix_contribution) -
799 elem_rhs_contribution;
800 }
801 else if (auto * bc_pointer = _p->getBoundaryCondition(*fi->boundaryIDs().begin()))
802 {
803 mooseAssert(fi->boundaryIDs().size() == 1, "We should only have one boundary on every face.");
804
805 bc_pointer->setupFaceData(
806 fi, fi->faceType(std::make_pair(_p->number(), _global_pressure_system_number)));
807
808 const ElemInfo & elem_info =
809 hasBlocks(fi->elemPtr()->subdomain_id()) ? *fi->elemInfo() : *fi->neighborInfo();
810 const auto p_elem_value = _p->getElemValue(elem_info, time_arg);
811 const auto matrix_contribution =
813 const auto rhs_contribution =
815
816 // On the boundary, only the element side has a contribution
817 p_grad_flux = (p_elem_value * matrix_contribution - rhs_contribution);
818 }
819 // Compute the new face flux
820 _face_mass_flux[fi->id()] = -_HbyA_flux[fi->id()] + p_grad_flux;
821 }
822
824}
825
826void
828{
829 // We loop through the faces and populate the coupling fields (face H/A and 1/H)
830 // with 0s for now. Pressure corrector solves will always come after the
831 // momentum source so we expect these fields to change before the actual solve.
832 for (auto & fi : _fe_problem.mesh().faceInfo())
833 {
834 _Ainv[fi->id()];
835 _HbyA_flux[fi->id()];
836 }
837}
838
839void
841 const std::vector<std::unique_ptr<NumericVector<Number>>> & raw_hbya,
842 const std::vector<std::unique_ptr<NumericVector<Number>>> & raw_Ainv)
843{
844 // We have the raw H/A and 1/A vectors in a petsc format. This function
845 // will create face functors from them
846 using namespace Moose::FV;
847 const auto time_arg = Moose::currentState();
848
849 // Create the petsc vector readers for faster repeated access
850 std::vector<PetscVectorReader> hbya_reader;
851 for (const auto dim_i : index_range(raw_hbya))
852 hbya_reader.emplace_back(*raw_hbya[dim_i]);
853
854 std::vector<PetscVectorReader> ainv_reader;
855 for (const auto dim_i : index_range(raw_Ainv))
856 ainv_reader.emplace_back(*raw_Ainv[dim_i]);
857
858 // We loop through the faces and populate the coupling fields (face H/A and 1/A)
859 for (auto & fi : _flow_face_info)
860 {
861 Real face_rho = 0;
862 RealVectorValue face_hbya;
863
864 // We do the lookup in advance
865 auto & Ainv = _Ainv[fi->id()];
866
867 // If it is internal, we just interpolate (using geometric weights) to the face
868 if (_vel[0]->isInternalFace(*fi))
869 {
870 // Get the dof indices for the element and the neighbor
871 const auto & elem_info = *fi->elemInfo();
872 const auto & neighbor_info = *fi->neighborInfo();
873 const auto elem_dof = elem_info.dofIndices()[_global_momentum_system_numbers[0]][0];
874 const auto neighbor_dof = neighbor_info.dofIndices()[_global_momentum_system_numbers[0]][0];
875
876 // Get the density values for the element and neighbor. We need this multiplication to make
877 // the coupling fields mass fluxes.
878 const Real elem_rho = _rho(makeElemArg(fi->elemPtr()), time_arg);
879 const Real neighbor_rho = _rho(makeElemArg(fi->neighborPtr()), time_arg);
880
881 // Now we do the interpolation to the face
882 interpolate(Moose::FV::InterpMethod::Average, face_rho, elem_rho, neighbor_rho, *fi, true);
883 for (const auto dim_i : index_range(raw_hbya))
884 {
885 interpolate(Moose::FV::InterpMethod::Average,
886 face_hbya(dim_i),
887 hbya_reader[dim_i](elem_dof),
888 hbya_reader[dim_i](neighbor_dof),
889 *fi,
890 true);
892 Ainv(dim_i),
893 elem_rho * ainv_reader[dim_i](elem_dof),
894 neighbor_rho * ainv_reader[dim_i](neighbor_dof),
895 *fi,
896 true);
897 }
898 }
899 else
900 {
901 const bool elem_is_fluid = hasBlocks(fi->elemPtr()->subdomain_id());
902
903 // We need this multiplier in case the face is an internal face and
904 const Real boundary_normal_multiplier = elem_is_fluid ? 1.0 : -1.0;
905
906 const ElemInfo & elem_info = elem_is_fluid ? *fi->elemInfo() : *fi->neighborInfo();
907 const auto elem_dof = elem_info.dofIndices()[_global_momentum_system_numbers[0]][0];
908
909 mooseAssert(fi->boundaryIDs().size() == 1, "We should only have one boundary on every face.");
910 const auto * pressure_bc = _p->getBoundaryCondition(*fi->boundaryIDs().begin());
911
912 // For the legacy Dirichlet-velocity plus extrapolated-pressure path, we still need a
913 // special boundary HbyA reconstruction to keep the face flux consistent. When the pressure
914 // BC itself is a LinearFVPressureFluxBC, that object already enforces the prescribed
915 // boundary mass flux, so we use the standard one-term boundary expansion instead.
916 if (_vel[0]->isDirichletBoundaryFace(*fi) &&
917 !dynamic_cast<const LinearFVPressureFluxBC *>(pressure_bc))
918 {
919 const Moose::FaceArg boundary_face{
920 fi, Moose::FV::LimiterType::CentralDifference, true, false, elem_info.elem(), nullptr};
921 face_rho = _rho(boundary_face, Moose::currentState());
922
923 for (const auto dim_i : make_range(_dim))
924 {
925
926 face_hbya(dim_i) =
927 -MetaPhysicL::raw_value((*_vel[dim_i])(boundary_face, Moose::currentState()));
928 face_hbya(dim_i) *= boundary_normal_multiplier;
929 }
930 }
931 // Otherwise we just do a one-term expansion (so we just use the element value)
932 else
933 {
934 const auto elem_dof = elem_info.dofIndices()[_global_momentum_system_numbers[0]][0];
935
936 face_rho = _rho(makeElemArg(elem_info.elem()), time_arg);
937 for (const auto dim_i : make_range(_dim))
938 face_hbya(dim_i) = boundary_normal_multiplier * hbya_reader[dim_i](elem_dof);
939 }
940
941 // We just do a one-term expansion for 1/A no matter what
942 const Real elem_rho = _rho(makeElemArg(elem_info.elem()), time_arg);
943 for (const auto dim_i : index_range(raw_Ainv))
944 Ainv(dim_i) = elem_rho * ainv_reader[dim_i](elem_dof);
945 }
946 // Lastly, we populate the face flux resulted by H/A
947 _HbyA_flux[fi->id()] = face_hbya * fi->normal() * face_rho;
948 }
949}
950
951void
953{
954 if (verbose)
955 {
956 _console << "************************************" << std::endl;
957 _console << "Computing HbyA" << std::endl;
958 _console << "************************************" << std::endl;
959 }
961 "The momentum system shall be linked before calling this function!");
962
963 mooseAssert(!_grad_p_current.empty(),
964 "A coupling pressure-gradient snapshot must exist before computing H/A.");
965 mooseAssert(_grad_p_current.size() == _momentum_implicit_systems.size(),
966 "The coupling pressure-gradient snapshot must have one component per momentum "
967 "system.");
968
969#ifndef NDEBUG
970 for (const auto system_i : index_range(_momentum_implicit_systems))
971 {
972 const auto & snapshot = *_grad_p_current[system_i];
973 const auto & momentum_layout = *_momentum_implicit_systems[system_i]->current_local_solution;
974 mooseAssert(snapshot.size() == momentum_layout.size() &&
975 snapshot.local_size() == momentum_layout.local_size() &&
976 snapshot.first_local_index() == momentum_layout.first_local_index() &&
977 snapshot.last_local_index() == momentum_layout.last_local_index(),
978 "The coupling pressure-gradient snapshot layout must match its momentum system.");
979 }
980#endif
981
982 const auto & coupling_pressure_gradient = _grad_p_current;
983
984 _HbyA_raw.clear();
985 _Ainv_raw.clear();
986
987 for (auto system_i : index_range(_momentum_systems))
988 {
989 LinearImplicitSystem * momentum_system = _momentum_implicit_systems[system_i];
990
991 NumericVector<Number> & rhs = *(momentum_system->rhs);
992 NumericVector<Number> & current_local_solution = *(momentum_system->current_local_solution);
993 NumericVector<Number> & solution = *(momentum_system->solution);
994 PetscMatrix<Number> * mmat = dynamic_cast<PetscMatrix<Number> *>(momentum_system->matrix);
995 mooseAssert(mmat,
996 "The matrices used in the segregated INSFVRhieChow objects need to be convertable "
997 "to PetscMatrix!");
998
999 if (verbose)
1000 {
1001 _console << "Matrix in rc object" << std::endl;
1002 mmat->print();
1003 }
1004
1005 // First, we extract the diagonal and we will hold on to it for a little while
1006 _Ainv_raw.push_back(current_local_solution.zero_clone());
1007 NumericVector<Number> & Ainv = *(_Ainv_raw.back());
1008
1009 mmat->get_diagonal(Ainv);
1010
1011 if (verbose)
1012 {
1013 _console << "Velocity solution in H(u)" << std::endl;
1014 solution.print();
1015 }
1016
1017 // Time to create H(u) = M_{offdiag} * u - b_{nonpressure}
1018 _HbyA_raw.push_back(current_local_solution.zero_clone());
1019 NumericVector<Number> & HbyA = *(_HbyA_raw.back());
1020
1021 // We start with the matrix product part, we will do
1022 // M*u - A*u for 2 reasons:
1023 // 1, We assume A*u petsc operation is faster than setting the matrix diagonal to 0
1024 // 2, In PISO loops we need to reuse the matrix so we can't just set the diagonals to 0
1025
1026 // We create a working vector to ease some of the operations, we initialize its values
1027 // with the current solution values to have something for the A*u term
1028 auto working_vector = momentum_system->current_local_solution->zero_clone();
1029 PetscVector<Number> * working_vector_petsc =
1030 dynamic_cast<PetscVector<Number> *>(working_vector.get());
1031 mooseAssert(working_vector_petsc,
1032 "The vectors used in the RhieChowMassFlux objects need to be convertable "
1033 "to PetscVectors!");
1034
1035 mmat->vector_mult(HbyA, solution);
1036 working_vector_petsc->pointwise_mult(Ainv, solution);
1037 HbyA.add(-1.0, *working_vector_petsc);
1038
1039 if (verbose)
1040 {
1041 _console << " H(u)" << std::endl;
1042 HbyA.print();
1043 }
1044
1045 // We continue by adding the momentum right hand side contributions
1046 HbyA.add(-1.0, rhs);
1047
1048 // Unfortunately, the pressure forces are included in the momentum RHS
1049 // so we have to correct them back using the same coupling gradient that
1050 // assembled the momentum pressure source.
1051 working_vector_petsc->pointwise_mult(*coupling_pressure_gradient[system_i], *_cell_volumes);
1052 HbyA.add(-1.0, *working_vector_petsc);
1053
1054 if (verbose)
1055 {
1056 _console << "total RHS" << std::endl;
1057 rhs.print();
1058 _console << "pressure RHS" << std::endl;
1059 coupling_pressure_gradient[system_i]->print();
1060 _console << " H(u)-rhs-relaxation_source" << std::endl;
1061 HbyA.print();
1062 }
1063
1064 // It is time to create element-wise 1/A-s based on the the diagonal of the momentum matrix
1065 *working_vector_petsc = 1.0;
1066 Ainv.pointwise_divide(*working_vector_petsc, Ainv);
1067
1068 // Create 1/A*(H(u)-RHS)
1069 HbyA.pointwise_mult(HbyA, Ainv);
1070
1071 if (verbose)
1072 {
1073 _console << " (H(u)-rhs)/A" << std::endl;
1074 HbyA.print();
1075 }
1076
1077 if (_pressure_projection_method == "consistent")
1078 {
1079
1080 // Consistent Corrections to SIMPLE
1081 // 1. Ainv_old = 1/a_p <- Ainv = 1/(a_p + \sum_n a_n)
1082 // 2. H(u) <- H(u*) + H(u') = H(u*) - (Ainv - Ainv_old) * grad(p) * Vc
1083
1084 if (verbose)
1085 _console << "Performing SIMPLEC projection." << std::endl;
1086
1087 // Lambda function to calculate the sum of diagonal and neighbor coefficients
1088 auto get_row_sum = [mmat](NumericVector<Number> & sum_vector)
1089 {
1090 // Ensure the sum_vector is zeroed out
1091 sum_vector.zero();
1092
1093 // Local row size
1094 const auto local_size = mmat->local_m();
1095
1096 for (const auto row_i : make_range(local_size))
1097 {
1098 // Get all non-zero components of the row of the matrix
1099 const auto global_index = mmat->row_start() + row_i;
1100 std::vector<numeric_index_type> indices;
1101 std::vector<Real> values;
1102 mmat->get_row(global_index, indices, values);
1103
1104 // Sum row elements (no absolute values)
1105 const Real row_sum = std::accumulate(values.cbegin(), values.cend(), 0.0);
1106
1107 // Add the sum of diagonal and elements to the sum_vector
1108 sum_vector.add(global_index, row_sum);
1109 }
1110 sum_vector.close();
1111 };
1112
1113 // Create a temporary vector to store the sum of diagonal and neighbor coefficients
1114 auto row_sum = current_local_solution.zero_clone();
1115 get_row_sum(*row_sum);
1116
1117 // Create vector with new inverse projection matrix
1118 auto Ainv_full = current_local_solution.zero_clone();
1119 *working_vector_petsc = 1.0;
1120 Ainv_full->pointwise_divide(*working_vector_petsc, *row_sum);
1121 const auto Ainv_full_old = Ainv_full->clone();
1122
1123 // Correct HbyA
1124 Ainv_full->add(-1.0, Ainv);
1125 working_vector_petsc->pointwise_mult(*Ainv_full, *coupling_pressure_gradient[system_i]);
1126 working_vector_petsc->pointwise_mult(*working_vector_petsc, *_cell_volumes);
1127 HbyA.add(-1.0, *working_vector_petsc);
1128
1129 // Correct Ainv
1130 Ainv = *Ainv_full_old;
1131 }
1132
1133 Ainv.pointwise_mult(Ainv, *_cell_volumes);
1134
1135 if (verbose)
1136 {
1137 _console << " 1/A" << std::endl;
1138 Ainv.print();
1139 }
1140 }
1141
1142 // We fill the 1/A and H/A functors
1144
1145 if (verbose)
1146 {
1147 _console << "************************************" << std::endl;
1148 _console << "DONE Computing HbyA " << std::endl;
1149 _console << "************************************" << std::endl;
1150 }
1151}
1152
1153const std::vector<NumericVector<Number> *> &
1158
1159bool
1164
1167{
1169 "reconstructedGradientMethod() should only be called when reconstructed "
1170 "pressure gradients are active.");
1171
1172 return dynamic_cast<const FVReconstructedPressureGradient &>(pressureGradientField().method());
1173}
1174
1177{
1178 auto & method = _fe_problem.getFVGradientMethod(pressureGradientField().method().name(), _tid);
1179 mooseAssert(&method == &pressureGradientField().method(),
1180 "The registered writable gradient method must match the pressure-gradient reader.");
1181 return dynamic_cast<FVReconstructedPressureGradient &>(method);
1182}
1183
1184void
1186{
1189 mooseError("RhieChowMassFlux '",
1190 name(),
1191 "' is missing a required pressure system, pressure-gradient field, base-gradient "
1192 "field, or pressure-correction diffusion kernel for reconstructed pressure "
1193 "gradients.");
1194
1196
1197 if (_momentum_systems.size() != _dim || _momentum_implicit_systems.size() != _dim ||
1199 mooseError("RhieChowMassFlux '",
1200 name(),
1201 "' requires one linked momentum system per spatial component for reconstructed "
1202 "pressure gradients.");
1203
1204 if (_pressure_system->nVariables() != 1)
1205 mooseError(
1206 "FVReconstructedPressureGradient assumes the pressure and momentum systems each have "
1207 "exactly one variable so their DOF indices can be used in vector operations. Pressure "
1208 "system '",
1210 "' has variables: ",
1212
1213 for (const auto system_i : index_range(_momentum_systems))
1214 {
1215 const auto * const momentum_system = _momentum_systems[system_i];
1216 if (!momentum_system || !_momentum_implicit_systems[system_i])
1217 mooseError("RhieChowMassFlux '",
1218 name(),
1219 "' has an invalid momentum-system layout for component ",
1220 system_i,
1221 ".");
1222
1223 if (momentum_system->nVariables() != 1)
1224 mooseError("FVReconstructedPressureGradient assumes the pressure and momentum systems each "
1225 "have exactly one variable so their DOF indices can be used in vector operations. "
1226 "Momentum system '",
1227 momentum_system->name(),
1228 "' has variables: ",
1229 Moose::stringify(momentum_system->getVariableNames()));
1230 }
1231}
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
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.
const LinearFVGradientReader * _pressure_gradient_field
Registered pressure gradient field used by Rhie-Chow and compatible momentum pressure kernels.
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.
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.
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.
std::unique_ptr< NumericVector< Number > > _cell_volumes
We will hold a vector of cell volumes to make sure we can do volume corrections rapidly.
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...
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...
void computeHbyA(const bool verbose)
Computes the inverse of the diagonal (1/A) of the system matrix plus the H/A components for the press...
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.
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}...
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)
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