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

Generated by: LCOV version 1.14