LCOV - code coverage report
Current view: top level - src/vectorpostprocessors - InteractionIntegral.C (source / functions) Hit Total Coverage
Test: idaholab/moose solid_mechanics: #31405 (292dce) with base fef103 Lines: 343 371 92.5 %
Date: 2025-09-04 07:57:23 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        2346 : InteractionIntegralTempl<is_ad>::qFunctionType()
      26             : {
      27        4692 :   return MooseEnum("Geometry Topology", "Geometry");
      28             : }
      29             : 
      30             : template <bool is_ad>
      31             : MooseEnum
      32        2346 : InteractionIntegralTempl<is_ad>::sifModeType()
      33             : {
      34        4692 :   return MooseEnum("KI KII KIII T", "KI");
      35             : }
      36             : 
      37             : template <bool is_ad>
      38             : InputParameters
      39        1173 : InteractionIntegralTempl<is_ad>::validParams()
      40             : {
      41        1173 :   InputParameters params = ElementVectorPostprocessor::validParams();
      42        2346 :   params.addRequiredCoupledVar(
      43             :       "displacements",
      44             :       "The displacements appropriate for the simulation geometry and coordinate system");
      45        2346 :   params.addCoupledVar("temperature",
      46             :                        "The temperature (optional). Must be provided to correctly compute "
      47             :                        "stress intensity factors in models with thermal strain gradients.");
      48        2346 :   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        2346 :   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        2346 :   params.addRequiredParam<UserObjectName>("crack_front_definition",
      57             :                                           "The CrackFrontDefinition user object name");
      58        2346 :   MooseEnum position_type("Angle Distance", "Distance");
      59        2346 :   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        2346 :   params.addRequiredParam<Real>(
      65             :       "K_factor", "Conversion factor between interaction integral and stress intensity factor K");
      66        2346 :   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        2346 :   params.addRequiredParam<Real>("poissons_ratio", "Poisson's ratio for the material.");
      71        2346 :   params.addRequiredParam<Real>("youngs_modulus", "Young's modulus of the material.");
      72        1173 :   params.set<bool>("use_displaced_mesh") = false;
      73        2346 :   params.addRequiredParam<unsigned int>("ring_index", "Ring ID");
      74        3519 :   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        3519 :   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        2346 :   params.addParam<MaterialPropertyName>("eigenstrain_gradient",
      84             :                                         "Material defining gradient of eigenstrain tensor");
      85        2346 :   params.addParam<MaterialPropertyName>("body_force", "Material defining body force");
      86        2346 :   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        2346 :   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        2346 :   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        2346 :   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        2346 :   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        2346 :   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        2346 :   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        1173 :   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        1173 :   return params;
     113        1173 : }
     114             : 
     115             : template <bool is_ad>
     116        1024 : InteractionIntegralTempl<is_ad>::InteractionIntegralTempl(const InputParameters & parameters)
     117             :   : ElementVectorPostprocessor(parameters),
     118        1024 :     _ndisp(coupledComponents("displacements")),
     119        1024 :     _crack_front_definition(&getUserObject<CrackFrontDefinition>("crack_front_definition")),
     120        1024 :     _treat_as_2d(false),
     121        2048 :     _stress(getGenericMaterialPropertyByName<RankTwoTensor, is_ad>("stress")),
     122        1024 :     _strain(getGenericMaterialPropertyByName<RankTwoTensor, is_ad>("elastic_strain")),
     123        1024 :     _fe_vars(getCoupledMooseVars()),
     124        1024 :     _fe_type(_fe_vars[0]->feType()),
     125        1024 :     _disp(coupledValues("displacements")),
     126        1024 :     _grad_disp(3),
     127        1024 :     _has_temp(isCoupled("temperature")),
     128        1024 :     _grad_temp(_has_temp ? coupledGradient("temperature") : _grad_zero),
     129        1024 :     _functionally_graded_youngs_modulus_crack_dir_gradient(
     130        1024 :         isParamSetByUser("functionally_graded_youngs_modulus_crack_dir_gradient")
     131        1122 :             ? &getMaterialProperty<Real>("functionally_graded_youngs_modulus_crack_dir_gradient")
     132             :             : nullptr),
     133        1024 :     _functionally_graded_youngs_modulus(
     134        1024 :         isParamSetByUser("functionally_graded_youngs_modulus")
     135        1122 :             ? &getMaterialProperty<Real>("functionally_graded_youngs_modulus")
     136             :             : nullptr),
     137        2048 :     _K_factor(getParam<Real>("K_factor")),
     138        2048 :     _has_symmetry_plane(isParamValid("symmetry_plane")),
     139        2048 :     _poissons_ratio(getParam<Real>("poissons_ratio")),
     140        2048 :     _youngs_modulus(getParam<Real>("youngs_modulus")),
     141        2048 :     _fgm_crack(isParamSetByUser("functionally_graded_youngs_modulus_crack_dir_gradient") &&
     142        1122 :                isParamSetByUser("functionally_graded_youngs_modulus")),
     143        2048 :     _ring_index(getParam<unsigned int>("ring_index")),
     144        1024 :     _total_deigenstrain_dT(
     145        1024 :         hasMaterialProperty<RankTwoTensor>("total_deigenstrain_dT")
     146        1136 :             ? &getGenericMaterialProperty<RankTwoTensor, is_ad>("total_deigenstrain_dT")
     147             :             : nullptr),
     148        1024 :     _has_additional_eigenstrain(false),
     149        1024 :     _additional_eigenstrain_gradient_00(isCoupled("additional_eigenstrain_00")
     150        1052 :                                             ? &coupledGradient("additional_eigenstrain_00")
     151             :                                             : nullptr),
     152        1024 :     _additional_eigenstrain_gradient_01(isCoupled("additional_eigenstrain_01")
     153        1052 :                                             ? &coupledGradient("additional_eigenstrain_01")
     154             :                                             : nullptr),
     155        1024 :     _additional_eigenstrain_gradient_11(isCoupled("additional_eigenstrain_11")
     156        1052 :                                             ? &coupledGradient("additional_eigenstrain_11")
     157             :                                             : nullptr),
     158        1024 :     _additional_eigenstrain_gradient_22(isCoupled("additional_eigenstrain_22")
     159        1052 :                                             ? &coupledGradient("additional_eigenstrain_22")
     160             :                                             : nullptr),
     161        1024 :     _additional_eigenstrain_gradient_02(isCoupled("additional_eigenstrain_02")
     162        1024 :                                             ? &coupledGradient("additional_eigenstrain_02")
     163             :                                             : nullptr),
     164        2048 :     _additional_eigenstrain_gradient_12(isCoupled("additional_eigenstrain_12")
     165        1024 :                                             ? &coupledGradient("additional_eigenstrain_12")
     166             :                                             : nullptr),
     167        2048 :     _q_function_type(getParam<MooseEnum>("q_function_type").template getEnum<QMethod>()),
     168        2048 :     _position_type(getParam<MooseEnum>("position_type").template getEnum<PositionType>()),
     169        2048 :     _sif_mode(getParam<MooseEnum>("sif_mode").template getEnum<SifMethod>()),
     170        1024 :     _x(declareVector("x")),
     171        1024 :     _y(declareVector("y")),
     172        1024 :     _z(declareVector("z")),
     173        1024 :     _position(declareVector("id")),
     174        4096 :     _interaction_integral(declareVector("II_" + Moose::stringify(getParam<MooseEnum>("sif_mode")) +
     175             :                                         "_" + Moose::stringify(_ring_index))),
     176        1024 :     _eigenstrain_gradient(nullptr),
     177        1024 :     _body_force(nullptr)
     178             : {
     179        1024 :   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        1024 :   if ((!_functionally_graded_youngs_modulus &&
     185        1024 :        _functionally_graded_youngs_modulus_crack_dir_gradient) ||
     186          49 :       (_functionally_graded_youngs_modulus &&
     187          49 :        !_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        1024 :   _kappa = 3.0 - 4.0 * _poissons_ratio;
     199        1024 :   _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        1024 :   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        3537 :   for (std::size_t i = 0; i < _ndisp; ++i)
     209        2513 :     _grad_disp[i] = &coupledGradient("displacements", i);
     210             : 
     211             :   // set unused dimensions to zero
     212        1583 :   for (std::size_t i = _ndisp; i < 3; ++i)
     213         559 :     _grad_disp[i] = &_grad_zero;
     214             : 
     215        2048 :   if (isParamValid("eigenstrain_gradient"))
     216             :   {
     217          28 :     _eigenstrain_gradient =
     218          28 :         &getGenericMaterialProperty<RankThreeTensor, is_ad>("eigenstrain_gradient");
     219          28 :     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        2048 :   if (isParamValid("body_force"))
     225          56 :     _body_force = &getMaterialProperty<RealVectorValue>("body_force");
     226             : 
     227        2076 :   _has_additional_eigenstrain = _additional_eigenstrain_gradient_00 &&
     228        1024 :                                 _additional_eigenstrain_gradient_01 &&
     229          28 :                                 _additional_eigenstrain_gradient_11;
     230             : 
     231        1024 :   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        1024 :   if (_has_additional_eigenstrain)
     239          28 :     mooseInfo("A generic eigenstrain provided by the user will be considered in the interaction "
     240             :               "integral (via domain integral action).");
     241        1024 : }
     242             : 
     243             : template <bool is_ad>
     244             : void
     245        1024 : InteractionIntegralTempl<is_ad>::initialSetup()
     246             : {
     247        1024 :   _treat_as_2d = _crack_front_definition->treatAs2D();
     248        1024 :   _using_mesh_cutter = _crack_front_definition->usingMeshCutter();
     249        1024 : }
     250             : 
     251             : template <bool is_ad>
     252             : void
     253        1208 : InteractionIntegralTempl<is_ad>::initialize()
     254             : {
     255             :   std::size_t num_pts;
     256        1208 :   if (_treat_as_2d && _using_mesh_cutter == false)
     257             :     num_pts = 1;
     258             :   else
     259         372 :     num_pts = _crack_front_definition->getNumCrackFrontPoints();
     260             : 
     261        1208 :   _x.assign(num_pts, 0.0);
     262        1208 :   _y.assign(num_pts, 0.0);
     263        1208 :   _z.assign(num_pts, 0.0);
     264        1208 :   _position.assign(num_pts, 0.0);
     265        1208 :   _interaction_integral.assign(num_pts, 0.0);
     266             : 
     267        4368 :   for (const auto * fe_var : _fe_vars)
     268             :   {
     269        3160 :     if (fe_var->feType() != _fe_type)
     270           0 :       mooseError("All coupled displacements must have the same type");
     271             :   }
     272        1208 : }
     273             : 
     274             : template <bool is_ad>
     275             : Real
     276     2575752 : 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     2575752 :   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      146856 :   _crack_front_definition->calculateRThetaToCrackFront(
     290      146856 :       _q_point[_qp], crack_front_point_index, _r, _theta);
     291             : 
     292      146856 :   RankTwoTensor aux_stress;
     293      146856 :   RankTwoTensor aux_du;
     294      146856 :   RankTwoTensor aux_strain;
     295      146856 :   RankTwoTensor aux_disp;
     296             : 
     297      146856 :   if (_sif_mode == SifMethod::KI || _sif_mode == SifMethod::KII || _sif_mode == SifMethod::KIII)
     298      132528 :     computeAuxFields(aux_stress, aux_du, aux_strain, aux_disp);
     299       14328 :   else if (_sif_mode == SifMethod::T)
     300       14328 :     computeTFields(aux_stress, aux_du);
     301             : 
     302      146856 :   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      146856 :       _crack_front_definition->rotateToCrackFrontCoords(grad_of_scalar_q, crack_front_point_index);
     308      293712 :   RankTwoTensor grad_disp_cf =
     309      146856 :       _crack_front_definition->rotateToCrackFrontCoords(grad_disp, crack_front_point_index);
     310      146856 :   RankTwoTensor stress_cf = _crack_front_definition->rotateToCrackFrontCoords(
     311      146856 :       MetaPhysicL::raw_value((_stress)[_qp]), crack_front_point_index);
     312      146856 :   RankTwoTensor strain_cf = _crack_front_definition->rotateToCrackFrontCoords(
     313      146856 :       MetaPhysicL::raw_value((_strain)[_qp]), crack_front_point_index);
     314             :   RealVectorValue grad_temp_cf =
     315      146856 :       _crack_front_definition->rotateToCrackFrontCoords(_grad_temp[_qp], crack_front_point_index);
     316             : 
     317      146856 :   if (getBlockCoordSystem() == Moose::COORD_RZ)
     318        1512 :     strain_cf(2, 2) = 0.0;
     319             : 
     320      146856 :   RankTwoTensor dq;
     321      146856 :   dq(0, 0) = crack_direction(0) * grad_q_cf(0);
     322      146856 :   dq(0, 1) = crack_direction(0) * grad_q_cf(1);
     323      146856 :   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      146856 :   RankTwoTensor tmp1 = dq * stress_cf;
     329      146856 :   Real term1 = aux_du.doubleContraction(tmp1);
     330             : 
     331             :   // Term2 = aux stress * x1-derivative of disp * dq
     332      146856 :   RankTwoTensor tmp2 = dq * aux_stress;
     333      146856 :   Real term2 = grad_disp_cf(0, 0) * tmp2(0, 0) + grad_disp_cf(1, 0) * tmp2(0, 1) +
     334      146856 :                grad_disp_cf(2, 0) * tmp2(0, 2);
     335             : 
     336             :   // Term3 = aux stress * strain * dq_x   (= stress * aux strain * dq_x)
     337      146856 :   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      146856 :   if (_has_temp)
     343             :   {
     344             :     Real sigma_alpha =
     345        2304 :         aux_stress.doubleContraction(MetaPhysicL::raw_value((*_total_deigenstrain_dT)[_qp]));
     346        2304 :     term4 = scalar_q * sigma_alpha * grad_temp_cf(0);
     347             :   }
     348             : 
     349             :   Real term4a = 0.0; // Gradient of arbitrary eigenstrain material contribution
     350      146856 :   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        1152 :         _crack_front_definition->getCrackDirection(crack_front_point_index);
     358        1152 :     RankTwoTensor eigenstrain_grad_in_crack_dir =
     359        1152 :         crack_dir * MetaPhysicL::raw_value((*_eigenstrain_gradient)[_qp]);
     360        1152 :     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      146856 :   if (_has_additional_eigenstrain)
     369             :   {
     370        1152 :     RankTwoTensor I_x(RankTwoTensor::initNone);
     371        1152 :     I_x(0, 0) = 1.0;
     372        1152 :     RankTwoTensor I_xy(RankTwoTensor::initNone);
     373        1152 :     I_xy(0, 1) = 1.0;
     374        1152 :     I_xy(1, 0) = 1.0;
     375        1152 :     RankTwoTensor I_y(RankTwoTensor::initNone);
     376        1152 :     I_y(1, 1) = 1.0;
     377        1152 :     RankTwoTensor I_z(RankTwoTensor::initNone);
     378        1152 :     I_z(2, 2) = 1.0;
     379             : 
     380             :     const RealVectorValue & crack_dir =
     381        1152 :         _crack_front_definition->getCrackDirection(crack_front_point_index);
     382             : 
     383             :     const auto eigenstrain_gradient_x =
     384        1152 :         I_x.mixedProductJkI((*_additional_eigenstrain_gradient_00)[_qp]);
     385             :     const auto eigenstrain_gradient_xy =
     386        1152 :         I_xy.mixedProductJkI((*_additional_eigenstrain_gradient_01)[_qp]);
     387             :     const auto eigenstrain_gradient_y =
     388        1152 :         I_y.mixedProductJkI((*_additional_eigenstrain_gradient_11)[_qp]);
     389             : 
     390        1152 :     GenericRankThreeTensor<is_ad> eigenstrain_gradient_z;
     391        1152 :     if (_additional_eigenstrain_gradient_22)
     392        1152 :       eigenstrain_gradient_z = I_z.mixedProductJkI((*_additional_eigenstrain_gradient_22)[_qp]);
     393             : 
     394        1152 :     RankTwoTensor eigenstrain_grad_in_crack_dir =
     395        1152 :         crack_dir * (MetaPhysicL::raw_value(eigenstrain_gradient_x) +
     396        1152 :                      MetaPhysicL::raw_value(eigenstrain_gradient_xy) +
     397        1152 :                      MetaPhysicL::raw_value(eigenstrain_gradient_y) +
     398        1152 :                      MetaPhysicL::raw_value(eigenstrain_gradient_z));
     399             : 
     400        1152 :     GenericRankThreeTensor<is_ad> eigenstrain_gradient_xz;
     401        1152 :     GenericRankThreeTensor<is_ad> eigenstrain_gradient_yz;
     402             :     // Add terms for three dimensions
     403        1152 :     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        1152 :     term4b = scalar_q * aux_stress.doubleContraction(eigenstrain_grad_in_crack_dir);
     422             :   }
     423             : 
     424             :   Real term5 = 0.0; // Body force
     425      146856 :   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        1152 :         _crack_front_definition->getCrackDirection(crack_front_point_index);
     431        2304 :     term5 = -scalar_q * MetaPhysicL::raw_value((*_body_force)[_qp]) * aux_du * crack_dir;
     432             :   }
     433             : 
     434             :   Real term6 = 0.0;
     435      146856 :   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        6672 :     RankTwoTensor cijklj_epsilonkl_aux; // Temporary second order tensor.
     443        6672 :     RankFourTensor cijklj;
     444        6672 :     cijklj.fillSymmetricIsotropicEandNu(1.0, _poissons_ratio);
     445        6672 :     cijklj *= (*_functionally_graded_youngs_modulus_crack_dir_gradient)[_qp];
     446        6672 :     cijklj_epsilonkl_aux = cijklj * aux_strain;
     447             : 
     448        6672 :     const Real term6_a = grad_disp_cf(0, 0) * cijklj_epsilonkl_aux(0, 0) +
     449        6672 :                          grad_disp_cf(1, 0) * cijklj_epsilonkl_aux(0, 1) +
     450        6672 :                          grad_disp_cf(2, 0) * cijklj_epsilonkl_aux(0, 2);
     451             : 
     452             :     // Term 6_2 = -Cijkl,1(x) epsilon_{kl} epsilon_{ij}^{aux}
     453        6672 :     RankTwoTensor cijkl1_epsilonkl;
     454        6672 :     cijkl1_epsilonkl = -cijklj * strain_cf;
     455        6672 :     const Real term6_b = cijkl1_epsilonkl.doubleContraction(aux_strain);
     456             : 
     457        6672 :     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      146856 :   if (getBlockCoordSystem() == Moose::COORD_RZ)
     465             :   {
     466        1512 :     const Real u_r = (*_disp[0])[_qp];
     467        1512 :     const Real radius_qp = _q_point[_qp](0);
     468             : 
     469        1512 :     const Real term7a =
     470        3024 :         (u_r / radius_qp * aux_stress(2, 2) + aux_disp(0, 0) / radius_qp * stress_cf(2, 2) -
     471        1512 :          aux_stress.doubleContraction(strain_cf)) /
     472             :         radius_qp;
     473             : 
     474             :     // term7b1: sigma_aux_ralpha * u_alpha,r
     475        1512 :     const Real term7b1 = aux_stress(0, 0) * grad_disp_cf(0, 0) +
     476        1512 :                          aux_stress(0, 1) * grad_disp_cf(1, 0) +
     477        1512 :                          aux_stress(0, 2) * grad_disp_cf(2, 0);
     478             : 
     479        1512 :     const Real term7b = 1.0 / radius_qp *
     480        1512 :                         (term7b1 - aux_stress(2, 2) * grad_disp_cf(0, 0) +
     481        1512 :                          stress_cf(2, 2) * (aux_du(0, 0) - aux_disp(0, 0) / radius_qp));
     482             : 
     483        1512 :     term7 = (term7a + term7b) * scalar_q;
     484             :   }
     485             : 
     486             :   Real q_avg_seg = 1.0;
     487      146856 :   if (!_crack_front_definition->treatAs2D())
     488             :   {
     489       78624 :     q_avg_seg =
     490      157248 :         (_crack_front_definition->getCrackFrontForwardSegmentLength(crack_front_point_index) +
     491       78624 :          _crack_front_definition->getCrackFrontBackwardSegmentLength(crack_front_point_index)) /
     492             :         2.0;
     493             :   }
     494             : 
     495      146856 :   Real eq = term1 + term2 + term3 + term4 + term4a + term4b + term5 + term6 + term7;
     496             : 
     497      146856 :   if (getBlockCoordSystem() == Moose::COORD_RZ)
     498             :   {
     499        1512 :     const Real radius_qp = _q_point[_qp](0);
     500        1512 :     eq *= radius_qp;
     501             : 
     502        1512 :     std::size_t num_crack_front_points = _crack_front_definition->getNumCrackFrontPoints();
     503        1512 :     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        1512 :     const Point * crack_front_point = _crack_front_definition->getCrackFrontPoint(0);
     508        1512 :     eq = eq / (*crack_front_point)(0);
     509             :   }
     510             : 
     511      146856 :   return eq / q_avg_seg;
     512             : }
     513             : 
     514             : template <bool is_ad>
     515             : void
     516      223872 : InteractionIntegralTempl<is_ad>::execute()
     517             : {
     518             :   // calculate phi and dphi for this element
     519      223872 :   const std::size_t dim = _current_elem->dim();
     520      223872 :   std::unique_ptr<FEBase> fe(FEBase::build(dim, _fe_type));
     521      223872 :   fe->attach_quadrature_rule(const_cast<QBase *>(_qrule));
     522      223872 :   _phi_curr_elem = &fe->get_phi();
     523      223872 :   _dphi_curr_elem = &fe->get_dphi();
     524      223872 :   fe->reinit(_current_elem);
     525             : 
     526             :   // calculate q for all nodes in this element
     527      223872 :   std::size_t ring_base = (_q_function_type == QMethod::Topology) ? 0 : 1;
     528             : 
     529      634836 :   for (auto icfp = beginIndex(_interaction_integral); icfp < _interaction_integral.size(); icfp++)
     530             :   {
     531      410964 :     _q_curr_elem.clear();
     532     2986716 :     for (std::size_t i = 0; i < _current_elem->n_nodes(); ++i)
     533             :     {
     534     2575752 :       const Node * this_node = _current_elem->node_ptr(i);
     535             :       Real q_this_node;
     536             : 
     537     2575752 :       if (_q_function_type == QMethod::Geometry)
     538     2575752 :         q_this_node = _crack_front_definition->DomainIntegralQFunction(
     539     2575752 :             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     2575752 :       _q_curr_elem.push_back(q_this_node);
     545             :     }
     546             : 
     547     2986716 :     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    20333928 :       for (std::size_t i = 0; i < _current_elem->n_nodes(); ++i)
     553             :       {
     554    17758176 :         scalar_q += (*_phi_curr_elem)[i][_qp] * _q_curr_elem[i];
     555             : 
     556    68184864 :         for (std::size_t j = 0; j < _current_elem->dim(); ++j)
     557    50426688 :           grad_of_scalar_q(j) += (*_dphi_curr_elem)[i][_qp](j) * _q_curr_elem[i];
     558             :       }
     559             : 
     560     2575752 :       if (getBlockCoordSystem() != Moose::COORD_RZ)
     561     2410512 :         _interaction_integral[icfp] +=
     562     2410512 :             _JxW[_qp] * _coord[_qp] * computeQpIntegral(icfp, scalar_q, grad_of_scalar_q);
     563             :       else
     564      165240 :         _interaction_integral[icfp] +=
     565      165240 :             _JxW[_qp] * computeQpIntegral(icfp, scalar_q, grad_of_scalar_q);
     566             :     }
     567             :   }
     568      223872 : }
     569             : 
     570             : template <bool is_ad>
     571             : void
     572        1208 : InteractionIntegralTempl<is_ad>::finalize()
     573             : {
     574        1208 :   gatherSum(_interaction_integral);
     575             : 
     576        3544 :   for (auto i = beginIndex(_interaction_integral); i < _interaction_integral.size(); ++i)
     577             :   {
     578        2336 :     if (_has_symmetry_plane)
     579        1040 :       _interaction_integral[i] *= 2.0;
     580             : 
     581        2336 :     const auto cfp = _crack_front_definition->getCrackFrontPoint(i);
     582        2336 :     _x[i] = (*cfp)(0);
     583        2336 :     _y[i] = (*cfp)(1);
     584        2336 :     _z[i] = (*cfp)(2);
     585             : 
     586        2336 :     if (_position_type == PositionType::Angle)
     587           0 :       _position[i] = _crack_front_definition->getAngleAlongFront(i);
     588             :     else
     589        2336 :       _position[i] = _crack_front_definition->getDistanceAlongFront(i);
     590             : 
     591        2336 :     if (_sif_mode == SifMethod::T && !_treat_as_2d)
     592         336 :       _interaction_integral[i] +=
     593         336 :           _poissons_ratio * _crack_front_definition->getCrackFrontTangentialStrain(i);
     594             : 
     595        2336 :     _interaction_integral[i] *= _K_factor;
     596             :   }
     597        1208 : }
     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      132528 : 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      132528 :   if (_sif_mode == SifMethod::KI)
     618             :     k(0) = 1.0;
     619       68736 :   else if (_sif_mode == SifMethod::KII)
     620             :     k(1) = 1.0;
     621       34368 :   else if (_sif_mode == SifMethod::KIII)
     622             :     k(2) = 1.0;
     623             : 
     624      132528 :   Real t = _theta;
     625      132528 :   Real t2 = _theta / 2.0;
     626      132528 :   Real tt2 = 3.0 * _theta / 2.0;
     627      132528 :   Real st = std::sin(t);
     628      132528 :   Real ct = std::cos(t);
     629      132528 :   Real st2 = std::sin(t2);
     630      132528 :   Real ct2 = std::cos(t2);
     631      132528 :   Real stt2 = std::sin(tt2);
     632      132528 :   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      132528 :   Real sqrt2PiR = std::sqrt(2.0 * libMesh::pi * _r);
     637             : 
     638             :   // Calculate auxiliary stress tensor
     639             :   aux_stress.zero();
     640             : 
     641      132528 :   aux_stress(0, 0) =
     642      132528 :       1.0 / sqrt2PiR * (k(0) * ct2 * (1.0 - st2 * stt2) - k(1) * st2 * (2.0 + ct2 * ctt2));
     643      132528 :   aux_stress(1, 1) = 1.0 / sqrt2PiR * (k(0) * ct2 * (1.0 + st2 * stt2) + k(1) * st2 * ct2 * ctt2);
     644      132528 :   aux_stress(0, 1) = 1.0 / sqrt2PiR * (k(0) * ct2 * st2 * ctt2 + k(1) * ct2 * (1.0 - st2 * stt2));
     645      132528 :   aux_stress(0, 2) = -1.0 / sqrt2PiR * k(2) * st2;
     646      132528 :   aux_stress(1, 2) = 1.0 / sqrt2PiR * k(2) * ct2;
     647             :   // plane stress
     648             :   // Real s33 = 0;
     649             :   // plane strain
     650      132528 :   aux_stress(2, 2) = _poissons_ratio * (aux_stress(0, 0) + aux_stress(1, 1));
     651             : 
     652      132528 :   aux_stress(1, 0) = aux_stress(0, 1);
     653      132528 :   aux_stress(2, 0) = aux_stress(0, 2);
     654      132528 :   aux_stress(2, 1) = aux_stress(1, 2);
     655             : 
     656             :   // Calculate auxiliary strain tensor (only needed to FGM)
     657      132528 :   if (_fgm_crack)
     658             :   {
     659             :     aux_strain.zero();
     660        6672 :     RankFourTensor spatial_elasticity_tensor;
     661        6672 :     spatial_elasticity_tensor.fillSymmetricIsotropicEandNu(
     662        6672 :         (*_functionally_graded_youngs_modulus)[_qp], _poissons_ratio);
     663        6672 :     aux_strain = spatial_elasticity_tensor.invSymm() * aux_stress;
     664             :   }
     665             : 
     666             :   // Calculate x1 derivative of auxiliary displacements
     667             :   grad_disp.zero();
     668      132528 :   grad_disp(0, 0) = k(0) / (4.0 * _shear_modulus * sqrt2PiR) *
     669      132528 :                         (ct * ct2 * _kappa + ct * ct2 - 2.0 * ct * ct2cu + st * st2 * _kappa +
     670      132528 :                          st * st2 - 6.0 * st * st2 * ct2sq) +
     671      132528 :                     k(1) / (4.0 * _shear_modulus * sqrt2PiR) *
     672      132528 :                         (ct * st2 * _kappa + ct * st2 + 2.0 * ct * st2 * ct2sq - st * ct2 * _kappa +
     673      132528 :                          3.0 * st * ct2 - 6.0 * st * ct2cu);
     674             : 
     675      132528 :   grad_disp(0, 1) = k(0) / (4.0 * _shear_modulus * sqrt2PiR) *
     676      132528 :                         (ct * st2 * _kappa + ct * st2 - 2.0 * ct * st2 * ct2sq - st * ct2 * _kappa -
     677      132528 :                          5.0 * st * ct2 + 6.0 * st * ct2cu) +
     678      132528 :                     k(1) / (4.0 * _shear_modulus * sqrt2PiR) *
     679      132528 :                         (-ct * ct2 * _kappa + 3.0 * ct * ct2 - 2.0 * ct * ct2cu -
     680      132528 :                          st * st2 * _kappa + 3.0 * st * st2 - 6.0 * st * st2 * ct2sq);
     681             : 
     682      132528 :   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      132528 :   if (getBlockCoordSystem() == Moose::COORD_RZ)
     687             :   {
     688        1512 :     const Real prefactor_rz = std::sqrt(_r / 2.0 / libMesh::pi) / 2.0 / _shear_modulus;
     689        1512 :     aux_disp(0, 0) = prefactor_rz * ((k(0) * ct2 * (_kappa - 1.0 + 2.0 * st2sq)) +
     690        1512 :                                      (k(1) * st2 * (_kappa + 1.0 + 2.0 * ct2sq)));
     691        1512 :     aux_disp(0, 1) = prefactor_rz * ((k(0) * st2 * (_kappa + 1.0 - 2.0 * ct2sq)) -
     692        1512 :                                      (k(1) * ct2 * (_kappa - 1.0 - 2.0 * st2sq)));
     693        1512 :     aux_disp(0, 2) = prefactor_rz * k(2) * 4.0 * st2;
     694             :   }
     695      132528 : }
     696             : 
     697             : template <bool is_ad>
     698             : void
     699       14328 : InteractionIntegralTempl<is_ad>::computeTFields(RankTwoTensor & aux_stress,
     700             :                                                 RankTwoTensor & grad_disp)
     701             : {
     702             : 
     703       14328 :   Real t = _theta;
     704       14328 :   Real st = std::sin(t);
     705       14328 :   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       14328 :   Real oneOverPiR = 1.0 / (libMesh::pi * _r);
     710             : 
     711             :   aux_stress.zero();
     712       14328 :   aux_stress(0, 0) = -oneOverPiR * ctcu;
     713       14328 :   aux_stress(0, 1) = -oneOverPiR * st * ctsq;
     714       14328 :   aux_stress(1, 0) = -oneOverPiR * st * ctsq;
     715       14328 :   aux_stress(1, 1) = -oneOverPiR * ct * stsq;
     716       14328 :   aux_stress(2, 2) = -oneOverPiR * _poissons_ratio * (ctcu + ct * stsq);
     717             : 
     718             :   grad_disp.zero();
     719       14328 :   grad_disp(0, 0) = oneOverPiR / (4.0 * _youngs_modulus) *
     720       14328 :                     (ct * (4.0 * Utility::pow<2>(_poissons_ratio) - 3.0 + _poissons_ratio) -
     721       14328 :                      std::cos(3.0 * t) * (1.0 + _poissons_ratio));
     722       14328 :   grad_disp(0, 1) = -oneOverPiR / (4.0 * _youngs_modulus) *
     723       14328 :                     (st * (4.0 * Utility::pow<2>(_poissons_ratio) - 3.0 + _poissons_ratio) +
     724       14328 :                      std::sin(3.0 * t) * (1.0 + _poissons_ratio));
     725       14328 : }
     726             : 
     727             : template class InteractionIntegralTempl<false>;
     728             : template class InteractionIntegralTempl<true>;

Generated by: LCOV version 1.14