LCOV - code coverage report
Current view: top level - src/vectorpostprocessors - JIntegral.C (source / functions) Hit Total Coverage
Test: idaholab/moose solid_mechanics: f45d79 Lines: 134 140 95.7 %
Date: 2025-07-25 05:00:39 Functions: 7 8 87.5 %
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 "JIntegral.h"
      11             : #include "RankTwoTensor.h"
      12             : #include "Conversion.h"
      13             : #include "libmesh/string_to_enum.h"
      14             : #include "libmesh/quadrature.h"
      15             : #include "libmesh/utility.h"
      16             : #include "CrackFrontDefinition.h"
      17             : registerMooseObject("SolidMechanicsApp", JIntegral);
      18             : 
      19             : InputParameters
      20        1040 : JIntegral::validParams()
      21             : {
      22        1040 :   InputParameters params = ElementVectorPostprocessor::validParams();
      23        2080 :   params.addRequiredCoupledVar(
      24             :       "displacements",
      25             :       "The displacements appropriate for the simulation geometry and coordinate system");
      26        2080 :   params.addRequiredParam<UserObjectName>("crack_front_definition",
      27             :                                           "The CrackFrontDefinition user object name");
      28        2080 :   MooseEnum position_type("Angle Distance", "Distance");
      29        2080 :   params.addParam<MooseEnum>(
      30             :       "position_type",
      31             :       position_type,
      32        1040 :       "The method used to calculate position along crack front.  Options are: " +
      33        1040 :           position_type.getRawNames());
      34        2080 :   MooseEnum integral_vec("JIntegral CIntegral KFromJIntegral", "JIntegral");
      35        2080 :   params.addRequiredParam<MooseEnum>("integral",
      36             :                                      integral_vec,
      37        1040 :                                      "Domain integrals to calculate.  Choices are: " +
      38        1040 :                                          integral_vec.getRawNames());
      39        2080 :   params.addParam<unsigned int>("symmetry_plane",
      40             :                                 "Account for a symmetry plane passing through "
      41             :                                 "the plane of the crack, normal to the specified "
      42             :                                 "axis (0=x, 1=y, 2=z)");
      43        2080 :   params.addParam<Real>("poissons_ratio", "Poisson's ratio");
      44        2080 :   params.addParam<Real>("youngs_modulus", "Young's modulus of the material.");
      45        1040 :   params.set<bool>("use_displaced_mesh") = false;
      46        2080 :   params.addRequiredParam<unsigned int>("ring_index", "Ring ID");
      47        2080 :   MooseEnum q_function_type("Geometry Topology", "Geometry");
      48        2080 :   params.addParam<MooseEnum>("q_function_type",
      49             :                              q_function_type,
      50        1040 :                              "The method used to define the integration domain. Options are: " +
      51        1040 :                                  q_function_type.getRawNames());
      52        1040 :   params.addClassDescription("Computes the J-Integral, a measure of the strain energy release rate "
      53             :                              "at a crack tip, which can be used as a criterion for fracture "
      54             :                              "growth. It can, alternatively, compute the C(t) integral");
      55        1040 :   return params;
      56        1040 : }
      57             : 
      58         816 : JIntegral::JIntegral(const InputParameters & parameters)
      59             :   : ElementVectorPostprocessor(parameters),
      60         816 :     _crack_front_definition(&getUserObject<CrackFrontDefinition>("crack_front_definition")),
      61        1632 :     _integral(getParam<MooseEnum>("integral").getEnum<IntegralType>()),
      62         816 :     _J_thermal_term_vec(hasMaterialProperty<RealVectorValue>("J_thermal_term_vec")
      63        2448 :                             ? &getMaterialProperty<RealVectorValue>("J_thermal_term_vec")
      64             :                             : nullptr),
      65        1632 :     _Eshelby_tensor(_integral != IntegralType::CIntegral
      66        1584 :                         ? &getMaterialProperty<RankTwoTensor>("Eshelby_tensor")
      67             :                         : nullptr),
      68         816 :     _Eshelby_tensor_dissipation(
      69         816 :         _integral == IntegralType::CIntegral
      70         864 :             ? &getMaterialProperty<RankTwoTensor>("Eshelby_tensor_dissipation")
      71             :             : nullptr),
      72        1632 :     _fe_vars(getCoupledMooseVars()),
      73         816 :     _fe_type(_fe_vars[0]->feType()),
      74        1632 :     _has_symmetry_plane(isParamValid("symmetry_plane")),
      75        1680 :     _poissons_ratio(isParamValid("poissons_ratio") ? getParam<Real>("poissons_ratio") : 0),
      76        1680 :     _youngs_modulus(isParamValid("youngs_modulus") ? getParam<Real>("youngs_modulus") : 0),
      77        1632 :     _ring_index(getParam<unsigned int>("ring_index")),
      78        1632 :     _q_function_type(getParam<MooseEnum>("q_function_type").getEnum<QMethod>()),
      79        1632 :     _position_type(getParam<MooseEnum>("position_type").getEnum<PositionType>()),
      80         816 :     _x(declareVector("x")),
      81         816 :     _y(declareVector("y")),
      82         816 :     _z(declareVector("z")),
      83         816 :     _position(declareVector("id")),
      84         816 :     _j_integral(declareVector((_integral == IntegralType::KFromJIntegral
      85             :                                    ? "K_"
      86         864 :                                    : (_integral == IntegralType::JIntegral ? "J_" : "C_")) +
      87        1632 :                               Moose::stringify(_ring_index)))
      88             : {
      89         816 : }
      90             : 
      91             : void
      92         816 : JIntegral::initialSetup()
      93             : {
      94         816 :   _treat_as_2d = _crack_front_definition->treatAs2D();
      95         816 :   _using_mesh_cutter = _crack_front_definition->usingMeshCutter();
      96         840 :   if (_integral == IntegralType::KFromJIntegral &&
      97         912 :       (!isParamValid("youngs_modulus") || !isParamValid("poissons_ratio")))
      98           0 :     mooseError("youngs_modulus and poissons_ratio must be specified if integrals = KFromJIntegral");
      99         816 : }
     100             : 
     101             : void
     102         816 : JIntegral::initialize()
     103             : {
     104             :   std::size_t num_pts;
     105         816 :   if (_treat_as_2d && _using_mesh_cutter == false)
     106             :     num_pts = 1;
     107             :   else
     108         222 :     num_pts = _crack_front_definition->getNumCrackFrontPoints();
     109             : 
     110         816 :   _x.assign(num_pts, 0.0);
     111         816 :   _y.assign(num_pts, 0.0);
     112         816 :   _z.assign(num_pts, 0.0);
     113         816 :   _position.assign(num_pts, 0.0);
     114         816 :   _j_integral.assign(num_pts, 0.0);
     115             : 
     116        2718 :   for (const auto * fe_var : _fe_vars)
     117             :   {
     118        1902 :     if (fe_var->feType() != _fe_type)
     119           0 :       mooseError("displacements", "All coupled displacements must have the same type");
     120             :   }
     121         816 : }
     122             : 
     123             : Real
     124     2338896 : JIntegral::computeQpIntegral(const std::size_t crack_front_point_index,
     125             :                              const Real scalar_q,
     126             :                              const RealVectorValue & grad_of_scalar_q)
     127             : {
     128     2338896 :   RankTwoTensor grad_of_vector_q;
     129             :   const RealVectorValue & crack_direction =
     130     2338896 :       _crack_front_definition->getCrackDirection(crack_front_point_index);
     131     2338896 :   grad_of_vector_q(0, 0) = crack_direction(0) * grad_of_scalar_q(0);
     132     2338896 :   grad_of_vector_q(0, 1) = crack_direction(0) * grad_of_scalar_q(1);
     133     2338896 :   grad_of_vector_q(0, 2) = crack_direction(0) * grad_of_scalar_q(2);
     134     2338896 :   grad_of_vector_q(1, 0) = crack_direction(1) * grad_of_scalar_q(0);
     135     2338896 :   grad_of_vector_q(1, 1) = crack_direction(1) * grad_of_scalar_q(1);
     136     2338896 :   grad_of_vector_q(1, 2) = crack_direction(1) * grad_of_scalar_q(2);
     137     2338896 :   grad_of_vector_q(2, 0) = crack_direction(2) * grad_of_scalar_q(0);
     138     2338896 :   grad_of_vector_q(2, 1) = crack_direction(2) * grad_of_scalar_q(1);
     139     2338896 :   grad_of_vector_q(2, 2) = crack_direction(2) * grad_of_scalar_q(2);
     140             : 
     141             :   Real eq;
     142             : 
     143     2338896 :   if (_integral != IntegralType::CIntegral)
     144     2313296 :     eq = (*_Eshelby_tensor)[_qp].doubleContraction(grad_of_vector_q);
     145             :   else
     146       25600 :     eq = (*_Eshelby_tensor_dissipation)[_qp].doubleContraction(grad_of_vector_q);
     147             : 
     148             :   // Thermal component
     149             :   Real eq_thermal = 0.0;
     150     2338896 :   if (_J_thermal_term_vec && _integral != IntegralType::CIntegral)
     151             :   {
     152     9253184 :     for (std::size_t i = 0; i < 3; i++)
     153     6939888 :       eq_thermal += crack_direction(i) * scalar_q * (*_J_thermal_term_vec)[_qp](i);
     154             :   }
     155             : 
     156             :   Real q_avg_seg = 1.0;
     157     2338896 :   if (!_crack_front_definition->treatAs2D())
     158             :   {
     159     1892256 :     q_avg_seg =
     160     3784512 :         (_crack_front_definition->getCrackFrontForwardSegmentLength(crack_front_point_index) +
     161     1892256 :          _crack_front_definition->getCrackFrontBackwardSegmentLength(crack_front_point_index)) /
     162             :         2.0;
     163             :   }
     164             : 
     165     2338896 :   Real etot = -eq + eq_thermal;
     166             : 
     167     2338896 :   return etot / q_avg_seg;
     168             : }
     169             : 
     170             : void
     171      147240 : JIntegral::execute()
     172             : {
     173             :   // calculate phi and dphi for this element
     174      147240 :   const std::size_t dim = _current_elem->dim();
     175      147240 :   std::unique_ptr<libMesh::FEBase> fe(libMesh::FEBase::build(dim, _fe_type));
     176      147240 :   fe->attach_quadrature_rule(const_cast<QBase *>(_qrule));
     177      147240 :   _phi_curr_elem = &fe->get_phi();
     178      147240 :   _dphi_curr_elem = &fe->get_dphi();
     179      147240 :   fe->reinit(_current_elem);
     180             : 
     181             :   // calculate q for all nodes in this element
     182      147240 :   std::size_t ring_base = (_q_function_type == QMethod::Topology) ? 0 : 1;
     183             : 
     184      494280 :   for (std::size_t icfp = 0; icfp < _j_integral.size(); icfp++)
     185             :   {
     186      347040 :     _q_curr_elem.clear();
     187     2685936 :     for (std::size_t i = 0; i < _current_elem->n_nodes(); ++i)
     188             :     {
     189     2338896 :       const Node * this_node = _current_elem->node_ptr(i);
     190             :       Real q_this_node;
     191             : 
     192     2338896 :       if (_q_function_type == QMethod::Geometry)
     193     2292432 :         q_this_node = _crack_front_definition->DomainIntegralQFunction(
     194     2292432 :             icfp, _ring_index - ring_base, this_node);
     195             :       else // QMethod::Topology
     196       46464 :         q_this_node = _crack_front_definition->DomainIntegralTopologicalQFunction(
     197       46464 :             icfp, _ring_index - ring_base, this_node);
     198             : 
     199     2338896 :       _q_curr_elem.push_back(q_this_node);
     200             :     }
     201             : 
     202     2685936 :     for (_qp = 0; _qp < _qrule->n_points(); _qp++)
     203             :     {
     204             :       Real scalar_q = 0.0;
     205             :       RealVectorValue grad_of_scalar_q(0.0, 0.0, 0.0);
     206             : 
     207    19300368 :       for (std::size_t i = 0; i < _current_elem->n_nodes(); ++i)
     208             :       {
     209    16961472 :         scalar_q += (*_phi_curr_elem)[i][_qp] * _q_curr_elem[i];
     210             : 
     211    66096192 :         for (std::size_t j = 0; j < _current_elem->dim(); ++j)
     212    49134720 :           grad_of_scalar_q(j) += (*_dphi_curr_elem)[i][_qp](j) * _q_curr_elem[i];
     213             :       }
     214             : 
     215     2338896 :       _j_integral[icfp] +=
     216     2338896 :           _JxW[_qp] * _coord[_qp] * computeQpIntegral(icfp, scalar_q, grad_of_scalar_q);
     217             :     }
     218             :   }
     219      147240 : }
     220             : 
     221             : void
     222         816 : JIntegral::finalize()
     223             : {
     224         816 :   gatherSum(_j_integral);
     225             : 
     226        2460 :   for (std::size_t i = 0; i < _j_integral.size(); ++i)
     227             :   {
     228        1644 :     if (_has_symmetry_plane)
     229        1458 :       _j_integral[i] *= 2.0;
     230             : 
     231        1644 :     Real sign = (_j_integral[i] > 0.0) ? 1.0 : ((_j_integral[i] < 0.0) ? -1.0 : 0.0);
     232             : 
     233        1644 :     if (_integral == IntegralType::KFromJIntegral)
     234          24 :       _j_integral[i] = sign * std::sqrt(std::abs(_j_integral[i]) * _youngs_modulus /
     235          24 :                                         (1.0 - Utility::pow<2>(_poissons_ratio)));
     236             : 
     237        1644 :     const auto cfp = _crack_front_definition->getCrackFrontPoint(i);
     238        1644 :     _x[i] = (*cfp)(0);
     239        1644 :     _y[i] = (*cfp)(1);
     240        1644 :     _z[i] = (*cfp)(2);
     241             : 
     242        1644 :     if (_position_type == PositionType::Angle)
     243         252 :       _position[i] = _crack_front_definition->getAngleAlongFront(i);
     244             :     else
     245        1392 :       _position[i] = _crack_front_definition->getDistanceAlongFront(i);
     246             :   }
     247         816 : }
     248             : 
     249             : void
     250           0 : JIntegral::threadJoin(const UserObject & y)
     251             : {
     252             :   const auto & uo = static_cast<const JIntegral &>(y);
     253             : 
     254           0 :   for (auto i = beginIndex(_j_integral); i < _j_integral.size(); ++i)
     255           0 :     _j_integral[i] += uo._j_integral[i];
     256           0 : }

Generated by: LCOV version 1.14