LCOV - code coverage report
Current view: top level - src/materials - ADComputeSmearedCrackingStress.C (source / functions) Hit Total Coverage
Test: idaholab/moose solid_mechanics: #32971 (54bef8) with base c6cf66 Lines: 268 299 89.6 %
Date: 2026-05-29 20:40:07 Functions: 11 11 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #include "ADComputeSmearedCrackingStress.h"
      11             : #include "ElasticityTensorTools.h"
      12             : #include "StressUpdateBase.h"
      13             : #include "Conversion.h"
      14             : 
      15             : registerMooseObject("SolidMechanicsApp", ADComputeSmearedCrackingStress);
      16             : 
      17             : InputParameters
      18         209 : ADComputeSmearedCrackingStress::validParams()
      19             : {
      20         209 :   InputParameters params = ADComputeMultipleInelasticStress::validParams();
      21         209 :   params.addClassDescription(
      22             :       "Compute stress using a fixed smeared cracking model. Uses automatic differentiation");
      23         418 :   params.addRequiredParam<std::vector<MaterialName>>(
      24             :       "softening_models",
      25             :       "The material objects used to compute softening behavior for loading a crack."
      26             :       "Either 1 or 3 models must be specified. If a single model is specified, it is"
      27             :       "used for all directions. If 3 models are specified, they will be used for the"
      28             :       "3 crack directions in sequence");
      29         418 :   params.addRequiredCoupledVar(
      30             :       "cracking_stress",
      31             :       "The stress threshold beyond which cracking occurs. Negative values prevent cracking.");
      32         209 :   MultiMooseEnum direction("x y z");
      33         418 :   params.addParam<MultiMooseEnum>(
      34             :       "prescribed_crack_directions", direction, "Prescribed directions of first cracks");
      35         418 :   params.addParam<unsigned int>(
      36         418 :       "max_cracks", 3, "The maximum number of cracks allowed at a material point.");
      37         418 :   params.addRangeCheckedParam<Real>("cracking_neg_fraction",
      38             :                                     0,
      39             :                                     "cracking_neg_fraction <= 1 & cracking_neg_fraction >= 0",
      40             :                                     "The fraction of the cracking strain at which "
      41             :                                     "a transition begins during decreasing "
      42             :                                     "strain to the original stiffness.");
      43         418 :   params.addParam<Real>(
      44             :       "max_stress_correction",
      45         418 :       1.0,
      46             :       "Maximum permitted correction to the predicted stress as a ratio of the "
      47             :       "stress change to the predicted stress from the previous step's damage level. "
      48             :       "Values less than 1 will improve robustness, but not be as accurate.");
      49             : 
      50         627 :   params.addRangeCheckedParam<Real>(
      51             :       "shear_retention_factor",
      52         418 :       0.0,
      53             :       "shear_retention_factor>=0 & shear_retention_factor<=1.0",
      54             :       "Fraction of original shear stiffness to be retained after cracking");
      55         209 :   params.set<std::vector<MaterialName>>("inelastic_models") = {};
      56             : 
      57         418 :   MooseEnum crackedElasticityType("DIAGONAL FULL", "DIAGONAL");
      58         418 :   crackedElasticityType.addDocumentation(
      59             :       "DIAGONAL", "Zero out terms coupling with directions orthogonal to a crack (legacy)");
      60         418 :   crackedElasticityType.addDocumentation(
      61             :       "FULL", "Consistently scale all entries based on damage (recommended)");
      62         418 :   params.addParam<MooseEnum>(
      63             :       "cracked_elasticity_type",
      64             :       crackedElasticityType,
      65             :       "Method to modify the local elasticity tensor to account for cracking");
      66             : 
      67         209 :   return params;
      68         209 : }
      69             : 
      70         157 : ADComputeSmearedCrackingStress::ADComputeSmearedCrackingStress(const InputParameters & parameters)
      71             :   : ADComputeMultipleInelasticStress(parameters),
      72         157 :     _cracking_stress(adCoupledValue("cracking_stress")),
      73         314 :     _max_cracks(getParam<unsigned int>("max_cracks")),
      74         314 :     _cracking_neg_fraction(getParam<Real>("cracking_neg_fraction")),
      75         314 :     _shear_retention_factor(getParam<Real>("shear_retention_factor")),
      76         314 :     _max_stress_correction(getParam<Real>("max_stress_correction")),
      77         157 :     _cracked_elasticity_type(
      78         157 :         getParam<MooseEnum>("cracked_elasticity_type").getEnum<CrackedElasticityType>()),
      79         157 :     _crack_damage(declareADProperty<RealVectorValue>(_base_name + "crack_damage")),
      80         314 :     _crack_damage_old(getMaterialPropertyOld<RealVectorValue>(_base_name + "crack_damage")),
      81         157 :     _crack_flags(declareADProperty<RealVectorValue>(_base_name + "crack_flags")),
      82         157 :     _crack_rotation(declareADProperty<RankTwoTensor>(_base_name + "crack_rotation")),
      83         314 :     _crack_rotation_old(getMaterialPropertyOld<RankTwoTensor>(_base_name + "crack_rotation")),
      84         157 :     _crack_initiation_strain(
      85         157 :         declareADProperty<RealVectorValue>(_base_name + "crack_initiation_strain")),
      86         157 :     _crack_initiation_strain_old(
      87         157 :         getMaterialPropertyOld<RealVectorValue>(_base_name + "crack_initiation_strain")),
      88         157 :     _crack_max_strain(declareADProperty<RealVectorValue>(_base_name + "crack_max_strain")),
      89         471 :     _crack_max_strain_old(getMaterialPropertyOld<RealVectorValue>(_base_name + "crack_max_strain"))
      90             : {
      91             :   MultiMooseEnum prescribed_crack_directions =
      92         314 :       getParam<MultiMooseEnum>("prescribed_crack_directions");
      93         157 :   if (prescribed_crack_directions.size() > 0)
      94             :   {
      95          39 :     if (prescribed_crack_directions.size() > 3)
      96           0 :       mooseError("A maximum of three crack directions may be specified");
      97         126 :     for (unsigned int i = 0; i < prescribed_crack_directions.size(); ++i)
      98             :     {
      99         156 :       for (unsigned int j = 0; j < i; ++j)
     100          69 :         if (prescribed_crack_directions[i] == prescribed_crack_directions[j])
     101           0 :           mooseError("Entries in 'prescribed_crack_directions' cannot be repeated");
     102          87 :       _prescribed_crack_directions.push_back(
     103          87 :           static_cast<unsigned int>(prescribed_crack_directions.get(i)));
     104             :     }
     105             : 
     106             :     // Fill in the last remaining direction if 2 are specified
     107          39 :     if (_prescribed_crack_directions.size() == 2)
     108             :     {
     109           6 :       std::set<unsigned int> available_dirs = {0, 1, 2};
     110          18 :       for (auto dir : _prescribed_crack_directions)
     111          12 :         if (available_dirs.erase(dir) != 1)
     112           0 :           mooseError("Invalid prescribed crack direction:" + Moose::stringify(dir));
     113           6 :       if (available_dirs.size() != 1)
     114           0 :         mooseError("Error in finding remaining available crack direction");
     115           6 :       _prescribed_crack_directions.push_back(*available_dirs.begin());
     116             :     }
     117             :   }
     118         314 :   if (!isParamSetByUser("cracked_elasticity_type"))
     119          31 :     paramWarning(
     120             :         "cracked_elasticity_type",
     121             :         "Defaulting to the legacy option of 'DIAGONAL', but the 'FULL' option is preferred");
     122             : 
     123         156 :   _local_elastic_vector.resize(9);
     124         156 : }
     125             : 
     126             : void
     127         616 : ADComputeSmearedCrackingStress::initQpStatefulProperties()
     128             : {
     129         616 :   ADComputeMultipleInelasticStress::initQpStatefulProperties();
     130             : 
     131         616 :   _crack_damage[_qp] = 0.0;
     132             : 
     133         616 :   _crack_initiation_strain[_qp] = 0.0;
     134         616 :   _crack_max_strain[_qp](0) = 0.0;
     135             : 
     136         616 :   switch (_prescribed_crack_directions.size())
     137             :   {
     138         440 :     case 0:
     139             :     {
     140         440 :       _crack_rotation[_qp] = ADRankTwoTensor::Identity();
     141         440 :       break;
     142             :     }
     143          64 :     case 1:
     144             :     {
     145          64 :       _crack_rotation[_qp].zero();
     146          64 :       switch (_prescribed_crack_directions[0])
     147             :       {
     148          32 :         case 0:
     149             :         {
     150          32 :           _crack_rotation[_qp](0, 0) = 1.0;
     151          32 :           _crack_rotation[_qp](1, 1) = 1.0;
     152          32 :           _crack_rotation[_qp](2, 2) = 1.0;
     153          32 :           break;
     154             :         }
     155           0 :         case 1:
     156             :         {
     157           0 :           _crack_rotation[_qp](1, 0) = 1.0;
     158           0 :           _crack_rotation[_qp](0, 1) = 1.0;
     159           0 :           _crack_rotation[_qp](2, 2) = 1.0;
     160           0 :           break;
     161             :         }
     162          32 :         case 2:
     163             :         {
     164          32 :           _crack_rotation[_qp](2, 0) = 1.0;
     165          32 :           _crack_rotation[_qp](0, 1) = 1.0;
     166          32 :           _crack_rotation[_qp](1, 2) = 1.0;
     167          32 :           break;
     168             :         }
     169             :       }
     170             :       break;
     171             :     }
     172           0 :     case 2:
     173             :     {
     174           0 :       mooseError("Number of prescribed crack directions cannot be 2");
     175             :       break;
     176             :     }
     177             :     case 3:
     178             :     {
     179         448 :       for (unsigned int i = 0; i < _prescribed_crack_directions.size(); ++i)
     180             :       {
     181             :         ADRealVectorValue crack_dir_vec;
     182         336 :         crack_dir_vec(_prescribed_crack_directions[i]) = 1.0;
     183         336 :         _crack_rotation[_qp].fillColumn(i, crack_dir_vec);
     184             :       }
     185             :     }
     186             :   }
     187         616 : }
     188             : 
     189             : void
     190         154 : ADComputeSmearedCrackingStress::initialSetup()
     191             : {
     192         154 :   ADComputeMultipleInelasticStress::initialSetup();
     193             : 
     194         308 :   if (!hasGuaranteedMaterialProperty(_elasticity_tensor_name, Guarantee::ISOTROPIC))
     195           0 :     mooseError("ADComputeSmearedCrackingStress requires that the elasticity tensor be "
     196             :                "guaranteed isotropic");
     197             : 
     198         462 :   std::vector<MaterialName> soft_matls = getParam<std::vector<MaterialName>>("softening_models");
     199         351 :   for (auto soft_matl : soft_matls)
     200             :   {
     201             :     ADSmearedCrackSofteningBase * scsb =
     202         197 :         dynamic_cast<ADSmearedCrackSofteningBase *>(&getMaterialByName(soft_matl));
     203         197 :     if (scsb)
     204         197 :       _softening_models.push_back(scsb);
     205             :     else
     206           0 :       paramError("softening_models", "Model " + soft_matl + " is not a softening model");
     207             :   }
     208         154 :   if (_softening_models.size() == 1)
     209             :   {
     210             :     // Reuse the same model in all 3 directions
     211         132 :     _softening_models.push_back(_softening_models[0]);
     212         132 :     _softening_models.push_back(_softening_models[0]);
     213             :   }
     214          22 :   else if (_softening_models.size() != 3)
     215           1 :     paramError("softening_models", "Either 1 or 3 softening models must be specified");
     216         153 : }
     217             : 
     218             : void
     219      463820 : ADComputeSmearedCrackingStress::computeQpStress()
     220             : {
     221             : 
     222      463820 :   if (!previouslyCracked())
     223       32808 :     computeQpStressIntermediateConfiguration();
     224             :   else
     225             :   {
     226      431012 :     _elastic_strain[_qp] = _elastic_strain_old[_qp] + _strain_increment[_qp];
     227             : 
     228             :     // Propagate behavior from the (now inactive) inelastic models
     229      431012 :     _inelastic_strain[_qp] = _inelastic_strain_old[_qp];
     230      431012 :     for (auto model : _models)
     231             :     {
     232           0 :       model->setQp(_qp);
     233           0 :       model->propagateQpStatefulProperties();
     234             :     }
     235             : 
     236             :     // Since the other inelastic models are inactive, they will not limit the time step
     237      431012 :     _material_timestep_limit[_qp] = std::numeric_limits<Real>::max();
     238             : 
     239             :     // update _local_elasticity_tensor based on cracking state in previous time step
     240      431012 :     updateLocalElasticityTensor();
     241             : 
     242             :     // Calculate stress in intermediate configuration
     243      431012 :     _stress[_qp] = _local_elasticity_tensor * _elastic_strain[_qp];
     244             :   }
     245             : 
     246             :   // compute crack status and adjust stress
     247      463820 :   updateCrackingStateAndStress();
     248             : 
     249      463820 :   if (_perform_finite_strain_rotations)
     250             :   {
     251      463820 :     finiteStrainRotation();
     252      463820 :     _crack_rotation[_qp] = _rotation_increment[_qp] * _crack_rotation[_qp];
     253             :   }
     254      463820 : }
     255             : 
     256             : void
     257      431012 : ADComputeSmearedCrackingStress::updateLocalElasticityTensor()
     258             : {
     259             :   const ADReal youngs_modulus =
     260      431012 :       ElasticityTensorTools::getIsotropicYoungsModulus(_elasticity_tensor[_qp]);
     261             : 
     262             :   bool cracking_locally_active = false;
     263             : 
     264      431012 :   const ADReal cracking_stress = _cracking_stress[_qp];
     265             : 
     266      431012 :   if (cracking_stress > 0)
     267             :   {
     268      431012 :     ADRealVectorValue stiffness_ratio_local(1.0, 1.0, 1.0);
     269      431012 :     const ADRankTwoTensor & R = _crack_rotation_old[_qp];
     270      431012 :     ADRankTwoTensor ePrime(_elastic_strain_old[_qp]);
     271      431012 :     ePrime.rotate(R.transpose());
     272             : 
     273     1724048 :     for (unsigned int i = 0; i < 3; ++i)
     274             :     {
     275             :       // Update elasticity tensor based on crack status of the end of last time step
     276     1293036 :       if (_crack_damage_old[_qp](i) > 0.0)
     277             :       {
     278     1197960 :         if (_cracking_neg_fraction == 0.0 && MooseUtils::absoluteFuzzyLessThan(ePrime(i, i), 0.0))
     279       57936 :           stiffness_ratio_local(i) = 1.0;
     280           0 :         else if (_cracking_neg_fraction > 0.0 &&
     281     1082088 :                  ePrime(i, i) < _crack_initiation_strain_old[_qp](i) * _cracking_neg_fraction &&
     282      541044 :                  ePrime(i, i) > -_crack_initiation_strain_old[_qp](i) * _cracking_neg_fraction)
     283             :         {
     284           0 :           const ADReal etr = _cracking_neg_fraction * _crack_initiation_strain_old[_qp](i);
     285             :           const ADReal Eo = cracking_stress / _crack_initiation_strain_old[_qp](i);
     286           0 :           const ADReal Ec = Eo * (1.0 - _crack_damage_old[_qp](i));
     287           0 :           const ADReal a = (Ec - Eo) / (4 * etr);
     288           0 :           const ADReal b = (Ec + Eo) / 2;
     289             :           // Compute the ratio of the current transition stiffness to the original stiffness
     290           0 :           stiffness_ratio_local(i) = (2.0 * a * etr + b) / Eo;
     291             :           cracking_locally_active = true;
     292             :         }
     293             :         else
     294             :         {
     295      541044 :           stiffness_ratio_local(i) = (1.0 - _crack_damage_old[_qp](i));
     296             :           cracking_locally_active = true;
     297             :         }
     298             :       }
     299             :     }
     300             : 
     301      431012 :     if (cracking_locally_active)
     302             :     {
     303             :       // Update the elasticity tensor in the crack coordinate system
     304      373508 :       if (_cracked_elasticity_type == CrackedElasticityType::DIAGONAL)
     305             :       {
     306        1104 :         const bool c0_coupled = MooseUtils::absoluteFuzzyEqual(stiffness_ratio_local(0), 1.0);
     307        1104 :         const bool c1_coupled = MooseUtils::absoluteFuzzyEqual(stiffness_ratio_local(1), 1.0);
     308        1104 :         const bool c2_coupled = MooseUtils::absoluteFuzzyEqual(stiffness_ratio_local(2), 1.0);
     309             : 
     310        2208 :         const ADReal c01 = (c0_coupled && c1_coupled ? 1.0 : 0.0);
     311        1776 :         const ADReal c02 = (c0_coupled && c2_coupled ? 1.0 : 0.0);
     312        1536 :         const ADReal c12 = (c1_coupled && c2_coupled ? 1.0 : 0.0);
     313             : 
     314             :         const ADReal c01_shear_retention =
     315        1104 :             (c0_coupled && c1_coupled ? 1.0 : _shear_retention_factor);
     316             :         const ADReal c02_shear_retention =
     317        1104 :             (c0_coupled && c2_coupled ? 1.0 : _shear_retention_factor);
     318             :         const ADReal c12_shear_retention =
     319        1104 :             (c1_coupled && c2_coupled ? 1.0 : _shear_retention_factor);
     320             : 
     321        1104 :         _local_elastic_vector[0] = (c0_coupled ? _elasticity_tensor[_qp](0, 0, 0, 0)
     322        1104 :                                                : stiffness_ratio_local(0) * youngs_modulus);
     323        2208 :         _local_elastic_vector[1] = _elasticity_tensor[_qp](0, 0, 1, 1) * c01;
     324        2208 :         _local_elastic_vector[2] = _elasticity_tensor[_qp](0, 0, 2, 2) * c02;
     325        1104 :         _local_elastic_vector[3] = (c1_coupled ? _elasticity_tensor[_qp](1, 1, 1, 1)
     326        1104 :                                                : stiffness_ratio_local(1) * youngs_modulus);
     327        2208 :         _local_elastic_vector[4] = _elasticity_tensor[_qp](1, 1, 2, 2) * c12;
     328        1104 :         _local_elastic_vector[5] = (c2_coupled ? _elasticity_tensor[_qp](2, 2, 2, 2)
     329        1104 :                                                : stiffness_ratio_local(2) * youngs_modulus);
     330        2208 :         _local_elastic_vector[6] = _elasticity_tensor[_qp](1, 2, 1, 2) * c12_shear_retention;
     331        2208 :         _local_elastic_vector[7] = _elasticity_tensor[_qp](0, 2, 0, 2) * c02_shear_retention;
     332        2208 :         _local_elastic_vector[8] = _elasticity_tensor[_qp](0, 1, 0, 1) * c01_shear_retention;
     333             :       }
     334             :       else // _cracked_elasticity_type == CrackedElasticityType::FULL
     335             :       {
     336             :         const ADReal & c0 = stiffness_ratio_local(0);
     337             :         const ADReal & c1 = stiffness_ratio_local(1);
     338             :         const ADReal & c2 = stiffness_ratio_local(2);
     339             : 
     340             :         const ADReal c01 = c0 * c1;
     341             :         const ADReal c02 = c0 * c2;
     342             :         const ADReal c12 = c1 * c2;
     343             : 
     344      372692 :         const ADReal c01_shear_retention = std::max(c01, _shear_retention_factor);
     345      372404 :         const ADReal c02_shear_retention = std::max(c02, _shear_retention_factor);
     346      372404 :         const ADReal c12_shear_retention = std::max(c12, _shear_retention_factor);
     347             : 
     348      744808 :         _local_elastic_vector[0] = _elasticity_tensor[_qp](0, 0, 0, 0) * c0;
     349      744808 :         _local_elastic_vector[1] = _elasticity_tensor[_qp](0, 0, 1, 1) * c01;
     350      744808 :         _local_elastic_vector[2] = _elasticity_tensor[_qp](0, 0, 2, 2) * c02;
     351      744808 :         _local_elastic_vector[3] = _elasticity_tensor[_qp](1, 1, 1, 1) * c1;
     352      744808 :         _local_elastic_vector[4] = _elasticity_tensor[_qp](1, 1, 2, 2) * c12;
     353      744808 :         _local_elastic_vector[5] = _elasticity_tensor[_qp](2, 2, 2, 2) * c2;
     354      744808 :         _local_elastic_vector[6] = _elasticity_tensor[_qp](1, 2, 1, 2) * c12_shear_retention;
     355      744808 :         _local_elastic_vector[7] = _elasticity_tensor[_qp](0, 2, 0, 2) * c02_shear_retention;
     356      744808 :         _local_elastic_vector[8] = _elasticity_tensor[_qp](0, 1, 0, 1) * c01_shear_retention;
     357             :       }
     358             : 
     359             :       // Filling with 9 components is sufficient because these are the only nonzero entries
     360             :       // for isotropic or orthotropic materials.
     361      373508 :       _local_elasticity_tensor.fillFromInputVector(_local_elastic_vector,
     362             :                                                    ADRankFourTensor::symmetric9);
     363             : 
     364             :       // Rotate the modified elasticity tensor back into global coordinates
     365      373508 :       _local_elasticity_tensor.rotate(R);
     366             :     }
     367             :   }
     368             :   if (!cracking_locally_active)
     369       57504 :     _local_elasticity_tensor = _elasticity_tensor[_qp];
     370      431012 : }
     371             : 
     372             : void
     373      463820 : ADComputeSmearedCrackingStress::updateCrackingStateAndStress()
     374             : {
     375             :   const ADReal youngs_modulus =
     376      463820 :       ElasticityTensorTools::getIsotropicYoungsModulus(_elasticity_tensor[_qp]);
     377             : 
     378             :   const ADReal poissons_ratio =
     379      463820 :       ElasticityTensorTools::getIsotropicPoissonsRatio(_elasticity_tensor[_qp]);
     380             : 
     381      463820 :   ADReal cracking_stress = _cracking_stress[_qp];
     382             : 
     383      463820 :   if (cracking_stress > 0)
     384             :   {
     385             :     // Initializing crack states
     386      463784 :     _crack_rotation[_qp] = _crack_rotation_old[_qp];
     387             : 
     388     1855136 :     for (unsigned i = 0; i < 3; ++i)
     389             :     {
     390     1391352 :       _crack_max_strain[_qp](i) = _crack_max_strain_old[_qp](i);
     391     1391352 :       _crack_initiation_strain[_qp](i) = _crack_initiation_strain_old[_qp](i);
     392     1391352 :       _crack_damage[_qp](i) = _crack_damage_old[_qp](i);
     393             :     }
     394             : 
     395             :     // Compute crack orientations: updated _crack_rotation[_qp] based on current strain
     396             :     ADRealVectorValue strain_in_crack_dir;
     397      463784 :     computeCrackStrainAndOrientation(strain_in_crack_dir);
     398             : 
     399     1855136 :     for (unsigned i = 0; i < 3; ++i)
     400             :     {
     401     1391352 :       if (strain_in_crack_dir(i) > _crack_max_strain[_qp](i))
     402      487657 :         _crack_max_strain[_qp](i) = strain_in_crack_dir(i);
     403             :     }
     404             : 
     405             :     // Check for new cracks.
     406             :     // Rotate stress to cracked orientation.
     407      463784 :     const ADRankTwoTensor & R = _crack_rotation[_qp];
     408      463784 :     ADRankTwoTensor sigmaPrime(_stress[_qp]);
     409      463784 :     sigmaPrime.rotate(R.transpose()); // stress in crack coordinates
     410             : 
     411             :     unsigned int num_cracks = 0;
     412     1855136 :     for (unsigned int i = 0; i < 3; ++i)
     413             :     {
     414     1391352 :       if (_crack_damage_old[_qp](i) > 0.0)
     415      598980 :         ++num_cracks;
     416             :     }
     417             : 
     418             :     bool cracked(false);
     419             :     ADRealVectorValue sigma;
     420             :     mooseAssert(_softening_models.size() == 3, "Must have 3 softening models");
     421     1855136 :     for (unsigned int i = 0; i < 3; ++i)
     422             :     {
     423     1391352 :       sigma(i) = sigmaPrime(i, i);
     424             : 
     425     1391352 :       ADReal stiffness_ratio = 1.0 - _crack_damage[_qp](i);
     426             : 
     427     1391352 :       const bool pre_existing_crack = (_crack_damage_old[_qp](i) > 0.0);
     428             :       const bool met_stress_criterion = (sigma(i) > cracking_stress);
     429     1391352 :       const bool loading_existing_crack = (strain_in_crack_dir(i) >= _crack_max_strain[_qp](i));
     430     1391352 :       const bool allowed_to_crack = (pre_existing_crack || num_cracks < _max_cracks);
     431             :       bool new_crack = false;
     432             : 
     433     1391352 :       cracked |= pre_existing_crack;
     434             : 
     435             :       // Adjustments for newly created cracks
     436     1391352 :       if (met_stress_criterion && !pre_existing_crack && allowed_to_crack)
     437             :       {
     438             :         new_crack = true;
     439        4684 :         ++num_cracks;
     440             : 
     441             :         // Assume Poisson's ratio drops to zero for this direction.  Stiffness is then Young's
     442             :         // modulus.
     443        4684 :         _crack_initiation_strain[_qp](i) = cracking_stress / youngs_modulus;
     444             : 
     445        4684 :         if (_crack_max_strain[_qp](i) < _crack_initiation_strain[_qp](i))
     446         408 :           _crack_max_strain[_qp](i) = _crack_initiation_strain[_qp](i);
     447             :       }
     448             : 
     449             :       // Update stress and stiffness ratio according to specified crack release model
     450     1386668 :       if (new_crack || (pre_existing_crack && loading_existing_crack))
     451             :       {
     452             :         cracked = true;
     453      386968 :         _softening_models[i]->computeCrackingRelease(sigma(i),
     454             :                                                      stiffness_ratio,
     455             :                                                      strain_in_crack_dir(i),
     456      386968 :                                                      _crack_initiation_strain[_qp](i),
     457      386968 :                                                      _crack_max_strain[_qp](i),
     458             :                                                      cracking_stress,
     459             :                                                      youngs_modulus,
     460             :                                                      poissons_ratio);
     461      773936 :         _crack_damage[_qp](i) = 1.0 - stiffness_ratio;
     462             :       }
     463             : 
     464      820376 :       else if (cracked && _cracking_neg_fraction > 0 &&
     465     2008768 :                _crack_initiation_strain[_qp](i) * _cracking_neg_fraction > strain_in_crack_dir(i) &&
     466     1004384 :                -_crack_initiation_strain[_qp](i) * _cracking_neg_fraction < strain_in_crack_dir(i))
     467             :       {
     468           0 :         const ADReal etr = _cracking_neg_fraction * _crack_initiation_strain[_qp](i);
     469           0 :         const ADReal Eo = cracking_stress / _crack_initiation_strain[_qp](i);
     470           0 :         const ADReal Ec = Eo * (1.0 - _crack_damage_old[_qp](i));
     471           0 :         const ADReal a = (Ec - Eo) / (4.0 * etr);
     472           0 :         const ADReal b = 0.5 * (Ec + Eo);
     473           0 :         const ADReal c = 0.25 * (Ec - Eo) * etr;
     474           0 :         sigma(i) = (a * strain_in_crack_dir(i) + b) * strain_in_crack_dir(i) + c;
     475             :       }
     476             :     }
     477             : 
     478      463784 :     if (cracked)
     479      432688 :       updateStressTensorForCracking(_stress[_qp], sigma);
     480             :   }
     481             : 
     482      927640 :   _crack_flags[_qp](0) = 1.0 - _crack_damage[_qp](2);
     483      927640 :   _crack_flags[_qp](1) = 1.0 - _crack_damage[_qp](1);
     484      927640 :   _crack_flags[_qp](2) = 1.0 - _crack_damage[_qp](0);
     485      463820 : }
     486             : 
     487             : void
     488      463784 : ADComputeSmearedCrackingStress::computeCrackStrainAndOrientation(
     489             :     ADRealVectorValue & strain_in_crack_dir)
     490             : {
     491             :   // The rotation tensor is ordered such that directions for pre-existing cracks appear first
     492             :   // in the list of columns.  For example, if there is one existing crack, its direction is in the
     493             :   // first column in the rotation tensor.
     494      463784 :   const unsigned int num_known_dirs = getNumKnownCrackDirs();
     495             : 
     496      463784 :   if (num_known_dirs == 0)
     497             :   {
     498       28500 :     std::vector<ADReal> eigval(3, 0.0);
     499       28500 :     ADRankTwoTensor eigvec;
     500             : 
     501       28500 :     _elastic_strain[_qp].symmetricEigenvaluesEigenvectors(eigval, eigvec);
     502             : 
     503             :     // If the elastic strain is beyond the cracking strain, save the eigen vectors as
     504             :     // the rotation tensor. Reverse their order so that the third principal strain
     505             :     // (most tensile) will correspond to the first crack.
     506       28500 :     _crack_rotation[_qp].fillColumn(0, eigvec.column(2));
     507       28500 :     _crack_rotation[_qp].fillColumn(1, eigvec.column(1));
     508       28500 :     _crack_rotation[_qp].fillColumn(2, eigvec.column(0));
     509             : 
     510       28500 :     strain_in_crack_dir(0) = eigval[2];
     511       28500 :     strain_in_crack_dir(1) = eigval[1];
     512       28500 :     strain_in_crack_dir(2) = eigval[0];
     513       28500 :   }
     514      435284 :   else if (num_known_dirs == 1)
     515             :   {
     516             :     // This is easily the most complicated case.
     517             :     // 1.  Rotate the elastic strain to the orientation associated with the known
     518             :     //     crack.
     519             :     // 2.  Extract the lower 2x2 block into a separate tensor.
     520             :     // 3.  Run the eigen solver on the result.
     521             :     // 4.  Update the rotation tensor to reflect the effect of the 2 eigenvectors.
     522             : 
     523             :     // 1.
     524      194620 :     const ADRankTwoTensor & R = _crack_rotation[_qp];
     525      194620 :     RankTwoTensor ePrime(raw_value(_elastic_strain[_qp]));
     526      389240 :     ePrime.rotate(raw_value(R.transpose())); // elastic strain in crack coordinates
     527             : 
     528             :     // 2.
     529      194620 :     ColumnMajorMatrix e2x2(2, 2);
     530      194620 :     e2x2(0, 0) = ePrime(1, 1);
     531      194620 :     e2x2(1, 0) = ePrime(2, 1);
     532      194620 :     e2x2(0, 1) = ePrime(1, 2);
     533      194620 :     e2x2(1, 1) = ePrime(2, 2);
     534             : 
     535             :     // 3.
     536      194620 :     ColumnMajorMatrix e_val2x1(2, 1);
     537      194620 :     ColumnMajorMatrix e_vec2x2(2, 2);
     538      194620 :     e2x2.eigen(e_val2x1, e_vec2x2);
     539             : 
     540             :     // 4.
     541             :     ADRankTwoTensor eigvec(
     542      194620 :         1.0, 0.0, 0.0, 0.0, e_vec2x2(0, 1), e_vec2x2(1, 1), 0.0, e_vec2x2(0, 0), e_vec2x2(1, 0));
     543             : 
     544      194620 :     _crack_rotation[_qp] = _crack_rotation_old[_qp] * eigvec; // Roe implementation
     545             : 
     546      194620 :     strain_in_crack_dir(0) = ePrime(0, 0);
     547      194620 :     strain_in_crack_dir(1) = e_val2x1(1, 0);
     548      194620 :     strain_in_crack_dir(2) = e_val2x1(0, 0);
     549             :   }
     550      240664 :   else if (num_known_dirs == 2 || num_known_dirs == 3)
     551             :   {
     552             :     // Rotate to cracked orientation and pick off the strains in the rotated
     553             :     // coordinate directions.
     554      240664 :     const ADRankTwoTensor & R = _crack_rotation[_qp];
     555      240664 :     ADRankTwoTensor ePrime(_elastic_strain[_qp]);
     556      240664 :     ePrime.rotate(R.transpose()); // elastic strain in crack coordinates
     557             : 
     558      240664 :     strain_in_crack_dir(0) = ePrime(0, 0);
     559             :     strain_in_crack_dir(1) = ePrime(1, 1);
     560      240664 :     strain_in_crack_dir(2) = ePrime(2, 2);
     561             :   }
     562             :   else
     563           0 :     mooseError("Invalid number of known crack directions");
     564      463784 : }
     565             : 
     566             : unsigned int
     567      463784 : ADComputeSmearedCrackingStress::getNumKnownCrackDirs() const
     568             : {
     569             :   unsigned int num_known_dirs = 0;
     570     1855136 :   for (unsigned int i = 0; i < 3; ++i)
     571             :   {
     572     1391352 :     if (_crack_damage_old[_qp](i) > 0.0 || _prescribed_crack_directions.size() >= i + 1)
     573      859972 :       ++num_known_dirs;
     574             :   }
     575      463784 :   return num_known_dirs;
     576             : }
     577             : 
     578             : void
     579      432688 : ADComputeSmearedCrackingStress::updateStressTensorForCracking(ADRankTwoTensor & tensor,
     580             :                                                               const ADRealVectorValue & sigma)
     581             : {
     582             :   // Get transformation matrix
     583      432688 :   const ADRankTwoTensor & R = _crack_rotation[_qp];
     584             :   // Rotate to crack frame
     585      432688 :   tensor.rotate(R.transpose());
     586             : 
     587             :   // Reset stress if cracked
     588     1730752 :   for (unsigned int i = 0; i < 3; ++i)
     589     1298064 :     if (_crack_damage[_qp](i) > 0.0)
     590             :     {
     591      601960 :       const ADReal stress_correction_ratio = (tensor(i, i) - sigma(i)) / tensor(i, i);
     592      601960 :       if (stress_correction_ratio > _max_stress_correction)
     593           0 :         tensor(i, i) *= (1.0 - _max_stress_correction);
     594      601960 :       else if (stress_correction_ratio < -_max_stress_correction)
     595           0 :         tensor(i, i) *= (1.0 + _max_stress_correction);
     596             :       else
     597      601960 :         tensor(i, i) = sigma(i);
     598             :     }
     599             : 
     600             :   // Rotate back to global frame
     601      432688 :   tensor.rotate(R);
     602      432688 : }
     603             : 
     604             : bool
     605      463820 : ADComputeSmearedCrackingStress::previouslyCracked()
     606             : {
     607      652772 :   for (unsigned int i = 0; i < 3; ++i)
     608      619964 :     if (_crack_damage_old[_qp](i) > 0.0)
     609             :       return true;
     610             :   return false;
     611             : }

Generated by: LCOV version 1.14