LCOV - code coverage report
Current view: top level - src/materials - ComputeSmearedCrackingStress.C (source / functions) Hit Total Coverage
Test: idaholab/moose solid_mechanics: #32971 (54bef8) with base c6cf66 Lines: 263 293 89.8 %
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 "ComputeSmearedCrackingStress.h"
      11             : #include "ElasticityTensorTools.h"
      12             : #include "StressUpdateBase.h"
      13             : #include "Conversion.h"
      14             : 
      15             : registerMooseObject("SolidMechanicsApp", ComputeSmearedCrackingStress);
      16             : 
      17             : InputParameters
      18         197 : ComputeSmearedCrackingStress::validParams()
      19             : {
      20         197 :   InputParameters params = ComputeMultipleInelasticStress::validParams();
      21         197 :   params.addClassDescription("Compute stress using a fixed smeared cracking model");
      22         394 :   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         394 :   params.addRequiredCoupledVar(
      29             :       "cracking_stress",
      30             :       "The stress threshold beyond which cracking occurs. Negative values prevent cracking.");
      31         197 :   MultiMooseEnum direction("x y z");
      32         394 :   params.addParam<MultiMooseEnum>(
      33             :       "prescribed_crack_directions", direction, "Prescribed directions of first cracks");
      34         394 :   params.addParam<unsigned int>(
      35         394 :       "max_cracks", 3, "The maximum number of cracks allowed at a material point.");
      36         394 :   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         394 :   params.addParam<Real>(
      43             :       "max_stress_correction",
      44         394 :       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         591 :   params.addRangeCheckedParam<Real>(
      50             :       "shear_retention_factor",
      51         394 :       0.0,
      52             :       "shear_retention_factor>=0 & shear_retention_factor<=1.0",
      53             :       "Fraction of original shear stiffness to be retained after cracking");
      54         197 :   params.set<std::vector<MaterialName>>("inelastic_models") = {};
      55             : 
      56         394 :   MooseEnum crackedElasticityType("DIAGONAL FULL", "DIAGONAL");
      57         394 :   crackedElasticityType.addDocumentation(
      58             :       "DIAGONAL", "Zero out terms coupling with directions orthogonal to a crack (legacy)");
      59         394 :   crackedElasticityType.addDocumentation(
      60             :       "FULL", "Consistently scale all entries based on damage (recommended)");
      61         394 :   params.addParam<MooseEnum>(
      62             :       "cracked_elasticity_type",
      63             :       crackedElasticityType,
      64             :       "Method to modify the local elasticity tensor to account for cracking");
      65             : 
      66         197 :   return params;
      67         197 : }
      68             : 
      69         148 : ComputeSmearedCrackingStress::ComputeSmearedCrackingStress(const InputParameters & parameters)
      70             :   : ComputeMultipleInelasticStress(parameters),
      71         148 :     _cracking_stress(coupledValue("cracking_stress")),
      72         296 :     _max_cracks(getParam<unsigned int>("max_cracks")),
      73         296 :     _cracking_neg_fraction(getParam<Real>("cracking_neg_fraction")),
      74         296 :     _shear_retention_factor(getParam<Real>("shear_retention_factor")),
      75         296 :     _max_stress_correction(getParam<Real>("max_stress_correction")),
      76         148 :     _cracked_elasticity_type(
      77         148 :         getParam<MooseEnum>("cracked_elasticity_type").getEnum<CrackedElasticityType>()),
      78         148 :     _crack_damage(declareProperty<RealVectorValue>(_base_name + "crack_damage")),
      79         296 :     _crack_damage_old(getMaterialPropertyOld<RealVectorValue>(_base_name + "crack_damage")),
      80         148 :     _crack_flags(declareProperty<RealVectorValue>(_base_name + "crack_flags")),
      81         148 :     _crack_rotation(declareProperty<RankTwoTensor>(_base_name + "crack_rotation")),
      82         296 :     _crack_rotation_old(getMaterialPropertyOld<RankTwoTensor>(_base_name + "crack_rotation")),
      83         148 :     _crack_initiation_strain(
      84         148 :         declareProperty<RealVectorValue>(_base_name + "crack_initiation_strain")),
      85         148 :     _crack_initiation_strain_old(
      86         148 :         getMaterialPropertyOld<RealVectorValue>(_base_name + "crack_initiation_strain")),
      87         148 :     _crack_max_strain(declareProperty<RealVectorValue>(_base_name + "crack_max_strain")),
      88         444 :     _crack_max_strain_old(getMaterialPropertyOld<RealVectorValue>(_base_name + "crack_max_strain"))
      89             : {
      90             :   MultiMooseEnum prescribed_crack_directions =
      91         296 :       getParam<MultiMooseEnum>("prescribed_crack_directions");
      92         148 :   if (prescribed_crack_directions.size() > 0)
      93             :   {
      94          30 :     if (prescribed_crack_directions.size() > 3)
      95           0 :       mooseError("A maximum of three crack directions may be specified");
      96          90 :     for (unsigned int i = 0; i < prescribed_crack_directions.size(); ++i)
      97             :     {
      98         102 :       for (unsigned int j = 0; j < i; ++j)
      99          42 :         if (prescribed_crack_directions[i] == prescribed_crack_directions[j])
     100           0 :           mooseError("Entries in 'prescribed_crack_directions' cannot be repeated");
     101          60 :       _prescribed_crack_directions.push_back(
     102          60 :           static_cast<unsigned int>(prescribed_crack_directions.get(i)));
     103             :     }
     104             : 
     105             :     // Fill in the last remaining direction if 2 are specified
     106          30 :     if (_prescribed_crack_directions.size() == 2)
     107             :     {
     108           6 :       std::set<unsigned int> available_dirs = {0, 1, 2};
     109          18 :       for (auto dir : _prescribed_crack_directions)
     110          12 :         if (available_dirs.erase(dir) != 1)
     111           0 :           mooseError("Invalid prescribed crack direction:" + Moose::stringify(dir));
     112           6 :       if (available_dirs.size() != 1)
     113           0 :         mooseError("Error in finding remaining available crack direction");
     114           6 :       _prescribed_crack_directions.push_back(*available_dirs.begin());
     115             :     }
     116             :   }
     117         296 :   if (!isParamSetByUser("cracked_elasticity_type"))
     118          31 :     paramWarning(
     119             :         "cracked_elasticity_type",
     120             :         "Defaulting to the legacy option of 'DIAGONAL', but the 'FULL' option is preferred");
     121             : 
     122         147 :   _local_elastic_vector.resize(9);
     123         147 : }
     124             : 
     125             : void
     126         584 : ComputeSmearedCrackingStress::initQpStatefulProperties()
     127             : {
     128         584 :   ComputeMultipleInelasticStress::initQpStatefulProperties();
     129             : 
     130         584 :   _crack_damage[_qp] = 0.0;
     131             : 
     132         584 :   _crack_initiation_strain[_qp] = 0.0;
     133         584 :   _crack_max_strain[_qp](0) = 0.0;
     134             : 
     135         584 :   switch (_prescribed_crack_directions.size())
     136             :   {
     137         440 :     case 0:
     138             :     {
     139         440 :       _crack_rotation[_qp] = RankTwoTensor::Identity();
     140         440 :       break;
     141             :     }
     142          64 :     case 1:
     143             :     {
     144          64 :       _crack_rotation[_qp].zero();
     145          64 :       switch (_prescribed_crack_directions[0])
     146             :       {
     147          32 :         case 0:
     148             :         {
     149          32 :           _crack_rotation[_qp](0, 0) = 1.0;
     150          32 :           _crack_rotation[_qp](1, 1) = 1.0;
     151          32 :           _crack_rotation[_qp](2, 2) = 1.0;
     152          32 :           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          32 :         case 2:
     162             :         {
     163          32 :           _crack_rotation[_qp](2, 0) = 1.0;
     164          32 :           _crack_rotation[_qp](0, 1) = 1.0;
     165          32 :           _crack_rotation[_qp](1, 2) = 1.0;
     166          32 :           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         320 :       for (unsigned int i = 0; i < _prescribed_crack_directions.size(); ++i)
     179             :       {
     180             :         RealVectorValue crack_dir_vec;
     181         240 :         crack_dir_vec(_prescribed_crack_directions[i]) = 1.0;
     182         240 :         _crack_rotation[_qp].fillColumn(i, crack_dir_vec);
     183             :       }
     184             :     }
     185             :   }
     186         584 : }
     187             : 
     188             : void
     189         145 : ComputeSmearedCrackingStress::initialSetup()
     190             : {
     191         145 :   ComputeMultipleInelasticStress::initialSetup();
     192             : 
     193         290 :   if (!hasGuaranteedMaterialProperty(_elasticity_tensor_name, Guarantee::ISOTROPIC))
     194           0 :     mooseError("ComputeSmearedCrackingStress requires that the elasticity tensor be "
     195             :                "guaranteed isotropic");
     196             : 
     197         435 :   std::vector<MaterialName> soft_matls = getParam<std::vector<MaterialName>>("softening_models");
     198         315 :   for (auto soft_matl : soft_matls)
     199             :   {
     200             :     SmearedCrackSofteningBase * scsb =
     201         170 :         dynamic_cast<SmearedCrackSofteningBase *>(&getMaterialByName(soft_matl));
     202         170 :     if (scsb)
     203         170 :       _softening_models.push_back(scsb);
     204             :     else
     205           0 :       paramError("softening_models", "Model " + soft_matl + " is not a softening model");
     206             :   }
     207         145 :   if (_softening_models.size() == 1)
     208             :   {
     209             :     // Reuse the same model in all 3 directions
     210         132 :     _softening_models.push_back(_softening_models[0]);
     211         132 :     _softening_models.push_back(_softening_models[0]);
     212             :   }
     213          13 :   else if (_softening_models.size() != 3)
     214           1 :     paramError("softening_models", "Either 1 or 3 softening models must be specified");
     215         144 : }
     216             : 
     217             : void
     218      640360 : ComputeSmearedCrackingStress::computeQpStress()
     219             : {
     220             :   bool force_elasticity_rotation = false;
     221             : 
     222      640360 :   if (!previouslyCracked())
     223       37800 :     computeQpStressIntermediateConfiguration();
     224             :   else
     225             :   {
     226      602560 :     _elastic_strain[_qp] = _elastic_strain_old[_qp] + _strain_increment[_qp];
     227             : 
     228             :     // Propagate behavior from the (now inactive) inelastic models
     229      602560 :     _inelastic_strain[_qp] = _inelastic_strain_old[_qp];
     230      602560 :     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      602560 :     _material_timestep_limit[_qp] = std::numeric_limits<Real>::max();
     238             : 
     239             :     // update _local_elasticity_tensor based on cracking state in previous time step
     240      602560 :     updateLocalElasticityTensor();
     241             : 
     242             :     // Calculate stress in intermediate configuration
     243      602560 :     _stress[_qp] = _local_elasticity_tensor * _elastic_strain[_qp];
     244             : 
     245      602560 :     _Jacobian_mult[_qp] = _local_elasticity_tensor;
     246             :     force_elasticity_rotation = true;
     247             :   }
     248             : 
     249             :   // compute crack status and adjust stress
     250      640360 :   updateCrackingStateAndStress();
     251             : 
     252      640360 :   if (_perform_finite_strain_rotations)
     253             :   {
     254      640360 :     finiteStrainRotation(force_elasticity_rotation);
     255      640360 :     _crack_rotation[_qp] = _rotation_increment[_qp] * _crack_rotation[_qp];
     256             :   }
     257      640360 : }
     258             : 
     259             : void
     260      602560 : ComputeSmearedCrackingStress::updateLocalElasticityTensor()
     261             : {
     262             :   const Real youngs_modulus =
     263      602560 :       ElasticityTensorTools::getIsotropicYoungsModulus(_elasticity_tensor[_qp]);
     264             : 
     265             :   bool cracking_locally_active = false;
     266             : 
     267      602560 :   const Real cracking_stress = _cracking_stress[_qp];
     268             : 
     269      602560 :   if (cracking_stress > 0)
     270             :   {
     271             :     RealVectorValue stiffness_ratio_local(1.0, 1.0, 1.0);
     272      602560 :     const RankTwoTensor & R = _crack_rotation_old[_qp];
     273      602560 :     RankTwoTensor ePrime(_elastic_strain_old[_qp]);
     274      602560 :     ePrime.rotate(R.transpose());
     275             : 
     276     2410240 :     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     1807680 :       if (_crack_damage_old[_qp](i) > 0.0)
     280             :       {
     281      636336 :         if (_cracking_neg_fraction == 0.0 && MooseUtils::absoluteFuzzyLessThan(ePrime(i, i), 0.0))
     282       77248 :           stiffness_ratio_local(i) = 1.0;
     283           0 :         else if (_cracking_neg_fraction > 0.0 &&
     284      559088 :                  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      559088 :           stiffness_ratio_local(i) = (1.0 - _crack_damage_old[_qp](i));
     299             :           cracking_locally_active = true;
     300             :         }
     301             :       }
     302             :     }
     303             : 
     304      602560 :     if (cracking_locally_active)
     305             :     {
     306             :       // Update the elasticity tensor in the crack coordinate system
     307      525888 :       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        1456 :         const Real c01 = (c0_coupled && c1_coupled ? 1.0 : 0.0);
     314        1456 :         const Real c02 = (c0_coupled && c2_coupled ? 1.0 : 0.0);
     315        1456 :         const Real c12 = (c1_coupled && c2_coupled ? 1.0 : 0.0);
     316             : 
     317        1456 :         const Real c01_shear_retention = (c0_coupled && c1_coupled ? 1.0 : _shear_retention_factor);
     318        1456 :         const Real c02_shear_retention = (c0_coupled && c2_coupled ? 1.0 : _shear_retention_factor);
     319        1456 :         const Real c12_shear_retention = (c1_coupled && c2_coupled ? 1.0 : _shear_retention_factor);
     320             : 
     321        1456 :         _local_elastic_vector[0] = (c0_coupled ? _elasticity_tensor[_qp](0, 0, 0, 0)
     322             :                                                : stiffness_ratio_local(0) * youngs_modulus);
     323        1456 :         _local_elastic_vector[1] = _elasticity_tensor[_qp](0, 0, 1, 1) * c01;
     324        1456 :         _local_elastic_vector[2] = _elasticity_tensor[_qp](0, 0, 2, 2) * c02;
     325        1456 :         _local_elastic_vector[3] = (c1_coupled ? _elasticity_tensor[_qp](1, 1, 1, 1)
     326             :                                                : stiffness_ratio_local(1) * youngs_modulus);
     327        1456 :         _local_elastic_vector[4] = _elasticity_tensor[_qp](1, 1, 2, 2) * c12;
     328        1456 :         _local_elastic_vector[5] = (c2_coupled ? _elasticity_tensor[_qp](2, 2, 2, 2)
     329             :                                                : stiffness_ratio_local(2) * youngs_modulus);
     330        1456 :         _local_elastic_vector[6] = _elasticity_tensor[_qp](1, 2, 1, 2) * c12_shear_retention;
     331        1456 :         _local_elastic_vector[7] = _elasticity_tensor[_qp](0, 2, 0, 2) * c02_shear_retention;
     332        1456 :         _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      524432 :         const Real c01 = c0 * c1;
     341      524432 :         const Real c02 = c0 * c2;
     342      524432 :         const Real c12 = c1 * c2;
     343             : 
     344      524720 :         const Real c01_shear_retention = std::max(c01, _shear_retention_factor);
     345      524432 :         const Real c02_shear_retention = std::max(c02, _shear_retention_factor);
     346      524432 :         const Real c12_shear_retention = std::max(c12, _shear_retention_factor);
     347             : 
     348      524432 :         _local_elastic_vector[0] = _elasticity_tensor[_qp](0, 0, 0, 0) * c0;
     349      524432 :         _local_elastic_vector[1] = _elasticity_tensor[_qp](0, 0, 1, 1) * c01;
     350      524432 :         _local_elastic_vector[2] = _elasticity_tensor[_qp](0, 0, 2, 2) * c02;
     351      524432 :         _local_elastic_vector[3] = _elasticity_tensor[_qp](1, 1, 1, 1) * c1;
     352      524432 :         _local_elastic_vector[4] = _elasticity_tensor[_qp](1, 1, 2, 2) * c12;
     353      524432 :         _local_elastic_vector[5] = _elasticity_tensor[_qp](2, 2, 2, 2) * c2;
     354      524432 :         _local_elastic_vector[6] = _elasticity_tensor[_qp](1, 2, 1, 2) * c12_shear_retention;
     355      524432 :         _local_elastic_vector[7] = _elasticity_tensor[_qp](0, 2, 0, 2) * c02_shear_retention;
     356      524432 :         _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      525888 :       _local_elasticity_tensor.fillFromInputVector(_local_elastic_vector,
     362             :                                                    RankFourTensor::symmetric9);
     363             : 
     364             :       // Rotate the modified elasticity tensor back into global coordinates
     365      525888 :       _local_elasticity_tensor.rotate(R);
     366             :     }
     367             :   }
     368             :   if (!cracking_locally_active)
     369       76672 :     _local_elasticity_tensor = _elasticity_tensor[_qp];
     370      602560 : }
     371             : 
     372             : void
     373      640360 : ComputeSmearedCrackingStress::updateCrackingStateAndStress()
     374             : {
     375             :   const Real youngs_modulus =
     376      640360 :       ElasticityTensorTools::getIsotropicYoungsModulus(_elasticity_tensor[_qp]);
     377             : 
     378             :   const Real poissons_ratio =
     379             :       ElasticityTensorTools::getIsotropicPoissonsRatio(_elasticity_tensor[_qp]);
     380             : 
     381      640360 :   Real cracking_stress = _cracking_stress[_qp];
     382             : 
     383      640360 :   if (cracking_stress > 0)
     384             :   {
     385             :     // Initializing crack states
     386      640324 :     _crack_rotation[_qp] = _crack_rotation_old[_qp];
     387             : 
     388     2561296 :     for (unsigned i = 0; i < 3; ++i)
     389             :     {
     390     1920972 :       _crack_max_strain[_qp](i) = _crack_max_strain_old[_qp](i);
     391     1920972 :       _crack_initiation_strain[_qp](i) = _crack_initiation_strain_old[_qp](i);
     392     1920972 :       _crack_damage[_qp](i) = _crack_damage_old[_qp](i);
     393             :     }
     394             : 
     395             :     // Compute crack orientations: updated _crack_rotation[_qp] based on current strain
     396             :     RealVectorValue strain_in_crack_dir;
     397      640324 :     computeCrackStrainAndOrientation(strain_in_crack_dir);
     398             : 
     399     2561296 :     for (unsigned i = 0; i < 3; ++i)
     400             :     {
     401     1920972 :       if (strain_in_crack_dir(i) > _crack_max_strain[_qp](i))
     402      639496 :         _crack_max_strain[_qp](i) = strain_in_crack_dir(i);
     403             :     }
     404             : 
     405             :     // Check for new cracks.
     406             :     // Rotate stress to cracked orientation.
     407      640324 :     const RankTwoTensor & R = _crack_rotation[_qp];
     408      640324 :     RankTwoTensor sigmaPrime(_stress[_qp]);
     409      640324 :     sigmaPrime.rotate(R.transpose()); // stress in crack coordinates
     410             : 
     411             :     unsigned int num_cracks = 0;
     412     2561296 :     for (unsigned int i = 0; i < 3; ++i)
     413             :     {
     414     1920972 :       if (_crack_damage_old[_qp](i) > 0.0)
     415      636336 :         ++num_cracks;
     416             :     }
     417             : 
     418             :     bool cracked(false);
     419             :     RealVectorValue sigma;
     420             :     mooseAssert(_softening_models.size() == 3, "Must have 3 softening models");
     421     2561296 :     for (unsigned int i = 0; i < 3; ++i)
     422             :     {
     423     1920972 :       sigma(i) = sigmaPrime(i, i);
     424             : 
     425     1920972 :       Real stiffness_ratio = 1.0 - _crack_damage[_qp](i);
     426             : 
     427     1920972 :       const bool pre_existing_crack = (_crack_damage_old[_qp](i) > 0.0);
     428     1920972 :       const bool met_stress_criterion = (sigma(i) > cracking_stress);
     429     1920972 :       const bool loading_existing_crack = (strain_in_crack_dir(i) >= _crack_max_strain[_qp](i));
     430     1920972 :       const bool allowed_to_crack = (pre_existing_crack || num_cracks < _max_cracks);
     431             :       bool new_crack = false;
     432             : 
     433     1920972 :       cracked |= pre_existing_crack;
     434             : 
     435             :       // Adjustments for newly created cracks
     436     1920972 :       if (met_stress_criterion && !pre_existing_crack && allowed_to_crack)
     437             :       {
     438             :         new_crack = true;
     439        3928 :         ++num_cracks;
     440             : 
     441             :         // Assume Poisson's ratio drops to zero for this direction.  Stiffness is then Young's
     442             :         // modulus.
     443        3928 :         _crack_initiation_strain[_qp](i) = cracking_stress / youngs_modulus;
     444             : 
     445        3928 :         if (_crack_max_strain[_qp](i) < _crack_initiation_strain[_qp](i))
     446         564 :           _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     1917044 :       if (new_crack || (pre_existing_crack && loading_existing_crack))
     451             :       {
     452             :         cracked = true;
     453      171180 :         _softening_models[i]->computeCrackingRelease(sigma(i),
     454             :                                                      stiffness_ratio,
     455             :                                                      strain_in_crack_dir(i),
     456      171180 :                                                      _crack_initiation_strain[_qp](i),
     457             :                                                      _crack_max_strain[_qp](i),
     458             :                                                      cracking_stress,
     459             :                                                      youngs_modulus,
     460             :                                                      poissons_ratio);
     461      171180 :         _crack_damage[_qp](i) = 1.0 - stiffness_ratio;
     462             :       }
     463             : 
     464     1312396 :       else if (cracked && _cracking_neg_fraction > 0 &&
     465     1749792 :                _crack_initiation_strain[_qp](i) * _cracking_neg_fraction > strain_in_crack_dir(i) &&
     466             :                -_crack_initiation_strain[_qp](i) * _cracking_neg_fraction < strain_in_crack_dir(i))
     467             :       {
     468             :         const Real etr = _cracking_neg_fraction * _crack_initiation_strain[_qp](i);
     469           0 :         const Real Eo = cracking_stress / _crack_initiation_strain[_qp](i);
     470           0 :         const Real Ec = Eo * (1.0 - _crack_damage_old[_qp](i));
     471           0 :         const Real a = (Ec - Eo) / (4.0 * etr);
     472           0 :         const Real b = 0.5 * (Ec + Eo);
     473           0 :         const Real 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      640324 :     if (cracked)
     479      604056 :       updateStressTensorForCracking(_stress[_qp], sigma);
     480             :   }
     481             : 
     482      640360 :   _crack_flags[_qp](0) = 1.0 - _crack_damage[_qp](2);
     483      640360 :   _crack_flags[_qp](1) = 1.0 - _crack_damage[_qp](1);
     484      640360 :   _crack_flags[_qp](2) = 1.0 - _crack_damage[_qp](0);
     485      640360 : }
     486             : 
     487             : void
     488      640324 : ComputeSmearedCrackingStress::computeCrackStrainAndOrientation(
     489             :     RealVectorValue & 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      640324 :   const unsigned int num_known_dirs = getNumKnownCrackDirs();
     495             : 
     496      640324 :   if (num_known_dirs == 0)
     497             :   {
     498       36852 :     std::vector<Real> eigval(3, 0.0);
     499       36852 :     RankTwoTensor eigvec;
     500             : 
     501       36852 :     _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       36852 :     _crack_rotation[_qp].fillColumn(0, eigvec.column(2));
     507       36852 :     _crack_rotation[_qp].fillColumn(1, eigvec.column(1));
     508       36852 :     _crack_rotation[_qp].fillColumn(2, eigvec.column(0));
     509             : 
     510       36852 :     strain_in_crack_dir(0) = eigval[2];
     511       36852 :     strain_in_crack_dir(1) = eigval[1];
     512       36852 :     strain_in_crack_dir(2) = eigval[0];
     513       36852 :   }
     514      603472 :   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      325256 :     const RankTwoTensor & R = _crack_rotation[_qp];
     525      325256 :     RankTwoTensor ePrime(_elastic_strain[_qp]);
     526      325256 :     ePrime.rotate(R.transpose()); // elastic strain in crack coordinates
     527             : 
     528             :     // 2.
     529      325256 :     ColumnMajorMatrix e2x2(2, 2);
     530      325256 :     e2x2(0, 0) = ePrime(1, 1);
     531      325256 :     e2x2(1, 0) = ePrime(2, 1);
     532      325256 :     e2x2(0, 1) = ePrime(1, 2);
     533      325256 :     e2x2(1, 1) = ePrime(2, 2);
     534             : 
     535             :     // 3.
     536      325256 :     ColumnMajorMatrix e_val2x1(2, 1);
     537      325256 :     ColumnMajorMatrix e_vec2x2(2, 2);
     538      325256 :     e2x2.eigen(e_val2x1, e_vec2x2);
     539             : 
     540             :     // 4.
     541             :     RankTwoTensor eigvec(
     542      325256 :         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      325256 :     _crack_rotation[_qp] = _crack_rotation_old[_qp] * eigvec; // Roe implementation
     545             : 
     546      325256 :     strain_in_crack_dir(0) = ePrime(0, 0);
     547      325256 :     strain_in_crack_dir(1) = e_val2x1(1, 0);
     548      325256 :     strain_in_crack_dir(2) = e_val2x1(0, 0);
     549             :   }
     550      278216 :   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      278216 :     const RankTwoTensor & R = _crack_rotation[_qp];
     555      278216 :     RankTwoTensor ePrime(_elastic_strain[_qp]);
     556      278216 :     ePrime.rotate(R.transpose()); // elastic strain in crack coordinates
     557             : 
     558      278216 :     strain_in_crack_dir(0) = ePrime(0, 0);
     559      278216 :     strain_in_crack_dir(1) = ePrime(1, 1);
     560      278216 :     strain_in_crack_dir(2) = ePrime(2, 2);
     561             :   }
     562             :   else
     563           0 :     mooseError("Invalid number of known crack directions");
     564      640324 : }
     565             : 
     566             : unsigned int
     567      640324 : ComputeSmearedCrackingStress::getNumKnownCrackDirs() const
     568             : {
     569             :   unsigned int num_known_dirs = 0;
     570     2561296 :   for (unsigned int i = 0; i < 3; ++i)
     571             :   {
     572     1920972 :     if (_crack_damage_old[_qp](i) > 0.0 || _prescribed_crack_directions.size() >= i + 1)
     573      981120 :       ++num_known_dirs;
     574             :   }
     575      640324 :   return num_known_dirs;
     576             : }
     577             : 
     578             : void
     579      604056 : ComputeSmearedCrackingStress::updateStressTensorForCracking(RankTwoTensor & tensor,
     580             :                                                             const RealVectorValue & sigma)
     581             : {
     582             :   // Get transformation matrix
     583      604056 :   const RankTwoTensor & R = _crack_rotation[_qp];
     584             :   // Rotate to crack frame
     585      604056 :   tensor.rotate(R.transpose());
     586             : 
     587             :   // Reset stress if cracked
     588     2416224 :   for (unsigned int i = 0; i < 3; ++i)
     589     1812168 :     if (_crack_damage[_qp](i) > 0.0)
     590             :     {
     591      638212 :       const Real stress_correction_ratio = (tensor(i, i) - sigma(i)) / tensor(i, i);
     592      638212 :       if (stress_correction_ratio > _max_stress_correction)
     593           0 :         tensor(i, i) *= (1.0 - _max_stress_correction);
     594      638212 :       else if (stress_correction_ratio < -_max_stress_correction)
     595           0 :         tensor(i, i) *= (1.0 + _max_stress_correction);
     596             :       else
     597      638212 :         tensor(i, i) = sigma(i);
     598             :     }
     599             : 
     600             :   // Rotate back to global frame
     601      604056 :   tensor.rotate(R);
     602      604056 : }
     603             : 
     604             : bool
     605      640360 : ComputeSmearedCrackingStress::previouslyCracked()
     606             : {
     607     1082160 :   for (unsigned int i = 0; i < 3; ++i)
     608     1044360 :     if (_crack_damage_old[_qp](i) > 0.0)
     609             :       return true;
     610             :   return false;
     611             : }

Generated by: LCOV version 1.14