LCOV - code coverage report
Current view: top level - src/fvkernels - INSFVTKESourceSink.C (source / functions) Hit Total Coverage
Test: idaholab/moose navier_stokes: 80a80d Lines: 141 156 90.4 %
Date: 2025-08-11 20:02:08 Functions: 4 4 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 "INSFVTKESourceSink.h"
      11             : #include "NonlinearSystemBase.h"
      12             : #include "NavierStokesMethods.h"
      13             : #include "libmesh/nonlinear_solver.h"
      14             : 
      15             : registerMooseObject("NavierStokesApp", INSFVTKESourceSink);
      16             : 
      17             : InputParameters
      18        2102 : INSFVTKESourceSink::validParams()
      19             : {
      20        2102 :   InputParameters params = FVElementalKernel::validParams();
      21        2102 :   params.addClassDescription("Elemental kernel to compute the production and destruction "
      22             :                              " terms of turbulent kinetic energy (TKE).");
      23        4204 :   params.addRequiredParam<MooseFunctorName>("u", "The velocity in the x direction.");
      24        4204 :   params.addParam<MooseFunctorName>("v", "The velocity in the y direction.");
      25        4204 :   params.addParam<MooseFunctorName>("w", "The velocity in the z direction.");
      26        2102 :   params.addRequiredParam<MooseFunctorName>(NS::TKED,
      27             :                                             "Coupled turbulent kinetic energy dissipation rate.");
      28        2102 :   params.addRequiredParam<MooseFunctorName>(NS::density, "fluid density");
      29        2102 :   params.addRequiredParam<MooseFunctorName>(NS::mu, "Dynamic viscosity.");
      30        2102 :   params.addRequiredParam<MooseFunctorName>(NS::mu_t, "Turbulent viscosity.");
      31        4204 :   params.addParam<std::vector<BoundaryName>>(
      32             :       "walls", {}, "Boundaries that correspond to solid walls.");
      33        4204 :   params.addParam<bool>(
      34             :       "linearized_model",
      35        4204 :       true,
      36             :       "Boolean to determine if the problem should be use in a linear or nonlinear solve.");
      37        4204 :   MooseEnum wall_treatment("eq_newton eq_incremental eq_linearized neq", "neq");
      38        4204 :   params.addParam<MooseEnum>("wall_treatment",
      39             :                              wall_treatment,
      40             :                              "The method used for computing the wall functions "
      41             :                              "'eq_newton', 'eq_incremental', 'eq_linearized', 'neq'");
      42        4204 :   params.addParam<Real>("C_mu", 0.09, "Coupled turbulent kinetic energy closure.");
      43        4204 :   params.addParam<Real>("C_pl", 10.0, "Production Limiter Constant Multiplier.");
      44        2102 :   params.set<unsigned short>("ghost_layers") = 2;
      45        4204 :   params.addParam<bool>("newton_solve", false, "Whether a Newton nonlinear solve is being used");
      46        4204 :   params.addParamNamesToGroup("newton_solve", "Advanced");
      47             : 
      48        2102 :   return params;
      49        2102 : }
      50             : 
      51         541 : INSFVTKESourceSink::INSFVTKESourceSink(const InputParameters & params)
      52             :   : FVElementalKernel(params),
      53         541 :     _dim(_subproblem.mesh().dimension()),
      54        1082 :     _u_var(getFunctor<ADReal>("u")),
      55        1623 :     _v_var(params.isParamValid("v") ? &(getFunctor<ADReal>("v")) : nullptr),
      56         541 :     _w_var(params.isParamValid("w") ? &(getFunctor<ADReal>("w")) : nullptr),
      57         541 :     _epsilon(getFunctor<ADReal>(NS::TKED)),
      58         541 :     _rho(getFunctor<ADReal>(NS::density)),
      59         541 :     _mu(getFunctor<ADReal>(NS::mu)),
      60         541 :     _mu_t(getFunctor<ADReal>(NS::mu_t)),
      61        1082 :     _wall_boundary_names(getParam<std::vector<BoundaryName>>("walls")),
      62        1082 :     _linearized_model(getParam<bool>("linearized_model")),
      63        1082 :     _wall_treatment(getParam<MooseEnum>("wall_treatment").getEnum<NS::WallTreatmentEnum>()),
      64        1082 :     _C_mu(getParam<Real>("C_mu")),
      65        1082 :     _C_pl(getParam<Real>("C_pl")),
      66        1623 :     _newton_solve(getParam<bool>("newton_solve"))
      67             : {
      68         541 :   if (_dim >= 2 && !_v_var)
      69           0 :     paramError("v", "In two or more dimensions, the v velocity must be supplied!");
      70             : 
      71         541 :   if (_dim >= 3 && !_w_var)
      72           0 :     paramError("w", "In three or more dimensions, the w velocity must be supplied!");
      73         541 : }
      74             : 
      75             : void
      76        1997 : INSFVTKESourceSink::initialSetup()
      77             : {
      78        3994 :   NS::getWallBoundedElements(
      79        1997 :       _wall_boundary_names, _fe_problem, _subproblem, blockIDs(), _wall_bounded);
      80        1997 :   NS::getWallDistance(_wall_boundary_names, _fe_problem, _subproblem, blockIDs(), _dist);
      81        1997 :   NS::getElementFaceArgs(_wall_boundary_names, _fe_problem, _subproblem, blockIDs(), _face_infos);
      82        1997 : }
      83             : 
      84             : ADReal
      85      482560 : INSFVTKESourceSink::computeQpResidual()
      86             : {
      87      482560 :   ADReal residual = 0.0;
      88      482560 :   ADReal production = 0.0;
      89      482560 :   ADReal destruction = 0.0;
      90             : 
      91      482560 :   const auto state = determineState();
      92      482560 :   const auto elem_arg = makeElemArg(_current_elem);
      93             :   const auto old_state =
      94      482560 :       _linearized_model ? Moose::StateArg(1, Moose::SolutionIterationType::Nonlinear) : state;
      95      482560 :   const auto rho = _rho(elem_arg, state);
      96      482560 :   const auto mu = _mu(elem_arg, state);
      97             :   // To prevent negative values & preserve sparsity pattern
      98      482560 :   auto TKE = _newton_solve
      99     1304640 :                  ? std::max(_var(elem_arg, old_state), ADReal(0) * _var(elem_arg, old_state))
     100      482560 :                  : _var(elem_arg, old_state);
     101             :   // Prevent computation of sqrt(0) with undefined automatic derivatives
     102             :   // This is not needed for segregated solves, as TKE has minimum bound in the solver
     103      482560 :   if (_newton_solve)
     104       84880 :     TKE = std::max(TKE, 1e-10);
     105             : 
     106      482560 :   if (_wall_bounded.find(_current_elem) != _wall_bounded.end())
     107             :   {
     108             :     std::vector<ADReal> y_plus_vec, velocity_grad_norm_vec;
     109             : 
     110      112000 :     Real tot_weight = 0.0;
     111             : 
     112      224000 :     ADRealVectorValue velocity(_u_var(elem_arg, state));
     113      112000 :     if (_v_var)
     114      112000 :       velocity(1) = (*_v_var)(elem_arg, state);
     115      112000 :     if (_w_var)
     116           0 :       velocity(2) = (*_w_var)(elem_arg, state);
     117             : 
     118      112000 :     const auto & face_info_vec = libmesh_map_find(_face_infos, _current_elem);
     119      112000 :     const auto & distance_vec = libmesh_map_find(_dist, _current_elem);
     120             : 
     121      231360 :     for (unsigned int i = 0; i < distance_vec.size(); i++)
     122             :     {
     123             :       const auto parallel_speed = NS::computeSpeed<ADReal>(
     124      358080 :           velocity - velocity * face_info_vec[i]->normal() * face_info_vec[i]->normal());
     125      119360 :       const auto distance = distance_vec[i];
     126             : 
     127             :       ADReal y_plus;
     128      119360 :       if (_wall_treatment == NS::WallTreatmentEnum::NEQ) // Non-equilibrium / Non-iterative
     129       12960 :         y_plus = distance * std::sqrt(std::sqrt(_C_mu) * TKE) * rho / mu;
     130             :       else // Equilibrium / Iterative
     131      115040 :         y_plus = NS::findyPlus<ADReal>(mu, rho, std::max(parallel_speed, 1e-10), distance);
     132             : 
     133      119360 :       y_plus_vec.push_back(y_plus);
     134             : 
     135             :       const ADReal velocity_grad_norm = parallel_speed / distance;
     136             : 
     137             :       /// Do not erase!!
     138             :       // More complete expansion for velocity gradient. Leave commented for now.
     139             :       // Will be useful later when doing two-phase or compressible flow
     140             :       // ADReal velocity_grad_norm_sq =
     141             :       //     Utility::pow<2>(_u_var->gradient(elem_arg, state) *
     142             :       //                     _normal[_current_elem][i]);
     143             :       // if (_dim >= 2)
     144             :       //   velocity_grad_norm_sq +=
     145             :       //       Utility::pow<2>(_v_var->gradient(elem_arg, state) *
     146             :       //                       _normal[_current_elem][i]);
     147             :       // if (_dim >= 3)
     148             :       //   velocity_grad_norm_sq +=
     149             :       //       Utility::pow<2>(_w_var->gradient(elem_arg, state) *
     150             :       //                       _normal[_current_elem][i]);
     151             :       // ADReal velocity_grad_norm = std::sqrt(velocity_grad_norm_sq);
     152             : 
     153      119360 :       velocity_grad_norm_vec.push_back(velocity_grad_norm);
     154             : 
     155      119360 :       tot_weight += 1.0;
     156             :     }
     157             : 
     158      231360 :     for (unsigned int i = 0; i < y_plus_vec.size(); i++)
     159             :     {
     160      119360 :       const auto y_plus = y_plus_vec[i];
     161             : 
     162      119360 :       const auto fi = face_info_vec[i];
     163      119360 :       const bool defined_on_elem_side = _var.hasFaceSide(*fi, true);
     164      119360 :       const Elem * const loc_elem = defined_on_elem_side ? &fi->elem() : fi->neighborPtr();
     165      119360 :       const Moose::FaceArg facearg = {
     166      119360 :           fi, Moose::FV::LimiterType::CentralDifference, false, false, loc_elem, nullptr};
     167      119360 :       const ADReal wall_mut = _mu_t(facearg, state);
     168      119360 :       const ADReal wall_mu = _mu(facearg, state);
     169             : 
     170      238720 :       const auto destruction_visc = 2.0 * wall_mu / Utility::pow<2>(distance_vec[i]) / tot_weight;
     171      119360 :       const auto destruction_log = std::pow(_C_mu, 0.75) * rho * std::pow(TKE, 0.5) /
     172      238720 :                                    (NS::von_karman_constant * distance_vec[i]) / tot_weight;
     173      119360 :       const auto tau_w = (wall_mut + wall_mu) * velocity_grad_norm_vec[i];
     174             : 
     175             :       // Additional 0-value terms to make sure new derivative entries are not added during the solve
     176      119360 :       if (y_plus < 11.25)
     177             :       {
     178       78956 :         destruction += destruction_visc;
     179       78956 :         if (_newton_solve)
     180       88080 :           destruction += 0 * destruction_log + 0 * tau_w;
     181             :       }
     182             :       else
     183             :       {
     184       40404 :         destruction += destruction_log;
     185       40404 :         if (_newton_solve)
     186           0 :           destruction += 0 * destruction_visc;
     187       40404 :         production += tau_w * std::pow(_C_mu, 0.25) / std::sqrt(TKE) /
     188       80808 :                       (NS::von_karman_constant * distance_vec[i]) / tot_weight;
     189             :       }
     190             :     }
     191             : 
     192      224000 :     residual = (destruction - production) * _var(elem_arg, state);
     193             :     // Additional 0-value term to make sure new derivative entries are not added during the solve
     194      112000 :     if (_newton_solve)
     195       52760 :       residual += 0 * _epsilon(elem_arg, old_state);
     196             :   }
     197             :   else
     198             :   {
     199      370560 :     const auto & grad_u = _u_var.gradient(elem_arg, state);
     200      370560 :     const auto Sij_xx = 2.0 * grad_u(0);
     201      370560 :     ADReal Sij_xy = 0.0;
     202      370560 :     ADReal Sij_xz = 0.0;
     203      370560 :     ADReal Sij_yy = 0.0;
     204      370560 :     ADReal Sij_yz = 0.0;
     205      370560 :     ADReal Sij_zz = 0.0;
     206             : 
     207      370560 :     const auto grad_xx = grad_u(0);
     208      370560 :     ADReal grad_xy = 0.0;
     209      370560 :     ADReal grad_xz = 0.0;
     210      370560 :     ADReal grad_yx = 0.0;
     211      370560 :     ADReal grad_yy = 0.0;
     212      370560 :     ADReal grad_yz = 0.0;
     213      370560 :     ADReal grad_zx = 0.0;
     214      370560 :     ADReal grad_zy = 0.0;
     215      370560 :     ADReal grad_zz = 0.0;
     216             : 
     217      370560 :     auto trace = Sij_xx / 3.0;
     218             : 
     219      370560 :     if (_dim >= 2)
     220             :     {
     221      370560 :       const auto & grad_v = (*_v_var).gradient(elem_arg, state);
     222      370560 :       Sij_xy = grad_u(1) + grad_v(0);
     223      741120 :       Sij_yy = 2.0 * grad_v(1);
     224             : 
     225      370560 :       grad_xy = grad_u(1);
     226      370560 :       grad_yx = grad_v(0);
     227      370560 :       grad_yy = grad_v(1);
     228             : 
     229      741120 :       trace += Sij_yy / 3.0;
     230             : 
     231      370560 :       if (_dim >= 3)
     232             :       {
     233           0 :         const auto & grad_w = (*_w_var).gradient(elem_arg, state);
     234             : 
     235           0 :         Sij_xz = grad_u(2) + grad_w(0);
     236           0 :         Sij_yz = grad_v(2) + grad_w(1);
     237           0 :         Sij_zz = 2.0 * grad_w(2);
     238             : 
     239           0 :         grad_xz = grad_u(2);
     240           0 :         grad_yz = grad_v(2);
     241           0 :         grad_zx = grad_w(0);
     242           0 :         grad_zy = grad_w(1);
     243           0 :         grad_zz = grad_w(2);
     244             : 
     245           0 :         trace += Sij_zz / 3.0;
     246             :       }
     247             :     }
     248             : 
     249             :     const auto symmetric_strain_tensor_sq_norm =
     250      370560 :         (Sij_xx - trace) * grad_xx + Sij_xy * grad_xy + Sij_xz * grad_xz + Sij_xy * grad_yx +
     251      370560 :         (Sij_yy - trace) * grad_yy + Sij_yz * grad_yz + Sij_xz * grad_zx + Sij_yz * grad_zy +
     252      370560 :         (Sij_zz - trace) * grad_zz;
     253             : 
     254      741120 :     production = _mu_t(elem_arg, state) * symmetric_strain_tensor_sq_norm;
     255             : 
     256      370560 :     const auto tke_old_raw = raw_value(TKE);
     257      370560 :     const auto epsilon_old = _epsilon(elem_arg, old_state);
     258             : 
     259      370560 :     if (MooseUtils::absoluteFuzzyEqual(tke_old_raw, 0))
     260           0 :       destruction = rho * epsilon_old;
     261             :     else
     262      741120 :       destruction = rho * _var(elem_arg, state) / tke_old_raw * raw_value(epsilon_old);
     263             : 
     264             :     // k-Production limiter (needed for flows with stagnation zones)
     265             :     const ADReal production_limit =
     266      741120 :         _C_pl * rho * (_newton_solve ? std::max(epsilon_old, ADReal(0)) : epsilon_old);
     267             : 
     268             :     // Apply production limiter
     269      370560 :     production = std::min(production, production_limit);
     270             : 
     271      370560 :     residual = destruction - production;
     272             : 
     273             :     // Additional 0-value terms to make sure new derivative entries are not added during the solve
     274      370560 :     if (_newton_solve)
     275      117000 :       residual += 0 * _epsilon(elem_arg, state);
     276             :   }
     277             : 
     278      482560 :   return residual;
     279             : }

Generated by: LCOV version 1.14