LCOV - code coverage report
Current view: top level - src/vectorpostprocessors - InteractionIntegral.C (source / functions) Hit Total Coverage
Test: idaholab/moose solid_mechanics: f45d79 Lines: 343 371 92.5 %
Date: 2025-07-25 05:00:39 Functions: 22 24 91.7 %
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 "InteractionIntegral.h"
      11             : #include "MooseMesh.h"
      12             : #include "RankTwoTensor.h"
      13             : #include "Conversion.h"
      14             : #include "libmesh/string_to_enum.h"
      15             : #include "libmesh/quadrature.h"
      16             : #include "DerivativeMaterialInterface.h"
      17             : #include "libmesh/utility.h"
      18             : #include "CrackFrontDefinition.h"
      19             : 
      20             : registerMooseObject("SolidMechanicsApp", InteractionIntegral);
      21             : registerMooseObject("SolidMechanicsApp", ADInteractionIntegral);
      22             : 
      23             : template <bool is_ad>
      24             : MooseEnum
      25        1928 : InteractionIntegralTempl<is_ad>::qFunctionType()
      26             : {
      27        3856 :   return MooseEnum("Geometry Topology", "Geometry");
      28             : }
      29             : 
      30             : template <bool is_ad>
      31             : MooseEnum
      32        1928 : InteractionIntegralTempl<is_ad>::sifModeType()
      33             : {
      34        3856 :   return MooseEnum("KI KII KIII T", "KI");
      35             : }
      36             : 
      37             : template <bool is_ad>
      38             : InputParameters
      39         964 : InteractionIntegralTempl<is_ad>::validParams()
      40             : {
      41         964 :   InputParameters params = ElementVectorPostprocessor::validParams();
      42        1928 :   params.addRequiredCoupledVar(
      43             :       "displacements",
      44             :       "The displacements appropriate for the simulation geometry and coordinate system");
      45        1928 :   params.addCoupledVar("temperature",
      46             :                        "The temperature (optional). Must be provided to correctly compute "
      47             :                        "stress intensity factors in models with thermal strain gradients.");
      48        1928 :   params.addParam<MaterialPropertyName>(
      49             :       "functionally_graded_youngs_modulus",
      50             :       "Spatially varying elasticity modulus variable. This input is required when "
      51             :       "using the functionally graded material capability.");
      52        1928 :   params.addParam<MaterialPropertyName>(
      53             :       "functionally_graded_youngs_modulus_crack_dir_gradient",
      54             :       "Gradient of the spatially varying Young's modulus provided in "
      55             :       "'functionally_graded_youngs_modulus' in the direction of crack extension.");
      56        1928 :   params.addRequiredParam<UserObjectName>("crack_front_definition",
      57             :                                           "The CrackFrontDefinition user object name");
      58        1928 :   MooseEnum position_type("Angle Distance", "Distance");
      59        1928 :   params.addParam<MooseEnum>(
      60             :       "position_type",
      61             :       position_type,
      62             :       "The method used to calculate position along crack front.  Options are: " +
      63             :           position_type.getRawNames());
      64        1928 :   params.addRequiredParam<Real>(
      65             :       "K_factor", "Conversion factor between interaction integral and stress intensity factor K");
      66        1928 :   params.addParam<unsigned int>("symmetry_plane",
      67             :                                 "Account for a symmetry plane passing through "
      68             :                                 "the plane of the crack, normal to the specified "
      69             :                                 "axis (0=x, 1=y, 2=z)");
      70        1928 :   params.addRequiredParam<Real>("poissons_ratio", "Poisson's ratio for the material.");
      71        1928 :   params.addRequiredParam<Real>("youngs_modulus", "Young's modulus of the material.");
      72         964 :   params.set<bool>("use_displaced_mesh") = false;
      73        1928 :   params.addRequiredParam<unsigned int>("ring_index", "Ring ID");
      74        2892 :   params.addParam<MooseEnum>("q_function_type",
      75             :                              InteractionIntegralTempl<is_ad>::qFunctionType(),
      76             :                              "The method used to define the integration domain. Options are: " +
      77             :                                  InteractionIntegralTempl<is_ad>::qFunctionType().getRawNames());
      78        2892 :   params.addRequiredParam<MooseEnum>(
      79             :       "sif_mode",
      80             :       InteractionIntegralTempl<is_ad>::sifModeType(),
      81             :       "Stress intensity factor to calculate. Choices are: " +
      82             :           InteractionIntegralTempl<is_ad>::sifModeType().getRawNames());
      83        1928 :   params.addParam<MaterialPropertyName>("eigenstrain_gradient",
      84             :                                         "Material defining gradient of eigenstrain tensor");
      85        1928 :   params.addParam<MaterialPropertyName>("body_force", "Material defining body force");
      86        1928 :   params.addCoupledVar("additional_eigenstrain_00",
      87             :                        "Optional additional eigenstrain variable that will be accounted for in the "
      88             :                        "interaction integral (component 00 or XX).");
      89        1928 :   params.addCoupledVar("additional_eigenstrain_01",
      90             :                        "Optional additional eigenstrain variable that will be accounted for in the "
      91             :                        "interaction integral (component 01 or XY).");
      92        1928 :   params.addCoupledVar("additional_eigenstrain_11",
      93             :                        "Optional additional eigenstrain variable that will be accounted for in the "
      94             :                        "interaction integral (component 11 or YY).");
      95        1928 :   params.addCoupledVar("additional_eigenstrain_22",
      96             :                        "Optional additional eigenstrain variable that will be accounted for in the "
      97             :                        "interaction integral (component 22 or ZZ).");
      98        1928 :   params.addCoupledVar("additional_eigenstrain_02",
      99             :                        "Optional additional eigenstrain variable that will be accounted for in the "
     100             :                        "interaction integral (component 02 or XZ).");
     101        1928 :   params.addCoupledVar("additional_eigenstrain_12",
     102             :                        "Optional additional eigenstrain variable that will be accounted for in the "
     103             :                        "interaction integral (component 12 or XZ).");
     104        1928 :   params.addParamNamesToGroup(
     105             :       "additional_eigenstrain_00 additional_eigenstrain_01 additional_eigenstrain_11 "
     106             :       "additional_eigenstrain_22 additional_eigenstrain_02 additional_eigenstrain_12",
     107             :       "Generic eigenstrains for the computation of the interaction integral.");
     108         964 :   params.addClassDescription(
     109             :       "Computes the interaction integral, which is used to compute various "
     110             :       "fracture mechanics parameters at a crack tip, including KI, KII, KIII, "
     111             :       "and the T stress.");
     112         964 :   return params;
     113         964 : }
     114             : 
     115             : template <bool is_ad>
     116         840 : InteractionIntegralTempl<is_ad>::InteractionIntegralTempl(const InputParameters & parameters)
     117             :   : ElementVectorPostprocessor(parameters),
     118         840 :     _ndisp(coupledComponents("displacements")),
     119         840 :     _crack_front_definition(&getUserObject<CrackFrontDefinition>("crack_front_definition")),
     120         840 :     _treat_as_2d(false),
     121        1680 :     _stress(getGenericMaterialPropertyByName<RankTwoTensor, is_ad>("stress")),
     122         840 :     _strain(getGenericMaterialPropertyByName<RankTwoTensor, is_ad>("elastic_strain")),
     123         840 :     _fe_vars(getCoupledMooseVars()),
     124         840 :     _fe_type(_fe_vars[0]->feType()),
     125         840 :     _disp(coupledValues("displacements")),
     126         840 :     _grad_disp(3),
     127         840 :     _has_temp(isCoupled("temperature")),
     128         840 :     _grad_temp(_has_temp ? coupledGradient("temperature") : _grad_zero),
     129         840 :     _functionally_graded_youngs_modulus_crack_dir_gradient(
     130         840 :         isParamSetByUser("functionally_graded_youngs_modulus_crack_dir_gradient")
     131         924 :             ? &getMaterialProperty<Real>("functionally_graded_youngs_modulus_crack_dir_gradient")
     132             :             : nullptr),
     133         840 :     _functionally_graded_youngs_modulus(
     134         840 :         isParamSetByUser("functionally_graded_youngs_modulus")
     135         924 :             ? &getMaterialProperty<Real>("functionally_graded_youngs_modulus")
     136             :             : nullptr),
     137        1680 :     _K_factor(getParam<Real>("K_factor")),
     138        1680 :     _has_symmetry_plane(isParamValid("symmetry_plane")),
     139        1680 :     _poissons_ratio(getParam<Real>("poissons_ratio")),
     140        1680 :     _youngs_modulus(getParam<Real>("youngs_modulus")),
     141        1680 :     _fgm_crack(isParamSetByUser("functionally_graded_youngs_modulus_crack_dir_gradient") &&
     142         924 :                isParamSetByUser("functionally_graded_youngs_modulus")),
     143        1680 :     _ring_index(getParam<unsigned int>("ring_index")),
     144         840 :     _total_deigenstrain_dT(
     145         840 :         hasMaterialProperty<RankTwoTensor>("total_deigenstrain_dT")
     146         936 :             ? &getGenericMaterialProperty<RankTwoTensor, is_ad>("total_deigenstrain_dT")
     147             :             : nullptr),
     148         840 :     _has_additional_eigenstrain(false),
     149         840 :     _additional_eigenstrain_gradient_00(isCoupled("additional_eigenstrain_00")
     150         864 :                                             ? &coupledGradient("additional_eigenstrain_00")
     151             :                                             : nullptr),
     152         840 :     _additional_eigenstrain_gradient_01(isCoupled("additional_eigenstrain_01")
     153         864 :                                             ? &coupledGradient("additional_eigenstrain_01")
     154             :                                             : nullptr),
     155         840 :     _additional_eigenstrain_gradient_11(isCoupled("additional_eigenstrain_11")
     156         864 :                                             ? &coupledGradient("additional_eigenstrain_11")
     157             :                                             : nullptr),
     158         840 :     _additional_eigenstrain_gradient_22(isCoupled("additional_eigenstrain_22")
     159         864 :                                             ? &coupledGradient("additional_eigenstrain_22")
     160             :                                             : nullptr),
     161         840 :     _additional_eigenstrain_gradient_02(isCoupled("additional_eigenstrain_02")
     162         840 :                                             ? &coupledGradient("additional_eigenstrain_02")
     163             :                                             : nullptr),
     164        1680 :     _additional_eigenstrain_gradient_12(isCoupled("additional_eigenstrain_12")
     165         840 :                                             ? &coupledGradient("additional_eigenstrain_12")
     166             :                                             : nullptr),
     167        1680 :     _q_function_type(getParam<MooseEnum>("q_function_type").template getEnum<QMethod>()),
     168        1680 :     _position_type(getParam<MooseEnum>("position_type").template getEnum<PositionType>()),
     169        1680 :     _sif_mode(getParam<MooseEnum>("sif_mode").template getEnum<SifMethod>()),
     170         840 :     _x(declareVector("x")),
     171         840 :     _y(declareVector("y")),
     172         840 :     _z(declareVector("z")),
     173         840 :     _position(declareVector("id")),
     174        3360 :     _interaction_integral(declareVector("II_" + Moose::stringify(getParam<MooseEnum>("sif_mode")) +
     175             :                                         "_" + Moose::stringify(_ring_index))),
     176         840 :     _eigenstrain_gradient(nullptr),
     177         840 :     _body_force(nullptr)
     178             : {
     179         840 :   if (_has_temp && !_total_deigenstrain_dT)
     180           0 :     mooseError("InteractionIntegral Error: To include thermal strain term in interaction integral, "
     181             :                "must both couple temperature in DomainIntegral block and compute "
     182             :                "total_deigenstrain_dT using ThermalFractureIntegral material model.");
     183             : 
     184         840 :   if ((!_functionally_graded_youngs_modulus &&
     185         840 :        _functionally_graded_youngs_modulus_crack_dir_gradient) ||
     186          42 :       (_functionally_graded_youngs_modulus &&
     187          42 :        !_functionally_graded_youngs_modulus_crack_dir_gradient))
     188           0 :     paramError("functionally_graded_youngs_modulus_crack_dir_gradient",
     189             :                "You have selected to compute the interaction integral for a crack in FGM. That "
     190             :                "selection requires the user to provide a spatially varying elasticity modulus "
     191             :                "that "
     192             :                "defines the transition of material properties (i.e. "
     193             :                "'functionally_graded_youngs_modulus') and its "
     194             :                "spatial derivative in the crack direction (i.e. "
     195             :                "'functionally_graded_youngs_modulus_crack_dir_gradient').");
     196             : 
     197             :   // plane strain
     198         840 :   _kappa = 3.0 - 4.0 * _poissons_ratio;
     199         840 :   _shear_modulus = _youngs_modulus / (2.0 * (1.0 + _poissons_ratio));
     200             : 
     201             :   // Checking for consistency between mesh size and length of the provided displacements vector
     202         840 :   if (_ndisp != _mesh.dimension())
     203           0 :     paramError("displacements",
     204             :                "InteractionIntegral Error: number of variables supplied in 'displacements' must "
     205             :                "match the mesh dimension.");
     206             : 
     207             :   // fetch gradients of coupled variables
     208        2898 :   for (std::size_t i = 0; i < _ndisp; ++i)
     209        2058 :     _grad_disp[i] = &coupledGradient("displacements", i);
     210             : 
     211             :   // set unused dimensions to zero
     212        1302 :   for (std::size_t i = _ndisp; i < 3; ++i)
     213         462 :     _grad_disp[i] = &_grad_zero;
     214             : 
     215        1680 :   if (isParamValid("eigenstrain_gradient"))
     216             :   {
     217          24 :     _eigenstrain_gradient =
     218          24 :         &getGenericMaterialProperty<RankThreeTensor, is_ad>("eigenstrain_gradient");
     219          24 :     if (_total_deigenstrain_dT)
     220           0 :       paramError("eigenstrain_gradient",
     221             :                  "eigenstrain_gradient cannot be specified for materials that provide the "
     222             :                  "total_deigenstrain_dT material property");
     223             :   }
     224        1680 :   if (isParamValid("body_force"))
     225          48 :     _body_force = &getMaterialProperty<RealVectorValue>("body_force");
     226             : 
     227        1704 :   _has_additional_eigenstrain = _additional_eigenstrain_gradient_00 &&
     228         840 :                                 _additional_eigenstrain_gradient_01 &&
     229          24 :                                 _additional_eigenstrain_gradient_11;
     230             : 
     231         840 :   if (_mesh.dimension() == 3 && _has_additional_eigenstrain)
     232           0 :     if (!(_additional_eigenstrain_gradient_22 && _additional_eigenstrain_gradient_12 &&
     233           0 :           _additional_eigenstrain_gradient_02))
     234           0 :       paramError("additional_eigenstrain_gradient_12",
     235             :                  "If additional eigenstrains are provided for the computation of the interaction "
     236             :                  "integral in three dimensions, make sure the six components are provided.");
     237             : 
     238         840 :   if (_has_additional_eigenstrain)
     239          24 :     mooseInfo("A generic eigenstrain provided by the user will be considered in the interaction "
     240             :               "integral (via domain integral action).");
     241         840 : }
     242             : 
     243             : template <bool is_ad>
     244             : void
     245         840 : InteractionIntegralTempl<is_ad>::initialSetup()
     246             : {
     247         840 :   _treat_as_2d = _crack_front_definition->treatAs2D();
     248         840 :   _using_mesh_cutter = _crack_front_definition->usingMeshCutter();
     249         840 : }
     250             : 
     251             : template <bool is_ad>
     252             : void
     253         840 : InteractionIntegralTempl<is_ad>::initialize()
     254             : {
     255             :   std::size_t num_pts;
     256         840 :   if (_treat_as_2d && _using_mesh_cutter == false)
     257             :     num_pts = 1;
     258             :   else
     259         258 :     num_pts = _crack_front_definition->getNumCrackFrontPoints();
     260             : 
     261         840 :   _x.assign(num_pts, 0.0);
     262         840 :   _y.assign(num_pts, 0.0);
     263         840 :   _z.assign(num_pts, 0.0);
     264         840 :   _position.assign(num_pts, 0.0);
     265         840 :   _interaction_integral.assign(num_pts, 0.0);
     266             : 
     267        3042 :   for (const auto * fe_var : _fe_vars)
     268             :   {
     269        2202 :     if (fe_var->feType() != _fe_type)
     270           0 :       mooseError("All coupled displacements must have the same type");
     271             :   }
     272         840 : }
     273             : 
     274             : template <bool is_ad>
     275             : Real
     276     1717168 : InteractionIntegralTempl<is_ad>::computeQpIntegral(const std::size_t crack_front_point_index,
     277             :                                                    const Real scalar_q,
     278             :                                                    const RealVectorValue & grad_of_scalar_q)
     279             : {
     280             :   // If q is zero, then dq is also zero, so all terms in the interaction integral would
     281             :   // return zero. As such, let us avoid unnecessary, frequent computations
     282     1717168 :   if (scalar_q < TOLERANCE * TOLERANCE * TOLERANCE)
     283             :     return 0.0;
     284             : 
     285             :   // In the crack front coordinate system, the crack direction is (1,0,0)
     286             :   RealVectorValue crack_direction(1.0, 0.0, 0.0);
     287             : 
     288             :   // Calculate (r,theta) position of qp relative to crack front
     289       97904 :   _crack_front_definition->calculateRThetaToCrackFront(
     290       97904 :       _q_point[_qp], crack_front_point_index, _r, _theta);
     291             : 
     292       97904 :   RankTwoTensor aux_stress;
     293       97904 :   RankTwoTensor aux_du;
     294       97904 :   RankTwoTensor aux_strain;
     295       97904 :   RankTwoTensor aux_disp;
     296             : 
     297       97904 :   if (_sif_mode == SifMethod::KI || _sif_mode == SifMethod::KII || _sif_mode == SifMethod::KIII)
     298       88352 :     computeAuxFields(aux_stress, aux_du, aux_strain, aux_disp);
     299        9552 :   else if (_sif_mode == SifMethod::T)
     300        9552 :     computeTFields(aux_stress, aux_du);
     301             : 
     302       97904 :   auto grad_disp = RankTwoTensor::initializeFromRows(
     303             :       (*_grad_disp[0])[_qp], (*_grad_disp[1])[_qp], (*_grad_disp[2])[_qp]);
     304             : 
     305             :   // Rotate stress, strain, displacement and temperature to crack front coordinate system
     306             :   RealVectorValue grad_q_cf =
     307       97904 :       _crack_front_definition->rotateToCrackFrontCoords(grad_of_scalar_q, crack_front_point_index);
     308      195808 :   RankTwoTensor grad_disp_cf =
     309       97904 :       _crack_front_definition->rotateToCrackFrontCoords(grad_disp, crack_front_point_index);
     310       97904 :   RankTwoTensor stress_cf = _crack_front_definition->rotateToCrackFrontCoords(
     311       97904 :       MetaPhysicL::raw_value((_stress)[_qp]), crack_front_point_index);
     312       97904 :   RankTwoTensor strain_cf = _crack_front_definition->rotateToCrackFrontCoords(
     313       97904 :       MetaPhysicL::raw_value((_strain)[_qp]), crack_front_point_index);
     314             :   RealVectorValue grad_temp_cf =
     315       97904 :       _crack_front_definition->rotateToCrackFrontCoords(_grad_temp[_qp], crack_front_point_index);
     316             : 
     317       97904 :   if (getBlockCoordSystem() == Moose::COORD_RZ)
     318        1008 :     strain_cf(2, 2) = 0.0;
     319             : 
     320       97904 :   RankTwoTensor dq;
     321       97904 :   dq(0, 0) = crack_direction(0) * grad_q_cf(0);
     322       97904 :   dq(0, 1) = crack_direction(0) * grad_q_cf(1);
     323       97904 :   dq(0, 2) = crack_direction(0) * grad_q_cf(2);
     324             : 
     325             :   // Calculate interaction integral terms
     326             : 
     327             :   // Term1 = stress * x1-derivative of aux disp * dq
     328       97904 :   RankTwoTensor tmp1 = dq * stress_cf;
     329       97904 :   Real term1 = aux_du.doubleContraction(tmp1);
     330             : 
     331             :   // Term2 = aux stress * x1-derivative of disp * dq
     332       97904 :   RankTwoTensor tmp2 = dq * aux_stress;
     333       97904 :   Real term2 = grad_disp_cf(0, 0) * tmp2(0, 0) + grad_disp_cf(1, 0) * tmp2(0, 1) +
     334       97904 :                grad_disp_cf(2, 0) * tmp2(0, 2);
     335             : 
     336             :   // Term3 = aux stress * strain * dq_x   (= stress * aux strain * dq_x)
     337       97904 :   Real term3 = -dq(0, 0) * aux_stress.doubleContraction(strain_cf);
     338             : 
     339             :   // Term4 (thermal strain term) = q * aux_stress * alpha * dtheta_x
     340             :   // - the term including the derivative of alpha is not implemented
     341             :   Real term4 = 0.0;
     342       97904 :   if (_has_temp)
     343             :   {
     344             :     Real sigma_alpha =
     345        1536 :         aux_stress.doubleContraction(MetaPhysicL::raw_value((*_total_deigenstrain_dT)[_qp]));
     346        1536 :     term4 = scalar_q * sigma_alpha * grad_temp_cf(0);
     347             :   }
     348             : 
     349             :   Real term4a = 0.0; // Gradient of arbitrary eigenstrain material contribution
     350       97904 :   if (_eigenstrain_gradient)
     351             :   {
     352             :     // Thermal strain gradient term in Nakamura and Parks, IJSS, 1992:
     353             :     // alpha * dT/dx_k*aux_stress*scalar_q
     354             :     // Generalization to the gradient of an arbitrary eigenstrain:
     355             :     // d_eigenstrain/dx_k*aux_stress*scalar_q
     356             :     const RealVectorValue & crack_dir =
     357         768 :         _crack_front_definition->getCrackDirection(crack_front_point_index);
     358         768 :     RankTwoTensor eigenstrain_grad_in_crack_dir =
     359         768 :         crack_dir * MetaPhysicL::raw_value((*_eigenstrain_gradient)[_qp]);
     360         768 :     term4a = scalar_q * aux_stress.doubleContraction(eigenstrain_grad_in_crack_dir);
     361             :   }
     362             : 
     363             :   // Gradient of arbitrary eigenstrain variable contribution.
     364             :   // Note that, unlike approaches 4 and 4a, approach 4b can integrate any type of eigenstrain, i.e.
     365             :   // it does not have to be thermal or volumetric.
     366             :   Real term4b = 0.0;
     367             :   // Term4b (eigen strain term) = q * aux_stress * d{eigen_strain}_x
     368       97904 :   if (_has_additional_eigenstrain)
     369             :   {
     370         768 :     RankTwoTensor I_x(RankTwoTensor::initNone);
     371         768 :     I_x(0, 0) = 1.0;
     372         768 :     RankTwoTensor I_xy(RankTwoTensor::initNone);
     373         768 :     I_xy(0, 1) = 1.0;
     374         768 :     I_xy(1, 0) = 1.0;
     375         768 :     RankTwoTensor I_y(RankTwoTensor::initNone);
     376         768 :     I_y(1, 1) = 1.0;
     377         768 :     RankTwoTensor I_z(RankTwoTensor::initNone);
     378         768 :     I_z(2, 2) = 1.0;
     379             : 
     380             :     const RealVectorValue & crack_dir =
     381         768 :         _crack_front_definition->getCrackDirection(crack_front_point_index);
     382             : 
     383             :     const auto eigenstrain_gradient_x =
     384         768 :         I_x.mixedProductJkI((*_additional_eigenstrain_gradient_00)[_qp]);
     385             :     const auto eigenstrain_gradient_xy =
     386         768 :         I_xy.mixedProductJkI((*_additional_eigenstrain_gradient_01)[_qp]);
     387             :     const auto eigenstrain_gradient_y =
     388         768 :         I_y.mixedProductJkI((*_additional_eigenstrain_gradient_11)[_qp]);
     389             : 
     390         768 :     GenericRankThreeTensor<is_ad> eigenstrain_gradient_z;
     391         768 :     if (_additional_eigenstrain_gradient_22)
     392         768 :       eigenstrain_gradient_z = I_z.mixedProductJkI((*_additional_eigenstrain_gradient_22)[_qp]);
     393             : 
     394         768 :     RankTwoTensor eigenstrain_grad_in_crack_dir =
     395         768 :         crack_dir * (MetaPhysicL::raw_value(eigenstrain_gradient_x) +
     396         768 :                      MetaPhysicL::raw_value(eigenstrain_gradient_xy) +
     397         768 :                      MetaPhysicL::raw_value(eigenstrain_gradient_y) +
     398         768 :                      MetaPhysicL::raw_value(eigenstrain_gradient_z));
     399             : 
     400         768 :     GenericRankThreeTensor<is_ad> eigenstrain_gradient_xz;
     401         768 :     GenericRankThreeTensor<is_ad> eigenstrain_gradient_yz;
     402             :     // Add terms for three dimensions
     403         768 :     if (_mesh.dimension() == 3)
     404             :     {
     405             :       // Add influence of xz and yz.
     406           0 :       RankTwoTensor I_xz(RankTwoTensor::initNone);
     407           0 :       I_xz(0, 2) = I_xz(2, 0) = 1.0;
     408           0 :       eigenstrain_gradient_xz = I_xz.mixedProductJkI((*_additional_eigenstrain_gradient_02)[_qp]);
     409           0 :       RankTwoTensor I_yz(RankTwoTensor::initNone);
     410           0 :       I_yz(1, 2) = I_yz(2, 1) = 1.0;
     411           0 :       eigenstrain_gradient_yz = I_yz.mixedProductJkI((*_additional_eigenstrain_gradient_12)[_qp]);
     412             : 
     413           0 :       eigenstrain_grad_in_crack_dir = crack_dir * (MetaPhysicL::raw_value(eigenstrain_gradient_xz) +
     414           0 :                                                    MetaPhysicL::raw_value(eigenstrain_gradient_yz) +
     415           0 :                                                    MetaPhysicL::raw_value(eigenstrain_gradient_x) +
     416           0 :                                                    MetaPhysicL::raw_value(eigenstrain_gradient_xy) +
     417           0 :                                                    MetaPhysicL::raw_value(eigenstrain_gradient_y) +
     418           0 :                                                    MetaPhysicL::raw_value(eigenstrain_gradient_z));
     419             :     }
     420             : 
     421         768 :     term4b = scalar_q * aux_stress.doubleContraction(eigenstrain_grad_in_crack_dir);
     422             :   }
     423             : 
     424             :   Real term5 = 0.0; // Body force
     425       97904 :   if (_body_force)
     426             :   {
     427             :     // Body force term in Nakamura and Parks, IJSS, 1992:
     428             :     // b_i*aux_du*crack_dir*scalar_q
     429             :     const RealVectorValue & crack_dir =
     430         768 :         _crack_front_definition->getCrackDirection(crack_front_point_index);
     431        1536 :     term5 = -scalar_q * MetaPhysicL::raw_value((*_body_force)[_qp]) * aux_du * crack_dir;
     432             :   }
     433             : 
     434             :   Real term6 = 0.0;
     435       97904 :   if (_fgm_crack && scalar_q != 0)
     436             :   {
     437             :     // See Equation 49 of J.-H. Kim and G. H. Paulino. Consistent formulations of the interaction
     438             :     // integral method for fracture of functionally graded materials. Journal of Applied Mechanics,
     439             :     // 72(3) 351-364 2004.
     440             : 
     441             :     // Term 6_1 = Cijkl,j(x) epsilon_{kl}^{aux} disp_{i,1}
     442        4448 :     RankTwoTensor cijklj_epsilonkl_aux; // Temporary second order tensor.
     443        4448 :     RankFourTensor cijklj;
     444        4448 :     cijklj.fillSymmetricIsotropicEandNu(1.0, _poissons_ratio);
     445        4448 :     cijklj *= (*_functionally_graded_youngs_modulus_crack_dir_gradient)[_qp];
     446        4448 :     cijklj_epsilonkl_aux = cijklj * aux_strain;
     447             : 
     448        4448 :     const Real term6_a = grad_disp_cf(0, 0) * cijklj_epsilonkl_aux(0, 0) +
     449        4448 :                          grad_disp_cf(1, 0) * cijklj_epsilonkl_aux(0, 1) +
     450        4448 :                          grad_disp_cf(2, 0) * cijklj_epsilonkl_aux(0, 2);
     451             : 
     452             :     // Term 6_2 = -Cijkl,1(x) epsilon_{kl} epsilon_{ij}^{aux}
     453        4448 :     RankTwoTensor cijkl1_epsilonkl;
     454        4448 :     cijkl1_epsilonkl = -cijklj * strain_cf;
     455        4448 :     const Real term6_b = cijkl1_epsilonkl.doubleContraction(aux_strain);
     456             : 
     457        4448 :     term6 = (term6_a + term6_b) * scalar_q;
     458             :   }
     459             : 
     460             :   // Add terms for possibly RZ (axisymmetric problem).
     461             :   // See Nahta and Moran, 1993, Domain integrals for axisymmetric interface crack problems, 30(15)
     462             :   // Note: Sign problem in Eq. (26). Also Kolosov constant is wrong in Eq. (13)
     463             :   Real term7 = 0.0;
     464       97904 :   if (getBlockCoordSystem() == Moose::COORD_RZ)
     465             :   {
     466        1008 :     const Real u_r = (*_disp[0])[_qp];
     467        1008 :     const Real radius_qp = _q_point[_qp](0);
     468             : 
     469        1008 :     const Real term7a =
     470        2016 :         (u_r / radius_qp * aux_stress(2, 2) + aux_disp(0, 0) / radius_qp * stress_cf(2, 2) -
     471        1008 :          aux_stress.doubleContraction(strain_cf)) /
     472             :         radius_qp;
     473             : 
     474             :     // term7b1: sigma_aux_ralpha * u_alpha,r
     475        1008 :     const Real term7b1 = aux_stress(0, 0) * grad_disp_cf(0, 0) +
     476        1008 :                          aux_stress(0, 1) * grad_disp_cf(1, 0) +
     477        1008 :                          aux_stress(0, 2) * grad_disp_cf(2, 0);
     478             : 
     479        1008 :     const Real term7b = 1.0 / radius_qp *
     480        1008 :                         (term7b1 - aux_stress(2, 2) * grad_disp_cf(0, 0) +
     481        1008 :                          stress_cf(2, 2) * (aux_du(0, 0) - aux_disp(0, 0) / radius_qp));
     482             : 
     483        1008 :     term7 = (term7a + term7b) * scalar_q;
     484             :   }
     485             : 
     486             :   Real q_avg_seg = 1.0;
     487       97904 :   if (!_crack_front_definition->treatAs2D())
     488             :   {
     489       52416 :     q_avg_seg =
     490      104832 :         (_crack_front_definition->getCrackFrontForwardSegmentLength(crack_front_point_index) +
     491       52416 :          _crack_front_definition->getCrackFrontBackwardSegmentLength(crack_front_point_index)) /
     492             :         2.0;
     493             :   }
     494             : 
     495       97904 :   Real eq = term1 + term2 + term3 + term4 + term4a + term4b + term5 + term6 + term7;
     496             : 
     497       97904 :   if (getBlockCoordSystem() == Moose::COORD_RZ)
     498             :   {
     499        1008 :     const Real radius_qp = _q_point[_qp](0);
     500        1008 :     eq *= radius_qp;
     501             : 
     502        1008 :     std::size_t num_crack_front_points = _crack_front_definition->getNumCrackFrontPoints();
     503        1008 :     if (num_crack_front_points != 1)
     504           0 :       mooseError("Crack front has more than one point, but this is a 2D-RZ problem. Please revise "
     505             :                  "your input file.");
     506             : 
     507        1008 :     const Point * crack_front_point = _crack_front_definition->getCrackFrontPoint(0);
     508        1008 :     eq = eq / (*crack_front_point)(0);
     509             :   }
     510             : 
     511       97904 :   return eq / q_avg_seg;
     512             : }
     513             : 
     514             : template <bool is_ad>
     515             : void
     516      149248 : InteractionIntegralTempl<is_ad>::execute()
     517             : {
     518             :   // calculate phi and dphi for this element
     519      149248 :   const std::size_t dim = _current_elem->dim();
     520      149248 :   std::unique_ptr<FEBase> fe(FEBase::build(dim, _fe_type));
     521      149248 :   fe->attach_quadrature_rule(const_cast<QBase *>(_qrule));
     522      149248 :   _phi_curr_elem = &fe->get_phi();
     523      149248 :   _dphi_curr_elem = &fe->get_dphi();
     524      149248 :   fe->reinit(_current_elem);
     525             : 
     526             :   // calculate q for all nodes in this element
     527      149248 :   std::size_t ring_base = (_q_function_type == QMethod::Topology) ? 0 : 1;
     528             : 
     529      423224 :   for (auto icfp = beginIndex(_interaction_integral); icfp < _interaction_integral.size(); icfp++)
     530             :   {
     531      273976 :     _q_curr_elem.clear();
     532     1991144 :     for (std::size_t i = 0; i < _current_elem->n_nodes(); ++i)
     533             :     {
     534     1717168 :       const Node * this_node = _current_elem->node_ptr(i);
     535             :       Real q_this_node;
     536             : 
     537     1717168 :       if (_q_function_type == QMethod::Geometry)
     538     1717168 :         q_this_node = _crack_front_definition->DomainIntegralQFunction(
     539     1717168 :             icfp, _ring_index - ring_base, this_node);
     540             :       else // QMethod::Topology
     541           0 :         q_this_node = _crack_front_definition->DomainIntegralTopologicalQFunction(
     542           0 :             icfp, _ring_index - ring_base, this_node);
     543             : 
     544     1717168 :       _q_curr_elem.push_back(q_this_node);
     545             :     }
     546             : 
     547     1991144 :     for (_qp = 0; _qp < _qrule->n_points(); _qp++)
     548             :     {
     549             :       Real scalar_q = 0.0;
     550             :       RealVectorValue grad_of_scalar_q(0.0, 0.0, 0.0);
     551             : 
     552    13555952 :       for (std::size_t i = 0; i < _current_elem->n_nodes(); ++i)
     553             :       {
     554    11838784 :         scalar_q += (*_phi_curr_elem)[i][_qp] * _q_curr_elem[i];
     555             : 
     556    45456576 :         for (std::size_t j = 0; j < _current_elem->dim(); ++j)
     557    33617792 :           grad_of_scalar_q(j) += (*_dphi_curr_elem)[i][_qp](j) * _q_curr_elem[i];
     558             :       }
     559             : 
     560     1717168 :       if (getBlockCoordSystem() != Moose::COORD_RZ)
     561     1607008 :         _interaction_integral[icfp] +=
     562     1607008 :             _JxW[_qp] * _coord[_qp] * computeQpIntegral(icfp, scalar_q, grad_of_scalar_q);
     563             :       else
     564      110160 :         _interaction_integral[icfp] +=
     565      110160 :             _JxW[_qp] * computeQpIntegral(icfp, scalar_q, grad_of_scalar_q);
     566             :     }
     567             :   }
     568      149248 : }
     569             : 
     570             : template <bool is_ad>
     571             : void
     572         840 : InteractionIntegralTempl<is_ad>::finalize()
     573             : {
     574         840 :   gatherSum(_interaction_integral);
     575             : 
     576        2484 :   for (auto i = beginIndex(_interaction_integral); i < _interaction_integral.size(); ++i)
     577             :   {
     578        1644 :     if (_has_symmetry_plane)
     579         780 :       _interaction_integral[i] *= 2.0;
     580             : 
     581        1644 :     const auto cfp = _crack_front_definition->getCrackFrontPoint(i);
     582        1644 :     _x[i] = (*cfp)(0);
     583        1644 :     _y[i] = (*cfp)(1);
     584        1644 :     _z[i] = (*cfp)(2);
     585             : 
     586        1644 :     if (_position_type == PositionType::Angle)
     587           0 :       _position[i] = _crack_front_definition->getAngleAlongFront(i);
     588             :     else
     589        1644 :       _position[i] = _crack_front_definition->getDistanceAlongFront(i);
     590             : 
     591        1644 :     if (_sif_mode == SifMethod::T && !_treat_as_2d)
     592         252 :       _interaction_integral[i] +=
     593         252 :           _poissons_ratio * _crack_front_definition->getCrackFrontTangentialStrain(i);
     594             : 
     595        1644 :     _interaction_integral[i] *= _K_factor;
     596             :   }
     597         840 : }
     598             : 
     599             : template <bool is_ad>
     600             : void
     601           0 : InteractionIntegralTempl<is_ad>::threadJoin(const UserObject & y)
     602             : {
     603             :   const auto & uo = static_cast<const InteractionIntegralTempl<is_ad> &>(y);
     604             : 
     605           0 :   for (auto i = beginIndex(_interaction_integral); i < _interaction_integral.size(); ++i)
     606           0 :     _interaction_integral[i] += uo._interaction_integral[i];
     607           0 : }
     608             : 
     609             : template <bool is_ad>
     610             : void
     611       88352 : InteractionIntegralTempl<is_ad>::computeAuxFields(RankTwoTensor & aux_stress,
     612             :                                                   RankTwoTensor & grad_disp,
     613             :                                                   RankTwoTensor & aux_strain,
     614             :                                                   RankTwoTensor & aux_disp)
     615             : {
     616             :   RealVectorValue k(0.0);
     617       88352 :   if (_sif_mode == SifMethod::KI)
     618             :     k(0) = 1.0;
     619       45824 :   else if (_sif_mode == SifMethod::KII)
     620             :     k(1) = 1.0;
     621       22912 :   else if (_sif_mode == SifMethod::KIII)
     622             :     k(2) = 1.0;
     623             : 
     624       88352 :   Real t = _theta;
     625       88352 :   Real t2 = _theta / 2.0;
     626       88352 :   Real tt2 = 3.0 * _theta / 2.0;
     627       88352 :   Real st = std::sin(t);
     628       88352 :   Real ct = std::cos(t);
     629       88352 :   Real st2 = std::sin(t2);
     630       88352 :   Real ct2 = std::cos(t2);
     631       88352 :   Real stt2 = std::sin(tt2);
     632       88352 :   Real ctt2 = std::cos(tt2);
     633             :   Real ct2sq = Utility::pow<2>(ct2);
     634             :   Real st2sq = Utility::pow<2>(st2);
     635             :   Real ct2cu = Utility::pow<3>(ct2);
     636       88352 :   Real sqrt2PiR = std::sqrt(2.0 * libMesh::pi * _r);
     637             : 
     638             :   // Calculate auxiliary stress tensor
     639             :   aux_stress.zero();
     640             : 
     641       88352 :   aux_stress(0, 0) =
     642       88352 :       1.0 / sqrt2PiR * (k(0) * ct2 * (1.0 - st2 * stt2) - k(1) * st2 * (2.0 + ct2 * ctt2));
     643       88352 :   aux_stress(1, 1) = 1.0 / sqrt2PiR * (k(0) * ct2 * (1.0 + st2 * stt2) + k(1) * st2 * ct2 * ctt2);
     644       88352 :   aux_stress(0, 1) = 1.0 / sqrt2PiR * (k(0) * ct2 * st2 * ctt2 + k(1) * ct2 * (1.0 - st2 * stt2));
     645       88352 :   aux_stress(0, 2) = -1.0 / sqrt2PiR * k(2) * st2;
     646       88352 :   aux_stress(1, 2) = 1.0 / sqrt2PiR * k(2) * ct2;
     647             :   // plane stress
     648             :   // Real s33 = 0;
     649             :   // plane strain
     650       88352 :   aux_stress(2, 2) = _poissons_ratio * (aux_stress(0, 0) + aux_stress(1, 1));
     651             : 
     652       88352 :   aux_stress(1, 0) = aux_stress(0, 1);
     653       88352 :   aux_stress(2, 0) = aux_stress(0, 2);
     654       88352 :   aux_stress(2, 1) = aux_stress(1, 2);
     655             : 
     656             :   // Calculate auxiliary strain tensor (only needed to FGM)
     657       88352 :   if (_fgm_crack)
     658             :   {
     659             :     aux_strain.zero();
     660        4448 :     RankFourTensor spatial_elasticity_tensor;
     661        4448 :     spatial_elasticity_tensor.fillSymmetricIsotropicEandNu(
     662        4448 :         (*_functionally_graded_youngs_modulus)[_qp], _poissons_ratio);
     663        4448 :     aux_strain = spatial_elasticity_tensor.invSymm() * aux_stress;
     664             :   }
     665             : 
     666             :   // Calculate x1 derivative of auxiliary displacements
     667             :   grad_disp.zero();
     668       88352 :   grad_disp(0, 0) = k(0) / (4.0 * _shear_modulus * sqrt2PiR) *
     669       88352 :                         (ct * ct2 * _kappa + ct * ct2 - 2.0 * ct * ct2cu + st * st2 * _kappa +
     670       88352 :                          st * st2 - 6.0 * st * st2 * ct2sq) +
     671       88352 :                     k(1) / (4.0 * _shear_modulus * sqrt2PiR) *
     672       88352 :                         (ct * st2 * _kappa + ct * st2 + 2.0 * ct * st2 * ct2sq - st * ct2 * _kappa +
     673       88352 :                          3.0 * st * ct2 - 6.0 * st * ct2cu);
     674             : 
     675       88352 :   grad_disp(0, 1) = k(0) / (4.0 * _shear_modulus * sqrt2PiR) *
     676       88352 :                         (ct * st2 * _kappa + ct * st2 - 2.0 * ct * st2 * ct2sq - st * ct2 * _kappa -
     677       88352 :                          5.0 * st * ct2 + 6.0 * st * ct2cu) +
     678       88352 :                     k(1) / (4.0 * _shear_modulus * sqrt2PiR) *
     679       88352 :                         (-ct * ct2 * _kappa + 3.0 * ct * ct2 - 2.0 * ct * ct2cu -
     680       88352 :                          st * st2 * _kappa + 3.0 * st * st2 - 6.0 * st * st2 * ct2sq);
     681             : 
     682       88352 :   grad_disp(0, 2) = k(2) / (_shear_modulus * sqrt2PiR) * (st2 * ct - ct2 * st);
     683             : 
     684             :   // Calculate aux displacement field (used in RZ calculations) - Warp3D manual
     685             :   aux_disp.zero();
     686       88352 :   if (getBlockCoordSystem() == Moose::COORD_RZ)
     687             :   {
     688        1008 :     const Real prefactor_rz = std::sqrt(_r / 2.0 / libMesh::pi) / 2.0 / _shear_modulus;
     689        1008 :     aux_disp(0, 0) = prefactor_rz * ((k(0) * ct2 * (_kappa - 1.0 + 2.0 * st2sq)) +
     690        1008 :                                      (k(1) * st2 * (_kappa + 1.0 + 2.0 * ct2sq)));
     691        1008 :     aux_disp(0, 1) = prefactor_rz * ((k(0) * st2 * (_kappa + 1.0 - 2.0 * ct2sq)) -
     692        1008 :                                      (k(1) * ct2 * (_kappa - 1.0 - 2.0 * st2sq)));
     693        1008 :     aux_disp(0, 2) = prefactor_rz * k(2) * 4.0 * st2;
     694             :   }
     695       88352 : }
     696             : 
     697             : template <bool is_ad>
     698             : void
     699        9552 : InteractionIntegralTempl<is_ad>::computeTFields(RankTwoTensor & aux_stress,
     700             :                                                 RankTwoTensor & grad_disp)
     701             : {
     702             : 
     703        9552 :   Real t = _theta;
     704        9552 :   Real st = std::sin(t);
     705        9552 :   Real ct = std::cos(t);
     706             :   Real stsq = Utility::pow<2>(st);
     707             :   Real ctsq = Utility::pow<2>(ct);
     708             :   Real ctcu = Utility::pow<3>(ct);
     709        9552 :   Real oneOverPiR = 1.0 / (libMesh::pi * _r);
     710             : 
     711             :   aux_stress.zero();
     712        9552 :   aux_stress(0, 0) = -oneOverPiR * ctcu;
     713        9552 :   aux_stress(0, 1) = -oneOverPiR * st * ctsq;
     714        9552 :   aux_stress(1, 0) = -oneOverPiR * st * ctsq;
     715        9552 :   aux_stress(1, 1) = -oneOverPiR * ct * stsq;
     716        9552 :   aux_stress(2, 2) = -oneOverPiR * _poissons_ratio * (ctcu + ct * stsq);
     717             : 
     718             :   grad_disp.zero();
     719        9552 :   grad_disp(0, 0) = oneOverPiR / (4.0 * _youngs_modulus) *
     720        9552 :                     (ct * (4.0 * Utility::pow<2>(_poissons_ratio) - 3.0 + _poissons_ratio) -
     721        9552 :                      std::cos(3.0 * t) * (1.0 + _poissons_ratio));
     722        9552 :   grad_disp(0, 1) = -oneOverPiR / (4.0 * _youngs_modulus) *
     723        9552 :                     (st * (4.0 * Utility::pow<2>(_poissons_ratio) - 3.0 + _poissons_ratio) +
     724        9552 :                      std::sin(3.0 * t) * (1.0 + _poissons_ratio));
     725        9552 : }
     726             : 
     727             : template class InteractionIntegralTempl<false>;
     728             : template class InteractionIntegralTempl<true>;

Generated by: LCOV version 1.14