LCOV - code coverage report
Current view: top level - src/fvkernels - INSFVMomentumDiffusion.C (source / functions) Hit Total Coverage
Test: idaholab/moose navier_stokes: ba1ead Lines: 84 93 90.3 %
Date: 2025-08-13 06:50:25 Functions: 5 5 100.0 %
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             : #include "INSFVMomentumDiffusion.h"
      11             : #include "INSFVRhieChowInterpolator.h"
      12             : #include "NS.h"
      13             : #include "NavierStokesMethods.h"
      14             : #include "SystemBase.h"
      15             : #include "NonlinearSystemBase.h"
      16             : #include "RelationshipManager.h"
      17             : #include "Factory.h"
      18             : #include "libmesh/nonlinear_solver.h"
      19             : 
      20             : registerMooseObject("NavierStokesApp", INSFVMomentumDiffusion);
      21             : 
      22             : InputParameters
      23       31666 : INSFVMomentumDiffusion::validParams()
      24             : {
      25       31666 :   auto params = INSFVFluxKernel::validParams();
      26       31666 :   params += FVDiffusionInterpolationInterface::validParams();
      27       31666 :   params.addRequiredParam<MooseFunctorName>(NS::mu, "The viscosity");
      28       31666 :   params.addClassDescription(
      29             :       "Implements the Laplace form of the viscous stress in the Navier-Stokes equation.");
      30             : 
      31       63332 :   MooseEnum coeff_interp_method("average harmonic", "harmonic");
      32       63332 :   params.addParam<MooseEnum>("mu_interp_method",
      33             :                              coeff_interp_method,
      34             :                              "Switch that can select face interpolation method for the viscosity.");
      35       31666 :   params.set<unsigned short>("ghost_layers") = 2;
      36             : 
      37             :   // We add the relationship manager here, this will select the right number of
      38             :   // ghosting layers depending on the chosen interpolation method
      39       63332 :   params.addRelationshipManager(
      40             :       "ElementSideNeighborLayers",
      41             :       Moose::RelationshipManagerType::GEOMETRIC | Moose::RelationshipManagerType::ALGEBRAIC |
      42             :           Moose::RelationshipManagerType::COUPLING,
      43             :       [](const InputParameters & obj_params, InputParameters & rm_params)
      44       36782 :       { FVRelationshipManagerInterface::setRMParamsDiffusion(obj_params, rm_params, 3); });
      45             : 
      46       63332 :   params.addParam<bool>(
      47             :       "complete_expansion",
      48       63332 :       false,
      49             :       "Boolean parameter to use complete momentum expansion is the diffusion term.");
      50       63332 :   params.addParam<MooseFunctorName>("u", "The velocity in the x direction.");
      51       63332 :   params.addParam<MooseFunctorName>("v", "The velocity in the y direction.");
      52       63332 :   params.addParam<MooseFunctorName>("w", "The velocity in the z direction.");
      53       63332 :   params.addParam<bool>(
      54       63332 :       "limit_interpolation", false, "Flag to limit interpolation to positive values.");
      55       63332 :   params.addParam<bool>("newton_solve", false, "Whether a Newton nonlinear solve is being used");
      56       63332 :   params.addParamNamesToGroup("newton_solve", "Advanced");
      57       31666 :   return params;
      58       31666 : }
      59             : 
      60       17593 : INSFVMomentumDiffusion::INSFVMomentumDiffusion(const InputParameters & params)
      61             :   : INSFVFluxKernel(params),
      62             :     SolutionInvalidInterface(this),
      63             :     FVDiffusionInterpolationInterface(params),
      64       35186 :     _mu(getFunctor<ADReal>(NS::mu)),
      65       17593 :     _mu_interp_method(
      66       35186 :         Moose::FV::selectInterpolationMethod(getParam<MooseEnum>("mu_interp_method"))),
      67       21197 :     _u_var(params.isParamValid("u") ? &getFunctor<ADReal>("u") : nullptr),
      68       21197 :     _v_var(params.isParamValid("v") ? &getFunctor<ADReal>("v") : nullptr),
      69       17593 :     _w_var(params.isParamValid("w") ? &getFunctor<ADReal>("w") : nullptr),
      70       35186 :     _complete_expansion(getParam<bool>("complete_expansion")),
      71       35186 :     _limit_interpolation(getParam<bool>("limit_interpolation")),
      72       17593 :     _dim(_subproblem.mesh().dimension()),
      73       70372 :     _newton_solve(getParam<bool>("newton_solve"))
      74             : {
      75       17593 :   if (_complete_expansion && !_u_var)
      76           0 :     paramError("u", "The u velocity must be defined when 'complete_expansion=true'.");
      77             : 
      78       17593 :   if (_complete_expansion && _dim >= 2 && !_v_var)
      79           0 :     paramError("v",
      80             :                "The v velocity must be defined when 'complete_expansion=true'"
      81             :                "and problem dimension is larger or equal to 2.");
      82             : 
      83       17593 :   if (_complete_expansion && _dim >= 3 && !_w_var)
      84           0 :     paramError("w",
      85             :                "The w velocity must be defined when 'complete_expansion=true'"
      86             :                "and problem dimension is larger or equal to three.");
      87       17593 : }
      88             : 
      89             : ADReal
      90    68129875 : INSFVMomentumDiffusion::computeStrongResidual(const bool populate_a_coeffs)
      91             : {
      92    68129875 :   const Moose::StateArg state = determineState();
      93    68129875 :   const auto dudn = gradUDotNormal(state, _correct_skewness);
      94             :   ADReal face_mu;
      95             : 
      96    68129875 :   if (onBoundary(*_face_info))
      97     6214730 :     face_mu = _mu(makeCDFace(*_face_info), state);
      98             :   else
      99    61915145 :     Moose::FV::interpolate(_mu_interp_method,
     100             :                            face_mu,
     101    61915145 :                            _mu(elemArg(), state),
     102   123830290 :                            _mu(neighborArg(), state),
     103    61915145 :                            *_face_info,
     104             :                            true);
     105             : 
     106             :   // Protecting from negative viscosity at interpolation
     107             :   // to preserve convergence
     108    68129875 :   if (face_mu < 0.0)
     109             :   {
     110         216 :     if (!(_limit_interpolation))
     111             :     {
     112         108 :       mooseDoOnce(mooseWarning(
     113             :           "Negative face viscosity has been encountered. Value ",
     114             :           raw_value(face_mu),
     115             :           " at ",
     116             :           _face_info->faceCentroid(),
     117             :           " limiting it to 0!\nFurther warnings for this issue will be silenced, but the "
     118             :           "occurrences will be recorded through the solution invalidity interface."));
     119         117 :       flagInvalidSolution("Negative face dynamic viscosity has been encountered.");
     120             :     }
     121             :     // Keep face_mu here for sparsity pattern detection
     122         432 :     face_mu = 0 * face_mu;
     123             :   }
     124             : 
     125    68129875 :   if (populate_a_coeffs)
     126             :   {
     127    56621403 :     if (_face_type == FaceInfo::VarFaceNeighbors::ELEM ||
     128             :         _face_type == FaceInfo::VarFaceNeighbors::BOTH)
     129             :     {
     130    56620593 :       const auto dof_number = _face_info->elem().dof_number(_sys.number(), _var.number(), 0);
     131             :       // A gradient is a linear combination of degrees of freedom so it's safe to straight-up index
     132             :       // into the derivatives vector at the dof we care about
     133    56620593 :       _ae = dudn.derivatives()[dof_number];
     134    56620593 :       _ae *= -face_mu;
     135             :     }
     136    56621403 :     if (_face_type == FaceInfo::VarFaceNeighbors::NEIGHBOR ||
     137             :         _face_type == FaceInfo::VarFaceNeighbors::BOTH)
     138             :     {
     139    52764394 :       const auto dof_number = _face_info->neighbor().dof_number(_sys.number(), _var.number(), 0);
     140    52764394 :       _an = dudn.derivatives()[dof_number];
     141    52764394 :       _an *= face_mu;
     142             :     }
     143             :   }
     144             : 
     145    68129875 :   ADReal dudn_transpose = 0.0;
     146    68129875 :   if (_complete_expansion)
     147             :   {
     148             :     // Computing the gradient from coupled variables
     149             :     // Normally, we can do this with `_var.gradient(face, state)` but we will need the transpose
     150             :     // gradient. So, we compute all at once
     151             :     Moose::FaceArg face;
     152     2642440 :     if (onBoundary(*_face_info))
     153      399840 :       face = singleSidedFaceArg();
     154             :     else
     155     2242600 :       face = makeCDFace(*_face_info, _correct_skewness);
     156             : 
     157             :     ADRealTensorValue gradient;
     158     2642440 :     if (_dim == 1)
     159             :     {
     160           0 :       const auto & grad_u = _u_var->gradient(face, state);
     161           0 :       gradient = ADRealTensorValue(grad_u, ADRealVectorValue(0, 0, 0), ADRealVectorValue(0, 0, 0));
     162             :     }
     163     2642440 :     else if (_dim == 2)
     164             :     {
     165     2642440 :       const auto & grad_u = _u_var->gradient(face, state);
     166     2642440 :       const auto & grad_v = _v_var->gradient(face, state);
     167     5284880 :       gradient = ADRealTensorValue(grad_u, grad_v, ADRealVectorValue(0, 0, 0));
     168             :     }
     169             :     else // if (_dim == 3)
     170             :     {
     171           0 :       const auto & grad_u = _u_var->gradient(face, state);
     172           0 :       const auto & grad_v = _v_var->gradient(face, state);
     173           0 :       const auto & grad_w = _w_var->gradient(face, state);
     174           0 :       gradient = ADRealTensorValue(grad_u, grad_v, grad_w);
     175             :     }
     176             : 
     177             :     // Getting transpose of the gradient matrix
     178     2642440 :     const auto gradient_transpose = gradient.transpose();
     179             : 
     180     2642440 :     dudn_transpose += gradient_transpose.row(_index) * _face_info->normal();
     181             :   }
     182             : 
     183   136259750 :   return -face_mu * (dudn + dudn_transpose);
     184             : }
     185             : 
     186             : void
     187    96478312 : INSFVMomentumDiffusion::gatherRCData(const FaceInfo & fi)
     188             : {
     189    96478312 :   if (skipForBoundary(fi))
     190             :     return;
     191             : 
     192    94279455 :   _face_info = &fi;
     193    94279455 :   _normal = fi.normal();
     194    94279455 :   _face_type = fi.faceType(std::make_pair(_var.number(), _var.sys().number()));
     195             : 
     196   188558910 :   addResidualAndJacobian(computeStrongResidual(true) * (fi.faceArea() * fi.faceCoord()));
     197             : 
     198    94279455 :   if (_face_type == FaceInfo::VarFaceNeighbors::ELEM ||
     199             :       _face_type == FaceInfo::VarFaceNeighbors::BOTH)
     200   188557050 :     _rc_uo.addToA(&fi.elem(), _index, _ae * (fi.faceArea() * fi.faceCoord()));
     201    94279455 :   if (_face_type == FaceInfo::VarFaceNeighbors::NEIGHBOR ||
     202             :       _face_type == FaceInfo::VarFaceNeighbors::BOTH)
     203   267208002 :     _rc_uo.addToA(fi.neighborPtr(), _index, _an * (fi.faceArea() * fi.faceCoord()));
     204             : }
     205             : 
     206             : ADReal
     207    13803892 : INSFVMomentumDiffusion::computeSegregatedContribution()
     208             : {
     209    13803892 :   return computeStrongResidual(false);
     210             : }

Generated by: LCOV version 1.14