LCOV - code coverage report
Current view: top level - src/kernels - ConservativeAdvection.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 116 128 90.6 %
Date: 2026-05-29 20:35:17 Functions: 16 20 80.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 "ConservativeAdvection.h"
      11             : #include "SystemBase.h"
      12             : 
      13             : using namespace libMesh;
      14             : 
      15             : registerMooseObject("MooseApp", ConservativeAdvection);
      16             : registerMooseObject("MooseApp", ADConservativeAdvection);
      17             : 
      18             : template <bool is_ad>
      19             : InputParameters
      20        6856 : ConservativeAdvectionTempl<is_ad>::generalParams()
      21             : {
      22        6856 :   InputParameters params = GenericKernel<is_ad>::validParams();
      23       13712 :   params.addClassDescription("Conservative form of $\\nabla \\cdot \\vec{v} u$ which in its weak "
      24             :                              "form is given by: $(-\\nabla \\psi_i, \\vec{v} u)$. Velocity can be "
      25             :                              "given as 1) a variable, for which the gradient is automatically "
      26             :                              "taken, 2) a vector variable, or a 3) vector material.");
      27       27424 :   params.addParam<MaterialPropertyName>(
      28             :       "velocity_scalar_coef",
      29             :       "1.0",
      30             :       "Name of material property multiplied against the velocity to scale advection strength.");
      31       27424 :   MooseEnum upwinding_type("none full", "none");
      32       27424 :   params.addParam<MooseEnum>("upwinding_type",
      33             :                              upwinding_type,
      34             :                              "Type of upwinding used.  None: Typically results in overshoots and "
      35             :                              "undershoots, but numerical diffusion is minimized.  Full: Overshoots "
      36             :                              "and undershoots are avoided, but numerical diffusion is large");
      37       27424 :   params.addParam<MaterialPropertyName>("advected_quantity",
      38             :                                         "An optional material property to be advected. If not "
      39             :                                         "supplied, then the variable will be used.");
      40       20568 :   params.addCoupledVar(
      41             :       "velocity_as_variable_gradient",
      42             :       "Gradient of this coupled variable is used to define the advection velocity. "
      43             :       "Can be supplied instead of velocity material or velocity variable.");
      44       13712 :   return params;
      45        6856 : }
      46             : 
      47             : template <>
      48             : InputParameters
      49        3695 : ConservativeAdvectionTempl<false>::validParams()
      50             : {
      51        3695 :   InputParameters params = generalParams();
      52       14780 :   params.addCoupledVar("velocity", "Velocity vector");
      53       22170 :   params.deprecateCoupledVar("velocity", "velocity_variable", "12/31/2025");
      54       11085 :   params.addParam<MaterialPropertyName>("velocity_material", "Velocity vector given as a material");
      55        3695 :   return params;
      56           0 : }
      57             : 
      58             : template <>
      59             : InputParameters
      60        3161 : ConservativeAdvectionTempl<true>::validParams()
      61             : {
      62        3161 :   InputParameters params = generalParams();
      63       12644 :   params.addCoupledVar("velocity_variable", "Velocity vector given as a variable");
      64       12644 :   params.addParam<MaterialPropertyName>("velocity", "Velocity vector given as a material");
      65       15805 :   params.deprecateParam("velocity", "velocity_material", "12/31/2025");
      66        3161 :   return params;
      67           0 : }
      68             : 
      69             : template <bool is_ad>
      70         384 : ConservativeAdvectionTempl<is_ad>::ConservativeAdvectionTempl(const InputParameters & parameters)
      71             :   : GenericKernel<is_ad>(parameters),
      72         384 :     _scalar(this->template getGenericMaterialProperty<Real, is_ad>("velocity_scalar_coef")),
      73         768 :     _coupled_variable_present(isParamValid("velocity_as_variable_gradient")),
      74         433 :     _coupled_variable_var(_coupled_variable_present ? coupled("velocity_as_variable_gradient") : 0),
      75         384 :     _velocity(
      76         384 :         _coupled_variable_present
      77        1152 :             ? &this->template coupledGenericGradient<is_ad>("velocity_as_variable_gradient")
      78        1389 :             : (this->isParamValid("velocity_variable")
      79        1321 :                    ? &this->template coupledGenericVectorValue<is_ad>("velocity_variable")
      80         588 :                    : (this->isParamValid("velocity_material")
      81         576 :                           ? &this->template getGenericMaterialProperty<RealVectorValue, is_ad>(
      82             :                                      "velocity_material")
      83          62 :                                  .get()
      84             :                           : nullptr))),
      85         768 :     _user_supplied_adv_quant(isParamValid("advected_quantity")),
      86         384 :     _adv_quant(
      87         384 :         _user_supplied_adv_quant
      88         384 :             ? this->template getGenericMaterialProperty<Real, is_ad>("advected_quantity").get()
      89             :             : _u),
      90         384 :     _upwinding(
      91         768 :         this->template getParam<MooseEnum>("upwinding_type").template getEnum<UpwindingType>()),
      92         384 :     _u_nodal(_var.template genericDofValues<is_ad>()),
      93         768 :     _upwind_node(0),
      94        1536 :     _dtotal_mass_out(0)
      95             : {
      96         384 :   if (_coupled_variable_present && _coupled_variable_var == _var.number())
      97           6 :     paramError("velocity_as_variable_gradient",
      98             :                "Use a different kernel (i.e., diffusion) if the gradient used as the velocity is "
      99             :                "the same as the member variable");
     100         643 :   if (_upwinding != UpwindingType::none && this->isParamValid("advected_quantity"))
     101           0 :     paramError(
     102             :         "advected_quantity",
     103             :         "Upwinding is not compatible with an advected quantity that is not the primary variable.");
     104             : 
     105         848 :   if (!_velocity || (_coupled_variable_present && this->isParamValid("velocity_material")) ||
     106        1604 :       (_coupled_variable_present && this->isParamValid("velocity_variable")) ||
     107        2040 :       (this->isParamValid("velocity_variable") && this->isParamValid("velocity_material")))
     108          12 :     paramError(
     109             :         "velocity_as_variable_gradient",
     110             :         "One and only one of the following input variables must be specified: velocity_variable, "
     111             :         "velocity_material, or velocity_as_variable_gradient.");
     112             : 
     113         375 :   if (this->_has_diag_save_in)
     114           0 :     paramError("diag_save_in",
     115             :                "_local_ke not computed for global AD indexing. Save-in is deprecated anyway. Use "
     116             :                "the tagging system instead.");
     117         375 : }
     118             : 
     119             : template <bool is_ad>
     120             : GenericReal<is_ad>
     121    29543744 : ConservativeAdvectionTempl<is_ad>::negSpeedQp() const
     122             : {
     123    29543744 :   return -_grad_test[_i][_qp] * (*_velocity)[_qp] * _scalar[_qp];
     124             : }
     125             : 
     126             : template <bool is_ad>
     127             : GenericReal<is_ad>
     128     8324648 : ConservativeAdvectionTempl<is_ad>::computeQpResidual()
     129             : {
     130             :   // This is the no-upwinded version
     131             :   // It gets called via GenericKernel<is_ad>::computeResidual()
     132     8324648 :   return negSpeedQp() * _adv_quant[_qp];
     133             : }
     134             : 
     135             : template <>
     136             : Real
     137    16829184 : ConservativeAdvectionTempl<false>::computeQpJacobian()
     138             : {
     139             :   // This is the no-upwinded version
     140             :   // It gets called via GenericKernel<false>::computeJacobian()
     141    16829184 :   if (!_user_supplied_adv_quant)
     142    16829184 :     return negSpeedQp() * _phi[_j][_qp];
     143           0 :   return 0.0;
     144             : }
     145             : 
     146             : template <>
     147             : Real
     148           0 : ConservativeAdvectionTempl<true>::computeQpJacobian()
     149             : {
     150           0 :   mooseError("Internal error, should never get here when using AD");
     151             :   return 0.0;
     152             : }
     153             : 
     154             : template <>
     155             : Real
     156      163632 : ConservativeAdvectionTempl<false>::computeQpOffDiagJacobian(unsigned int jvar)
     157             : {
     158             :   // This is the non-upwinded version
     159             :   // It gets called via GenericKernel<false>::computeOffDiagJacobian()
     160      163632 :   if (_coupled_variable_present && _coupled_variable_var == jvar)
     161        6960 :     return -_grad_test[_i][_qp] * _grad_phi[_j][_qp] * _adv_quant[_qp] * _scalar[_qp];
     162             :   else
     163      156672 :     return 0.0;
     164             : }
     165             : 
     166             : template <>
     167             : Real
     168           0 : ConservativeAdvectionTempl<true>::computeQpOffDiagJacobian(unsigned int /*jvar*/)
     169             : {
     170           0 :   mooseError("Internal error, should never get here when using AD");
     171             :   return 0.0;
     172             : }
     173             : 
     174             : template <bool is_ad>
     175             : void
     176      700570 : ConservativeAdvectionTempl<is_ad>::computeResidual()
     177             : {
     178      700570 :   switch (_upwinding)
     179             :   {
     180      557074 :     case UpwindingType::none:
     181      557074 :       GenericKernel<is_ad>::computeResidual();
     182      557074 :       break;
     183      143496 :     case UpwindingType::full:
     184      143496 :       fullUpwind(JacRes::CALCULATE_RESIDUAL);
     185      143496 :       break;
     186             :   }
     187      700570 : }
     188             : 
     189             : template <bool is_ad>
     190             : void
     191      409708 : ConservativeAdvectionTempl<is_ad>::computeJacobian()
     192             : {
     193      409708 :   switch (_upwinding)
     194             :   {
     195      267184 :     case UpwindingType::none:
     196      267184 :       GenericKernel<is_ad>::computeJacobian();
     197      267184 :       break;
     198      142524 :     case UpwindingType::full:
     199      142524 :       fullUpwind(JacRes::CALCULATE_JACOBIAN);
     200      142524 :       break;
     201             :   }
     202      409708 : }
     203             : 
     204             : template <bool is_ad>
     205             : void
     206      286020 : ConservativeAdvectionTempl<is_ad>::fullUpwind(JacRes res_or_jac)
     207             : {
     208             :   // The number of nodes in the element
     209      286020 :   const unsigned int num_nodes = _test.size();
     210             : 
     211             :   // Even if we are computing the Jacobian we still need to compute the outflow from each node to
     212             :   // see which nodes are upwind and which are downwind
     213      286020 :   _my_local_re.resize(_var.dofIndices().size());
     214             : 
     215      286020 :   if (!is_ad && (res_or_jac == JacRes::CALCULATE_JACOBIAN))
     216      142524 :     prepareMatrixTag(this->_assembly, _var.number(), _var.number());
     217             : 
     218             :   // Compute the outflux from each node and store in _my_local_re
     219             :   // If _my_local_re is positive at the node, mass (or whatever the Variable represents) is flowing
     220             :   // out of the node
     221      286020 :   _upwind_node.resize(num_nodes);
     222     1394748 :   for (_i = 0; _i < num_nodes; ++_i)
     223             :   {
     224     5498640 :     for (_qp = 0; _qp < this->_qrule->n_points(); _qp++)
     225     4389912 :       _my_local_re(_i) += this->_JxW[_qp] * this->_coord[_qp] * negSpeedQp();
     226     1108728 :     _upwind_node[_i] = (MetaPhysicL::raw_value(_my_local_re(_i)) >= 0.0);
     227             :   }
     228             : 
     229             :   // Variables used to ensure mass conservation
     230      286020 :   GenericReal<is_ad> total_mass_out = 0.0;
     231      286020 :   GenericReal<is_ad> total_in = 0.0;
     232      286020 :   if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
     233      142524 :     _dtotal_mass_out.assign(num_nodes, 0.0);
     234             : 
     235     1394748 :   for (const auto n : make_range(num_nodes))
     236             :   {
     237     1108728 :     if (_upwind_node[n])
     238             :     {
     239             :       if constexpr (!is_ad)
     240      554472 :         if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
     241             :         {
     242      275292 :           if (_test.size() == _phi.size())
     243             :             /* u at node=n depends only on the u at node=n, by construction.  For
     244             :              * linear-lagrange variables, this means that Jacobian entries involving the derivative
     245             :              * will only be nonzero for derivatives wrt variable at node=n.  Hence the
     246             :              * (n, n) in the line below.  The above "if" statement catches other variable types
     247             :              * (eg constant monomials)
     248             :              */
     249      275292 :             _local_ke(n, n) += _my_local_re(n);
     250             : 
     251      275292 :           _dtotal_mass_out[n] += _local_ke(n, n);
     252             :         }
     253      554472 :       _my_local_re(n) *= getUNodal(n);
     254      554472 :       total_mass_out += _my_local_re(n);
     255             :     }
     256             :     else                           // downwind node
     257      554256 :       total_in -= _my_local_re(n); // note the -= means the result is positive
     258             :   }
     259             : 
     260             :   // Conserve mass over all phases by proportioning the total_mass_out mass to the inflow nodes,
     261             :   // weighted by their local_re values
     262     1394748 :   for (const auto n : make_range(num_nodes))
     263     1108728 :     if (!_upwind_node[n]) // downwind node
     264             :     {
     265             :       if constexpr (!is_ad)
     266      554256 :         if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
     267     1357056 :           for (_j = 0; _j < _phi.size(); _j++)
     268     1081872 :             _local_ke(n, _j) += _my_local_re(n) * _dtotal_mass_out[_j] / total_in;
     269      554256 :       _my_local_re(n) *= total_mass_out / total_in;
     270             :     }
     271             : 
     272             :   // Add the result to the residual and jacobian
     273      286020 :   if (res_or_jac == JacRes::CALCULATE_RESIDUAL)
     274             :   {
     275      143496 :     this->addResiduals(this->_assembly, _my_local_re, _var.dofIndices(), _var.scalingFactor());
     276             : 
     277      143496 :     if (this->_has_save_in)
     278           0 :       for (const auto & var : this->_save_in)
     279           0 :         var->sys().solution().add_vector(MetaPhysicL::raw_value(_my_local_re), var->dofIndices());
     280             :   }
     281             : 
     282      286020 :   if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
     283             :   {
     284             :     if constexpr (is_ad)
     285           0 :       this->addJacobian(this->_assembly, _my_local_re, _var.dofIndices(), _var.scalingFactor());
     286             :     else
     287      142524 :       accumulateTaggedLocalMatrix();
     288             :   }
     289      286020 : }
     290             : 
     291             : template class ConservativeAdvectionTempl<false>;
     292             : template class ConservativeAdvectionTempl<true>;

Generated by: LCOV version 1.14