LCOV - code coverage report
Current view: top level - src/materials - NEMLStressBase.C (source / functions) Hit Total Coverage
Test: idaholab/blackbear: 276f95 Lines: 88 100 88.0 %
Date: 2025-10-28 03:10:25 Functions: 7 7 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /****************************************************************/
       2             : /*               DO NOT MODIFY THIS HEADER                      */
       3             : /*                       BlackBear                              */
       4             : /*                                                              */
       5             : /*           (c) 2017 Battelle Energy Alliance, LLC             */
       6             : /*                   ALL RIGHTS RESERVED                        */
       7             : /*                                                              */
       8             : /*          Prepared by Battelle Energy Alliance, LLC           */
       9             : /*            Under Contract No. DE-AC07-05ID14517              */
      10             : /*            With the U. S. Department of Energy               */
      11             : /*                                                              */
      12             : /*            See COPYRIGHT for full restrictions               */
      13             : /****************************************************************/
      14             : 
      15             : #ifdef NEML_ENABLED
      16             : 
      17             : #include "NEMLStressBase.h"
      18             : #include "Conversion.h"
      19             : 
      20             : #include <limits>
      21             : #include <type_traits>
      22             : 
      23             : InputParameters
      24        1686 : NEMLStressBase::validParams()
      25             : {
      26        1686 :   InputParameters params = ComputeStressBase::validParams();
      27        3372 :   params.addCoupledVar("temperature", 0.0, "Coupled temperature");
      28        3372 :   params.setDocUnit("temperature", "temperature");
      29        3372 :   params.addParam<Real>("target_increment",
      30             :                         "L2 norm of the inelastic strain increment to target by adjusting the "
      31             :                         "timestep");
      32        3372 :   params.setDocUnit("target_increment", "unitless");
      33        3372 :   params.addParam<bool>("debug",
      34        3372 :                         false,
      35             :                         "Print history and strain state at the current quadrature point when a "
      36             :                         "NEML stress update fails.");
      37        1686 :   return params;
      38           0 : }
      39             : 
      40        1290 : NEMLStressBase::NEMLStressBase(const InputParameters & parameters)
      41             :   : ComputeStressBase(parameters),
      42        1290 :     _hist(declareProperty<std::vector<Real>>(_base_name + "hist")),
      43        2580 :     _hist_old(getMaterialPropertyOld<std::vector<Real>>(_base_name + "hist")),
      44        1290 :     _mechanical_strain_old(
      45        1290 :         getMaterialPropertyOldByName<RankTwoTensor>(_base_name + "mechanical_strain")),
      46        2580 :     _stress_old(getMaterialPropertyOld<RankTwoTensor>(_base_name + "stress")),
      47        1290 :     _energy(declareProperty<Real>(_base_name + "energy")),
      48        2580 :     _energy_old(getMaterialPropertyOld<Real>(_base_name + "energy")),
      49        1290 :     _dissipation(declareProperty<Real>(_base_name + "dissipation")),
      50        2580 :     _dissipation_old(getMaterialPropertyOld<Real>(_base_name + "dissipation")),
      51        1290 :     _temperature(coupledValue("temperature")),
      52        1290 :     _temperature_old(coupledValueOld("temperature")),
      53        1290 :     _inelastic_strain(declareProperty<RankTwoTensor>(_base_name + "inelastic_strain")),
      54        2580 :     _compute_dt(isParamValid("target_increment")),
      55        1332 :     _target_increment(_compute_dt ? getParam<Real>("target_increment") : 0.0),
      56        1290 :     _inelastic_strain_old(
      57        1332 :         _compute_dt ? &getMaterialPropertyOld<RankTwoTensor>(_base_name + "inelastic_strain")
      58             :                     : nullptr),
      59        1290 :     _material_dt(_compute_dt ? &declareProperty<Real>("material_timestep_limit") : nullptr),
      60        1290 :     _damage_index(nullptr),
      61        3870 :     _debug(getParam<bool>("debug"))
      62             : {
      63             :   // We're letting NEML write to raw pointers. Best make sure the stored types are
      64             :   // the same on both ends.
      65             :   static_assert(std::is_same<Real, double>::value,
      66             :                 "MOOSE/libMesh must be compiled with double precision Real types");
      67        1290 : }
      68             : 
      69             : void
      70      209924 : NEMLStressBase::computeQpStress()
      71             : {
      72             :   // We must update:
      73             :   // 1) _stress
      74             :   // 2) _Jacobian_mult
      75             :   // 3) _elastic_strain
      76             :   // 4) _history
      77             : 
      78             :   // First do some declaration and translation
      79             :   double s_np1[6];
      80             :   double s_n[6];
      81      209924 :   rankTwoTensorToNeml(_stress_old[_qp], s_n);
      82             : 
      83             :   double e_np1[6];
      84      209924 :   rankTwoTensorToNeml(_mechanical_strain[_qp], e_np1);
      85             :   double e_n[6];
      86      209924 :   rankTwoTensorToNeml(_mechanical_strain_old[_qp], e_n);
      87             : 
      88      209924 :   double t_np1 = _t;
      89      209924 :   double t_n = _t - _dt;
      90             : 
      91      209924 :   double T_np1 = _temperature[_qp];
      92      209924 :   double T_n = _temperature_old[_qp];
      93             : 
      94             :   mooseAssert(_model->nstore() == _hist[_qp].size(), "History data storage size mismatch");
      95      209924 :   double * const h_np1 = (_model->nstore() > 0 ? _hist[_qp].data() : nullptr);
      96             :   mooseAssert(_model->nstore() == _hist_old[_qp].size(), "History data storage size mismatch");
      97      209924 :   const double * const h_n = (_model->nstore() > 0 ? _hist_old[_qp].data() : nullptr);
      98             : 
      99             :   double A_np1[36];
     100             : 
     101             :   double u_np1;
     102      209924 :   double u_n = _energy_old[_qp];
     103             : 
     104             :   double p_np1;
     105      209924 :   double p_n = _dissipation_old[_qp];
     106             : 
     107             :   double estrain[6];
     108             : 
     109             :   // Actually call the update
     110             :   try
     111             :   {
     112      209924 :     _model->update_sd(
     113             :         e_np1, e_n, T_np1, T_n, t_np1, t_n, s_np1, s_n, h_np1, h_n, A_np1, u_np1, u_n, p_np1, p_n);
     114             :   }
     115           0 :   catch (const neml::NEMLError & e)
     116             :   {
     117           0 :     if (_debug)
     118           0 :       mooseException("NEML stress update failed!\n",
     119             :                      "NEML message: ",
     120             :                      e.message(),
     121             :                      "\n",
     122             :                      "_qp=",
     123             :                      _qp,
     124             :                      " _q_point=",
     125             :                      _q_point[_qp],
     126             :                      " _current_elem->id()=",
     127             :                      _current_elem->id(),
     128             :                      '\n',
     129             : 
     130             :                      "Time increment: ",
     131             :                      _dt,
     132             :                      '\n',
     133             : 
     134             :                      "Old temperature: ",
     135             :                      _temperature_old[_qp],
     136             :                      '\n',
     137             :                      "New temperature: ",
     138             :                      _temperature[_qp],
     139             :                      '\n',
     140             : 
     141             :                      "Old echanical strain: ",
     142             :                      _mechanical_strain_old[_qp],
     143             :                      '\n',
     144             :                      "New mechanical strain: ",
     145             :                      _mechanical_strain[_qp],
     146             :                      '\n',
     147             : 
     148             :                      "Old stress: ",
     149             :                      _stress_old[_qp],
     150             :                      '\n',
     151             : 
     152             :                      "Old history: ",
     153             :                      Moose::stringify(_hist_old[_qp]),
     154             : 
     155             :                      "New history: ",
     156             :                      Moose::stringify(_hist[_qp]));
     157             :     else
     158           0 :       mooseException("NEML stress updated failed: ", e.message());
     159           0 :   }
     160             : 
     161             :   // Do more translation, now back to tensors
     162      209924 :   nemlToRankTwoTensor(s_np1, _stress[_qp]);
     163      209924 :   nemlToRankFourTensor(A_np1, _Jacobian_mult[_qp]);
     164             : 
     165             :   // Get the elastic strain
     166             :   try
     167             :   {
     168      209924 :     _model->elastic_strains(s_np1, T_np1, h_np1, estrain);
     169             :   }
     170           0 :   catch (const neml::NEMLError & e)
     171             :   {
     172           0 :     mooseError("Error in NEML call for elastic strain: ", e.message());
     173           0 :   }
     174             : 
     175             :   // Translate
     176      209924 :   nemlToRankTwoTensor(estrain, _elastic_strain[_qp]);
     177             : 
     178             :   // For EPP purposes calculate the inelastic strain
     179             :   double pstrain[6];
     180     1469468 :   for (unsigned int i = 0; i < 6; ++i)
     181     1259544 :     pstrain[i] = e_np1[i] - estrain[i];
     182             : 
     183      209924 :   nemlToRankTwoTensor(pstrain, _inelastic_strain[_qp]);
     184             : 
     185             :   // compute material timestep
     186      209924 :   if (_compute_dt)
     187             :   {
     188        5600 :     const auto increment = (_inelastic_strain[_qp] - (*_inelastic_strain_old)[_qp]).L2norm();
     189        5600 :     (*_material_dt)[_qp] =
     190        5600 :         increment > 0 ? _dt * _target_increment / increment : std::numeric_limits<Real>::max();
     191             :   }
     192             : 
     193             :   // Store dissipation
     194      209924 :   _energy[_qp] = u_np1;
     195      209924 :   _dissipation[_qp] = p_np1;
     196             :   // get damage index
     197      209924 :   if (_damage_index != nullptr)
     198       34094 :     (*_damage_index)[_qp] = _model->get_damage(h_np1);
     199      209924 : }
     200             : 
     201             : void
     202        2504 : NEMLStressBase::initQpStatefulProperties()
     203             : {
     204        2504 :   ComputeStressBase::initQpStatefulProperties();
     205             : 
     206             :   // Figure out initial history
     207        2504 :   _hist[_qp].resize(_model->nstore());
     208             : 
     209        2504 :   if (_model->nstore() > 0)
     210             :   {
     211             :     try
     212             :     {
     213        2504 :       _model->init_store(_hist[_qp].data());
     214             :     }
     215           0 :     catch (const neml::NEMLError & e)
     216             :     {
     217           0 :       mooseError("Error initializing NEML history: ", e.message());
     218           0 :     }
     219             :   }
     220        2504 :   _energy[_qp] = 0.0;
     221        2504 :   _dissipation[_qp] = 0.0;
     222             : 
     223        2504 :   if (_damage_index != nullptr)
     224         924 :     (*_damage_index)[_qp] = 0.0;
     225        2504 : }
     226             : 
     227             : void
     228    10426968 : NEMLStressBase::rankTwoTensorToNeml(const RankTwoTensor & in, double * const out)
     229             : {
     230    10426968 :   double inds[6][2] = {{0, 0}, {1, 1}, {2, 2}, {1, 2}, {0, 2}, {0, 1}};
     231    10426968 :   double mults[6] = {1.0, 1.0, 1.0, sqrt(2.0), sqrt(2.0), sqrt(2.0)};
     232             : 
     233    72988776 :   for (unsigned int i = 0; i < 6; ++i)
     234    62561808 :     out[i] = in(inds[i][0], inds[i][1]) * mults[i];
     235    10426968 : }
     236             : 
     237             : void
     238     7161236 : NEMLStressBase::nemlToRankTwoTensor(const double * const in, RankTwoTensor & out)
     239             : {
     240             :   static const unsigned int inds[6][2] = {{0, 0}, {1, 1}, {2, 2}, {1, 2}, {0, 2}, {0, 1}};
     241             :   static const double mults[6] = {
     242             :       1.0, 1.0, 1.0, 1.0 / std::sqrt(2.0), 1.0 / std::sqrt(2.0), 1.0 / std::sqrt(2.0)};
     243             : 
     244    50128652 :   for (unsigned int i = 0; i < 6; ++i)
     245             :   {
     246    42967416 :     out(inds[i][0], inds[i][1]) = in[i] * mults[i];
     247    42967416 :     out(inds[i][1], inds[i][0]) = in[i] * mults[i];
     248             :   }
     249     7161236 : }
     250             : 
     251             : void
     252      209924 : NEMLStressBase::nemlToRankFourTensor(const double * const in, RankFourTensor & out)
     253             : {
     254             :   static const unsigned int inds[6][2] = {{0, 0}, {1, 1}, {2, 2}, {1, 2}, {0, 2}, {0, 1}};
     255             :   static const double mults[6] = {1.0, 1.0, 1.0, 1.0 / sqrt(2.0), 1.0 / sqrt(2.0), 1.0 / sqrt(2.0)};
     256             : 
     257     1469468 :   for (unsigned int i = 0; i < 6; ++i)
     258     8816808 :     for (unsigned int j = 0; j < 6; ++j)
     259             :     {
     260     7557264 :       out(inds[i][0], inds[i][1], inds[j][0], inds[j][1]) = in[i * 6 + j] * (mults[i] * mults[j]);
     261     7557264 :       out(inds[i][1], inds[i][0], inds[j][0], inds[j][1]) = in[i * 6 + j] * (mults[i] * mults[j]);
     262     7557264 :       out(inds[i][0], inds[i][1], inds[j][1], inds[j][0]) = in[i * 6 + j] * (mults[i] * mults[j]);
     263     7557264 :       out(inds[i][1], inds[i][0], inds[j][1], inds[j][0]) = in[i * 6 + j] * (mults[i] * mults[j]);
     264             :     }
     265      209924 : }
     266             : 
     267             : #endif // NEML_ENABLED

Generated by: LCOV version 1.14