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