LCOV - code coverage report
Current view: top level - src/materials - ComputeLinearElasticPFFractureStress.C (source / functions) Hit Total Coverage
Test: idaholab/moose solid_mechanics: f45d79 Lines: 0 131 0.0 %
Date: 2025-07-25 05:00:39 Functions: 0 7 0.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 "ComputeLinearElasticPFFractureStress.h"
      11             : #include "MathUtils.h"
      12             : 
      13             : registerMooseObject("SolidMechanicsApp", ComputeLinearElasticPFFractureStress);
      14             : 
      15             : InputParameters
      16           0 : ComputeLinearElasticPFFractureStress::validParams()
      17             : {
      18           0 :   InputParameters params = ComputePFFractureStressBase::validParams();
      19           0 :   params.addClassDescription("Computes the stress and free energy derivatives for the phase field "
      20             :                              "fracture model, with small strain");
      21           0 :   MooseEnum Decomposition("strain_spectral strain_vol_dev stress_spectral none", "none");
      22           0 :   params.addParam<MooseEnum>("decomposition_type",
      23             :                              Decomposition,
      24           0 :                              "Decomposition approaches.  Choices are: " +
      25           0 :                                  Decomposition.getRawNames());
      26           0 :   return params;
      27           0 : }
      28             : 
      29           0 : ComputeLinearElasticPFFractureStress::ComputeLinearElasticPFFractureStress(
      30           0 :     const InputParameters & parameters)
      31             :   : ComputePFFractureStressBase(parameters),
      32             :     GuaranteeConsumer(this),
      33           0 :     _decomposition_type(getParam<MooseEnum>("decomposition_type").getEnum<Decomposition_type>())
      34             : {
      35           0 : }
      36             : 
      37             : void
      38           0 : ComputeLinearElasticPFFractureStress::initialSetup()
      39             : {
      40           0 :   if ((_decomposition_type == Decomposition_type::strain_vol_dev ||
      41           0 :        _decomposition_type == Decomposition_type::strain_spectral) &&
      42           0 :       !hasGuaranteedMaterialProperty(_elasticity_tensor_name, Guarantee::ISOTROPIC))
      43           0 :     mooseError("Decomposition approach of strain_vol_dev and strain_spectral can only be used with "
      44             :                "isotropic elasticity tensor materials, use stress_spectral for anistropic "
      45             :                "elasticity tensor materials");
      46           0 : }
      47             : 
      48             : void
      49           0 : ComputeLinearElasticPFFractureStress::computeStrainSpectral(Real & F_pos, Real & F_neg)
      50             : {
      51             :   // Isotropic elasticity is assumed and should be enforced
      52           0 :   const Real lambda = _elasticity_tensor[_qp](0, 0, 1, 1);
      53           0 :   const Real mu = _elasticity_tensor[_qp](0, 1, 0, 1);
      54             : 
      55           0 :   RankTwoTensor I2(RankTwoTensor::initIdentity);
      56             : 
      57             :   // Compute eigenvectors and eigenvalues of mechanical strain and projection tensor
      58           0 :   RankTwoTensor eigvec;
      59           0 :   std::vector<Real> eigval(LIBMESH_DIM);
      60             :   RankFourTensor Ppos =
      61           0 :       _mechanical_strain[_qp].positiveProjectionEigenDecomposition(eigval, eigvec);
      62           0 :   RankFourTensor I4sym(RankFourTensor::initIdentitySymmetricFour);
      63             : 
      64             :   // Calculate tensors of outerproduct of eigen vectors
      65           0 :   std::vector<RankTwoTensor> etens(LIBMESH_DIM);
      66             : 
      67           0 :   for (const auto i : make_range(Moose::dim))
      68           0 :     etens[i] = RankTwoTensor::selfOuterProduct(eigvec.column(i));
      69             : 
      70             :   // Separate out positive and negative eigen values
      71           0 :   std::vector<Real> epos(LIBMESH_DIM), eneg(LIBMESH_DIM);
      72           0 :   for (const auto i : make_range(Moose::dim))
      73             :   {
      74           0 :     epos[i] = (std::abs(eigval[i]) + eigval[i]) / 2.0;
      75           0 :     eneg[i] = -(std::abs(eigval[i]) - eigval[i]) / 2.0;
      76             :   }
      77             : 
      78             :   // Seprate positive and negative sums of all eigenvalues
      79             :   Real etr = 0.0;
      80           0 :   for (const auto i : make_range(Moose::dim))
      81           0 :     etr += eigval[i];
      82             : 
      83           0 :   const Real etrpos = (std::abs(etr) + etr) / 2.0;
      84           0 :   const Real etrneg = -(std::abs(etr) - etr) / 2.0;
      85             : 
      86             :   // Calculate the tensile (postive) and compressive (negative) parts of stress
      87           0 :   RankTwoTensor stress0pos, stress0neg;
      88           0 :   for (const auto i : make_range(Moose::dim))
      89             :   {
      90           0 :     stress0pos += etens[i] * (lambda * etrpos + 2.0 * mu * epos[i]);
      91           0 :     stress0neg += etens[i] * (lambda * etrneg + 2.0 * mu * eneg[i]);
      92             :   }
      93             : 
      94             :   // sum squares of epos and eneg
      95             :   Real pval(0.0), nval(0.0);
      96           0 :   for (const auto i : make_range(Moose::dim))
      97             :   {
      98           0 :     pval += epos[i] * epos[i];
      99           0 :     nval += eneg[i] * eneg[i];
     100             :   }
     101             : 
     102           0 :   _stress[_qp] = stress0pos * _D[_qp] - _pressure[_qp] * I2 * _I[_qp] + stress0neg;
     103             : 
     104             :   // Energy with positive principal strains
     105           0 :   F_pos = lambda * etrpos * etrpos / 2.0 + mu * pval;
     106           0 :   F_neg = -lambda * etrneg * etrneg / 2.0 + mu * nval;
     107             : 
     108             :   // 2nd derivative wrt c and strain = 0.0 if we used the previous step's history varible
     109           0 :   if (_use_current_hist)
     110           0 :     _d2Fdcdstrain[_qp] = stress0pos * _dDdc[_qp];
     111             : 
     112             :   // Used in StressDivergencePFFracTensors off-diagonal Jacobian
     113           0 :   _dstress_dc[_qp] = stress0pos * _dDdc[_qp] - _pressure[_qp] * I2 * _dIdc[_qp];
     114             : 
     115           0 :   _Jacobian_mult[_qp] = (I4sym - (1 - _D[_qp]) * Ppos) * _elasticity_tensor[_qp];
     116           0 : }
     117             : 
     118             : void
     119           0 : ComputeLinearElasticPFFractureStress::computeStressSpectral(Real & F_pos, Real & F_neg)
     120             : {
     121             :   // Compute Uncracked stress
     122           0 :   RankTwoTensor stress = _elasticity_tensor[_qp] * _mechanical_strain[_qp];
     123             : 
     124           0 :   RankTwoTensor I2(RankTwoTensor::initIdentity);
     125             : 
     126             :   // Create the positive and negative projection tensors
     127           0 :   RankFourTensor I4sym(RankFourTensor::initIdentitySymmetricFour);
     128             :   std::vector<Real> eigval;
     129           0 :   RankTwoTensor eigvec;
     130           0 :   RankFourTensor Ppos = stress.positiveProjectionEigenDecomposition(eigval, eigvec);
     131             : 
     132             :   // Project the positive and negative stresses
     133           0 :   RankTwoTensor stress0pos = Ppos * stress;
     134           0 :   RankTwoTensor stress0neg = stress - stress0pos;
     135             : 
     136             :   // Compute the positive and negative elastic energies
     137           0 :   F_pos = (stress0pos).doubleContraction(_mechanical_strain[_qp]) / 2.0;
     138           0 :   F_neg = (stress0neg).doubleContraction(_mechanical_strain[_qp]) / 2.0;
     139             : 
     140           0 :   _stress[_qp] = stress0pos * _D[_qp] - _pressure[_qp] * I2 * _I[_qp] + stress0neg;
     141             : 
     142             :   // 2nd derivative wrt c and strain = 0.0 if we used the previous step's history varible
     143           0 :   if (_use_current_hist)
     144           0 :     _d2Fdcdstrain[_qp] = stress0pos * _dDdc[_qp];
     145             : 
     146             :   // Used in StressDivergencePFFracTensors off-diagonal Jacobian
     147           0 :   _dstress_dc[_qp] = stress0pos * _dDdc[_qp] - _pressure[_qp] * I2 * _dIdc[_qp];
     148             : 
     149           0 :   _Jacobian_mult[_qp] = (I4sym - (1 - _D[_qp]) * Ppos) * _elasticity_tensor[_qp];
     150           0 : }
     151             : 
     152             : void
     153           0 : ComputeLinearElasticPFFractureStress::computeStrainVolDev(Real & F_pos, Real & F_neg)
     154             : {
     155             :   // Isotropic elasticity is assumed and should be enforced
     156           0 :   const Real lambda = _elasticity_tensor[_qp](0, 0, 1, 1);
     157           0 :   const Real mu = _elasticity_tensor[_qp](0, 1, 0, 1);
     158           0 :   const Real k = lambda + 2.0 * mu / LIBMESH_DIM;
     159             : 
     160           0 :   RankTwoTensor I2(RankTwoTensor::initIdentity);
     161             :   RankFourTensor I2I2 = I2.outerProduct(I2);
     162             : 
     163           0 :   RankFourTensor Jacobian_pos, Jacobian_neg;
     164           0 :   RankTwoTensor strain0vol, strain0dev;
     165           0 :   RankTwoTensor stress0pos, stress0neg;
     166             :   Real strain0tr, strain0tr_neg, strain0tr_pos;
     167             : 
     168           0 :   strain0dev = _mechanical_strain[_qp].deviatoric();
     169           0 :   strain0vol = _mechanical_strain[_qp] - strain0dev;
     170           0 :   strain0tr = _mechanical_strain[_qp].trace();
     171           0 :   strain0tr_neg = std::min(strain0tr, 0.0);
     172           0 :   strain0tr_pos = strain0tr - strain0tr_neg;
     173           0 :   stress0neg = k * strain0tr_neg * I2;
     174           0 :   stress0pos = _elasticity_tensor[_qp] * _mechanical_strain[_qp] - stress0neg;
     175             :   // Energy with positive principal strains
     176           0 :   RankTwoTensor strain0dev2 = strain0dev * strain0dev;
     177           0 :   F_pos = 0.5 * k * strain0tr_pos * strain0tr_pos + mu * strain0dev2.trace();
     178           0 :   F_neg = 0.5 * k * strain0tr_neg * strain0tr_neg;
     179             : 
     180           0 :   _stress[_qp] = stress0pos * _D[_qp] - _pressure[_qp] * I2 * _I[_qp] + stress0neg;
     181             : 
     182             :   // 2nd derivative wrt c and strain = 0.0 if we used the previous step's history varible
     183           0 :   if (_use_current_hist)
     184           0 :     _d2Fdcdstrain[_qp] = stress0pos * _dDdc[_qp];
     185             : 
     186             :   // Used in StressDivergencePFFracTensors off-diagonal Jacobian
     187           0 :   _dstress_dc[_qp] = stress0pos * _dDdc[_qp] - _pressure[_qp] * I2 * _dIdc[_qp];
     188             : 
     189           0 :   if (strain0tr < 0)
     190           0 :     Jacobian_neg = k * I2I2;
     191           0 :   Jacobian_pos = _elasticity_tensor[_qp] - Jacobian_neg;
     192           0 :   _Jacobian_mult[_qp] = _D[_qp] * Jacobian_pos + Jacobian_neg;
     193           0 : }
     194             : 
     195             : void
     196           0 : ComputeLinearElasticPFFractureStress::computeQpStress()
     197             : {
     198             :   Real F_pos, F_neg;
     199           0 :   RankTwoTensor I2(RankTwoTensor::initIdentity);
     200             : 
     201           0 :   switch (_decomposition_type)
     202             :   {
     203           0 :     case Decomposition_type::strain_spectral:
     204           0 :       computeStrainSpectral(F_pos, F_neg);
     205             :       break;
     206           0 :     case Decomposition_type::strain_vol_dev:
     207           0 :       computeStrainVolDev(F_pos, F_neg);
     208             :       break;
     209           0 :     case Decomposition_type::stress_spectral:
     210           0 :       computeStressSpectral(F_pos, F_neg);
     211             :       break;
     212           0 :     default:
     213             :     {
     214           0 :       RankTwoTensor stress = _elasticity_tensor[_qp] * _mechanical_strain[_qp];
     215           0 :       F_pos = stress.doubleContraction(_mechanical_strain[_qp]) / 2.0;
     216           0 :       F_neg = 0.0;
     217           0 :       if (_use_current_hist)
     218           0 :         _d2Fdcdstrain[_qp] = stress * _dDdc[_qp];
     219             : 
     220           0 :       _stress[_qp] = _D[_qp] * stress - _pressure[_qp] * I2 * _I[_qp];
     221           0 :       _dstress_dc[_qp] = stress * _dDdc[_qp] - _pressure[_qp] * I2 * _dIdc[_qp];
     222           0 :       _Jacobian_mult[_qp] = _D[_qp] * _elasticity_tensor[_qp];
     223             :     }
     224             :   }
     225             : 
     226             :   // // Assign history variable
     227           0 :   Real hist_variable = _H_old[_qp];
     228           0 :   if (_use_snes_vi_solver)
     229             :   {
     230           0 :     _H[_qp] = F_pos;
     231             : 
     232           0 :     if (_use_current_hist)
     233             :       hist_variable = _H[_qp];
     234             :   }
     235             :   else
     236             :   {
     237           0 :     if (F_pos > _H_old[_qp])
     238           0 :       _H[_qp] = F_pos;
     239             :     else
     240           0 :       _H[_qp] = _H_old[_qp];
     241             : 
     242           0 :     if (_use_current_hist)
     243           0 :       hist_variable = _H[_qp];
     244             : 
     245           0 :     if (hist_variable < _barrier[_qp])
     246             :       hist_variable = _barrier[_qp];
     247             :   }
     248             : 
     249             :   // Elastic free energy density
     250           0 :   _E[_qp] =
     251           0 :       hist_variable * _D[_qp] + F_neg - _pressure[_qp] * _mechanical_strain[_qp].trace() * _I[_qp];
     252           0 :   _dEdc[_qp] =
     253           0 :       hist_variable * _dDdc[_qp] - _pressure[_qp] * _mechanical_strain[_qp].trace() * _dIdc[_qp];
     254           0 :   _d2Ed2c[_qp] = hist_variable * _d2Dd2c[_qp] -
     255           0 :                  _pressure[_qp] * _mechanical_strain[_qp].trace() * _d2Id2c[_qp];
     256           0 : }

Generated by: LCOV version 1.14