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 "CrystalPlasticityStressUpdateBase.h"
11 :
12 : #include "libmesh/utility.h"
13 : #include "libmesh/int_range.h"
14 : #include "Conversion.h"
15 : #include "MooseException.h"
16 :
17 : InputParameters
18 1009 : CrystalPlasticityStressUpdateBase::validParams()
19 : {
20 1009 : InputParameters params = Material::validParams();
21 2018 : params.addParam<std::string>(
22 : "base_name",
23 : "Optional parameter that allows the user to define multiple crystal plasticity mechanisms");
24 1009 : params.addClassDescription(
25 : "Crystal Plasticity base class: handles the Newton iteration over the stress residual and "
26 : "calculates the Jacobian based on constitutive laws provided by inheriting classes");
27 :
28 : // The return stress increment classes are intended to be iterative materials, so must set compute
29 : // = false for all inheriting classes
30 1009 : params.set<bool>("compute") = false;
31 1009 : params.suppressParameter<bool>("compute");
32 :
33 2018 : params.addParam<MooseEnum>(
34 : "crystal_lattice_type",
35 3027 : MooseEnum("BCC FCC HCP", "FCC"),
36 : "Crystal lattice type or representative unit cell, i.e., BCC, FCC, HCP, etc.");
37 :
38 3027 : params.addRangeCheckedParam<std::vector<Real>>(
39 : "unit_cell_dimension",
40 2018 : std::vector<Real>{1.0, 1.0, 1.0},
41 : "unit_cell_dimension_size = 3",
42 : "The dimension of the unit cell along three directions, where a cubic unit cell is assumed "
43 : "for cubic crystals and a hexagonal unit cell (a, a, c) is assumed for HCP crystals. These "
44 : "dimensions will be taken into account while computing the slip systems."
45 : " Default size is 1.0 along all three directions.");
46 :
47 2018 : params.addRequiredParam<unsigned int>(
48 : "number_slip_systems",
49 : "The total number of possible active slip systems for the crystalline material");
50 2018 : params.addRequiredParam<FileName>(
51 : "slip_sys_file_name",
52 : "Name of the file containing the slip systems, one slip system per row, with the slip plane "
53 : "normal given before the slip plane direction.");
54 2018 : params.addParam<Real>("number_cross_slip_directions",
55 2018 : 0,
56 : "Quanity of unique slip directions, used to determine cross slip familes");
57 2018 : params.addParam<Real>("number_cross_slip_planes",
58 2018 : 0,
59 : "Quanity of slip planes belonging to a single cross slip direction; used "
60 : "to determine cross slip families");
61 2018 : params.addParam<Real>(
62 : "slip_increment_tolerance",
63 2018 : 2e-2,
64 : "Maximum allowable slip in an increment for each individual constitutive model");
65 2018 : params.addParam<Real>(
66 2018 : "stol", 1e-2, "Constitutive internal state variable relative change tolerance");
67 2018 : params.addParam<Real>("resistance_tol",
68 2018 : 1.0e-2,
69 : "Constitutive slip system resistance relative residual tolerance for each "
70 : "individual constitutive model");
71 2018 : params.addParam<Real>("zero_tol",
72 2018 : 1e-12,
73 : "Tolerance for residual check when variable value is zero for each "
74 : "individual constitutive model");
75 2018 : params.addParam<bool>(
76 : "print_state_variable_convergence_error_messages",
77 2018 : false,
78 : "Whether or not to print warning messages from the crystal plasticity specific convergence "
79 : "checks on both the constiutive model internal state variables.");
80 1009 : return params;
81 0 : }
82 :
83 758 : CrystalPlasticityStressUpdateBase::CrystalPlasticityStressUpdateBase(
84 758 : const InputParameters & parameters)
85 : : Material(parameters),
86 1040 : _base_name(isParamValid("base_name") ? getParam<std::string>("base_name") + "_" : ""),
87 758 : _crystal_lattice_type(
88 758 : getParam<MooseEnum>("crystal_lattice_type").getEnum<CrystalLatticeType>()),
89 1516 : _unit_cell_dimension(getParam<std::vector<Real>>("unit_cell_dimension")),
90 1516 : _number_slip_systems(getParam<unsigned int>("number_slip_systems")),
91 1516 : _slip_sys_file_name(getParam<FileName>("slip_sys_file_name")),
92 1516 : _number_cross_slip_directions(getParam<Real>("number_cross_slip_directions")),
93 1516 : _number_cross_slip_planes(getParam<Real>("number_cross_slip_planes")),
94 :
95 1516 : _rel_state_var_tol(getParam<Real>("stol")),
96 1516 : _slip_incr_tol(getParam<Real>("slip_increment_tolerance")),
97 1516 : _resistance_tol(getParam<Real>("resistance_tol")),
98 1516 : _zero_tol(getParam<Real>("zero_tol")),
99 :
100 758 : _slip_resistance(declareProperty<std::vector<Real>>(_base_name + "slip_resistance")),
101 1516 : _slip_resistance_old(getMaterialPropertyOld<std::vector<Real>>(_base_name + "slip_resistance")),
102 758 : _slip_increment(declareProperty<std::vector<Real>>(_base_name + "slip_increment")),
103 :
104 758 : _slip_direction(_number_slip_systems),
105 758 : _slip_plane_normal(_number_slip_systems),
106 758 : _flow_direction(declareProperty<std::vector<RankTwoTensor>>(_base_name + "flow_direction")),
107 758 : _tau(declareProperty<std::vector<Real>>(_base_name + "applied_shear_stress")),
108 2274 : _print_convergence_message(getParam<bool>("print_state_variable_convergence_error_messages"))
109 : {
110 758 : getSlipSystems();
111 754 : sortCrossSlipFamilies();
112 :
113 1508 : if (parameters.isParamSetByUser("number_cross_slip_directions"))
114 0 : _calculate_cross_slip = true;
115 : else
116 754 : _calculate_cross_slip = false;
117 754 : }
118 :
119 : void
120 43104 : CrystalPlasticityStressUpdateBase::initQpStatefulProperties()
121 : {
122 43104 : setMaterialVectorSize();
123 43104 : }
124 :
125 : void
126 1690830 : CrystalPlasticityStressUpdateBase::setMaterialVectorSize()
127 : {
128 1690830 : _tau[_qp].resize(_number_slip_systems);
129 1690830 : _flow_direction[_qp].resize(_number_slip_systems);
130 21805619 : for (const auto i : make_range(_number_slip_systems))
131 : {
132 20114789 : _flow_direction[_qp][i].zero();
133 20114789 : _tau[_qp][i] = 0.0;
134 : }
135 :
136 1690830 : _slip_resistance[_qp].resize(_number_slip_systems);
137 1690830 : _slip_increment[_qp].resize(_number_slip_systems);
138 1690830 : }
139 :
140 : void
141 758 : CrystalPlasticityStressUpdateBase::getSlipSystems()
142 : {
143 : bool orthonormal_error = false;
144 :
145 : // read in the slip system data from auxiliary text file
146 758 : MooseUtils::DelimitedFileReader _reader(_slip_sys_file_name);
147 : _reader.setFormatFlag(MooseUtils::DelimitedFileReader::FormatFlag::ROWS);
148 758 : _reader.read();
149 :
150 : // check the size of the input
151 758 : if (_reader.getData().size() != _number_slip_systems)
152 0 : paramError(
153 : "number_slip_systems",
154 : "The number of rows in the slip system file should match the number of slip system.");
155 :
156 9556 : for (const auto i : make_range(_number_slip_systems))
157 : {
158 : // initialize to zero
159 8798 : _slip_direction[i].zero();
160 : _slip_plane_normal[i].zero();
161 : }
162 :
163 758 : if (_crystal_lattice_type == CrystalLatticeType::HCP)
164 200 : transformHexagonalMillerBravaisSlipSystems(_reader);
165 558 : else if (_crystal_lattice_type == CrystalLatticeType::BCC ||
166 : _crystal_lattice_type == CrystalLatticeType::FCC)
167 : {
168 7062 : for (const auto i : make_range(_number_slip_systems))
169 : {
170 : // directly grab the raw data and scale it by the unit cell dimension
171 45528 : for (const auto j : index_range(_reader.getData(i)))
172 : {
173 39024 : if (j < LIBMESH_DIM)
174 19512 : _slip_plane_normal[i](j) = _reader.getData(i)[j] / _unit_cell_dimension[j];
175 : else
176 19512 : _slip_direction[i](j - LIBMESH_DIM) =
177 19512 : _reader.getData(i)[j] * _unit_cell_dimension[j - LIBMESH_DIM];
178 : }
179 : }
180 : }
181 :
182 9529 : for (const auto i : make_range(_number_slip_systems))
183 : {
184 : // normalize
185 8775 : _slip_plane_normal[i] /= _slip_plane_normal[i].norm();
186 8775 : _slip_direction[i] /= _slip_direction[i].norm();
187 :
188 8775 : if (_crystal_lattice_type != CrystalLatticeType::HCP)
189 : {
190 : const auto magnitude = _slip_plane_normal[i] * _slip_direction[i];
191 6504 : if (std::abs(magnitude) > libMesh::TOLERANCE)
192 : {
193 : orthonormal_error = true;
194 : break;
195 : }
196 : }
197 : }
198 :
199 754 : if (orthonormal_error)
200 0 : mooseError("CrystalPlasticityStressUpdateBase Error: The slip system file contains a slip "
201 : "direction and plane normal pair that are not orthonormal in the Cartesian "
202 : "coordinate system.");
203 754 : }
204 :
205 : void
206 200 : CrystalPlasticityStressUpdateBase::transformHexagonalMillerBravaisSlipSystems(
207 : const MooseUtils::DelimitedFileReader & reader)
208 : {
209 : const unsigned int miller_bravais_indices = 4;
210 : RealVectorValue temporary_slip_direction, temporary_slip_plane;
211 : // temporary_slip_plane.resize(LIBMESH_DIM);
212 : // temporary_slip_direction.resize(LIBMESH_DIM);
213 :
214 200 : if (_unit_cell_dimension[0] != _unit_cell_dimension[1] ||
215 200 : _unit_cell_dimension[0] == _unit_cell_dimension[2])
216 1 : mooseError("CrystalPlasticityStressUpdateBase Error: The specified unit cell dimensions are "
217 : "not consistent with expectations for "
218 : "HCP crystal hexagonal lattices.");
219 199 : else if (reader.getData(0).size() != miller_bravais_indices * 2)
220 1 : mooseError("CrystalPlasticityStressUpdateBase Error: The number of entries in the first row of "
221 : "the slip system file is not consistent with the expectations for the 4-index "
222 : "Miller-Bravais assumption for HCP crystals. This file should represent both the "
223 : "slip plane normal and the slip direction with 4-indices each.");
224 :
225 : // set up the tranformation matrices
226 198 : RankTwoTensor transform_matrix;
227 : transform_matrix.zero();
228 198 : transform_matrix(0, 0) = 1.0 / _unit_cell_dimension[0];
229 198 : transform_matrix(1, 0) = 1.0 / (_unit_cell_dimension[0] * std::sqrt(3.0));
230 198 : transform_matrix(1, 1) = 2.0 / (_unit_cell_dimension[0] * std::sqrt(3.0));
231 198 : transform_matrix(2, 2) = 1.0 / (_unit_cell_dimension[2]);
232 :
233 2469 : for (const auto i : make_range(_number_slip_systems))
234 : {
235 : // read in raw data from file and store in the temporary vectors
236 20441 : for (const auto j : index_range(reader.getData(i)))
237 : {
238 : // Check that the slip plane normal indices of the basal plane sum to zero for consistency
239 : Real basal_pl_sum = 0.0;
240 72680 : for (const auto k : make_range(LIBMESH_DIM))
241 54510 : basal_pl_sum += reader.getData(i)[k];
242 :
243 18170 : if (basal_pl_sum > _zero_tol)
244 1 : mooseError(
245 : "CrystalPlasticityStressUpdateBase Error: The specified HCP basal plane Miller-Bravais "
246 : "indices do not sum to zero. Check the values supplied in the associated text file.");
247 :
248 : // Check that the slip direction indices of the basal plane sum to zero for consistency
249 : Real basal_dir_sum = 0.0;
250 72676 : for (const auto k : make_range(miller_bravais_indices, miller_bravais_indices + LIBMESH_DIM))
251 54507 : basal_dir_sum += reader.getData(i)[k];
252 :
253 18169 : if (basal_dir_sum > _zero_tol)
254 1 : mooseError("CrystalPlasticityStressUpdateBase Error: The specified HCP slip direction "
255 : "Miller-Bravais indices in the basal plane (U, V, and T) do not sum to zero "
256 : "within the user specified tolerance (try loosing zero_tol if using the default "
257 : "value). Check the values supplied in the associated text file.");
258 :
259 18168 : if (j < miller_bravais_indices)
260 : {
261 : // Planes are directly copied over, per a_1 = x convention used here:
262 : // Store the first two indices for the basal plane, (h and k), and drop
263 : // the redundant third basal plane index (i)
264 9084 : if (j < 2)
265 4542 : temporary_slip_plane(j) = reader.getData(i)[j];
266 : // Store the c-axis index as the third entry in the orthorombic index convention
267 4542 : else if (j == 3)
268 2271 : temporary_slip_plane(j - 1) = reader.getData(i)[j];
269 : }
270 : else
271 : {
272 9084 : const auto direction_j = j - miller_bravais_indices;
273 : // Store the first two indices for the slip direction in the basal plane,
274 : //(U, V), and drop the redundant third basal plane index (T)
275 9084 : if (direction_j < 2)
276 4542 : temporary_slip_direction(direction_j) = reader.getData(i)[j];
277 : // Store the c-axis index as the third entry in the orthorombic index convention
278 4542 : else if (direction_j == 3)
279 2271 : temporary_slip_direction(direction_j - 1) = reader.getData(i)[j];
280 : }
281 : }
282 :
283 : // perform transformation calculation
284 2271 : _slip_direction[i] = transform_matrix * temporary_slip_direction;
285 2271 : _slip_plane_normal[i] = transform_matrix * temporary_slip_plane;
286 : }
287 196 : }
288 :
289 : void
290 754 : CrystalPlasticityStressUpdateBase::sortCrossSlipFamilies()
291 : {
292 754 : if (_number_cross_slip_directions == 0)
293 : {
294 754 : _cross_slip_familes.resize(0);
295 754 : return;
296 : }
297 :
298 : // If cross slip does occur, then set up the system of vectors for the families
299 0 : _cross_slip_familes.resize(_number_cross_slip_directions);
300 : // and set the first index of each inner vector
301 0 : for (unsigned int i = 0; i < _number_cross_slip_directions; ++i)
302 0 : _cross_slip_familes[i].resize(1);
303 :
304 : // Sort the index of the slip system based vectors into separte families
305 : unsigned int family_counter = 1;
306 0 : _cross_slip_familes[0][0] = 0;
307 :
308 0 : for (unsigned int i = 1; i < _number_slip_systems; ++i)
309 : {
310 0 : for (unsigned int j = 0; j < family_counter; ++j)
311 : {
312 : // check to see if the slip system direction i matches any of the existing slip directions
313 : // First calculate the dot product
314 : Real dot_product = 0.0;
315 0 : for (const auto k : make_range(Moose::dim))
316 : {
317 0 : unsigned int check_family_index = _cross_slip_familes[j][0];
318 0 : dot_product += std::abs(_slip_direction[check_family_index](k) - _slip_direction[i](k));
319 : }
320 : // Then check if the dot product is one, if yes, add to family and break
321 0 : if (MooseUtils::absoluteFuzzyEqual(dot_product, 0.0))
322 : {
323 0 : _cross_slip_familes[j].push_back(i);
324 0 : if (_cross_slip_familes[j].size() > _number_cross_slip_planes)
325 0 : mooseError(
326 : "Exceeded the number of cross slip planes allowed in a single cross slip family");
327 :
328 : break; // exit the loop over the exisiting cross slip families and move to the next slip
329 : // direction
330 : }
331 : // The slip system in question does not belong to an existing family
332 0 : else if (j == (family_counter - 1) && !MooseUtils::absoluteFuzzyEqual(dot_product, 0.0))
333 : {
334 0 : if (family_counter > _number_cross_slip_directions)
335 0 : mooseError("Exceeds the number of cross slip directions specified for this material");
336 :
337 0 : _cross_slip_familes[family_counter][0] = i;
338 0 : family_counter++;
339 0 : break;
340 : }
341 : }
342 : }
343 :
344 0 : if (_print_convergence_message)
345 : {
346 0 : mooseWarning("Checking the slip system ordering now:");
347 0 : for (unsigned int i = 0; i < _number_cross_slip_directions; ++i)
348 : {
349 : Moose::out << "In cross slip family " << i << std::endl;
350 0 : for (unsigned int j = 0; j < _number_cross_slip_planes; ++j)
351 0 : Moose::out << " is the slip direction number " << _cross_slip_familes[i][j] << std::endl;
352 : }
353 : }
354 : }
355 :
356 : unsigned int
357 0 : CrystalPlasticityStressUpdateBase::identifyCrossSlipFamily(const unsigned int index)
358 : {
359 0 : for (unsigned int i = 0; i < _number_cross_slip_directions; ++i)
360 0 : for (unsigned int j = 0; j < _number_cross_slip_planes; ++j)
361 0 : if (_cross_slip_familes[i][j] == index)
362 0 : return i;
363 :
364 : // Should never reach this statement
365 0 : mooseError("The supplied slip system index is not among the slip system families sorted.");
366 : }
367 :
368 : void
369 1645806 : CrystalPlasticityStressUpdateBase::calculateFlowDirection(const RankTwoTensor & crysrot)
370 : {
371 1645806 : calculateSchmidTensor(
372 1645806 : _number_slip_systems, _slip_plane_normal, _slip_direction, _flow_direction[_qp], crysrot);
373 1645806 : }
374 :
375 : void
376 1645806 : CrystalPlasticityStressUpdateBase::calculateSchmidTensor(
377 : const unsigned int & number_slip_systems,
378 : const std::vector<RealVectorValue> & plane_normal_vector,
379 : const std::vector<RealVectorValue> & direction_vector,
380 : std::vector<RankTwoTensor> & schmid_tensor,
381 : const RankTwoTensor & crysrot)
382 : {
383 : std::vector<RealVectorValue> local_direction_vector, local_plane_normal;
384 1645806 : local_direction_vector.resize(number_slip_systems);
385 1645806 : local_plane_normal.resize(number_slip_systems);
386 :
387 : // Update slip direction and normal with crystal orientation
388 21229427 : for (const auto i : make_range(_number_slip_systems))
389 : {
390 19583621 : local_direction_vector[i].zero();
391 : local_plane_normal[i].zero();
392 :
393 78334484 : for (const auto j : make_range(LIBMESH_DIM))
394 235003452 : for (const auto k : make_range(LIBMESH_DIM))
395 : {
396 176252589 : local_direction_vector[i](j) =
397 176252589 : local_direction_vector[i](j) + crysrot(j, k) * direction_vector[i](k);
398 :
399 176252589 : local_plane_normal[i](j) =
400 176252589 : local_plane_normal[i](j) + crysrot(j, k) * plane_normal_vector[i](k);
401 : }
402 :
403 : // Calculate Schmid tensor
404 78334484 : for (const auto j : make_range(LIBMESH_DIM))
405 235003452 : for (const auto k : make_range(LIBMESH_DIM))
406 : {
407 176252589 : schmid_tensor[i](j, k) = local_direction_vector[i](j) * local_plane_normal[i](k);
408 : }
409 : }
410 1645806 : }
411 :
412 : void
413 15282548 : CrystalPlasticityStressUpdateBase::calculateShearStress(
414 : const RankTwoTensor & pk2,
415 : const RankTwoTensor & inverse_eigenstrain_deformation_grad,
416 : const unsigned int & num_eigenstrains)
417 : {
418 15282548 : if (!num_eigenstrains)
419 : {
420 132368859 : for (const auto i : make_range(_number_slip_systems))
421 120701572 : _tau[_qp][i] = pk2.doubleContraction(_flow_direction[_qp][i]);
422 :
423 11667287 : return;
424 : }
425 :
426 3615261 : RankTwoTensor eigenstrain_deformation_grad = inverse_eigenstrain_deformation_grad.inverse();
427 52594233 : for (const auto i : make_range(_number_slip_systems))
428 : {
429 : // compute PK2_hat using deformation gradient
430 48978972 : RankTwoTensor pk2_hat = eigenstrain_deformation_grad.det() *
431 48978972 : eigenstrain_deformation_grad.transpose() * pk2 *
432 48978972 : inverse_eigenstrain_deformation_grad.transpose();
433 48978972 : _tau[_qp][i] = pk2_hat.doubleContraction(_flow_direction[_qp][i]);
434 : }
435 : }
436 :
437 : void
438 15121308 : CrystalPlasticityStressUpdateBase::calculateTotalPlasticDeformationGradientDerivative(
439 : RankFourTensor & dfpinvdpk2,
440 : const RankTwoTensor & inverse_plastic_deformation_grad_old,
441 : const RankTwoTensor & inverse_eigenstrain_deformation_grad_old,
442 : const unsigned int & num_eigenstrains)
443 : {
444 15121308 : std::vector<Real> dslip_dtau(_number_slip_systems, 0.0);
445 15121308 : std::vector<RankTwoTensor> dtaudpk2(_number_slip_systems);
446 15121308 : std::vector<RankTwoTensor> dfpinvdslip(_number_slip_systems);
447 :
448 15121308 : calculateConstitutiveSlipDerivative(dslip_dtau);
449 :
450 182563393 : for (const auto j : make_range(_number_slip_systems))
451 : {
452 167442085 : if (num_eigenstrains)
453 : {
454 : RankTwoTensor eigenstrain_deformation_grad_old =
455 46767852 : inverse_eigenstrain_deformation_grad_old.inverse();
456 46767852 : dtaudpk2[j] = eigenstrain_deformation_grad_old.det() * eigenstrain_deformation_grad_old *
457 46767852 : _flow_direction[_qp][j] * inverse_eigenstrain_deformation_grad_old;
458 : }
459 : else
460 120674233 : dtaudpk2[j] = _flow_direction[_qp][j];
461 167442085 : dfpinvdslip[j] = -inverse_plastic_deformation_grad_old * _flow_direction[_qp][j];
462 167442085 : dfpinvdpk2 += (dfpinvdslip[j] * dslip_dtau[j] * _substep_dt).outerProduct(dtaudpk2[j]);
463 : }
464 15121308 : }
465 :
466 : void
467 13422866 : CrystalPlasticityStressUpdateBase::calculateEquivalentSlipIncrement(
468 : RankTwoTensor & equivalent_slip_increment)
469 : {
470 : // Sum up the slip increments to find the equivalent plastic strain due to slip
471 158916852 : for (const auto i : make_range(_number_slip_systems))
472 145493986 : equivalent_slip_increment += _flow_direction[_qp][i] * _slip_increment[_qp][i] * _substep_dt;
473 13422866 : }
474 :
475 : void
476 1669278 : CrystalPlasticityStressUpdateBase::setQp(const unsigned int & qp)
477 : {
478 1669278 : _qp = qp;
479 1669278 : }
480 :
481 : void
482 1773911 : CrystalPlasticityStressUpdateBase::setSubstepDt(const Real & substep_dt)
483 : {
484 1773911 : _substep_dt = substep_dt;
485 1773911 : }
486 :
487 : bool
488 3743051 : CrystalPlasticityStressUpdateBase::isConstitutiveStateVariableConverged(
489 : const std::vector<Real> & current_var,
490 : const std::vector<Real> & var_before_update,
491 : const std::vector<Real> & previous_substep_var,
492 : const Real & tolerance)
493 : {
494 : // sometimes the state variable size may not equal to the number of slip systems
495 3743051 : unsigned int sz = current_var.size();
496 : mooseAssert(current_var.size() == sz, "Current variable size does not match");
497 : mooseAssert(var_before_update.size() == sz, "Variable before update size does not match");
498 : mooseAssert(previous_substep_var.size() == sz, "Previous substep variable size does not match");
499 :
500 : bool is_converged = true;
501 :
502 : Real diff_val = 0.0;
503 : Real abs_prev_substep_val = 0.0;
504 49042245 : for (const auto i : make_range(sz))
505 : {
506 45299194 : diff_val = std::abs(var_before_update[i] - current_var[i]);
507 45299194 : abs_prev_substep_val = std::abs(previous_substep_var[i]);
508 :
509 : // set to false if the state variable is not converged
510 45299194 : if (abs_prev_substep_val < _zero_tol && diff_val > _zero_tol)
511 : is_converged = false;
512 45171954 : else if (abs_prev_substep_val > _zero_tol && diff_val > tolerance * abs_prev_substep_val)
513 : is_converged = false;
514 : }
515 3743051 : return is_converged;
516 : }
|