LCOV - code coverage report
Current view: top level - src/kernels - ConservativeAdvection.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 99787a Lines: 116 130 89.2 %
Date: 2025-10-14 20:01:24 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       30268 : ConservativeAdvectionTempl<is_ad>::generalParams()
      21             : {
      22       30268 :   InputParameters params = GenericKernel<is_ad>::validParams();
      23       60536 :   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      121072 :   params.addParam<MaterialPropertyName>(
      28             :       "velocity_scalar_coef",
      29             :       "1.0",
      30             :       "Name of material property multiplied against the velocity to scale advection strength.");
      31      121072 :   MooseEnum upwinding_type("none full", "none");
      32      121072 :   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      121072 :   params.addParam<MaterialPropertyName>("advected_quantity",
      38             :                                         "An optional material property to be advected. If not "
      39             :                                         "supplied, then the variable will be used.");
      40       90804 :   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       60536 :   return params;
      45       30268 : }
      46             : 
      47             : template <>
      48             : InputParameters
      49       15411 : ConservativeAdvectionTempl<false>::validParams()
      50             : {
      51       15411 :   InputParameters params = generalParams();
      52       61644 :   params.addCoupledVar("velocity", "Velocity vector");
      53       92466 :   params.deprecateCoupledVar("velocity", "velocity_variable", "12/31/2025");
      54       46233 :   params.addParam<MaterialPropertyName>("velocity_material", "Velocity vector given as a material");
      55       15411 :   return params;
      56           0 : }
      57             : 
      58             : template <>
      59             : InputParameters
      60       14857 : ConservativeAdvectionTempl<true>::validParams()
      61             : {
      62       14857 :   InputParameters params = generalParams();
      63       59428 :   params.addCoupledVar("velocity_variable", "Velocity vector given as a variable");
      64       59428 :   params.addParam<MaterialPropertyName>("velocity", "Velocity vector given as a material");
      65       74285 :   params.deprecateParam("velocity", "velocity_material", "12/31/2025");
      66       14857 :   return params;
      67           0 : }
      68             : 
      69             : template <bool is_ad>
      70         404 : ConservativeAdvectionTempl<is_ad>::ConservativeAdvectionTempl(const InputParameters & parameters)
      71             :   : GenericKernel<is_ad>(parameters),
      72         404 :     _scalar(this->template getGenericMaterialProperty<Real, is_ad>("velocity_scalar_coef")),
      73         808 :     _coupled_variable_present(isParamValid("velocity_as_variable_gradient")),
      74         458 :     _coupled_variable_var(_coupled_variable_present ? coupled("velocity_as_variable_gradient") : 0),
      75         404 :     _velocity(
      76         404 :         _coupled_variable_present
      77        1212 :             ? &this->template coupledGenericGradient<is_ad>("velocity_as_variable_gradient")
      78        1454 :             : (this->isParamValid("velocity_variable")
      79        1379 :                    ? &this->template coupledGenericVectorValue<is_ad>("velocity_variable")
      80         629 :                    : (this->isParamValid("velocity_material")
      81         613 :                           ? &this->template getGenericMaterialProperty<RealVectorValue, is_ad>(
      82             :                                      "velocity_material")
      83          67 :                                  .get()
      84             :                           : nullptr))),
      85         808 :     _user_supplied_adv_quant(isParamValid("advected_quantity")),
      86         404 :     _adv_quant(
      87         404 :         _user_supplied_adv_quant
      88         404 :             ? this->template getGenericMaterialProperty<Real, is_ad>("advected_quantity").get()
      89             :             : _u),
      90         404 :     _upwinding(
      91         808 :         this->template getParam<MooseEnum>("upwinding_type").template getEnum<UpwindingType>()),
      92         404 :     _u_nodal(_var.template genericDofValues<is_ad>()),
      93         808 :     _upwind_node(0),
      94        1616 :     _dtotal_mass_out(0)
      95             : {
      96         404 :   if (_coupled_variable_present && _coupled_variable_var == _var.number())
      97           8 :     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         684 :   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         892 :   if (!_velocity || (_coupled_variable_present && this->isParamValid("velocity_material")) ||
     106        1684 :       (_coupled_variable_present && this->isParamValid("velocity_variable")) ||
     107        2126 :       (this->isParamValid("velocity_variable") && this->isParamValid("velocity_material")))
     108          16 :     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         392 :   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         392 : }
     118             : 
     119             : template <bool is_ad>
     120             : GenericReal<is_ad>
     121    19277240 : ConservativeAdvectionTempl<is_ad>::negSpeedQp() const
     122             : {
     123    19277240 :   return -_grad_test[_i][_qp] * (*_velocity)[_qp] * _scalar[_qp];
     124             : }
     125             : 
     126             : template <bool is_ad>
     127             : GenericReal<is_ad>
     128     4753368 : ConservativeAdvectionTempl<is_ad>::computeQpResidual()
     129             : {
     130             :   // This is the no-upwinded version
     131             :   // It gets called via GenericKernel<is_ad>::computeResidual()
     132     4753368 :   return negSpeedQp() * _adv_quant[_qp];
     133             : }
     134             : 
     135             : template <>
     136             : Real
     137     9589632 : ConservativeAdvectionTempl<false>::computeQpJacobian()
     138             : {
     139             :   // This is the no-upwinded version
     140             :   // It gets called via GenericKernel<false>::computeJacobian()
     141     9589632 :   if (!_user_supplied_adv_quant)
     142     9589632 :     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      182944 : ConservativeAdvectionTempl<false>::computeQpOffDiagJacobian(unsigned int jvar)
     157             : {
     158             :   // This is the non-upwinded version
     159             :   // It gets called via GenericKernel<false>::computeOffDiagJacobian()
     160      182944 :   if (_coupled_variable_present && _coupled_variable_var == jvar)
     161        7840 :     return -_grad_test[_i][_qp] * _grad_phi[_j][_qp] * _adv_quant[_qp] * _scalar[_qp];
     162             :   else
     163      175104 :     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      500950 : ConservativeAdvectionTempl<is_ad>::computeResidual()
     177             : {
     178      500950 :   switch (_upwinding)
     179             :   {
     180      339690 :     case UpwindingType::none:
     181      339690 :       GenericKernel<is_ad>::computeResidual();
     182      339690 :       break;
     183      161260 :     case UpwindingType::full:
     184      161260 :       fullUpwind(JacRes::CALCULATE_RESIDUAL);
     185      161260 :       break;
     186             :   }
     187      500950 : }
     188             : 
     189             : template <bool is_ad>
     190             : void
     191      314710 : ConservativeAdvectionTempl<is_ad>::computeJacobian()
     192             : {
     193      314710 :   switch (_upwinding)
     194             :   {
     195      154584 :     case UpwindingType::none:
     196      154584 :       GenericKernel<is_ad>::computeJacobian();
     197      154584 :       break;
     198      160126 :     case UpwindingType::full:
     199      160126 :       fullUpwind(JacRes::CALCULATE_JACOBIAN);
     200      160126 :       break;
     201             :   }
     202      314710 : }
     203             : 
     204             : template <bool is_ad>
     205             : void
     206      321386 : ConservativeAdvectionTempl<is_ad>::fullUpwind(JacRes res_or_jac)
     207             : {
     208             :   // The number of nodes in the element
     209      321386 :   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      321386 :   _my_local_re.resize(_var.dofIndices().size());
     214             : 
     215      321386 :   if (!is_ad && (res_or_jac == JacRes::CALCULATE_JACOBIAN))
     216      160126 :     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      321386 :   _upwind_node.resize(num_nodes);
     222     1567258 :   for (_i = 0; _i < num_nodes; ++_i)
     223             :   {
     224     6180112 :     for (_qp = 0; _qp < this->_qrule->n_points(); _qp++)
     225     4934240 :       _my_local_re(_i) += this->_JxW[_qp] * this->_coord[_qp] * negSpeedQp();
     226     1245872 :     _upwind_node[_i] = (MetaPhysicL::raw_value(_my_local_re(_i)) >= 0.0);
     227             :   }
     228             : 
     229             :   // Variables used to ensure mass conservation
     230      321386 :   GenericReal<is_ad> total_mass_out = 0.0;
     231      321386 :   GenericReal<is_ad> total_in = 0.0;
     232      321386 :   if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
     233      160126 :     _dtotal_mass_out.assign(num_nodes, 0.0);
     234             : 
     235     1567258 :   for (const auto n : make_range(num_nodes))
     236             :   {
     237     1245872 :     if (_upwind_node[n])
     238             :     {
     239             :       if constexpr (!is_ad)
     240      623080 :         if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
     241             :         {
     242      309272 :           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      309272 :             _local_ke(n, n) += _my_local_re(n);
     250             : 
     251      309272 :           _dtotal_mass_out[n] += _local_ke(n, n);
     252             :         }
     253      623080 :       _my_local_re(n) *= _u_nodal[n];
     254      623080 :       total_mass_out += _my_local_re(n);
     255             :     }
     256             :     else                           // downwind node
     257      622792 :       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     1567258 :   for (const auto n : make_range(num_nodes))
     263     1245872 :     if (!_upwind_node[n]) // downwind node
     264             :     {
     265             :       if constexpr (!is_ad)
     266      622792 :         if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
     267     1524472 :           for (_j = 0; _j < _phi.size(); _j++)
     268     1215344 :             _local_ke(n, _j) += _my_local_re(n) * _dtotal_mass_out[_j] / total_in;
     269      622792 :       _my_local_re(n) *= total_mass_out / total_in;
     270             :     }
     271             : 
     272             :   // Add the result to the residual and jacobian
     273      321386 :   if (res_or_jac == JacRes::CALCULATE_RESIDUAL)
     274             :   {
     275      161260 :     this->addResiduals(this->_assembly, _my_local_re, _var.dofIndices(), _var.scalingFactor());
     276             : 
     277      161260 :     if (this->_has_save_in)
     278             :     {
     279           0 :       Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
     280           0 :       for (const auto & var : this->_save_in)
     281           0 :         var->sys().solution().add_vector(MetaPhysicL::raw_value(_my_local_re), var->dofIndices());
     282           0 :     }
     283             :   }
     284             : 
     285      321386 :   if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
     286             :   {
     287             :     if constexpr (is_ad)
     288           0 :       this->addJacobian(this->_assembly, _my_local_re, _var.dofIndices(), _var.scalingFactor());
     289             :     else
     290      160126 :       accumulateTaggedLocalMatrix();
     291             :   }
     292      321386 : }
     293             : 
     294             : template class ConservativeAdvectionTempl<false>;
     295             : template class ConservativeAdvectionTempl<true>;

Generated by: LCOV version 1.14