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"
24
25// libMesh includes
26#include "libmesh/mesh_base.h"
27#include "libmesh/elem_range.h"
28#include "libmesh/petsc_matrix.h"
29
30using namespace libMesh;
31
33
36{
39
40 params.addClassDescription("Computes H/A and 1/A together with face mass fluxes for segregated "
41 "momentum-pressure equations using linear systems.");
42
43 params.addRequiredParam<VariableName>(NS::pressure, "The pressure variable.");
44 params.addRequiredParam<VariableName>("u", "The x-component of velocity");
45 params.addParam<VariableName>("v", "The y-component of velocity");
46 params.addParam<VariableName>("w", "The z-component of velocity");
47 params.addRequiredParam<std::string>(
48 "p_diffusion_kernel",
49 "The LinearFVPressureCorrectionDiffusion kernel acting on the pressure.");
50
51 params.addRequiredParam<MooseFunctorName>(NS::density, "Density functor");
52
53 // We disable the execution of this, should only provide functions
54 // for the SIMPLE executioner
55 ExecFlagEnum & exec_enum = params.set<ExecFlagEnum>("execute_on", true);
57 exec_enum = {EXEC_NONE};
58 params.suppressParameter<ExecFlagEnum>("execute_on");
59
60 // Pressure projection
61 params.addParam<MooseEnum>("pressure_projection_method",
62 MooseEnum("standard consistent", "standard"),
63 "The method to use in the pressure projection for Ainv - "
64 "standard (SIMPLE) or consistent (SIMPLEC)");
65 params.addParam<MooseEnum>(
66 "pressure_diffusion_interpolation",
67 MooseEnum("average harmonic", "average"),
68 "The face interpolation method for Ainv in the pressure correction diffusion term.");
69 return params;
70}
71
75 _moose_mesh(UserObject::_subproblem.mesh()),
76 _mesh(_moose_mesh.getMesh()),
77 _dim(blocksMaxDimension()),
78 _p(dynamic_cast<MooseLinearVariableFVReal *>(
79 &UserObject::_subproblem.getVariable(0, getParam<VariableName>(NS::pressure)))),
80 _vel(_dim, nullptr),
81 _HbyA_flux(_moose_mesh, blockIDs(), "HbyA_flux"),
82 _Ainv(_moose_mesh, blockIDs(), "Ainv"),
83 _face_mass_flux(
84 declareRestartableData<FaceCenteredMapFunctor<Real, std::unordered_map<dof_id_type, Real>>>(
85 "face_flux", _moose_mesh, blockIDs(), "face_values")),
86 _rho(getFunctor<Real>(NS::density)),
87 _pressure_projection_method(getParam<MooseEnum>("pressure_projection_method")),
88 _pressure_diffusion_interp_method(getParam<MooseEnum>("pressure_diffusion_interpolation") ==
89 "harmonic"
90 ? Moose::FV::InterpMethod::HarmonicAverage
91 : Moose::FV::InterpMethod::Average)
92{
93 if (!_p)
94 paramError(NS::pressure, "the pressure must be a MooseLinearVariableFVReal.");
96
97 std::vector<std::string> vel_names = {"u", "v", "w"};
98 for (const auto i : index_range(_vel))
99 {
100 _vel[i] = dynamic_cast<MooseLinearVariableFVReal *>(
101 &UserObject::_subproblem.getVariable(0, getParam<VariableName>(vel_names[i])));
102
103 if (!_vel[i])
104 paramError(vel_names[i], "the velocity must be a MOOSELinearVariableFVReal.");
105 checkBlocks(*_vel[i]);
106 }
107
108 // Register the elemental/face functors which will be queried in the pressure equation
109 for (const auto tid : make_range(libMesh::n_threads()))
110 {
113 }
114
115 if (!dynamic_cast<SIMPLE *>(getMooseApp().getExecutioner()) &&
116 !dynamic_cast<PIMPLE *>(getMooseApp().getExecutioner()))
117 mooseError(this->name(),
118 " should only be used with a linear segregated thermal-hydraulics solver!");
119}
120
121void
123 const std::vector<LinearSystem *> & momentum_systems,
124 const LinearSystem & pressure_system,
125 const std::vector<unsigned int> & momentum_system_numbers)
126{
127 _momentum_systems = momentum_systems;
128 _momentum_system_numbers = momentum_system_numbers;
129 _pressure_system = &pressure_system;
131
133 for (auto & system : _momentum_systems)
134 {
135 _global_momentum_system_numbers.push_back(system->number());
136 _momentum_implicit_systems.push_back(dynamic_cast<LinearImplicitSystem *>(&system->system()));
137 }
138
140
141 std::set<BoundaryID> velocity_boundary_ids;
142 for (const auto dim_i : make_range(_dim))
143 for (const auto & [boundary_id, _] : _vel[dim_i]->getBoundaryConditionMap())
144 velocity_boundary_ids.insert(boundary_id);
145
146 const auto is_dirichlet =
147 [](const MooseLinearVariableFVReal & variable, const BoundaryID boundary_id)
148 {
149 return dynamic_cast<const LinearFVAdvectionDiffusionFunctorDirichletBC *>(
150 variable.getBoundaryCondition(boundary_id)) != nullptr;
151 };
152
153 // The legacy boundary HbyA reconstruction uses the x-velocity BC as a proxy for every velocity
154 // component, so all components must have the same Dirichlet classification on those boundaries.
155 for (const auto boundary_id : velocity_boundary_ids)
156 {
157 const auto * const pressure_bc = _p->getBoundaryCondition(boundary_id);
158 if (dynamic_cast<const LinearFVPressureFluxBC *>(pressure_bc))
159 continue;
160
161 const bool velocity_is_dirichlet = is_dirichlet(*_vel[0], boundary_id);
162 for (const auto dim_i : make_range(_dim))
163 if (is_dirichlet(*_vel[dim_i], boundary_id) != velocity_is_dirichlet)
164 mooseError("All velocity components must either have Dirichlet boundary conditions or "
165 "non-Dirichlet boundary conditions on boundary '",
166 _moose_mesh.getBoundaryName(boundary_id),
167 "' when the pressure boundary condition is not a LinearFVPressureFluxBC.");
168 }
169}
170
171void
173{
174 _HbyA_flux.clear();
175 _Ainv.clear();
176 _face_mass_flux.clear();
178}
179
180void
182{
183 // We fetch the pressure diffusion kernel to ensure that the face flux correction
184 // is consistent with the pressure discretization in the Poisson equation.
185 std::vector<LinearFVFluxKernel *> flux_kernel;
186 auto base_query = _fe_problem.theWarehouse()
187 .query()
188 .template condition<AttribThread>(_tid)
189 .template condition<AttribSysNum>(_p->sys().number())
190 .template condition<AttribSystem>("LinearFVFluxKernel")
191 .template condition<AttribName>(getParam<std::string>("p_diffusion_kernel"))
192 .queryInto(flux_kernel);
193 if (flux_kernel.size() != 1)
195 "p_diffusion_kernel",
196 "The kernel with the given name could not be found or multiple instances were identified.");
197 _p_diffusion_kernel = dynamic_cast<LinearFVPressureCorrectionDiffusion *>(flux_kernel[0]);
199 paramError("p_diffusion_kernel",
200 "The provided diffusion kernel must be of type "
201 "LinearFVPressureCorrectionDiffusion.");
202}
203
204void
206{
207 // We cache the cell volumes into a petsc vector for corrections here so we can use
208 // the optimized petsc operations for the normalization
210 for (const auto & elem_info : _fe_problem.mesh().elemInfoVector())
211 // We have to check this because the variable might not be defined on the given
212 // block
213 if (hasBlocks(elem_info->subdomain_id()))
214 {
215 const auto elem_dof = elem_info->dofIndices()[_global_pressure_system_number][0];
216 _cell_volumes->set(elem_dof, elem_info->volume() * elem_info->coordFactor());
217 }
218
219 _cell_volumes->close();
220
221 _flow_face_info.clear();
222 for (auto & fi : _fe_problem.mesh().faceInfo())
223 if (hasBlocks(fi->elemPtr()->subdomain_id()) ||
224 (fi->neighborPtr() && hasBlocks(fi->neighborPtr()->subdomain_id())))
225 _flow_face_info.push_back(fi);
226}
227
228void
230{
231 for (const auto & pair : _HbyA_flux)
232 _HbyA_flux[pair.first] = 0;
233
234 for (const auto & pair : _Ainv)
235 _Ainv[pair.first] = 0;
236}
237
238void
240{
241 using namespace Moose::FV;
242
243 const auto time_arg = Moose::currentState();
244
245 // We loop through the faces and compute the resulting face fluxes from the
246 // initial conditions for velocity
247 for (auto & fi : _flow_face_info)
248 {
249 RealVectorValue density_times_velocity;
250
251 // On internal face we do a regular interpolation with geometric weights
252 if (_vel[0]->isInternalFace(*fi))
253 {
254 const auto & elem_info = *fi->elemInfo();
255 const auto & neighbor_info = *fi->neighborInfo();
256
257 Real elem_rho = _rho(makeElemArg(fi->elemPtr()), time_arg);
258 Real neighbor_rho = _rho(makeElemArg(fi->neighborPtr()), time_arg);
259
260 for (const auto dim_i : index_range(_vel))
261 interpolate(InterpMethod::Average,
262 density_times_velocity(dim_i),
263 _vel[dim_i]->getElemValue(elem_info, time_arg) * elem_rho,
264 _vel[dim_i]->getElemValue(neighbor_info, time_arg) * neighbor_rho,
265 *fi,
266 true);
267 }
268 // On the boundary, we just take the boundary values
269 else
270 {
271 const bool elem_is_fluid = hasBlocks(fi->elemPtr()->subdomain_id());
272 const Elem * const boundary_elem = elem_is_fluid ? fi->elemPtr() : fi->neighborPtr();
273
274 // We need this multiplier in case the face is an internal face and
275 const Real boundary_normal_multiplier = elem_is_fluid ? 1.0 : -1.0;
276 const Moose::FaceArg boundary_face{
277 fi, Moose::FV::LimiterType::CentralDifference, true, false, boundary_elem, nullptr};
278
279 const Real face_rho = _rho(boundary_face, time_arg);
280 for (const auto dim_i : index_range(_vel))
281 density_times_velocity(dim_i) = boundary_normal_multiplier * face_rho *
282 raw_value((*_vel[dim_i])(boundary_face, time_arg));
283 }
284
285 _face_mass_flux[fi->id()] = density_times_velocity * fi->normal();
286 }
287}
288
289Real
291{
292 return _face_mass_flux.evaluate(&fi);
293}
294
295Real
297{
298 const Moose::FaceArg face_arg{&fi,
299 /*limiter_type=*/Moose::FV::LimiterType::CentralDifference,
300 /*elem_is_upwind=*/true,
301 /*correct_skewness=*/false,
302 &fi.elem(),
303 /*state_limiter*/ nullptr};
304 const Real face_rho = _rho(face_arg, Moose::currentState());
305 return libmesh_map_find(_face_mass_flux, fi.id()) / face_rho;
306}
307
308Real
310 const FaceInfo & fi,
311 const Moose::StateArg & time,
312 const THREAD_ID /*tid*/,
313 bool libmesh_dbg_var(subtract_mesh_velocity)) const
314{
315 mooseAssert(!subtract_mesh_velocity, "RhieChowMassFlux does not support moving meshes yet!");
316
317 if (m != Moose::FV::InterpMethod::RhieChow)
318 mooseError("Interpolation methods other than Rhie-Chow are not supported!");
319 if (time.state != Moose::currentState().state)
320 mooseError("Older interpolation times are not supported!");
321
322 return getVolumetricFaceFlux(fi);
323}
324
325void
327{
328 using namespace Moose::FV;
329
330 const auto time_arg = Moose::currentState();
331
332 // Petsc vector reader to make the repeated reading from the vector faster
334
335 // We loop through the faces and compute the face fluxes using the pressure gradient
336 // and the momentum matrix/right hand side
337 for (auto & fi : _flow_face_info)
338 {
339 // Making sure the kernel knows which face we are on
341
342 // We are setting this to 1.0 because we don't want to multiply the kernel contributions
343 // with the surface area yet. The surface area will be factored in in the advection kernels.
345
346 Real p_grad_flux = 0.0;
347 if (_p->isInternalFace(*fi))
348 {
349 const auto & elem_info = *fi->elemInfo();
350 const auto & neighbor_info = *fi->neighborInfo();
351
352 // Fetching the dof indices for the pressure variable
353 const auto elem_dof = elem_info.dofIndices()[_global_pressure_system_number][0];
354 const auto neighbor_dof = neighbor_info.dofIndices()[_global_pressure_system_number][0];
355
356 // Fetching the values of the pressure for the element and the neighbor
357 const auto p_elem_value = p_reader(elem_dof);
358 const auto p_neighbor_value = p_reader(neighbor_dof);
359
360 // Compute the elem matrix contributions for the face
361 const auto elem_matrix_contribution = _p_diffusion_kernel->computeElemMatrixContribution();
362 const auto neighbor_matrix_contribution =
364 const auto elem_rhs_contribution =
366
367 // Compute the face flux from the matrix and right hand side contributions
368 p_grad_flux = (p_neighbor_value * neighbor_matrix_contribution +
369 p_elem_value * elem_matrix_contribution) -
370 elem_rhs_contribution;
371 }
372 else if (auto * bc_pointer = _p->getBoundaryCondition(*fi->boundaryIDs().begin()))
373 {
374 mooseAssert(fi->boundaryIDs().size() == 1, "We should only have one boundary on every face.");
375
376 bc_pointer->setupFaceData(
377 fi, fi->faceType(std::make_pair(_p->number(), _global_pressure_system_number)));
378
379 const ElemInfo & elem_info =
380 hasBlocks(fi->elemPtr()->subdomain_id()) ? *fi->elemInfo() : *fi->neighborInfo();
381 const auto p_elem_value = _p->getElemValue(elem_info, time_arg);
382 const auto matrix_contribution =
384 const auto rhs_contribution =
386
387 // On the boundary, only the element side has a contribution
388 p_grad_flux = (p_elem_value * matrix_contribution - rhs_contribution);
389 }
390 // Compute the new face flux
391 _face_mass_flux[fi->id()] = -_HbyA_flux[fi->id()] + p_grad_flux;
392 }
393}
394
395void
397{
398 auto & pressure_gradient = _pressure_system->linearFVGradientContainer();
399
400 // We set the dof value in the solution vector the same logic applies:
401 // u_C = -(H/A)_C - (1/A)_C*grad(p)_C where C is the cell index
402 for (const auto system_i : index_range(_momentum_implicit_systems))
403 {
404 auto working_vector = _Ainv_raw[system_i]->clone();
405 working_vector->pointwise_mult(*working_vector, *pressure_gradient[system_i]);
406 working_vector->add(*_HbyA_raw[system_i]);
407 working_vector->scale(-1.0);
408 (*_momentum_implicit_systems[system_i]->solution) = *working_vector;
409 _momentum_implicit_systems[system_i]->update();
410 _momentum_systems[system_i]->setSolution(
411 *_momentum_implicit_systems[system_i]->current_local_solution);
412 }
413}
414
415void
417{
418 // We loop through the faces and populate the coupling fields (face H/A and 1/H)
419 // with 0s for now. Pressure corrector solves will always come after the
420 // momentum source so we expect these fields to change before the actual solve.
421 for (auto & fi : _fe_problem.mesh().faceInfo())
422 {
423 _Ainv[fi->id()];
424 _HbyA_flux[fi->id()];
425 }
426}
427
428void
430 const std::vector<std::unique_ptr<NumericVector<Number>>> & raw_hbya,
431 const std::vector<std::unique_ptr<NumericVector<Number>>> & raw_Ainv)
432{
433 // We have the raw H/A and 1/A vectors in a petsc format. This function
434 // will create face functors from them
435 using namespace Moose::FV;
436 const auto time_arg = Moose::currentState();
437
438 // Create the petsc vector readers for faster repeated access
439 std::vector<PetscVectorReader> hbya_reader;
440 for (const auto dim_i : index_range(raw_hbya))
441 hbya_reader.emplace_back(*raw_hbya[dim_i]);
442
443 std::vector<PetscVectorReader> ainv_reader;
444 for (const auto dim_i : index_range(raw_Ainv))
445 ainv_reader.emplace_back(*raw_Ainv[dim_i]);
446
447 // We loop through the faces and populate the coupling fields (face H/A and 1/A)
448 for (auto & fi : _flow_face_info)
449 {
450 Real face_rho = 0;
451 RealVectorValue face_hbya;
452
453 // We do the lookup in advance
454 auto & Ainv = _Ainv[fi->id()];
455
456 // If it is internal, we just interpolate (using geometric weights) to the face
457 if (_vel[0]->isInternalFace(*fi))
458 {
459 // Get the dof indices for the element and the neighbor
460 const auto & elem_info = *fi->elemInfo();
461 const auto & neighbor_info = *fi->neighborInfo();
462 const auto elem_dof = elem_info.dofIndices()[_global_momentum_system_numbers[0]][0];
463 const auto neighbor_dof = neighbor_info.dofIndices()[_global_momentum_system_numbers[0]][0];
464
465 // Get the density values for the element and neighbor. We need this multiplication to make
466 // the coupling fields mass fluxes.
467 const Real elem_rho = _rho(makeElemArg(fi->elemPtr()), time_arg);
468 const Real neighbor_rho = _rho(makeElemArg(fi->neighborPtr()), time_arg);
469
470 // Now we do the interpolation to the face
471 interpolate(Moose::FV::InterpMethod::Average, face_rho, elem_rho, neighbor_rho, *fi, true);
472 for (const auto dim_i : index_range(raw_hbya))
473 {
474 interpolate(Moose::FV::InterpMethod::Average,
475 face_hbya(dim_i),
476 hbya_reader[dim_i](elem_dof),
477 hbya_reader[dim_i](neighbor_dof),
478 *fi,
479 true);
481 Ainv(dim_i),
482 elem_rho * ainv_reader[dim_i](elem_dof),
483 neighbor_rho * ainv_reader[dim_i](neighbor_dof),
484 *fi,
485 true);
486 }
487 }
488 else
489 {
490 const bool elem_is_fluid = hasBlocks(fi->elemPtr()->subdomain_id());
491
492 // We need this multiplier in case the face is an internal face and
493 const Real boundary_normal_multiplier = elem_is_fluid ? 1.0 : -1.0;
494
495 const ElemInfo & elem_info = elem_is_fluid ? *fi->elemInfo() : *fi->neighborInfo();
496 const auto elem_dof = elem_info.dofIndices()[_global_momentum_system_numbers[0]][0];
497
498 mooseAssert(fi->boundaryIDs().size() == 1, "We should only have one boundary on every face.");
499 const auto * pressure_bc = _p->getBoundaryCondition(*fi->boundaryIDs().begin());
500
501 // For the legacy Dirichlet-velocity plus extrapolated-pressure path, we still need a
502 // special boundary HbyA reconstruction to keep the face flux consistent. When the pressure
503 // BC itself is a LinearFVPressureFluxBC, that object already enforces the prescribed
504 // boundary mass flux, so we use the standard one-term boundary expansion instead.
505 if (_vel[0]->isDirichletBoundaryFace(*fi) &&
506 !dynamic_cast<const LinearFVPressureFluxBC *>(pressure_bc))
507 {
508 const Moose::FaceArg boundary_face{
509 fi, Moose::FV::LimiterType::CentralDifference, true, false, elem_info.elem(), nullptr};
510 face_rho = _rho(boundary_face, Moose::currentState());
511
512 for (const auto dim_i : make_range(_dim))
513 {
514
515 face_hbya(dim_i) =
516 -MetaPhysicL::raw_value((*_vel[dim_i])(boundary_face, Moose::currentState()));
517 face_hbya(dim_i) *= boundary_normal_multiplier;
518 }
519 }
520 // Otherwise we just do a one-term expansion (so we just use the element value)
521 else
522 {
523 const auto elem_dof = elem_info.dofIndices()[_global_momentum_system_numbers[0]][0];
524
525 face_rho = _rho(makeElemArg(elem_info.elem()), time_arg);
526 for (const auto dim_i : make_range(_dim))
527 face_hbya(dim_i) = boundary_normal_multiplier * hbya_reader[dim_i](elem_dof);
528 }
529
530 // We just do a one-term expansion for 1/A no matter what
531 const Real elem_rho = _rho(makeElemArg(elem_info.elem()), time_arg);
532 for (const auto dim_i : index_range(raw_Ainv))
533 Ainv(dim_i) = elem_rho * ainv_reader[dim_i](elem_dof);
534 }
535 // Lastly, we populate the face flux resulted by H/A
536 _HbyA_flux[fi->id()] = face_hbya * fi->normal() * face_rho;
537 }
538}
539
540void
541RhieChowMassFlux::computeHbyA(const bool with_updated_pressure, bool verbose)
542{
543 if (verbose)
544 {
545 _console << "************************************" << std::endl;
546 _console << "Computing HbyA" << std::endl;
547 _console << "************************************" << std::endl;
548 }
550 "The momentum system shall be linked before calling this function!");
551
552 auto & pressure_gradient = selectPressureGradient(with_updated_pressure);
553
554 _HbyA_raw.clear();
555 _Ainv_raw.clear();
556
557 for (auto system_i : index_range(_momentum_systems))
558 {
559 LinearImplicitSystem * momentum_system = _momentum_implicit_systems[system_i];
560
561 NumericVector<Number> & rhs = *(momentum_system->rhs);
562 NumericVector<Number> & current_local_solution = *(momentum_system->current_local_solution);
563 NumericVector<Number> & solution = *(momentum_system->solution);
564 PetscMatrix<Number> * mmat = dynamic_cast<PetscMatrix<Number> *>(momentum_system->matrix);
565 mooseAssert(mmat,
566 "The matrices used in the segregated INSFVRhieChow objects need to be convertable "
567 "to PetscMatrix!");
568
569 if (verbose)
570 {
571 _console << "Matrix in rc object" << std::endl;
572 mmat->print();
573 }
574
575 // First, we extract the diagonal and we will hold on to it for a little while
576 _Ainv_raw.push_back(current_local_solution.zero_clone());
577 NumericVector<Number> & Ainv = *(_Ainv_raw.back());
578
579 mmat->get_diagonal(Ainv);
580
581 if (verbose)
582 {
583 _console << "Velocity solution in H(u)" << std::endl;
584 solution.print();
585 }
586
587 // Time to create H(u) = M_{offdiag} * u - b_{nonpressure}
588 _HbyA_raw.push_back(current_local_solution.zero_clone());
589 NumericVector<Number> & HbyA = *(_HbyA_raw.back());
590
591 // We start with the matrix product part, we will do
592 // M*u - A*u for 2 reasons:
593 // 1, We assume A*u petsc operation is faster than setting the matrix diagonal to 0
594 // 2, In PISO loops we need to reuse the matrix so we can't just set the diagonals to 0
595
596 // We create a working vector to ease some of the operations, we initialize its values
597 // with the current solution values to have something for the A*u term
598 auto working_vector = momentum_system->current_local_solution->zero_clone();
599 PetscVector<Number> * working_vector_petsc =
600 dynamic_cast<PetscVector<Number> *>(working_vector.get());
601 mooseAssert(working_vector_petsc,
602 "The vectors used in the RhieChowMassFlux objects need to be convertable "
603 "to PetscVectors!");
604
605 mmat->vector_mult(HbyA, solution);
606 working_vector_petsc->pointwise_mult(Ainv, solution);
607 HbyA.add(-1.0, *working_vector_petsc);
608
609 if (verbose)
610 {
611 _console << " H(u)" << std::endl;
612 HbyA.print();
613 }
614
615 // We continue by adding the momentum right hand side contributions
616 HbyA.add(-1.0, rhs);
617
618 // Unfortunately, the pressure forces are included in the momentum RHS
619 // so we have to correct them back
620 working_vector_petsc->pointwise_mult(*pressure_gradient[system_i], *_cell_volumes);
621 HbyA.add(-1.0, *working_vector_petsc);
622
623 if (verbose)
624 {
625 _console << "total RHS" << std::endl;
626 rhs.print();
627 _console << "pressure RHS" << std::endl;
628 pressure_gradient[system_i]->print();
629 _console << " H(u)-rhs-relaxation_source" << std::endl;
630 HbyA.print();
631 }
632
633 // It is time to create element-wise 1/A-s based on the the diagonal of the momentum matrix
634 *working_vector_petsc = 1.0;
635 Ainv.pointwise_divide(*working_vector_petsc, Ainv);
636
637 // Create 1/A*(H(u)-RHS)
638 HbyA.pointwise_mult(HbyA, Ainv);
639
640 if (verbose)
641 {
642 _console << " (H(u)-rhs)/A" << std::endl;
643 HbyA.print();
644 }
645
646 if (_pressure_projection_method == "consistent")
647 {
648
649 // Consistent Corrections to SIMPLE
650 // 1. Ainv_old = 1/a_p <- Ainv = 1/(a_p + \sum_n a_n)
651 // 2. H(u) <- H(u*) + H(u') = H(u*) - (Ainv - Ainv_old) * grad(p) * Vc
652
653 if (verbose)
654 _console << "Performing SIMPLEC projection." << std::endl;
655
656 // Lambda function to calculate the sum of diagonal and neighbor coefficients
657 auto get_row_sum = [mmat](NumericVector<Number> & sum_vector)
658 {
659 // Ensure the sum_vector is zeroed out
660 sum_vector.zero();
661
662 // Local row size
663 const auto local_size = mmat->local_m();
664
665 for (const auto row_i : make_range(local_size))
666 {
667 // Get all non-zero components of the row of the matrix
668 const auto global_index = mmat->row_start() + row_i;
669 std::vector<numeric_index_type> indices;
670 std::vector<Real> values;
671 mmat->get_row(global_index, indices, values);
672
673 // Sum row elements (no absolute values)
674 const Real row_sum = std::accumulate(values.cbegin(), values.cend(), 0.0);
675
676 // Add the sum of diagonal and elements to the sum_vector
677 sum_vector.add(global_index, row_sum);
678 }
679 sum_vector.close();
680 };
681
682 // Create a temporary vector to store the sum of diagonal and neighbor coefficients
683 auto row_sum = current_local_solution.zero_clone();
684 get_row_sum(*row_sum);
685
686 // Create vector with new inverse projection matrix
687 auto Ainv_full = current_local_solution.zero_clone();
688 *working_vector_petsc = 1.0;
689 Ainv_full->pointwise_divide(*working_vector_petsc, *row_sum);
690 const auto Ainv_full_old = Ainv_full->clone();
691
692 // Correct HbyA
693 Ainv_full->add(-1.0, Ainv);
694 working_vector_petsc->pointwise_mult(*Ainv_full, *pressure_gradient[system_i]);
695 working_vector_petsc->pointwise_mult(*working_vector_petsc, *_cell_volumes);
696 HbyA.add(-1.0, *working_vector_petsc);
697
698 // Correct Ainv
699 Ainv = *Ainv_full_old;
700 }
701
702 Ainv.pointwise_mult(Ainv, *_cell_volumes);
703
704 if (verbose)
705 {
706 _console << " 1/A" << std::endl;
707 Ainv.print();
708 }
709 }
710
711 // We fill the 1/A and H/A functors
713
714 if (verbose)
715 {
716 _console << "************************************" << std::endl;
717 _console << "DONE Computing HbyA " << std::endl;
718 _console << "************************************" << std::endl;
719 }
720}
721
722std::vector<std::unique_ptr<NumericVector<Number>>> &
724{
725 if (updated_pressure)
726 {
727 _grad_p_current.clear();
728 for (const auto & component : _pressure_system->linearFVGradientContainer())
729 _grad_p_current.push_back(component->clone());
730 }
731
732 return _grad_p_current;
733}
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
const ConsoleStream _console
const Elem * elem() const
const std::vector< std::vector< dof_id_type > > & dofIndices() const
void addAvailableFlags(const ExecFlagType &flag, Args... flags)
virtual MooseMesh & mesh() override
TheWarehouse & theWarehouse() const
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
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 std::vector< std::unique_ptr< libMesh::NumericVector< libMesh::Number > > > & linearFVGradientContainer() const
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 ...
virtual System & system() override
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
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.
const Moose::FV::InterpMethod _pressure_diffusion_interp_method
Interpolation method used for the pressure diffusion coefficient on faces.
std::vector< std::unique_ptr< NumericVector< Number > > > _grad_p_current
for a PISO iteration we need to hold on to the original pressure gradient field.
std::vector< std::unique_ptr< NumericVector< Number > > > & selectPressureGradient(const bool updated_pressure)
Select the right pressure gradient field and return a reference to the container.
std::vector< const MooseLinearVariableFVReal * > _vel
The thread 0 copy of the x-velocity variable.
void initFaceMassFlux()
Initialize the container for face velocities.
LinearFVPressureCorrectionDiffusion * _p_diffusion_kernel
Pointer to the pressure diffusion term in the pressure Poisson equation.
std::vector< unsigned int > _momentum_system_numbers
Numbers of the momentum system(s)
virtual void initialize() override
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)
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)
void computeHbyA(const bool with_updated_pressure, const bool verbose)
Computes the inverse of the diagonal (1/A) of the system matrix plus the H/A components for the press...
void computeCellVelocity()
Update the cell values of the velocity variables.
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 LinearSystem * _pressure_system
Pointer to the pressure system.
std::vector< const FaceInfo * > _flow_face_info
The subset of the FaceInfo objects that actually cover the subdomains which the flow field is defined...
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.
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.
void initCouplingField()
Initialize the coupling fields (HbyA and Ainv)
FaceCenteredMapFunctor< RealVectorValue, std::unordered_map< dof_id_type, RealVectorValue > > _Ainv
A map functor from faces to $(1/A)_f$.
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)
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.
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}...
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)
void linkMomentumPressureSystems(const std::vector< LinearSystem * > &momentum_systems, const LinearSystem &pressure_system, const std::vector< unsigned int > &momentum_system_numbers)
Update the momentum system-related information.
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)
unsigned int number() const
Query query()
SubProblem & _subproblem
FEProblemBase & _fe_problem
const THREAD_ID _tid
subdomain_id_type subdomain_id() const
NumericVector< Number > * rhs
SparseMatrix< Number > * matrix
virtual void pointwise_mult(const NumericVector< T > &vec1, const NumericVector< T > &vec2)=0
virtual void print(std::ostream &os=libMesh::out) const
virtual std::unique_ptr< NumericVector< T > > zero_clone() const=0
virtual void add(const numeric_index_type i, const T value)=0
virtual void pointwise_divide(const NumericVector< T > &vec1, const NumericVector< T > &vec2)=0
virtual numeric_index_type local_m() const final
virtual numeric_index_type row_start() const override
virtual void get_row(numeric_index_type i, std::vector< numeric_index_type > &indices, std::vector< T > &values) const override
virtual void get_diagonal(NumericVector< T > &dest) const override
virtual void pointwise_mult(const NumericVector< T > &vec1, const NumericVector< T > &vec2) override
virtual void pointwise_divide(const NumericVector< T > &vec1, const NumericVector< T > &vec2) override
void vector_mult(NumericVector< T > &dest, const NumericVector< T > &arg) const
void print(std::ostream &os=libMesh::out, const bool sparse=false) const
std::unique_ptr< NumericVector< Number > > current_local_solution
std::unique_ptr< NumericVector< Number > > solution
MeshBase & mesh
auto raw_value(const Eigen::Map< T > &in)
StateArg currentState()
static const std::string density
Definition NS.h:34
static const std::string pressure
Definition NS.h:57
The following methods are specializations for using the Parallel::packed_range_* routines for a vecto...
auto index_range(const T &sizable)
uint8_t dof_id_type
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)
unsigned int n_threads()
unsigned int state