LCOV - code coverage report
Current view: top level - include/materials - INSADTauMaterial.h (source / functions) Hit Total Coverage
Test: idaholab/moose navier_stokes: 9fc4b0 Lines: 120 124 96.8 %
Date: 2025-08-14 10:14:56 Functions: 20 20 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             : #pragma once
      11             : 
      12             : #include "InputParameters.h"
      13             : #include "NonlinearSystemBase.h"
      14             : #include "FEProblemBase.h"
      15             : #include "MaterialProperty.h"
      16             : #include "MooseArray.h"
      17             : #include "INSADMaterial.h"
      18             : #include "NavierStokesMethods.h"
      19             : #include "Assembly.h"
      20             : #include "MooseVariableFE.h"
      21             : #include "MooseMesh.h"
      22             : 
      23             : #include "libmesh/elem.h"
      24             : #include "libmesh/node.h"
      25             : #include "libmesh/fe_type.h"
      26             : 
      27             : #include <vector>
      28             : 
      29             : class INSADMaterial;
      30             : 
      31             : template <typename T>
      32             : class INSADTauMaterialTempl : public T
      33             : {
      34             : public:
      35             :   static InputParameters validParams();
      36             : 
      37             :   INSADTauMaterialTempl(const InputParameters & parameters);
      38             : 
      39             : protected:
      40             :   virtual void computeProperties() override;
      41             :   virtual void computeQpProperties() override;
      42             : 
      43             :   /**
      44             :    * Compute the maximum dimension of the current element
      45             :    */
      46             :   void computeHMax();
      47             : 
      48             :   /**
      49             :    * Compute the viscous strong residual
      50             :    */
      51             :   void computeViscousStrongResidual();
      52             : 
      53             :   /**
      54             :    * Compute the strong form corresponding to RZ pieces of the viscous term
      55             :    */
      56             :   void viscousTermRZ();
      57             : 
      58             :   /**
      59             :    * Whether to seed with respect to velocity derivatives
      60             :    */
      61             :   bool doVelocityDerivatives() const;
      62             : 
      63             :   const Real _alpha;
      64             :   ADMaterialProperty<Real> & _tau;
      65             : 
      66             :   /// Strong residual corresponding to the momentum viscous term. This is only used by stabilization
      67             :   /// kernels
      68             :   ADMaterialProperty<RealVectorValue> & _viscous_strong_residual;
      69             : 
      70             :   /// The strong residual of the momentum equation
      71             :   ADMaterialProperty<RealVectorValue> & _momentum_strong_residual;
      72             : 
      73             :   ADReal _hmax;
      74             : 
      75             :   /// The velocity variable
      76             :   const VectorMooseVariable * const _velocity_var;
      77             : 
      78             :   /// A scalar Lagrange FE data member to compute the velocity second derivatives since
      79             :   /// they're currently not supported for vector FE types
      80             :   const FEBase * const & _scalar_lagrange_fe;
      81             : 
      82             :   /// Containers to hold the matrix of second spatial derivatives of velocity
      83             :   std::vector<ADRealTensorValue> _d2u;
      84             :   std::vector<ADRealTensorValue> _d2v;
      85             :   std::vector<ADRealTensorValue> _d2w;
      86             : 
      87             :   /// The velocity variable number
      88             :   const unsigned int _vel_number;
      89             : 
      90             :   /// The velocity system number
      91             :   const unsigned int _vel_sys_number;
      92             : 
      93             :   /// The speed of the medium. This is the norm of the relative velocity, e.g. the velocity minus
      94             :   /// the mesh velocity, at the current _qp
      95             :   ADReal _speed;
      96             : 
      97             :   using T::_ad_q_point;
      98             :   using T::_advected_mesh_strong_residual;
      99             :   using T::_advective_strong_residual;
     100             :   using T::_assembly;
     101             :   using T::_boussinesq_strong_residual;
     102             :   using T::_coord_sys;
     103             :   using T::_coupled_force_strong_residual;
     104             :   using T::_current_elem;
     105             :   using T::_disp_x_num;
     106             :   using T::_disp_x_sys_num;
     107             :   using T::_disp_y_num;
     108             :   using T::_disp_y_sys_num;
     109             :   using T::_disp_z_num;
     110             :   using T::_disp_z_sys_num;
     111             :   using T::_dt;
     112             :   using T::_fe_problem;
     113             :   using T::_grad_p;
     114             :   using T::_grad_velocity;
     115             :   using T::_gravity_strong_residual;
     116             :   using T::_has_advected_mesh;
     117             :   using T::_has_boussinesq;
     118             :   using T::_has_coupled_force;
     119             :   using T::_has_gravity;
     120             :   using T::_has_transient;
     121             :   using T::_mesh;
     122             :   using T::_mu;
     123             :   using T::_object_tracker;
     124             :   using T::_q_point;
     125             :   using T::_qp;
     126             :   using T::_qrule;
     127             :   using T::_relative_velocity;
     128             :   using T::_rho;
     129             :   using T::_rz_axial_coord;
     130             :   using T::_rz_radial_coord;
     131             :   using T::_td_strong_residual;
     132             :   using T::_use_displaced_mesh;
     133             :   using T::_velocity;
     134             :   using T::_viscous_form;
     135             :   using T::getVectorVar;
     136             : };
     137             : 
     138             : typedef INSADTauMaterialTempl<INSADMaterial> INSADTauMaterial;
     139             : 
     140             : template <typename T>
     141             : InputParameters
     142        4370 : INSADTauMaterialTempl<T>::validParams()
     143             : {
     144        4370 :   InputParameters params = T::validParams();
     145        4370 :   params.addClassDescription(
     146             :       "This is the material class used to compute the stabilization parameter tau.");
     147        8740 :   params.addParam<Real>("alpha", 1., "Multiplicative factor on the stabilization parameter tau.");
     148        4370 :   return params;
     149           0 : }
     150             : 
     151             : template <typename T>
     152        3417 : INSADTauMaterialTempl<T>::INSADTauMaterialTempl(const InputParameters & parameters)
     153             :   : T(parameters),
     154        3417 :     _alpha(this->template getParam<Real>("alpha")),
     155        3417 :     _tau(this->template declareADProperty<Real>("tau")),
     156        3417 :     _viscous_strong_residual(
     157        3417 :         this->template declareADProperty<RealVectorValue>("viscous_strong_residual")),
     158        3417 :     _momentum_strong_residual(
     159        6834 :         this->template declareADProperty<RealVectorValue>("momentum_strong_residual")),
     160        3417 :     _velocity_var(getVectorVar("velocity", 0)),
     161        3417 :     _scalar_lagrange_fe(
     162        3417 :         _assembly.getFE(FEType(_velocity_var->feType().order, LAGRANGE), _mesh.dimension())),
     163        3417 :     _vel_number(_velocity_var->number()),
     164        6834 :     _vel_sys_number(_velocity_var->sys().number())
     165             : {
     166        3417 :   _scalar_lagrange_fe->get_d2phi();
     167        3417 : }
     168             : 
     169             : template <typename T>
     170             : bool
     171    44754628 : INSADTauMaterialTempl<T>::doVelocityDerivatives() const
     172             : {
     173    51387120 :   return ADReal::do_derivatives &&
     174     6632492 :          (_vel_sys_number == _fe_problem.currentNonlinearSystem().number());
     175             : }
     176             : 
     177             : template <typename T>
     178             : void
     179     5682135 : INSADTauMaterialTempl<T>::computeHMax()
     180             : {
     181     5682135 :   if (_disp_x_num == libMesh::invalid_uint || !ADReal::do_derivatives)
     182             :   {
     183     5623776 :     _hmax = _current_elem->hmax();
     184     5623776 :     return;
     185             :   }
     186             : 
     187       58359 :   _hmax = 0;
     188       58359 :   std::array<unsigned int, 3> disps = {_disp_x_num, _disp_y_num, _disp_z_num};
     189       58359 :   std::array<unsigned int, 3> disp_sys_nums = {_disp_x_sys_num, _disp_y_sys_num, _disp_z_sys_num};
     190             : 
     191      351091 :   for (unsigned int n_outer = 0; n_outer < _current_elem->n_vertices(); n_outer++)
     192      969014 :     for (unsigned int n_inner = n_outer + 1; n_inner < _current_elem->n_vertices(); n_inner++)
     193             :     {
     194      676282 :       VectorValue<ADReal> diff = (_current_elem->point(n_outer) - _current_elem->point(n_inner));
     195     2705128 :       for (const auto i : index_range(disps))
     196             :       {
     197     2028846 :         const auto disp_num = disps[i];
     198     2028846 :         if (disp_num == libMesh::invalid_uint)
     199      261210 :           continue;
     200     1767636 :         const auto sys_num = disp_sys_nums[i];
     201             : 
     202             :         // Here we insert derivatives of the difference in nodal positions with respect to the
     203             :         // displacement degrees of freedom. From above, diff = outer_node_position -
     204             :         // inner_node_position
     205     1767636 :         diff(i).derivatives().insert(
     206     1767636 :             _current_elem->node_ref(n_outer).dof_number(sys_num, disp_num, 0)) = 1.;
     207     1767636 :         diff(i).derivatives().insert(
     208     1767636 :             _current_elem->node_ref(n_inner).dof_number(sys_num, disp_num, 0)) = -1.;
     209             :       }
     210             : 
     211      676282 :       _hmax = std::max(_hmax, diff.norm_sq());
     212             :     }
     213             : 
     214       58359 :   _hmax = std::sqrt(_hmax);
     215             : }
     216             : 
     217             : template <typename T>
     218             : void
     219     5682135 : INSADTauMaterialTempl<T>::computeProperties()
     220             : {
     221     5682135 :   computeHMax();
     222     5682135 :   computeViscousStrongResidual();
     223             : 
     224     5682135 :   T::computeProperties();
     225     5682135 : }
     226             : 
     227             : template <typename T>
     228             : void
     229     5682135 : INSADTauMaterialTempl<T>::computeViscousStrongResidual()
     230             : {
     231    39774945 :   auto resize_and_zero = [this](auto & d2vel)
     232             :   {
     233    17046405 :     d2vel.resize(_qrule->n_points());
     234    86552499 :     for (auto & d2qp : d2vel)
     235             :       d2qp = 0;
     236             :   };
     237     5682135 :   resize_and_zero(_d2u);
     238     5682135 :   resize_and_zero(_d2v);
     239     5682135 :   resize_and_zero(_d2w);
     240             : 
     241    50436763 :   auto get_d2 = [this](const auto i) -> std::vector<ADRealTensorValue> &
     242             :   {
     243    44754628 :     switch (i)
     244             :     {
     245    22199390 :       case 0:
     246    22199390 :         return _d2u;
     247    22199390 :       case 1:
     248    22199390 :         return _d2v;
     249      355848 :       case 2:
     250      355848 :         return _d2w;
     251           0 :       default:
     252           0 :         mooseError("invalid value of 'i'");
     253             :     }
     254             :   };
     255             : 
     256             :   // libMesh does not yet have the capability for computing second order spatial derivatives of
     257             :   // vector bases. Lacking that capability, we can compute the second order spatial derivatives "by
     258             :   // hand" using the scalar field version of the vector basis, e.g. LAGRANGE instead of
     259             :   // LAGRANGE_VEC. Adding this implementation allows us to be fully consistent with results from a
     260             :   // scalar velocity field component implementation of Navier-Stokes
     261     5682135 :   const auto & vel_dof_indices = _velocity_var->dofIndices();
     262    50436763 :   for (const auto i : index_range(vel_dof_indices))
     263             :   {
     264             :     // This may not work if the element and spatial dimensions are different
     265             :     mooseAssert(_current_elem->dim() == _mesh.dimension(),
     266             :                 "Below logic only applicable if element and mesh dimension are the same");
     267    44754628 :     const auto dimensional_component = i % _mesh.dimension();
     268    44754628 :     auto & d2vel = get_d2(dimensional_component);
     269    44754628 :     const auto dof_index = vel_dof_indices[i];
     270    44754628 :     ADReal dof_value = (*_velocity_var->sys().currentSolution())(dof_index);
     271    44754628 :     if (doVelocityDerivatives())
     272     6632492 :       dof_value.derivatives().insert(dof_index) = 1;
     273    44754628 :     const auto scalar_i_component = i / _mesh.dimension();
     274   232524896 :     for (const auto qp : make_range(_qrule->n_points()))
     275   375540536 :       d2vel[qp] += dof_value * _scalar_lagrange_fe->get_d2phi()[scalar_i_component][qp];
     276             :   }
     277             : 
     278             :   // Now that we have the second order spatial derivatives of velocity, we can compute the strong
     279             :   // form of the viscous residual for use in our stabilization calculations
     280    28850833 :   for (_qp = 0; _qp < _qrule->n_points(); ++_qp)
     281             :   {
     282    46337396 :     _viscous_strong_residual[_qp](0) = -_mu[_qp] * _d2u[_qp].tr();
     283    23168698 :     _viscous_strong_residual[_qp](1) =
     284    69506094 :         _mesh.dimension() >= 2 ? -_mu[_qp] * _d2v[_qp].tr() : ADReal(0);
     285    23168698 :     _viscous_strong_residual[_qp](2) =
     286    46619524 :         _mesh.dimension() == 3 ? -_mu[_qp] * _d2w[_qp].tr() : ADReal(0);
     287    23168698 :     if (_viscous_form == NS::ViscousForm::Traction)
     288             :     {
     289     4717626 :       _viscous_strong_residual[_qp] -= _mu[_qp] * _d2u[_qp].row(0);
     290     4717626 :       if (_mesh.dimension() >= 2)
     291    14152878 :         _viscous_strong_residual[_qp] -= _mu[_qp] * _d2v[_qp].row(1);
     292     4717626 :       if (_mesh.dimension() == 3)
     293           0 :         _viscous_strong_residual[_qp] -= _mu[_qp] * _d2w[_qp].row(2);
     294             :     }
     295    23168698 :     if (_coord_sys == Moose::COORD_RZ)
     296     5250618 :       viscousTermRZ();
     297             :   }
     298     5682135 : }
     299             : 
     300             : template <typename T>
     301             : void
     302     5250618 : INSADTauMaterialTempl<T>::viscousTermRZ()
     303             : {
     304             :   // To understand the code immediately below, visit
     305             :   // https://en.wikipedia.org/wiki/Del_in_cylindrical_and_spherical_coordinates.
     306             :   // The u_r / r^2 term comes from the vector Laplacian. The -du_i/dr * 1/r term comes from
     307             :   // the scalar Laplacian. The scalar Laplacian in axisymmetric cylindrical coordinates is
     308             :   // equivalent to the Cartesian Laplacian plus a 1/r * du_i/dr term. And of course we are
     309             :   // applying a minus sign here because the strong form is -\nabala^2 * \vec{u}
     310             :   //
     311             :   // Another note: libMesh implements grad(v) as dvi/dxj or more obviously:
     312             :   //
     313             :   // grad(v) = (vx_x vx_y)
     314             :   //           (vy_x vy_y)
     315             :   //
     316             :   // so, the gradient of the velocity with respect to the radial coordinate will correspond to a
     317             :   // *column* slice
     318             : 
     319     5250618 :   const auto & r = _ad_q_point[_qp](_rz_radial_coord);
     320             : 
     321             :   {
     322             :     // Do the "Laplace" form. This will be present in *both* Laplace and Traction forms
     323             :     ADRealVectorValue rz_term;
     324    15751854 :     for (const auto i : make_range((unsigned int)2))
     325    21002472 :       rz_term(i) = -_mu[_qp] * _grad_velocity[_qp](i, _rz_radial_coord) / r;
     326    10501236 :     rz_term(_rz_radial_coord) += _mu[_qp] * _velocity[_qp](_rz_radial_coord) / (r * r);
     327     5250618 :     _viscous_strong_residual[_qp] += rz_term;
     328             :   }
     329     5250618 :   if (_viscous_form == NS::ViscousForm::Traction)
     330             :   {
     331             :     ADRealVectorValue rz_term;
     332    13642578 :     for (const auto i : make_range((unsigned int)2))
     333             :       // This is the transpose of the above
     334    18190104 :       rz_term(i) = -_mu[_qp] * _grad_velocity[_qp](_rz_radial_coord, i) / r;
     335             :     // This is the same as above (since the transpose of the diagonal is the diagonal)
     336     9095052 :     rz_term(_rz_radial_coord) += _mu[_qp] * _velocity[_qp](_rz_radial_coord) / (r * r);
     337     4547526 :     _viscous_strong_residual[_qp] += rz_term;
     338             :   }
     339     5250618 : }
     340             : 
     341             : template <typename T>
     342             : void
     343    23168698 : INSADTauMaterialTempl<T>::computeQpProperties()
     344             : {
     345    23168698 :   T::computeQpProperties();
     346             : 
     347    23168698 :   const auto nu = _mu[_qp] / _rho[_qp];
     348    23168698 :   const auto transient_part = _has_transient ? 4. / (_dt * _dt) : 0.;
     349    23168698 :   _speed = NS::computeSpeed<ADReal>(_relative_velocity[_qp]);
     350    92674792 :   _tau[_qp] = _alpha / std::sqrt(transient_part + (2. * _speed / _hmax) * (2. * _speed / _hmax) +
     351    92674792 :                                  9. * (4. * nu / (_hmax * _hmax)) * (4. * nu / (_hmax * _hmax)));
     352             : 
     353    46337396 :   _momentum_strong_residual[_qp] =
     354    23168698 :       _advective_strong_residual[_qp] + _viscous_strong_residual[_qp] + _grad_p[_qp];
     355             : 
     356    23168698 :   if (_has_transient)
     357     2190754 :     _momentum_strong_residual[_qp] += _td_strong_residual[_qp];
     358             : 
     359    23168698 :   if (_has_gravity)
     360    11759808 :     _momentum_strong_residual[_qp] += _gravity_strong_residual[_qp];
     361             : 
     362    23168698 :   if (_has_boussinesq)
     363    11695296 :     _momentum_strong_residual[_qp] += _boussinesq_strong_residual[_qp];
     364             : 
     365    23168698 :   if (_has_advected_mesh)
     366      690184 :     _momentum_strong_residual[_qp] += _advected_mesh_strong_residual[_qp];
     367             : 
     368    23168698 :   if (_has_coupled_force)
     369     1668432 :     _momentum_strong_residual[_qp] += _coupled_force_strong_residual[_qp];
     370             : 
     371             :   // // Future addition
     372             :   // if (_object_tracker->hasMMS())
     373             :   //   _momentum_strong_residual[_qp] += _mms_function_strong_residual[_qp];
     374    23168698 : }

Generated by: LCOV version 1.14