LCOV - code coverage report
Current view: top level - src/userobjects - RhieChowMassFlux.C (source / functions) Hit Total Coverage
Test: idaholab/moose navier_stokes: 9fc4b0 Lines: 269 303 88.8 %
Date: 2025-08-14 10:14:56 Functions: 17 18 94.4 %
Legend: Lines: hit not hit

          Line data    Source code
       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"
      15             : #include "VectorCompositeFunctor.h"
      16             : #include "PIMPLE.h"
      17             : #include "SIMPLE.h"
      18             : #include "PetscVectorReader.h"
      19             : #include "LinearSystem.h"
      20             : #include "LinearFVBoundaryCondition.h"
      21             : 
      22             : // libMesh includes
      23             : #include "libmesh/mesh_base.h"
      24             : #include "libmesh/elem_range.h"
      25             : #include "libmesh/petsc_matrix.h"
      26             : 
      27             : using namespace libMesh;
      28             : 
      29             : registerMooseObject("NavierStokesApp", RhieChowMassFlux);
      30             : 
      31             : InputParameters
      32        1618 : RhieChowMassFlux::validParams()
      33             : {
      34        1618 :   auto params = RhieChowFaceFluxProvider::validParams();
      35        1618 :   params += NonADFunctorInterface::validParams();
      36             : 
      37        1618 :   params.addClassDescription("Computes H/A and 1/A together with face mass fluxes for segregated "
      38             :                              "momentum-pressure equations using linear systems.");
      39             : 
      40        1618 :   params.addRequiredParam<VariableName>(NS::pressure, "The pressure variable.");
      41        3236 :   params.addRequiredParam<VariableName>("u", "The x-component of velocity");
      42        3236 :   params.addParam<VariableName>("v", "The y-component of velocity");
      43        3236 :   params.addParam<VariableName>("w", "The z-component of velocity");
      44        3236 :   params.addRequiredParam<std::string>("p_diffusion_kernel",
      45             :                                        "The diffusion kernel acting on the pressure.");
      46             : 
      47        1618 :   params.addRequiredParam<MooseFunctorName>(NS::density, "Density functor");
      48             : 
      49             :   // We disable the execution of this, should only provide functions
      50             :   // for the SIMPLE executioner
      51        1618 :   ExecFlagEnum & exec_enum = params.set<ExecFlagEnum>("execute_on", true);
      52        1618 :   exec_enum.addAvailableFlags(EXEC_NONE);
      53        4854 :   exec_enum = {EXEC_NONE};
      54        1618 :   params.suppressParameter<ExecFlagEnum>("execute_on");
      55             : 
      56             :   // Pressure projection
      57        3236 :   params.addParam<MooseEnum>("pressure_projection_method",
      58        4854 :                              MooseEnum("standard consistent", "standard"),
      59             :                              "The method to use in the pressure projection for Ainv - "
      60             :                              "standard (SIMPLE) or consistent (SIMPLEC)");
      61             : 
      62        1618 :   return params;
      63        1618 : }
      64             : 
      65         809 : RhieChowMassFlux::RhieChowMassFlux(const InputParameters & params)
      66             :   : RhieChowFaceFluxProvider(params),
      67             :     NonADFunctorInterface(this),
      68        1618 :     _moose_mesh(UserObject::_subproblem.mesh()),
      69         809 :     _mesh(_moose_mesh.getMesh()),
      70         809 :     _dim(blocksMaxDimension()),
      71         809 :     _p(dynamic_cast<MooseLinearVariableFVReal *>(
      72         809 :         &UserObject::_subproblem.getVariable(0, getParam<VariableName>(NS::pressure)))),
      73         809 :     _vel(_dim, nullptr),
      74        1618 :     _HbyA_flux(_moose_mesh, blockIDs(), "HbyA_flux"),
      75        1618 :     _Ainv(_moose_mesh, blockIDs(), "Ainv"),
      76         809 :     _face_mass_flux(
      77        1618 :         declareRestartableData<FaceCenteredMapFunctor<Real, std::unordered_map<dof_id_type, Real>>>(
      78             :             "face_flux", _moose_mesh, blockIDs(), "face_values")),
      79         809 :     _rho(getFunctor<Real>(NS::density)),
      80        3236 :     _pressure_projection_method(getParam<MooseEnum>("pressure_projection_method"))
      81             : {
      82         809 :   if (!_p)
      83           0 :     paramError(NS::pressure, "the pressure must be a MooseLinearVariableFVReal.");
      84         809 :   checkBlocks(*_p);
      85             : 
      86         809 :   std::vector<std::string> vel_names = {"u", "v", "w"};
      87        2388 :   for (const auto i : index_range(_vel))
      88             :   {
      89        1579 :     _vel[i] = dynamic_cast<MooseLinearVariableFVReal *>(
      90        1579 :         &UserObject::_subproblem.getVariable(0, getParam<VariableName>(vel_names[i])));
      91             : 
      92        1579 :     if (!_vel[i])
      93           0 :       paramError(vel_names[i], "the velocity must be a MOOSELinearVariableFVReal.");
      94        1579 :     checkBlocks(*_vel[i]);
      95             :   }
      96             : 
      97             :   // Register the elemental/face functors which will be queried in the pressure equation
      98        1618 :   for (const auto tid : make_range(libMesh::n_threads()))
      99             :   {
     100         809 :     UserObject::_subproblem.addFunctor("Ainv", _Ainv, tid);
     101        1618 :     UserObject::_subproblem.addFunctor("HbyA", _HbyA_flux, tid);
     102             :   }
     103             : 
     104         809 :   if (!dynamic_cast<SIMPLE *>(getMooseApp().getExecutioner()) &&
     105         143 :       !dynamic_cast<PIMPLE *>(getMooseApp().getExecutioner()))
     106           0 :     mooseError(this->name(),
     107             :                " should only be used with a linear segregated thermal-hydraulics solver!");
     108         809 : }
     109             : 
     110             : void
     111         807 : RhieChowMassFlux::linkMomentumPressureSystems(
     112             :     const std::vector<LinearSystem *> & momentum_systems,
     113             :     const LinearSystem & pressure_system,
     114             :     const std::vector<unsigned int> & momentum_system_numbers)
     115             : {
     116         807 :   _momentum_systems = momentum_systems;
     117         807 :   _momentum_system_numbers = momentum_system_numbers;
     118         807 :   _pressure_system = &pressure_system;
     119         807 :   _global_pressure_system_number = _pressure_system->number();
     120             : 
     121         807 :   _momentum_implicit_systems.clear();
     122        2382 :   for (auto & system : _momentum_systems)
     123             :   {
     124        1575 :     _global_momentum_system_numbers.push_back(system->number());
     125        1575 :     _momentum_implicit_systems.push_back(dynamic_cast<LinearImplicitSystem *>(&system->system()));
     126             :   }
     127             : 
     128         807 :   setupMeshInformation();
     129         807 : }
     130             : 
     131             : void
     132           9 : RhieChowMassFlux::meshChanged()
     133             : {
     134             :   _HbyA_flux.clear();
     135             :   _Ainv.clear();
     136           9 :   _face_mass_flux.clear();
     137           9 :   setupMeshInformation();
     138           9 : }
     139             : 
     140             : void
     141         807 : RhieChowMassFlux::initialSetup()
     142             : {
     143             :   // We fetch the pressure diffusion kernel to ensure that the face flux correction
     144             :   // is consistent with the pressure discretization in the Poisson equation.
     145             :   std::vector<LinearFVFluxKernel *> flux_kernel;
     146         807 :   auto base_query = _fe_problem.theWarehouse()
     147         807 :                         .query()
     148         807 :                         .template condition<AttribThread>(_tid)
     149         807 :                         .template condition<AttribSysNum>(_p->sys().number())
     150         807 :                         .template condition<AttribSystem>("LinearFVFluxKernel")
     151        2421 :                         .template condition<AttribName>(getParam<std::string>("p_diffusion_kernel"))
     152         807 :                         .queryInto(flux_kernel);
     153         807 :   if (flux_kernel.size() != 1)
     154           0 :     paramError(
     155             :         "p_diffusion_kernel",
     156             :         "The kernel with the given name could not be found or multiple instances were identified.");
     157         807 :   _p_diffusion_kernel = dynamic_cast<LinearFVAnisotropicDiffusion *>(flux_kernel[0]);
     158         807 :   if (!_p_diffusion_kernel)
     159           0 :     paramError("p_diffusion_kernel",
     160             :                "The provided diffusion kernel should of type LinearFVAnisotropicDiffusion!");
     161         807 : }
     162             : 
     163             : void
     164         816 : RhieChowMassFlux::setupMeshInformation()
     165             : {
     166             :   // We cache the cell volumes into a petsc vector for corrections here so we can use
     167             :   // the optimized petsc operations for the normalization
     168        1632 :   _cell_volumes = _pressure_system->currentSolution()->zero_clone();
     169       91204 :   for (const auto & elem_info : _fe_problem.mesh().elemInfoVector())
     170             :     // We have to check this because the variable might not be defined on the given
     171             :     // block
     172       90388 :     if (hasBlocks(elem_info->subdomain_id()))
     173             :     {
     174       90028 :       const auto elem_dof = elem_info->dofIndices()[_global_pressure_system_number][0];
     175       90028 :       _cell_volumes->set(elem_dof, elem_info->volume() * elem_info->coordFactor());
     176             :     }
     177             : 
     178         816 :   _cell_volumes->close();
     179             : 
     180         816 :   _flow_face_info.clear();
     181      178326 :   for (auto & fi : _fe_problem.mesh().faceInfo())
     182      178099 :     if (hasBlocks(fi->elemPtr()->subdomain_id()) ||
     183        1418 :         (fi->neighborPtr() && hasBlocks(fi->neighborPtr()->subdomain_id())))
     184      176778 :       _flow_face_info.push_back(fi);
     185         816 : }
     186             : 
     187             : void
     188           0 : RhieChowMassFlux::initialize()
     189             : {
     190           0 :   for (const auto & pair : _HbyA_flux)
     191           0 :     _HbyA_flux[pair.first] = 0;
     192             : 
     193           0 :   for (const auto & pair : _Ainv)
     194           0 :     _Ainv[pair.first] = 0;
     195           0 : }
     196             : 
     197             : void
     198         739 : RhieChowMassFlux::initFaceMassFlux()
     199             : {
     200             :   using namespace Moose::FV;
     201             : 
     202         739 :   const auto time_arg = Moose::currentState();
     203             : 
     204             :   // We loop through the faces and compute the resulting face fluxes from the
     205             :   // initial conditions for velocity
     206      149026 :   for (auto & fi : _flow_face_info)
     207             :   {
     208             :     RealVectorValue density_times_velocity;
     209             : 
     210             :     // On internal face we do a regular interpolation with geometric weights
     211      148287 :     if (_vel[0]->isInternalFace(*fi))
     212             :     {
     213      131341 :       const auto & elem_info = *fi->elemInfo();
     214             :       const auto & neighbor_info = *fi->neighborInfo();
     215             : 
     216      131341 :       Real elem_rho = _rho(makeElemArg(fi->elemPtr()), time_arg);
     217      262682 :       Real neighbor_rho = _rho(makeElemArg(fi->neighborPtr()), time_arg);
     218             : 
     219      384929 :       for (const auto dim_i : index_range(_vel))
     220      253588 :         interpolate(InterpMethod::Average,
     221             :                     density_times_velocity(dim_i),
     222      253588 :                     _vel[dim_i]->getElemValue(elem_info, time_arg) * elem_rho,
     223      507176 :                     _vel[dim_i]->getElemValue(neighbor_info, time_arg) * neighbor_rho,
     224             :                     *fi,
     225             :                     true);
     226             :     }
     227             :     // On the boundary, we just take the boundary values
     228             :     else
     229             :     {
     230       16946 :       const bool elem_is_fluid = hasBlocks(fi->elemPtr()->subdomain_id());
     231       16946 :       const Elem * const boundary_elem = elem_is_fluid ? fi->elemPtr() : fi->neighborPtr();
     232             : 
     233             :       // We need this multiplier in case the face is an internal face and
     234       16946 :       const Real boundary_normal_multiplier = elem_is_fluid ? 1.0 : -1.0;
     235       16946 :       const Moose::FaceArg boundary_face{
     236       16946 :           fi, Moose::FV::LimiterType::CentralDifference, true, false, boundary_elem, nullptr};
     237             : 
     238       16946 :       const Real face_rho = _rho(boundary_face, time_arg);
     239       52248 :       for (const auto dim_i : index_range(_vel))
     240       35302 :         density_times_velocity(dim_i) = boundary_normal_multiplier * face_rho *
     241       35302 :                                         raw_value((*_vel[dim_i])(boundary_face, time_arg));
     242             :     }
     243             : 
     244      148287 :     _face_mass_flux[fi->id()] = density_times_velocity * fi->normal();
     245             :   }
     246         739 : }
     247             : 
     248             : Real
     249    54352223 : RhieChowMassFlux::getMassFlux(const FaceInfo & fi) const
     250             : {
     251    54352223 :   return _face_mass_flux.evaluate(&fi);
     252             : }
     253             : 
     254             : Real
     255     6326866 : RhieChowMassFlux::getVolumetricFaceFlux(const FaceInfo & fi) const
     256             : {
     257     6326866 :   const Moose::FaceArg face_arg{&fi,
     258             :                                 /*limiter_type=*/Moose::FV::LimiterType::CentralDifference,
     259             :                                 /*elem_is_upwind=*/true,
     260             :                                 /*correct_skewness=*/false,
     261             :                                 &fi.elem(),
     262     6326866 :                                 /*state_limiter*/ nullptr};
     263     6326866 :   const Real face_rho = _rho(face_arg, Moose::currentState());
     264     6326866 :   return libmesh_map_find(_face_mass_flux, fi.id()) / face_rho;
     265             : }
     266             : 
     267             : Real
     268         484 : RhieChowMassFlux::getVolumetricFaceFlux(const Moose::FV::InterpMethod m,
     269             :                                         const FaceInfo & fi,
     270             :                                         const Moose::StateArg & time,
     271             :                                         const THREAD_ID /*tid*/,
     272             :                                         bool libmesh_dbg_var(subtract_mesh_velocity)) const
     273             : {
     274             :   mooseAssert(!subtract_mesh_velocity, "RhieChowMassFlux does not support moving meshes yet!");
     275             : 
     276         484 :   if (m != Moose::FV::InterpMethod::RhieChow)
     277           0 :     mooseError("Interpolation methods other than Rhie-Chow are not supported!");
     278         484 :   if (time.state != Moose::currentState().state)
     279           0 :     mooseError("Older interpolation times are not supported!");
     280             : 
     281         484 :   return getVolumetricFaceFlux(fi);
     282             : }
     283             : 
     284             : void
     285      162117 : RhieChowMassFlux::computeFaceMassFlux()
     286             : {
     287             :   using namespace Moose::FV;
     288             : 
     289      162117 :   const auto time_arg = Moose::currentState();
     290             : 
     291             :   // Petsc vector reader to make the repeated reading from the vector faster
     292      162117 :   PetscVectorReader p_reader(*_pressure_system->system().current_local_solution);
     293             : 
     294             :   // We loop through the faces and compute the face fluxes using the pressure gradient
     295             :   // and the momentum matrix/right hand side
     296    24474026 :   for (auto & fi : _flow_face_info)
     297             :   {
     298             :     // Making sure the kernel knows which face we are on
     299    24311909 :     _p_diffusion_kernel->setupFaceData(fi);
     300             : 
     301             :     // We are setting this to 1.0 because we don't want to multiply the kernel contributions
     302             :     // with the surface area yet. The surface area will be factored in in the advection kernels.
     303    24311909 :     _p_diffusion_kernel->setCurrentFaceArea(1.0);
     304             : 
     305             :     Real p_grad_flux = 0.0;
     306    24311909 :     if (_p->isInternalFace(*fi))
     307             :     {
     308    21633829 :       const auto & elem_info = *fi->elemInfo();
     309             :       const auto & neighbor_info = *fi->neighborInfo();
     310             : 
     311             :       // Fetching the dof indices for the pressure variable
     312    21633829 :       const auto elem_dof = elem_info.dofIndices()[_global_pressure_system_number][0];
     313    21633829 :       const auto neighbor_dof = neighbor_info.dofIndices()[_global_pressure_system_number][0];
     314             : 
     315             :       // Fetching the values of the pressure for the element and the neighbor
     316    21633829 :       const auto p_elem_value = p_reader(elem_dof);
     317    21633829 :       const auto p_neighbor_value = p_reader(neighbor_dof);
     318             : 
     319             :       // Compute the elem matrix contributions for the face
     320    21633829 :       const auto elem_matrix_contribution = _p_diffusion_kernel->computeElemMatrixContribution();
     321             :       const auto neighbor_matrix_contribution =
     322    21633829 :           _p_diffusion_kernel->computeNeighborMatrixContribution();
     323             :       const auto elem_rhs_contribution =
     324    21633829 :           _p_diffusion_kernel->computeElemRightHandSideContribution();
     325             : 
     326             :       // Compute the face flux from the matrix and right hand side contributions
     327    21633829 :       p_grad_flux = (p_neighbor_value * neighbor_matrix_contribution +
     328    21633829 :                      p_elem_value * elem_matrix_contribution) -
     329             :                     elem_rhs_contribution;
     330             :     }
     331     2678080 :     else if (auto * bc_pointer = _p->getBoundaryCondition(*fi->boundaryIDs().begin()))
     332             :     {
     333             :       mooseAssert(fi->boundaryIDs().size() == 1, "We should only have one boundary on every face.");
     334             : 
     335     1908309 :       bc_pointer->setupFaceData(
     336     1908309 :           fi, fi->faceType(std::make_pair(_p->number(), _global_pressure_system_number)));
     337             : 
     338             :       const ElemInfo & elem_info =
     339     1908309 :           hasBlocks(fi->elemPtr()->subdomain_id()) ? *fi->elemInfo() : *fi->neighborInfo();
     340     1908309 :       const auto p_elem_value = _p->getElemValue(elem_info, time_arg);
     341             :       const auto matrix_contribution =
     342     1908309 :           _p_diffusion_kernel->computeBoundaryMatrixContribution(*bc_pointer);
     343             :       const auto rhs_contribution =
     344     1908309 :           _p_diffusion_kernel->computeBoundaryRHSContribution(*bc_pointer);
     345             : 
     346             :       // On the boundary, only the element side has a contribution
     347     1908309 :       p_grad_flux = (p_elem_value * matrix_contribution - rhs_contribution);
     348             :     }
     349             :     // Compute the new face flux
     350    24311909 :     _face_mass_flux[fi->id()] = -_HbyA_flux[fi->id()] + p_grad_flux;
     351             :   }
     352      162117 : }
     353             : 
     354             : void
     355      183817 : RhieChowMassFlux::computeCellVelocity()
     356             : {
     357      183817 :   auto & pressure_gradient = _pressure_system->gradientContainer();
     358             : 
     359             :   // We set the dof value in the solution vector the same logic applies:
     360             :   // u_C = -(H/A)_C - (1/A)_C*grad(p)_C where C is the cell index
     361      488638 :   for (const auto system_i : index_range(_momentum_implicit_systems))
     362             :   {
     363      304821 :     auto working_vector = _Ainv_raw[system_i]->clone();
     364      304821 :     working_vector->pointwise_mult(*working_vector, *pressure_gradient[system_i]);
     365      304821 :     working_vector->add(*_HbyA_raw[system_i]);
     366      304821 :     working_vector->scale(-1.0);
     367      304821 :     (*_momentum_implicit_systems[system_i]->solution) = *working_vector;
     368      304821 :     _momentum_implicit_systems[system_i]->update();
     369      304821 :     _momentum_systems[system_i]->setSolution(
     370      304821 :         *_momentum_implicit_systems[system_i]->current_local_solution);
     371      304821 :   }
     372      183817 : }
     373             : 
     374             : void
     375         807 : RhieChowMassFlux::initCouplingField()
     376             : {
     377             :   // We loop through the faces and populate the coupling fields (face H/A and 1/H)
     378             :   // with 0s for now. Pressure corrector solves will always come after the
     379             :   // momentum source so we expect these fields to change before the actual solve.
     380      177525 :   for (auto & fi : _fe_problem.mesh().faceInfo())
     381             :   {
     382      176718 :     _Ainv[fi->id()];
     383      176718 :     _HbyA_flux[fi->id()];
     384             :   }
     385         807 : }
     386             : 
     387             : void
     388      183817 : RhieChowMassFlux::populateCouplingFunctors(
     389             :     const std::vector<std::unique_ptr<NumericVector<Number>>> & raw_hbya,
     390             :     const std::vector<std::unique_ptr<NumericVector<Number>>> & raw_Ainv)
     391             : {
     392             :   // We have the raw H/A and 1/A vectors in a petsc format. This function
     393             :   // will create face functors from them
     394             :   using namespace Moose::FV;
     395      183817 :   const auto time_arg = Moose::currentState();
     396             : 
     397             :   // Create the petsc vector readers for faster repeated access
     398             :   std::vector<PetscVectorReader> hbya_reader;
     399      488638 :   for (const auto dim_i : index_range(raw_hbya))
     400      304821 :     hbya_reader.emplace_back(*raw_hbya[dim_i]);
     401             : 
     402             :   std::vector<PetscVectorReader> ainv_reader;
     403      488638 :   for (const auto dim_i : index_range(raw_Ainv))
     404      304821 :     ainv_reader.emplace_back(*raw_Ainv[dim_i]);
     405             : 
     406             :   // We loop through the faces and populate the coupling fields (face H/A and 1/H)
     407    26912226 :   for (auto & fi : _flow_face_info)
     408             :   {
     409             :     Real face_rho = 0;
     410             :     RealVectorValue face_hbya;
     411             : 
     412             :     // We do the lookup in advance
     413    26728409 :     auto & Ainv = _Ainv[fi->id()];
     414             : 
     415             :     // If it is internal, we just interpolate (using geometric weights) to the face
     416    26728409 :     if (_vel[0]->isInternalFace(*fi))
     417             :     {
     418             :       // Get the dof indices for the element and the neighbor
     419    23548329 :       const auto & elem_info = *fi->elemInfo();
     420             :       const auto & neighbor_info = *fi->neighborInfo();
     421    23548329 :       const auto elem_dof = elem_info.dofIndices()[_global_momentum_system_numbers[0]][0];
     422    23548329 :       const auto neighbor_dof = neighbor_info.dofIndices()[_global_momentum_system_numbers[0]][0];
     423             : 
     424             :       // Get the density values for the element and neighbor. We need this multiplication to make
     425             :       // the coupling fields mass fluxes.
     426    23548329 :       const Real elem_rho = _rho(makeElemArg(fi->elemPtr()), time_arg);
     427    47096658 :       const Real neighbor_rho = _rho(makeElemArg(fi->neighborPtr()), time_arg);
     428             : 
     429             :       // Now we do the interpolation to the face
     430    23548329 :       interpolate(Moose::FV::InterpMethod::Average, face_rho, elem_rho, neighbor_rho, *fi, true);
     431    69228512 :       for (const auto dim_i : index_range(raw_hbya))
     432             :       {
     433    45680183 :         interpolate(Moose::FV::InterpMethod::Average,
     434             :                     face_hbya(dim_i),
     435    45680183 :                     hbya_reader[dim_i](elem_dof),
     436    45680183 :                     hbya_reader[dim_i](neighbor_dof),
     437             :                     *fi,
     438             :                     true);
     439    45680183 :         interpolate(InterpMethod::Average,
     440             :                     Ainv(dim_i),
     441    45680183 :                     elem_rho * ainv_reader[dim_i](elem_dof),
     442    91360366 :                     neighbor_rho * ainv_reader[dim_i](neighbor_dof),
     443             :                     *fi,
     444             :                     true);
     445             :       }
     446             :     }
     447             :     else
     448             :     {
     449     3180080 :       const bool elem_is_fluid = hasBlocks(fi->elemPtr()->subdomain_id());
     450             : 
     451             :       // We need this multiplier in case the face is an internal face and
     452     3180080 :       const Real boundary_normal_multiplier = elem_is_fluid ? 1.0 : -1.0;
     453             : 
     454     3180080 :       const ElemInfo & elem_info = elem_is_fluid ? *fi->elemInfo() : *fi->neighborInfo();
     455     3180080 :       const auto elem_dof = elem_info.dofIndices()[_global_momentum_system_numbers[0]][0];
     456             : 
     457             :       // If it is a Dirichlet BC, we use the dirichlet value the make sure the face flux
     458             :       // is consistent
     459     3180080 :       if (_vel[0]->isDirichletBoundaryFace(*fi))
     460             :       {
     461     2830404 :         const Moose::FaceArg boundary_face{
     462     2830404 :             fi, Moose::FV::LimiterType::CentralDifference, true, false, elem_info.elem(), nullptr};
     463     2830404 :         face_rho = _rho(boundary_face, Moose::currentState());
     464             : 
     465     8495051 :         for (const auto dim_i : make_range(_dim))
     466     5664647 :           face_hbya(dim_i) =
     467     5664647 :               -boundary_normal_multiplier *
     468     5664647 :               MetaPhysicL::raw_value((*_vel[dim_i])(boundary_face, Moose::currentState()));
     469             :       }
     470             :       // Otherwise we just do a one-term expansion (so we just use the element value)
     471             :       else
     472             :       {
     473      349676 :         const auto elem_dof = elem_info.dofIndices()[_global_momentum_system_numbers[0]][0];
     474             : 
     475      349676 :         face_rho = _rho(makeElemArg(elem_info.elem()), time_arg);
     476     1014275 :         for (const auto dim_i : make_range(_dim))
     477      664599 :           face_hbya(dim_i) = boundary_normal_multiplier * hbya_reader[dim_i](elem_dof);
     478             :       }
     479             : 
     480             :       // We just do a one-term expansion for 1/A no matter what
     481     3180080 :       const Real elem_rho = _rho(makeElemArg(elem_info.elem()), time_arg);
     482     9509326 :       for (const auto dim_i : index_range(raw_Ainv))
     483     6329246 :         Ainv(dim_i) = elem_rho * ainv_reader[dim_i](elem_dof);
     484             :     }
     485             :     // Lastly, we populate the face flux resulted by H/A
     486    26728409 :     _HbyA_flux[fi->id()] = face_hbya * fi->normal() * face_rho;
     487             :   }
     488      183817 : }
     489             : 
     490             : void
     491      183817 : RhieChowMassFlux::computeHbyA(const bool with_updated_pressure, bool verbose)
     492             : {
     493      183817 :   if (verbose)
     494             :   {
     495           0 :     _console << "************************************" << std::endl;
     496           0 :     _console << "Computing HbyA" << std::endl;
     497           0 :     _console << "************************************" << std::endl;
     498             :   }
     499             :   mooseAssert(_momentum_implicit_systems.size() && _momentum_implicit_systems[0],
     500             :               "The momentum system shall be linked before calling this function!");
     501             : 
     502      183817 :   auto & pressure_gradient = selectPressureGradient(with_updated_pressure);
     503             : 
     504      183817 :   _HbyA_raw.clear();
     505      183817 :   _Ainv_raw.clear();
     506      488638 :   for (auto system_i : index_range(_momentum_systems))
     507             :   {
     508      304821 :     LinearImplicitSystem * momentum_system = _momentum_implicit_systems[system_i];
     509             : 
     510      304821 :     NumericVector<Number> & rhs = *(momentum_system->rhs);
     511             :     NumericVector<Number> & current_local_solution = *(momentum_system->current_local_solution);
     512             :     NumericVector<Number> & solution = *(momentum_system->solution);
     513      304821 :     PetscMatrix<Number> * mmat = dynamic_cast<PetscMatrix<Number> *>(momentum_system->matrix);
     514             :     mooseAssert(mmat,
     515             :                 "The matrices used in the segregated INSFVRhieChow objects need to be convertable "
     516             :                 "to PetscMatrix!");
     517             : 
     518      304821 :     if (verbose)
     519             :     {
     520           0 :       _console << "Matrix in rc object" << std::endl;
     521           0 :       mmat->print();
     522             :     }
     523             : 
     524             :     // First, we extract the diagonal and we will hold on to it for a little while
     525      609642 :     _Ainv_raw.push_back(current_local_solution.zero_clone());
     526             :     NumericVector<Number> & Ainv = *(_Ainv_raw.back());
     527             : 
     528      304821 :     mmat->get_diagonal(Ainv);
     529             : 
     530      304821 :     if (verbose)
     531             :     {
     532           0 :       _console << "Velocity solution in H(u)" << std::endl;
     533           0 :       solution.print();
     534             :     }
     535             : 
     536             :     // Time to create H(u) = M_{offdiag} * u - b_{nonpressure}
     537      609642 :     _HbyA_raw.push_back(current_local_solution.zero_clone());
     538             :     NumericVector<Number> & HbyA = *(_HbyA_raw.back());
     539             : 
     540             :     // We start with the matrix product part, we will do
     541             :     // M*u - A*u for 2 reasons:
     542             :     // 1, We assume A*u petsc operation is faster than setting the matrix diagonal to 0
     543             :     // 2, In PISO loops we need to reuse the matrix so we can't just set the diagonals to 0
     544             : 
     545             :     // We create a working vector to ease some of the operations, we initialize its values
     546             :     // with the current solution values to have something for the A*u term
     547      304821 :     auto working_vector = momentum_system->current_local_solution->zero_clone();
     548             :     PetscVector<Number> * working_vector_petsc =
     549      304821 :         dynamic_cast<PetscVector<Number> *>(working_vector.get());
     550             :     mooseAssert(working_vector_petsc,
     551             :                 "The vectors used in the RhieChowMassFlux objects need to be convertable "
     552             :                 "to PetscVectors!");
     553             : 
     554      304821 :     mmat->vector_mult(HbyA, solution);
     555      304821 :     working_vector_petsc->pointwise_mult(Ainv, solution);
     556      304821 :     HbyA.add(-1.0, *working_vector_petsc);
     557             : 
     558      304821 :     if (verbose)
     559             :     {
     560           0 :       _console << " H(u)" << std::endl;
     561           0 :       HbyA.print();
     562             :     }
     563             : 
     564             :     // We continue by adding the momentum right hand side contributions
     565      304821 :     HbyA.add(-1.0, rhs);
     566             : 
     567             :     // Unfortunately, the pressure forces are included in the momentum RHS
     568             :     // so we have to correct them back
     569      304821 :     working_vector_petsc->pointwise_mult(*pressure_gradient[system_i], *_cell_volumes);
     570      304821 :     HbyA.add(-1.0, *working_vector_petsc);
     571             : 
     572      304821 :     if (verbose)
     573             :     {
     574           0 :       _console << "total RHS" << std::endl;
     575           0 :       rhs.print();
     576           0 :       _console << "pressure RHS" << std::endl;
     577           0 :       pressure_gradient[system_i]->print();
     578           0 :       _console << " H(u)-rhs-relaxation_source" << std::endl;
     579           0 :       HbyA.print();
     580             :     }
     581             : 
     582             :     // It is time to create element-wise 1/A-s based on the the diagonal of the momentum matrix
     583      304821 :     *working_vector_petsc = 1.0;
     584      304821 :     Ainv.pointwise_divide(*working_vector_petsc, Ainv);
     585             : 
     586             :     // Create 1/A*(H(u)-RHS)
     587      304821 :     HbyA.pointwise_mult(HbyA, Ainv);
     588             : 
     589      304821 :     if (verbose)
     590             :     {
     591           0 :       _console << " (H(u)-rhs)/A" << std::endl;
     592           0 :       HbyA.print();
     593             :     }
     594             : 
     595      304821 :     if (_pressure_projection_method == "consistent")
     596             :     {
     597             : 
     598             :       // Consistent Corrections to SIMPLE
     599             :       // 1. Ainv_old = 1/a_p <- Ainv = 1/(a_p + \sum_n a_n)
     600             :       // 2. H(u) <- H(u*) + H(u') = H(u*) - (Ainv - Ainv_old) * grad(p) * Vc
     601             : 
     602        1053 :       if (verbose)
     603           0 :         _console << "Performing SIMPLEC projection." << std::endl;
     604             : 
     605             :       // Lambda function to calculate the sum of diagonal and neighbor coefficients
     606        1053 :       auto get_row_sum = [mmat](NumericVector<Number> & sum_vector)
     607             :       {
     608             :         // Ensure the sum_vector is zeroed out
     609        1053 :         sum_vector.zero();
     610             : 
     611             :         // Local row size
     612        1053 :         const auto local_size = mmat->local_m();
     613             : 
     614       55593 :         for (const auto row_i : make_range(local_size))
     615             :         {
     616             :           // Get all non-zero components of the row of the matrix
     617       54540 :           const auto global_index = mmat->row_start() + row_i;
     618             :           std::vector<numeric_index_type> indices;
     619             :           std::vector<Real> values;
     620       54540 :           mmat->get_row(global_index, indices, values);
     621             : 
     622             :           // Sum row elements (no absolute values)
     623             :           const Real row_sum = std::accumulate(values.cbegin(), values.cend(), 0.0);
     624             : 
     625             :           // Add the sum of diagonal and elements to the sum_vector
     626       54540 :           sum_vector.add(global_index, row_sum);
     627       54540 :         }
     628        1053 :         sum_vector.close();
     629        1053 :       };
     630             : 
     631             :       // Create a temporary vector to store the sum of diagonal and neighbor coefficients
     632        1053 :       auto row_sum = current_local_solution.zero_clone();
     633        1053 :       get_row_sum(*row_sum);
     634             : 
     635             :       // Create vector with new inverse projection matrix
     636        1053 :       auto Ainv_full = current_local_solution.zero_clone();
     637        1053 :       *working_vector_petsc = 1.0;
     638        1053 :       Ainv_full->pointwise_divide(*working_vector_petsc, *row_sum);
     639        1053 :       const auto Ainv_full_old = Ainv_full->clone();
     640             : 
     641             :       // Correct HbyA
     642        1053 :       Ainv_full->add(-1.0, Ainv);
     643        1053 :       working_vector_petsc->pointwise_mult(*Ainv_full, *pressure_gradient[system_i]);
     644        1053 :       working_vector_petsc->pointwise_mult(*working_vector_petsc, *_cell_volumes);
     645        1053 :       HbyA.add(-1.0, *working_vector_petsc);
     646             : 
     647             :       // Correct Ainv
     648        1053 :       Ainv = *Ainv_full_old;
     649        1053 :     }
     650             : 
     651      304821 :     Ainv.pointwise_mult(Ainv, *_cell_volumes);
     652      304821 :   }
     653             : 
     654             :   // We fill the 1/A and H/A functors
     655      183817 :   populateCouplingFunctors(_HbyA_raw, _Ainv_raw);
     656             : 
     657      183817 :   if (verbose)
     658             :   {
     659           0 :     _console << "************************************" << std::endl;
     660           0 :     _console << "DONE Computing HbyA " << std::endl;
     661           0 :     _console << "************************************" << std::endl;
     662             :   }
     663      183817 : }
     664             : 
     665             : std::vector<std::unique_ptr<NumericVector<Number>>> &
     666      183817 : RhieChowMassFlux::selectPressureGradient(const bool updated_pressure)
     667             : {
     668      183817 :   if (updated_pressure)
     669             :   {
     670      162117 :     _grad_p_current.clear();
     671      423538 :     for (const auto & component : _pressure_system->gradientContainer())
     672      522842 :       _grad_p_current.push_back(component->clone());
     673             :   }
     674             : 
     675      183817 :   return _grad_p_current;
     676             : }

Generated by: LCOV version 1.14