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 "ComputeMultiPlasticityStress.h"
11 : #include "MultiPlasticityDebugger.h"
12 : #include "MatrixTools.h"
13 :
14 : #include "MooseException.h"
15 : #include "RotationMatrix.h" // for rotVecToZ
16 :
17 : #include "libmesh/utility.h"
18 :
19 : registerMooseObject("SolidMechanicsApp", ComputeMultiPlasticityStress);
20 :
21 : InputParameters
22 4174 : ComputeMultiPlasticityStress::validParams()
23 : {
24 4174 : InputParameters params = ComputeStressBase::validParams();
25 4174 : params += MultiPlasticityDebugger::validParams();
26 4174 : params.addClassDescription("Base class for multi-surface finite-strain plasticity");
27 12522 : params.addRangeCheckedParam<unsigned int>("max_NR_iterations",
28 8348 : 20,
29 : "max_NR_iterations>0",
30 : "Maximum number of Newton-Raphson iterations allowed");
31 8348 : params.addRequiredParam<Real>("ep_plastic_tolerance",
32 : "The Newton-Raphson process is only deemed "
33 : "converged if the plastic strain increment "
34 : "constraints have L2 norm less than this.");
35 12522 : params.addRangeCheckedParam<Real>(
36 : "min_stepsize",
37 8348 : 0.01,
38 : "min_stepsize>0 & min_stepsize<=1",
39 : "If ordinary Newton-Raphson + line-search fails, then the applied strain increment is "
40 : "subdivided, and the return-map is tried again. This parameter is the minimum fraction of "
41 : "applied strain increment that may be applied before the algorithm gives up entirely");
42 12522 : params.addRangeCheckedParam<Real>("max_stepsize_for_dumb",
43 8348 : 0.01,
44 : "max_stepsize_for_dumb>0 & max_stepsize_for_dumb<=1",
45 : "If your deactivation_scheme is 'something_to_dumb', then "
46 : "'dumb' will only be used if the stepsize falls below this "
47 : "value. This parameter is useful because the 'dumb' scheme is "
48 : "computationally expensive");
49 : MooseEnum deactivation_scheme("optimized safe dumb optimized_to_safe safe_to_dumb "
50 : "optimized_to_safe_to_dumb optimized_to_dumb",
51 8348 : "optimized");
52 8348 : params.addParam<MooseEnum>(
53 : "deactivation_scheme",
54 : deactivation_scheme,
55 : "Scheme by which constraints are deactivated. (NOTE: This is irrelevant if there is only "
56 : "one yield surface.) safe: return to the yield surface and then deactivate constraints with "
57 : "negative plasticity multipliers. optimized: deactivate a constraint as soon as its "
58 : "plasticity multiplier becomes negative. dumb: iteratively try all combinations of active "
59 : "constraints until the solution is found. You may specify fall-back options. Eg "
60 : "optimized_to_safe: first use 'optimized', and if that fails, try the return with 'safe'.");
61 8348 : params.addParam<RealVectorValue>(
62 : "transverse_direction",
63 : "If this parameter is provided, before the return-map algorithm is "
64 : "called a rotation is performed so that the 'z' axis in the new "
65 : "frame lies along the transverse_direction in the original frame. "
66 : "After returning, the inverse rotation is performed. The "
67 : "transverse_direction will itself rotate with large strains. This "
68 : "is so that transversely-isotropic plasticity models may be easily "
69 : "defined in the frame where the isotropy holds in the x-y plane.");
70 8348 : params.addParam<bool>("ignore_failures",
71 8348 : false,
72 : "The return-map algorithm will return with the best admissible "
73 : "stresses and internal parameters that it can, even if they don't "
74 : "fully correspond to the applied strain increment. To speed "
75 : "computations, this flag can be set to true, the max_NR_iterations "
76 : "set small, and the min_stepsize large.");
77 8348 : MooseEnum tangent_operator("elastic linear nonlinear", "nonlinear");
78 8348 : params.addParam<MooseEnum>("tangent_operator",
79 : tangent_operator,
80 : "Type of tangent operator to return. 'elastic': return the "
81 : "elasticity tensor. 'linear': return the consistent tangent operator "
82 : "that is correct for plasticity with yield functions linear in "
83 : "stress. 'nonlinear': return the full, general consistent tangent "
84 : "operator. The calculations assume the hardening potentials are "
85 : "independent of stress and hardening parameters.");
86 8348 : params.addParam<bool>("perform_finite_strain_rotations",
87 8348 : true,
88 : "Tensors are correctly rotated in "
89 : "finite-strain simulations. For "
90 : "optimal performance you can set "
91 : "this to 'false' if you are only "
92 : "ever using small strains");
93 4174 : params.addClassDescription("Material for multi-surface finite-strain plasticity");
94 4174 : return params;
95 4174 : }
96 :
97 3122 : ComputeMultiPlasticityStress::ComputeMultiPlasticityStress(const InputParameters & parameters)
98 : : ComputeStressBase(parameters),
99 : MultiPlasticityDebugger(this),
100 3122 : _max_iter(getParam<unsigned int>("max_NR_iterations")),
101 6244 : _min_stepsize(getParam<Real>("min_stepsize")),
102 6244 : _max_stepsize_for_dumb(getParam<Real>("max_stepsize_for_dumb")),
103 6244 : _ignore_failures(getParam<bool>("ignore_failures")),
104 :
105 6244 : _tangent_operator_type((TangentOperatorEnum)(int)getParam<MooseEnum>("tangent_operator")),
106 :
107 6244 : _epp_tol(getParam<Real>("ep_plastic_tolerance")),
108 :
109 3122 : _dummy_pm(0),
110 :
111 3122 : _cumulative_pm(0),
112 :
113 6244 : _deactivation_scheme((DeactivationSchemeEnum)(int)getParam<MooseEnum>("deactivation_scheme")),
114 :
115 3122 : _n_supplied(parameters.isParamValid("transverse_direction")),
116 6612 : _n_input(_n_supplied ? getParam<RealVectorValue>("transverse_direction") : RealVectorValue()),
117 : _rot(RealTensorValue()),
118 :
119 6244 : _perform_finite_strain_rotations(getParam<bool>("perform_finite_strain_rotations")),
120 :
121 3122 : _elasticity_tensor_name(_base_name + "elasticity_tensor"),
122 3122 : _elasticity_tensor(getMaterialPropertyByName<RankFourTensor>(_elasticity_tensor_name)),
123 3122 : _plastic_strain(declareProperty<RankTwoTensor>("plastic_strain")),
124 6244 : _plastic_strain_old(getMaterialPropertyOld<RankTwoTensor>("plastic_strain")),
125 3122 : _intnl(declareProperty<std::vector<Real>>("plastic_internal_parameter")),
126 6244 : _intnl_old(getMaterialPropertyOld<std::vector<Real>>("plastic_internal_parameter")),
127 3122 : _yf(declareProperty<std::vector<Real>>("plastic_yield_function")),
128 3122 : _iter(declareProperty<Real>("plastic_NR_iterations")), // this is really an unsigned int, but
129 : // for visualisation i convert it to Real
130 3122 : _linesearch_needed(declareProperty<Real>("plastic_linesearch_needed")), // this is really a
131 : // boolean, but for
132 : // visualisation i
133 : // convert it to Real
134 3122 : _ld_encountered(declareProperty<Real>(
135 : "plastic_linear_dependence_encountered")), // this is really a boolean, but for
136 : // visualisation i convert it to Real
137 3122 : _constraints_added(declareProperty<Real>("plastic_constraints_added")), // this is really a
138 : // boolean, but for
139 : // visualisation i
140 : // convert it to Real
141 3122 : _n(declareProperty<RealVectorValue>("plastic_transverse_direction")),
142 6244 : _n_old(getMaterialPropertyOld<RealVectorValue>("plastic_transverse_direction")),
143 :
144 6244 : _strain_increment(getMaterialPropertyByName<RankTwoTensor>(_base_name + "strain_increment")),
145 6244 : _total_strain_old(getMaterialPropertyOldByName<RankTwoTensor>(_base_name + "total_strain")),
146 3122 : _rotation_increment(
147 3122 : getMaterialPropertyByName<RankTwoTensor>(_base_name + "rotation_increment")),
148 :
149 6244 : _stress_old(getMaterialPropertyOld<RankTwoTensor>(_base_name + "stress")),
150 6244 : _elastic_strain_old(getMaterialPropertyOld<RankTwoTensor>(_base_name + "elastic_strain")),
151 :
152 : // TODO: This design does NOT work. It makes these materials construction order dependent and it
153 : // disregards block restrictions.
154 3122 : _cosserat(hasMaterialProperty<RankTwoTensor>("curvature") &&
155 3140 : hasMaterialProperty<RankFourTensor>("elastic_flexural_rigidity_tensor")),
156 3140 : _curvature(_cosserat ? &getMaterialPropertyByName<RankTwoTensor>("curvature") : nullptr),
157 3122 : _elastic_flexural_rigidity_tensor(
158 3140 : _cosserat ? &getMaterialPropertyByName<RankFourTensor>("elastic_flexural_rigidity_tensor")
159 : : nullptr),
160 3122 : _couple_stress(_cosserat ? &declareProperty<RankTwoTensor>("couple_stress") : nullptr),
161 3140 : _couple_stress_old(_cosserat ? &getMaterialPropertyOld<RankTwoTensor>("couple_stress")
162 : : nullptr),
163 3122 : _Jacobian_mult_couple(_cosserat ? &declareProperty<RankFourTensor>("couple_Jacobian_mult")
164 : : nullptr),
165 :
166 3122 : _my_elasticity_tensor(RankFourTensor()),
167 3122 : _my_strain_increment(RankTwoTensor()),
168 3122 : _my_flexural_rigidity_tensor(RankFourTensor()),
169 6244 : _my_curvature(RankTwoTensor())
170 : {
171 3122 : if (_epp_tol <= 0)
172 0 : mooseError("ComputeMultiPlasticityStress: ep_plastic_tolerance must be positive");
173 :
174 3122 : if (_n_supplied)
175 : {
176 : // normalise the inputted transverse_direction
177 368 : if (_n_input.norm() == 0)
178 2 : mooseError(
179 : "ComputeMultiPlasticityStress: transverse_direction vector must not have zero length");
180 : else
181 366 : _n_input /= _n_input.norm();
182 : }
183 :
184 3120 : if (_num_surfaces == 1)
185 1842 : _deactivation_scheme = safe;
186 3120 : }
187 :
188 : void
189 87776 : ComputeMultiPlasticityStress::initQpStatefulProperties()
190 : {
191 87776 : ComputeStressBase::initQpStatefulProperties();
192 :
193 87776 : _plastic_strain[_qp].zero();
194 :
195 87776 : _intnl[_qp].assign(_num_models, 0);
196 :
197 87776 : _yf[_qp].assign(_num_surfaces, 0);
198 :
199 87776 : _dummy_pm.assign(_num_surfaces, 0);
200 :
201 87776 : _iter[_qp] = 0.0; // this is really an unsigned int, but for visualisation i convert it to Real
202 87776 : _linesearch_needed[_qp] = 0;
203 87776 : _ld_encountered[_qp] = 0;
204 87776 : _constraints_added[_qp] = 0;
205 :
206 87776 : _n[_qp] = _n_input;
207 :
208 87776 : if (_cosserat)
209 6400 : (*_couple_stress)[_qp].zero();
210 :
211 87776 : if (_fspb_debug == "jacobian")
212 : {
213 0 : checkDerivatives();
214 0 : mooseError("Finite-differencing completed. Exiting with no error");
215 : }
216 87776 : }
217 :
218 : void
219 6658928 : ComputeMultiPlasticityStress::computeQpStress()
220 : {
221 : // the following "_my" variables can get rotated by preReturnMap and postReturnMap
222 6658928 : _my_elasticity_tensor = _elasticity_tensor[_qp];
223 6658928 : _my_strain_increment = _strain_increment[_qp];
224 6658928 : if (_cosserat)
225 : {
226 19200 : _my_flexural_rigidity_tensor = (*_elastic_flexural_rigidity_tensor)[_qp];
227 19200 : _my_curvature = (*_curvature)[_qp];
228 : }
229 :
230 6658928 : if (_fspb_debug == "jacobian_and_linear_system")
231 : {
232 : // cannot do this at initQpStatefulProperties level since E_ijkl is not defined
233 0 : checkJacobian(_elasticity_tensor[_qp].invSymm(), _intnl_old[_qp]);
234 0 : checkSolution(_elasticity_tensor[_qp].invSymm());
235 0 : mooseError("Finite-differencing completed. Exiting with no error");
236 : }
237 :
238 6658928 : preReturnMap(); // do rotations to new frame if necessary
239 :
240 : unsigned int number_iterations;
241 6658928 : bool linesearch_needed = false;
242 6658928 : bool ld_encountered = false;
243 6658928 : bool constraints_added = false;
244 :
245 6658928 : _cumulative_pm.assign(_num_surfaces, 0);
246 : // try a "quick" return first - this can be purely elastic, or a customised plastic return defined
247 : // by a SolidMechanicsPlasticXXXX UserObject
248 6658928 : const bool found_solution = quickStep(rot(_stress_old[_qp]),
249 6658928 : _stress[_qp],
250 6658928 : _intnl_old[_qp],
251 6658928 : _intnl[_qp],
252 6658928 : _dummy_pm,
253 : _cumulative_pm,
254 13317856 : rot(_plastic_strain_old[_qp]),
255 6658928 : _plastic_strain[_qp],
256 6658928 : _my_elasticity_tensor,
257 6658928 : _my_strain_increment,
258 6658928 : _yf[_qp],
259 : number_iterations,
260 6658928 : _Jacobian_mult[_qp],
261 : computeQpStress_function,
262 : true);
263 :
264 : // if not purely elastic or the customised stuff failed, do some plastic return
265 6658928 : if (!found_solution)
266 138032 : plasticStep(rot(_stress_old[_qp]),
267 138032 : _stress[_qp],
268 138032 : _intnl_old[_qp],
269 138032 : _intnl[_qp],
270 138032 : rot(_plastic_strain_old[_qp]),
271 138032 : _plastic_strain[_qp],
272 : _my_elasticity_tensor,
273 : _my_strain_increment,
274 138032 : _yf[_qp],
275 : number_iterations,
276 : linesearch_needed,
277 : ld_encountered,
278 : constraints_added,
279 138032 : _Jacobian_mult[_qp]);
280 :
281 6658928 : if (_cosserat)
282 : {
283 19200 : (*_couple_stress)[_qp] = (*_elastic_flexural_rigidity_tensor)[_qp] * _my_curvature;
284 19200 : (*_Jacobian_mult_couple)[_qp] = _my_flexural_rigidity_tensor;
285 : }
286 :
287 6658928 : postReturnMap(); // rotate back from new frame if necessary
288 :
289 6658928 : _iter[_qp] = 1.0 * number_iterations;
290 6658928 : _linesearch_needed[_qp] = linesearch_needed;
291 6658928 : _ld_encountered[_qp] = ld_encountered;
292 6658928 : _constraints_added[_qp] = constraints_added;
293 :
294 : // Update measures of strain
295 6658928 : _elastic_strain[_qp] = _elastic_strain_old[_qp] + _my_strain_increment -
296 6658928 : (_plastic_strain[_qp] - _plastic_strain_old[_qp]);
297 :
298 : // Rotate the tensors to the current configuration
299 6658928 : if (_perform_finite_strain_rotations)
300 : {
301 6658928 : _stress[_qp] = _rotation_increment[_qp] * _stress[_qp] * _rotation_increment[_qp].transpose();
302 6658928 : _elastic_strain[_qp] =
303 6658928 : _rotation_increment[_qp] * _elastic_strain[_qp] * _rotation_increment[_qp].transpose();
304 6658928 : _plastic_strain[_qp] =
305 6658928 : _rotation_increment[_qp] * _plastic_strain[_qp] * _rotation_increment[_qp].transpose();
306 : }
307 6658928 : }
308 :
309 : RankTwoTensor
310 13593920 : ComputeMultiPlasticityStress::rot(const RankTwoTensor & tens)
311 : {
312 13593920 : if (!_n_supplied)
313 13541504 : return tens;
314 52416 : return tens.rotated(_rot);
315 : }
316 :
317 : void
318 6658928 : ComputeMultiPlasticityStress::preReturnMap()
319 : {
320 6658928 : if (_n_supplied)
321 : {
322 : // this is a rotation matrix that will rotate _n to the "z" axis
323 14304 : _rot = RotationMatrix::rotVecToZ(_n[_qp]);
324 :
325 : // rotate the tensors to this frame
326 14304 : _my_elasticity_tensor.rotate(_rot);
327 14304 : _my_strain_increment.rotate(_rot);
328 14304 : if (_cosserat)
329 : {
330 0 : _my_flexural_rigidity_tensor.rotate(_rot);
331 0 : _my_curvature.rotate(_rot);
332 : }
333 : }
334 6658928 : }
335 :
336 : void
337 6658928 : ComputeMultiPlasticityStress::postReturnMap()
338 : {
339 6658928 : if (_n_supplied)
340 : {
341 : // this is a rotation matrix that will rotate "z" axis back to _n
342 14304 : _rot = _rot.transpose();
343 :
344 : // rotate the tensors back to original frame where _n is correctly oriented
345 14304 : _my_elasticity_tensor.rotate(_rot);
346 14304 : _Jacobian_mult[_qp].rotate(_rot);
347 14304 : _my_strain_increment.rotate(_rot);
348 14304 : _stress[_qp].rotate(_rot);
349 14304 : _plastic_strain[_qp].rotate(_rot);
350 14304 : if (_cosserat)
351 : {
352 0 : _my_flexural_rigidity_tensor.rotate(_rot);
353 0 : (*_Jacobian_mult_couple)[_qp].rotate(_rot);
354 0 : _my_curvature.rotate(_rot);
355 0 : (*_couple_stress)[_qp].rotate(_rot);
356 : }
357 :
358 : // Rotate n by _rotation_increment
359 57216 : for (const auto i : make_range(Moose::dim))
360 : {
361 42912 : _n[_qp](i) = 0;
362 171648 : for (const auto j : make_range(Moose::dim))
363 128736 : _n[_qp](i) += _rotation_increment[_qp](i, j) * _n_old[_qp](j);
364 : }
365 : }
366 6658928 : }
367 :
368 : bool
369 6801584 : ComputeMultiPlasticityStress::quickStep(const RankTwoTensor & stress_old,
370 : RankTwoTensor & stress,
371 : const std::vector<Real> & intnl_old,
372 : std::vector<Real> & intnl,
373 : std::vector<Real> & pm,
374 : std::vector<Real> & cumulative_pm,
375 : const RankTwoTensor & plastic_strain_old,
376 : RankTwoTensor & plastic_strain,
377 : const RankFourTensor & E_ijkl,
378 : const RankTwoTensor & strain_increment,
379 : std::vector<Real> & yf,
380 : unsigned int & iterations,
381 : RankFourTensor & consistent_tangent_operator,
382 : const quickStep_called_from_t called_from,
383 : bool final_step)
384 : {
385 6801584 : iterations = 0;
386 :
387 : unsigned num_plastic_returns;
388 6801584 : RankTwoTensor delta_dp;
389 :
390 : // the following does the customized returnMap algorithm
391 : // for all the plastic models.
392 6801584 : unsigned custom_model = 0;
393 6801584 : bool successful_return = returnMapAll(stress_old + E_ijkl * strain_increment,
394 : intnl_old,
395 : E_ijkl,
396 : _epp_tol,
397 : stress,
398 : intnl,
399 : pm,
400 : cumulative_pm,
401 : delta_dp,
402 : yf,
403 : num_plastic_returns,
404 : custom_model);
405 :
406 : // the following updates the plastic_strain, when necessary
407 : // and calculates the consistent_tangent_operator, when necessary
408 6801584 : if (num_plastic_returns == 0)
409 : {
410 : // if successful_return = true, then a purely elastic step
411 : // if successful_return = false, then >=1 plastic model is in
412 : // inadmissible zone and failed to return using its customized
413 : // returnMap function.
414 : // In either case:
415 326500 : plastic_strain = plastic_strain_old;
416 326500 : if (successful_return && final_step)
417 : {
418 45812 : if (called_from == computeQpStress_function)
419 45812 : consistent_tangent_operator = E_ijkl;
420 : else // cannot necessarily use E_ijkl since different plastic models may have been active
421 : // during other substeps
422 : consistent_tangent_operator =
423 0 : consistentTangentOperator(stress, intnl, E_ijkl, pm, cumulative_pm);
424 : }
425 326500 : return successful_return;
426 : }
427 6475084 : else if (num_plastic_returns == 1 && successful_return)
428 : {
429 : // one model has successfully completed its custom returnMap algorithm
430 : // and the other models have signalled they are elastic at
431 : // the trial stress
432 6475084 : plastic_strain = plastic_strain_old + delta_dp;
433 6475084 : if (final_step)
434 : {
435 6475084 : if (called_from == computeQpStress_function && _f[custom_model]->useCustomCTO())
436 : {
437 6466028 : if (_tangent_operator_type == elastic)
438 0 : consistent_tangent_operator = E_ijkl;
439 : else
440 : {
441 : std::vector<Real> custom_model_pm;
442 12949432 : for (unsigned surface = 0; surface < _f[custom_model]->numberSurfaces(); ++surface)
443 6483404 : custom_model_pm.push_back(cumulative_pm[_surfaces_given_model[custom_model][surface]]);
444 : consistent_tangent_operator =
445 6466028 : _f[custom_model]->consistentTangentOperator(stress_old + E_ijkl * strain_increment,
446 : intnl_old[custom_model],
447 : stress,
448 : intnl[custom_model],
449 : E_ijkl,
450 : custom_model_pm);
451 : }
452 : }
453 : else // cannot necessarily use the custom consistentTangentOperator since different plastic
454 : // models may have been active during other substeps or the custom model says not to use
455 : // its custom CTO algorithm
456 : consistent_tangent_operator =
457 9056 : consistentTangentOperator(stress, intnl, E_ijkl, pm, cumulative_pm);
458 : }
459 6475084 : return true;
460 : }
461 : else // presumably returnMapAll is incorrectly coded!
462 0 : mooseError("ComputeMultiPlasticityStress::quickStep should not get here!");
463 : }
464 :
465 : bool
466 138032 : ComputeMultiPlasticityStress::plasticStep(const RankTwoTensor & stress_old,
467 : RankTwoTensor & stress,
468 : const std::vector<Real> & intnl_old,
469 : std::vector<Real> & intnl,
470 : const RankTwoTensor & plastic_strain_old,
471 : RankTwoTensor & plastic_strain,
472 : const RankFourTensor & E_ijkl,
473 : const RankTwoTensor & strain_increment,
474 : std::vector<Real> & yf,
475 : unsigned int & iterations,
476 : bool & linesearch_needed,
477 : bool & ld_encountered,
478 : bool & constraints_added,
479 : RankFourTensor & consistent_tangent_operator)
480 : {
481 : /**
482 : * the idea in the following is to potentially subdivide the
483 : * strain increment into smaller fractions, of size "step_size".
484 : * First step_size = 1 is tried, and if that fails then 0.5 is
485 : * tried, then 0.25, etc. As soon as a step is successful, its
486 : * results are put into the "good" variables, which are used
487 : * if a subsequent step fails. If >= 2 consecutive steps are
488 : * successful, the step_size is increased by 1.2
489 : *
490 : * The point of all this is that I hope that the
491 : * time-step for the entire mesh need not be cut if there
492 : * are only a few "bad" quadpoints where the return-map
493 : * is difficult
494 : */
495 :
496 : bool return_successful = false;
497 :
498 : // total number of Newton-Raphson iterations used
499 138032 : unsigned int iter = 0;
500 :
501 138032 : Real step_size = 1.0;
502 : Real time_simulated = 0.0;
503 :
504 : // the "good" variables hold the latest admissible stress
505 : // and internal parameters.
506 138032 : RankTwoTensor stress_good = stress_old;
507 138032 : RankTwoTensor plastic_strain_good = plastic_strain_old;
508 138032 : std::vector<Real> intnl_good(_num_models);
509 348448 : for (unsigned model = 0; model < _num_models; ++model)
510 210416 : intnl_good[model] = intnl_old[model];
511 138032 : std::vector<Real> yf_good(_num_surfaces);
512 :
513 : // Following is necessary because I want strain_increment to be "const"
514 : // but I also want to be able to subdivide an initial_stress
515 138032 : RankTwoTensor this_strain_increment = strain_increment;
516 :
517 138032 : RankTwoTensor dep = step_size * this_strain_increment;
518 :
519 138032 : _cumulative_pm.assign(_num_surfaces, 0);
520 :
521 : unsigned int num_consecutive_successes = 0;
522 280688 : while (time_simulated < 1.0 && step_size >= _min_stepsize)
523 : {
524 142656 : iter = 0;
525 142656 : return_successful = returnMap(stress_good,
526 : stress,
527 : intnl_good,
528 : intnl,
529 : plastic_strain_good,
530 : plastic_strain,
531 : E_ijkl,
532 : dep,
533 : yf,
534 : iter,
535 142656 : step_size <= _max_stepsize_for_dumb,
536 : linesearch_needed,
537 : ld_encountered,
538 : constraints_added,
539 142656 : time_simulated + step_size >= 1,
540 : consistent_tangent_operator,
541 : _cumulative_pm);
542 142656 : iterations += iter;
543 :
544 142656 : if (return_successful)
545 : {
546 141136 : num_consecutive_successes += 1;
547 : time_simulated += step_size;
548 :
549 141136 : if (time_simulated < 1.0) // this condition is just for optimization: if time_simulated=1 then
550 : // the "good" quantities are no longer needed
551 : {
552 3104 : stress_good = stress;
553 3104 : plastic_strain_good = plastic_strain;
554 14368 : for (unsigned model = 0; model < _num_models; ++model)
555 11264 : intnl_good[model] = intnl[model];
556 14368 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
557 11264 : yf_good[surface] = yf[surface];
558 3104 : if (num_consecutive_successes >= 2)
559 2224 : step_size *= 1.2;
560 : }
561 279360 : step_size = std::min(step_size, 1.0 - time_simulated); // avoid overshoots
562 : }
563 : else
564 : {
565 1520 : step_size *= 0.5;
566 : num_consecutive_successes = 0;
567 1520 : stress = stress_good;
568 1520 : plastic_strain = plastic_strain_good;
569 6448 : for (unsigned model = 0; model < _num_models; ++model)
570 4928 : intnl[model] = intnl_good[model];
571 1520 : yf.resize(_num_surfaces); // might have excited with junk
572 6448 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
573 4928 : yf[surface] = yf_good[surface];
574 1520 : dep = step_size * this_strain_increment;
575 : }
576 : }
577 :
578 138032 : if (!return_successful)
579 : {
580 0 : if (_ignore_failures)
581 : {
582 0 : stress = stress_good;
583 0 : plastic_strain = plastic_strain_good;
584 0 : for (unsigned model = 0; model < _num_models; ++model)
585 0 : intnl[model] = intnl_good[model];
586 0 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
587 0 : yf[surface] = yf_good[surface];
588 : }
589 : else
590 : {
591 : Moose::out << "After reducing the stepsize to " << step_size
592 0 : << " with original strain increment with L2norm " << this_strain_increment.L2norm()
593 : << " the returnMap algorithm failed" << std::endl;
594 :
595 0 : _fspb_debug_stress = stress_good + E_ijkl * dep;
596 0 : _fspb_debug_pm.assign(
597 0 : _num_surfaces,
598 : 1); // this is chosen arbitrarily - please change if a more suitable value occurs to you!
599 0 : _fspb_debug_intnl.resize(_num_models);
600 0 : for (unsigned model = 0; model < _num_models; ++model)
601 0 : _fspb_debug_intnl[model] = intnl_good[model];
602 0 : checkDerivatives();
603 0 : checkJacobian(_my_elasticity_tensor.invSymm(), _intnl_old[_qp]);
604 0 : checkSolution(_my_elasticity_tensor.invSymm());
605 0 : mooseError("Exiting\n");
606 : }
607 : }
608 :
609 138032 : return return_successful;
610 : }
611 :
612 : bool
613 142656 : ComputeMultiPlasticityStress::returnMap(const RankTwoTensor & stress_old,
614 : RankTwoTensor & stress,
615 : const std::vector<Real> & intnl_old,
616 : std::vector<Real> & intnl,
617 : const RankTwoTensor & plastic_strain_old,
618 : RankTwoTensor & plastic_strain,
619 : const RankFourTensor & E_ijkl,
620 : const RankTwoTensor & strain_increment,
621 : std::vector<Real> & f,
622 : unsigned int & iter,
623 : bool can_revert_to_dumb,
624 : bool & linesearch_needed,
625 : bool & ld_encountered,
626 : bool & constraints_added,
627 : bool final_step,
628 : RankFourTensor & consistent_tangent_operator,
629 : std::vector<Real> & cumulative_pm)
630 : {
631 :
632 : // The "consistency parameters" (plastic multipliers)
633 : // Change in plastic strain in this timestep = pm*flowPotential
634 : // Each pm must be non-negative
635 : std::vector<Real> pm;
636 142656 : pm.assign(_num_surfaces, 0.0);
637 :
638 142656 : bool successful_return = quickStep(stress_old,
639 : stress,
640 : intnl_old,
641 : intnl,
642 : pm,
643 : cumulative_pm,
644 : plastic_strain_old,
645 : plastic_strain,
646 : E_ijkl,
647 : strain_increment,
648 : f,
649 : iter,
650 : consistent_tangent_operator,
651 : returnMap_function,
652 : final_step);
653 :
654 142656 : if (successful_return)
655 : return successful_return;
656 :
657 : // Here we know that the trial stress and intnl_old
658 : // is inadmissible, and we have to return from those values
659 : // value to the yield surface. There are three
660 : // types of constraints we have to satisfy, listed
661 : // below, and calculated in calculateConstraints(...)
662 : // f<=0, epp=0, ic=0 (up to tolerances), and these are
663 : // guaranteed to hold if nr_res2<=0.5
664 : //
665 : // Kuhn-Tucker conditions must also be satisfied
666 : // These are:
667 : // pm>=0, which may not hold upon exit of the NR loops
668 : // due to _deactivation_scheme!=optimized;
669 : // pm*f=0 (up to tolerances), which may not hold upon exit
670 : // of the NR loops if a constraint got deactivated
671 : // due to linear dependence, and then f<0, and its pm>0
672 :
673 : // Plastic strain constraint, L2 norm must be zero (up to a tolerance)
674 142656 : RankTwoTensor epp;
675 :
676 : // Yield function constraint passed to this function as
677 : // std::vector<Real> & f
678 : // Each yield function must be <= 0 (up to tolerance)
679 : // Note that only the constraints that are active will be
680 : // contained in f until the final few lines of returnMap
681 :
682 : // Internal constraint(s), must be zero (up to a tolerance)
683 : // Note that only the constraints that are active will be
684 : // contained in ic.
685 : std::vector<Real> ic;
686 :
687 : // Record the stress before Newton-Raphson in case of failure-and-restart
688 142656 : RankTwoTensor initial_stress = stress;
689 :
690 142656 : iter = 0;
691 :
692 : // Initialize the set of active constraints
693 : // At this stage, the active constraints are
694 : // those that exceed their _f_tol
695 : // active constraints.
696 : std::vector<bool> act;
697 142656 : buildActiveConstraints(f, stress, intnl, E_ijkl, act);
698 :
699 : // Inverse of E_ijkl (assuming symmetric)
700 142656 : RankFourTensor E_inv = E_ijkl.invSymm();
701 :
702 : // convenience variable that holds the change in plastic strain incurred during the return
703 : // delta_dp = plastic_strain - plastic_strain_old
704 : // delta_dp = E^{-1}*(initial_stress - stress), where initial_stress = E*(strain -
705 : // plastic_strain_old)
706 142656 : RankTwoTensor delta_dp = RankTwoTensor();
707 :
708 : // whether single step was successful (whether line search was successful, and whether turning off
709 : // constraints was successful)
710 : bool single_step_success = true;
711 :
712 : // deactivation scheme
713 142656 : DeactivationSchemeEnum deact_scheme = _deactivation_scheme;
714 :
715 : // For complicated deactivation schemes we have to record the initial active set
716 : std::vector<bool> initial_act;
717 142656 : initial_act.resize(_num_surfaces);
718 142656 : if (_deactivation_scheme == optimized_to_safe ||
719 141024 : _deactivation_scheme == optimized_to_safe_to_dumb ||
720 : _deactivation_scheme == optimized_to_dumb)
721 : {
722 : // if "optimized" fails we can change the deactivation scheme to "safe", etc
723 1632 : deact_scheme = optimized;
724 12128 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
725 10496 : initial_act[surface] = act[surface];
726 : }
727 :
728 142656 : if (_deactivation_scheme == safe_to_dumb)
729 0 : deact_scheme = safe;
730 :
731 : // For "dumb" deactivation, the active set takes all combinations until a solution is found
732 142656 : int dumb_iteration = 0;
733 : std::vector<unsigned int> dumb_order;
734 :
735 142656 : if (_deactivation_scheme == dumb ||
736 142656 : (_deactivation_scheme == optimized_to_safe_to_dumb && can_revert_to_dumb) ||
737 142656 : (_deactivation_scheme == safe_to_dumb && can_revert_to_dumb) ||
738 0 : (_deactivation_scheme == optimized_to_dumb && can_revert_to_dumb))
739 0 : buildDumbOrder(stress, intnl, dumb_order);
740 :
741 142656 : if (_deactivation_scheme == dumb)
742 : {
743 0 : incrementDumb(dumb_iteration, dumb_order, act);
744 0 : yieldFunction(stress, intnl, act, f);
745 : }
746 :
747 : // To avoid any re-trials of "act" combinations that
748 : // we've already tried and rejected, i record the
749 : // combinations in actives_tried
750 : std::set<unsigned int> actives_tried;
751 142656 : actives_tried.insert(activeCombinationNumber(act));
752 :
753 : // The residual-squared that the line-search will reduce
754 : // Later it will get contributions from epp and ic, but
755 : // at present these are zero
756 142656 : Real nr_res2 = 0;
757 487984 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
758 345328 : if (act[surface])
759 203552 : nr_res2 += 0.5 * Utility::pow<2>(f[surface] / _f[modelNumber(surface)]->_f_tol);
760 :
761 : successful_return = false;
762 :
763 : bool still_finding_solution = true;
764 : while (still_finding_solution)
765 : {
766 : single_step_success = true;
767 : unsigned int local_iter = 0;
768 :
769 : // The Newton-Raphson loops
770 683804 : while (nr_res2 > 0.5 && local_iter++ < _max_iter && single_step_success)
771 531248 : single_step_success = singleStep(nr_res2,
772 : stress,
773 : intnl_old,
774 : intnl,
775 : pm,
776 : delta_dp,
777 : E_inv,
778 : f,
779 : epp,
780 : ic,
781 : act,
782 : deact_scheme,
783 : linesearch_needed,
784 : ld_encountered);
785 :
786 152556 : bool nr_good = (nr_res2 <= 0.5 && local_iter <= _max_iter && single_step_success);
787 :
788 152556 : iter += local_iter;
789 :
790 : // 'act' might have changed due to using deact_scheme = optimized, so
791 152556 : actives_tried.insert(activeCombinationNumber(act));
792 :
793 152556 : if (!nr_good)
794 : {
795 : // failure of NR routine.
796 : // We might be able to change the deactivation_scheme and
797 : // then re-try the NR starting from the initial values
798 : // Or, if deact_scheme == "dumb", we just increarse the
799 : // dumb_iteration number and re-try
800 : bool change_scheme = false;
801 : bool increment_dumb = false;
802 592 : change_scheme = canChangeScheme(deact_scheme, can_revert_to_dumb);
803 592 : if (!change_scheme && deact_scheme == dumb)
804 0 : increment_dumb = canIncrementDumb(dumb_iteration);
805 :
806 592 : still_finding_solution = (change_scheme || increment_dumb);
807 :
808 592 : if (change_scheme)
809 0 : changeScheme(initial_act,
810 : can_revert_to_dumb,
811 : initial_stress,
812 : intnl_old,
813 : deact_scheme,
814 : act,
815 : dumb_iteration,
816 : dumb_order);
817 :
818 592 : if (increment_dumb)
819 0 : incrementDumb(dumb_iteration, dumb_order, act);
820 :
821 592 : if (!still_finding_solution)
822 : {
823 : // we cannot change the scheme, or have run out of "dumb" options
824 : successful_return = false;
825 : break;
826 : }
827 : }
828 :
829 : bool kt_good = false;
830 : if (nr_good)
831 : {
832 : // check Kuhn-Tucker
833 151964 : kt_good = checkKuhnTucker(f, pm, act);
834 151964 : if (!kt_good)
835 : {
836 8204 : if (deact_scheme != dumb)
837 : {
838 8204 : applyKuhnTucker(f, pm, act);
839 :
840 : // true if we haven't tried this active set before
841 : still_finding_solution =
842 8204 : (actives_tried.find(activeCombinationNumber(act)) == actives_tried.end());
843 8204 : if (!still_finding_solution)
844 : {
845 : // must have tried turning off the constraints already.
846 : // so try changing the scheme
847 16 : if (canChangeScheme(deact_scheme, can_revert_to_dumb))
848 : {
849 : still_finding_solution = true;
850 0 : changeScheme(initial_act,
851 : can_revert_to_dumb,
852 : initial_stress,
853 : intnl_old,
854 : deact_scheme,
855 : act,
856 : dumb_iteration,
857 : dumb_order);
858 : }
859 : }
860 : }
861 : else
862 : {
863 : bool increment_dumb = false;
864 0 : increment_dumb = canIncrementDumb(dumb_iteration);
865 : still_finding_solution = increment_dumb;
866 0 : if (increment_dumb)
867 0 : incrementDumb(dumb_iteration, dumb_order, act);
868 : }
869 :
870 8204 : if (!still_finding_solution)
871 : {
872 : // have tried turning off the constraints already,
873 : // or have run out of "dumb" options
874 : successful_return = false;
875 : break;
876 : }
877 : }
878 : }
879 :
880 : bool admissible = false;
881 151948 : if (nr_good && kt_good)
882 : {
883 : // check admissible
884 : std::vector<Real> all_f;
885 143760 : if (_num_surfaces == 1)
886 : admissible = true; // for a single surface if NR has exited successfully then (stress,
887 : // intnl) must be admissible
888 : else
889 56160 : admissible = checkAdmissible(stress, intnl, all_f);
890 :
891 56160 : if (!admissible)
892 : {
893 : // Not admissible.
894 : // We can try adding constraints back in
895 : // We can try changing the deactivation scheme
896 : // Or, if deact_scheme == dumb, just increase dumb_iteration
897 2624 : bool add_constraints = canAddConstraints(act, all_f);
898 2624 : if (add_constraints)
899 : {
900 2624 : constraints_added = true;
901 2624 : std::vector<bool> act_plus(_num_surfaces,
902 2624 : false); // "act" with the positive constraints added in
903 15936 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
904 13312 : if (act[surface] ||
905 8064 : (!act[surface] && (all_f[surface] > _f[modelNumber(surface)]->_f_tol)))
906 : act_plus[surface] = true;
907 2624 : if (actives_tried.find(activeCombinationNumber(act_plus)) == actives_tried.end())
908 : {
909 : // haven't tried this combination of actives yet
910 1232 : constraints_added = true;
911 7568 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
912 6336 : act[surface] = act_plus[surface];
913 : }
914 : else
915 : add_constraints = false; // haven't managed to add a new combination
916 : }
917 :
918 : bool change_scheme = false;
919 : bool increment_dumb = false;
920 :
921 2624 : if (!add_constraints)
922 1392 : change_scheme = canChangeScheme(deact_scheme, can_revert_to_dumb);
923 :
924 2624 : if (!add_constraints && !change_scheme && deact_scheme == dumb)
925 0 : increment_dumb = canIncrementDumb(dumb_iteration);
926 :
927 2624 : still_finding_solution = (add_constraints || change_scheme || increment_dumb);
928 :
929 2624 : if (change_scheme)
930 480 : changeScheme(initial_act,
931 : can_revert_to_dumb,
932 : initial_stress,
933 : intnl_old,
934 : deact_scheme,
935 : act,
936 : dumb_iteration,
937 : dumb_order);
938 :
939 2624 : if (increment_dumb)
940 0 : incrementDumb(dumb_iteration, dumb_order, act);
941 :
942 2624 : if (!still_finding_solution)
943 : {
944 : // we cannot change the scheme, or have run out of "dumb" options
945 : successful_return = false;
946 : break;
947 : }
948 : }
949 : }
950 :
951 151036 : successful_return = (nr_good && admissible && kt_good);
952 : if (successful_return)
953 : break;
954 :
955 : if (still_finding_solution)
956 : {
957 9900 : stress = initial_stress;
958 9900 : delta_dp = RankTwoTensor(); // back to zero change in plastic strain
959 53468 : for (unsigned model = 0; model < _num_models; ++model)
960 43568 : intnl[model] = intnl_old[model]; // back to old internal params
961 9900 : pm.assign(_num_surfaces, 0.0); // back to zero plastic multipliers
962 :
963 9900 : unsigned num_active = numberActive(act);
964 9900 : if (num_active == 0)
965 : {
966 : successful_return = false;
967 : break; // failure
968 : }
969 :
970 9900 : actives_tried.insert(activeCombinationNumber(act));
971 :
972 : // Since "act" set has changed, either by changing deact_scheme, or by KT failing, so need to
973 : // re-calculate nr_res2
974 9900 : yieldFunction(stress, intnl, act, f);
975 :
976 9900 : nr_res2 = 0;
977 : unsigned ind = 0;
978 82252 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
979 72352 : if (act[surface])
980 : {
981 33772 : if (f[ind] > _f[modelNumber(surface)]->_f_tol)
982 33580 : nr_res2 += 0.5 * Utility::pow<2>(f[ind] / _f[modelNumber(surface)]->_f_tol);
983 33772 : ind++;
984 : }
985 : }
986 : }
987 :
988 : // returned, with either success or failure
989 141744 : if (successful_return)
990 : {
991 141136 : plastic_strain += delta_dp;
992 :
993 481536 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
994 340400 : cumulative_pm[surface] += pm[surface];
995 :
996 141136 : if (final_step)
997 : consistent_tangent_operator =
998 138032 : consistentTangentOperator(stress, intnl, E_ijkl, pm, cumulative_pm);
999 :
1000 141136 : if (f.size() != _num_surfaces)
1001 : {
1002 : // at this stage f.size() = num_active, but we need to return with all the yield functions
1003 : // evaluated, so:
1004 : act.assign(_num_surfaces, true);
1005 45168 : yieldFunction(stress, intnl, act, f);
1006 : }
1007 : }
1008 :
1009 : return successful_return;
1010 : }
1011 :
1012 : bool
1013 2624 : ComputeMultiPlasticityStress::canAddConstraints(const std::vector<bool> & act,
1014 : const std::vector<Real> & all_f)
1015 : {
1016 5552 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
1017 5552 : if (!act[surface] && (all_f[surface] > _f[modelNumber(surface)]->_f_tol))
1018 : return true;
1019 : return false;
1020 : }
1021 :
1022 : bool
1023 2000 : ComputeMultiPlasticityStress::canChangeScheme(DeactivationSchemeEnum current_deactivation_scheme,
1024 : bool can_revert_to_dumb)
1025 : {
1026 2000 : if (current_deactivation_scheme == optimized && _deactivation_scheme == optimized_to_safe)
1027 : return true;
1028 :
1029 1152 : if (current_deactivation_scheme == optimized && _deactivation_scheme == optimized_to_safe_to_dumb)
1030 : return true;
1031 :
1032 1520 : if (current_deactivation_scheme == safe && _deactivation_scheme == safe_to_dumb &&
1033 : can_revert_to_dumb)
1034 : return true;
1035 :
1036 400 : if (current_deactivation_scheme == safe && _deactivation_scheme == optimized_to_safe_to_dumb &&
1037 : can_revert_to_dumb)
1038 : return true;
1039 :
1040 1520 : if (current_deactivation_scheme == optimized && _deactivation_scheme == optimized_to_dumb &&
1041 : can_revert_to_dumb)
1042 : return true;
1043 :
1044 : return false;
1045 : }
1046 :
1047 : void
1048 480 : ComputeMultiPlasticityStress::changeScheme(const std::vector<bool> & initial_act,
1049 : bool can_revert_to_dumb,
1050 : const RankTwoTensor & initial_stress,
1051 : const std::vector<Real> & intnl_old,
1052 : DeactivationSchemeEnum & current_deactivation_scheme,
1053 : std::vector<bool> & act,
1054 : int & dumb_iteration,
1055 : std::vector<unsigned int> & dumb_order)
1056 : {
1057 480 : if (current_deactivation_scheme == optimized &&
1058 480 : (_deactivation_scheme == optimized_to_safe ||
1059 : _deactivation_scheme == optimized_to_safe_to_dumb))
1060 : {
1061 480 : current_deactivation_scheme = safe;
1062 3808 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
1063 3328 : act[surface] = initial_act[surface];
1064 : }
1065 0 : else if ((current_deactivation_scheme == safe &&
1066 0 : (_deactivation_scheme == optimized_to_safe_to_dumb ||
1067 : _deactivation_scheme == safe_to_dumb) &&
1068 0 : can_revert_to_dumb) ||
1069 0 : (current_deactivation_scheme == optimized && _deactivation_scheme == optimized_to_dumb &&
1070 : can_revert_to_dumb))
1071 : {
1072 0 : current_deactivation_scheme = dumb;
1073 0 : dumb_iteration = 0;
1074 0 : buildDumbOrder(initial_stress, intnl_old, dumb_order);
1075 0 : incrementDumb(dumb_iteration, dumb_order, act);
1076 : }
1077 480 : }
1078 :
1079 : bool
1080 531248 : ComputeMultiPlasticityStress::singleStep(Real & nr_res2,
1081 : RankTwoTensor & stress,
1082 : const std::vector<Real> & intnl_old,
1083 : std::vector<Real> & intnl,
1084 : std::vector<Real> & pm,
1085 : RankTwoTensor & delta_dp,
1086 : const RankFourTensor & E_inv,
1087 : std::vector<Real> & f,
1088 : RankTwoTensor & epp,
1089 : std::vector<Real> & ic,
1090 : std::vector<bool> & active,
1091 : DeactivationSchemeEnum deactivation_scheme,
1092 : bool & linesearch_needed,
1093 : bool & ld_encountered)
1094 : {
1095 : bool successful_step; // return value
1096 :
1097 531248 : Real nr_res2_before_step = nr_res2;
1098 531248 : RankTwoTensor stress_before_step;
1099 : std::vector<Real> intnl_before_step;
1100 : std::vector<Real> pm_before_step;
1101 531248 : RankTwoTensor delta_dp_before_step;
1102 :
1103 531248 : if (deactivation_scheme == optimized)
1104 : {
1105 : // we potentially use the "before_step" quantities, so record them here
1106 74476 : stress_before_step = stress;
1107 74476 : intnl_before_step.resize(_num_models);
1108 376564 : for (unsigned model = 0; model < _num_models; ++model)
1109 302088 : intnl_before_step[model] = intnl[model];
1110 74476 : pm_before_step.resize(_num_surfaces);
1111 376564 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
1112 302088 : pm_before_step[surface] = pm[surface];
1113 74476 : delta_dp_before_step = delta_dp;
1114 : }
1115 :
1116 : // During the Newton-Raphson procedure, we'll be
1117 : // changing the following parameters in order to
1118 : // (attempt to) satisfy the constraints.
1119 531248 : RankTwoTensor dstress; // change in stress
1120 : std::vector<Real> dpm; // change in plasticity multipliers ("consistency parameters"). For ALL
1121 : // contraints (active and deactive)
1122 : std::vector<Real>
1123 : dintnl; // change in internal parameters. For ALL internal params (active and deactive)
1124 :
1125 : // The constraints that have been deactivated for this NR step
1126 : // due to the flow directions being linearly dependent
1127 : std::vector<bool> deact_ld;
1128 531248 : deact_ld.assign(_num_surfaces, false);
1129 :
1130 : /* After NR and linesearch, if _deactivation_scheme == "optimized", the
1131 : * active plasticity multipliers are checked for non-negativity. If some
1132 : * are negative then they are deactivated forever, and the NR step is
1133 : * re-done starting from the _before_step quantities recorded above
1134 : */
1135 : bool constraints_changing = true;
1136 : bool reinstated_actives;
1137 1075520 : while (constraints_changing)
1138 : {
1139 : // calculate dstress, dpm and dintnl for one full Newton-Raphson step
1140 544304 : nrStep(stress, intnl_old, intnl, pm, E_inv, delta_dp, dstress, dpm, dintnl, active, deact_ld);
1141 :
1142 1469908 : for (unsigned surface = 0; surface < deact_ld.size(); ++surface)
1143 946596 : if (deact_ld[surface])
1144 : {
1145 20992 : ld_encountered = true;
1146 20992 : break;
1147 : }
1148 :
1149 : // perform a line search
1150 : // The line-search will exit with updated values
1151 544304 : successful_step = lineSearch(nr_res2,
1152 : stress,
1153 : intnl_old,
1154 : intnl,
1155 : pm,
1156 : E_inv,
1157 : delta_dp,
1158 : dstress,
1159 : dpm,
1160 : dintnl,
1161 : f,
1162 : epp,
1163 : ic,
1164 : active,
1165 : deact_ld,
1166 : linesearch_needed);
1167 :
1168 544304 : if (!successful_step)
1169 : // completely bomb out
1170 : return successful_step;
1171 :
1172 : // See if any active constraints need to be removed, and the step re-done
1173 : constraints_changing = false;
1174 544272 : if (deactivation_scheme == optimized)
1175 : {
1176 451020 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
1177 363520 : if (active[surface] && pm[surface] < 0.0)
1178 : constraints_changing = true;
1179 : }
1180 :
1181 87500 : if (constraints_changing)
1182 : {
1183 13056 : stress = stress_before_step;
1184 13056 : delta_dp = delta_dp_before_step;
1185 13056 : nr_res2 = nr_res2_before_step;
1186 74616 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
1187 : {
1188 61560 : if (active[surface] && pm[surface] < 0.0)
1189 : {
1190 : // turn off the constraint forever
1191 : active[surface] = false;
1192 14464 : pm_before_step[surface] = 0.0;
1193 14464 : intnl_before_step[modelNumber(surface)] =
1194 14464 : intnl_old[modelNumber(surface)]; // don't want to muck-up hardening!
1195 : }
1196 61560 : intnl[modelNumber(surface)] = intnl_before_step[modelNumber(surface)];
1197 61560 : pm[surface] = pm_before_step[surface];
1198 : }
1199 13056 : if (numberActive(active) == 0)
1200 : {
1201 : // completely bomb out
1202 : successful_step = false;
1203 : return successful_step;
1204 : }
1205 : }
1206 :
1207 : // reinstate any active values that have been turned off due to linear-dependence
1208 544272 : reinstated_actives = reinstateLinearDependentConstraints(deact_ld);
1209 : } // ends "constraints_changing" loop
1210 :
1211 : // if active constraints were reinstated then nr_res2 needs to be re-calculated so it is correct
1212 : // upson returning
1213 531216 : if (reinstated_actives)
1214 : {
1215 : bool completely_converged = true;
1216 16112 : if (successful_step && nr_res2 < 0.5)
1217 : {
1218 : // Here we have converged to the correct solution if
1219 : // all the yield functions are < 0. Excellent!
1220 : //
1221 : // This is quite tricky - perhaps i can refactor to make it more obvious.
1222 : //
1223 : // Because actives are now reinstated, the residual2
1224 : // calculation below will give nr_res2 > 0.5, because it won't
1225 : // realise that we only had to set the active-but-not-deactivated f = 0,
1226 : // and not the entire active set. If we pass that nr_res2 back from
1227 : // this function then the calling function will not realise we've converged!
1228 : // Therefore, check for this case
1229 : unsigned ind = 0;
1230 77276 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
1231 67952 : if (active[surface])
1232 43132 : if (f[ind++] > _f[modelNumber(surface)]->_f_tol)
1233 : completely_converged = false;
1234 : }
1235 : else
1236 : completely_converged = false;
1237 :
1238 9324 : if (!completely_converged)
1239 6788 : nr_res2 = residual2(pm, f, epp, ic, active, deact_ld);
1240 : }
1241 :
1242 : return successful_step;
1243 : }
1244 :
1245 : bool
1246 544272 : ComputeMultiPlasticityStress::reinstateLinearDependentConstraints(
1247 : std::vector<bool> & deactivated_due_to_ld)
1248 : {
1249 : bool reinstated_actives = false;
1250 1580768 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
1251 1036496 : if (deactivated_due_to_ld[surface])
1252 : reinstated_actives = true;
1253 :
1254 544272 : deactivated_due_to_ld.assign(_num_surfaces, false);
1255 544272 : return reinstated_actives;
1256 : }
1257 :
1258 : unsigned
1259 22956 : ComputeMultiPlasticityStress::numberActive(const std::vector<bool> & active)
1260 : {
1261 : unsigned num_active = 0;
1262 156868 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
1263 133912 : if (active[surface])
1264 67136 : num_active++;
1265 22956 : return num_active;
1266 : }
1267 :
1268 : bool
1269 56160 : ComputeMultiPlasticityStress::checkAdmissible(const RankTwoTensor & stress,
1270 : const std::vector<Real> & intnl,
1271 : std::vector<Real> & all_f)
1272 : {
1273 : std::vector<bool> act;
1274 56160 : act.assign(_num_surfaces, true);
1275 :
1276 56160 : yieldFunction(stress, intnl, act, all_f);
1277 :
1278 311888 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
1279 258352 : if (all_f[surface] > _f[modelNumber(surface)]->_f_tol)
1280 : return false;
1281 :
1282 : return true;
1283 : }
1284 :
1285 : bool
1286 151964 : ComputeMultiPlasticityStress::checkKuhnTucker(const std::vector<Real> & f,
1287 : const std::vector<Real> & pm,
1288 : const std::vector<bool> & active)
1289 : {
1290 : unsigned ind = 0;
1291 557684 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
1292 : {
1293 409604 : if (active[surface])
1294 : {
1295 217928 : if (f[ind++] < -_f[modelNumber(surface)]->_f_tol)
1296 16624 : if (pm[surface] != 0)
1297 : return false;
1298 : }
1299 191676 : else if (pm[surface] != 0)
1300 0 : mooseError("Crash due to plastic multiplier not being zero. This occurred because of poor "
1301 : "coding!!");
1302 : }
1303 522272 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
1304 378512 : if (pm[surface] < 0)
1305 : return false;
1306 :
1307 : return true;
1308 : }
1309 :
1310 : void
1311 8204 : ComputeMultiPlasticityStress::applyKuhnTucker(const std::vector<Real> & f,
1312 : const std::vector<Real> & pm,
1313 : std::vector<bool> & active)
1314 : {
1315 : bool turned_off = false;
1316 : unsigned ind = 0;
1317 :
1318 : // turn off all active surfaces that have f<0 and pm!=0
1319 70956 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
1320 : {
1321 62752 : if (active[surface])
1322 : {
1323 35368 : if (f[ind++] < -_f[modelNumber(surface)]->_f_tol)
1324 14816 : if (pm[surface] != 0)
1325 : {
1326 : turned_off = true;
1327 : active[surface] = false;
1328 : }
1329 : }
1330 27384 : else if (pm[surface] != 0)
1331 0 : mooseError("Crash due to plastic multiplier not being zero. This occurred because of poor "
1332 : "coding!!");
1333 : }
1334 :
1335 : // if didn't turn off anything yet, turn off surface with minimum pm
1336 8204 : if (!turned_off)
1337 : {
1338 : int surface_to_turn_off = -1;
1339 : Real min_pm = 0;
1340 41776 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
1341 37456 : if (pm[surface] < min_pm)
1342 : {
1343 : min_pm = pm[surface];
1344 4320 : surface_to_turn_off = surface;
1345 : }
1346 4320 : if (surface_to_turn_off >= 0)
1347 : active[surface_to_turn_off] = false;
1348 : }
1349 8204 : }
1350 :
1351 : Real
1352 771384 : ComputeMultiPlasticityStress::residual2(const std::vector<Real> & pm,
1353 : const std::vector<Real> & f,
1354 : const RankTwoTensor & epp,
1355 : const std::vector<Real> & ic,
1356 : const std::vector<bool> & active,
1357 : const std::vector<bool> & deactivated_due_to_ld)
1358 : {
1359 : Real nr_res2 = 0;
1360 : unsigned ind = 0;
1361 :
1362 2194704 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
1363 1423320 : if (active[surface])
1364 : {
1365 1044944 : if (!deactivated_due_to_ld[surface])
1366 : {
1367 968524 : if (!(pm[surface] == 0 && f[ind] <= 0))
1368 954748 : nr_res2 += 0.5 * Utility::pow<2>(f[ind] / _f[modelNumber(surface)]->_f_tol);
1369 : }
1370 76420 : else if (deactivated_due_to_ld[surface] && f[ind] > 0)
1371 26308 : nr_res2 += 0.5 * Utility::pow<2>(f[ind] / _f[modelNumber(surface)]->_f_tol);
1372 1044944 : ind++;
1373 : }
1374 :
1375 771384 : nr_res2 += 0.5 * Utility::pow<2>(epp.L2norm() / _epp_tol);
1376 :
1377 771384 : std::vector<bool> active_not_deact(_num_surfaces);
1378 2194704 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
1379 1878116 : active_not_deact[surface] = (active[surface] && !deactivated_due_to_ld[surface]);
1380 : ind = 0;
1381 2047200 : for (unsigned model = 0; model < _num_models; ++model)
1382 1275816 : if (anyActiveSurfaces(model, active_not_deact))
1383 956012 : nr_res2 += 0.5 * Utility::pow<2>(ic[ind++] / _f[model]->_ic_tol);
1384 :
1385 771384 : return nr_res2;
1386 : }
1387 :
1388 : bool
1389 544304 : ComputeMultiPlasticityStress::lineSearch(Real & nr_res2,
1390 : RankTwoTensor & stress,
1391 : const std::vector<Real> & intnl_old,
1392 : std::vector<Real> & intnl,
1393 : std::vector<Real> & pm,
1394 : const RankFourTensor & E_inv,
1395 : RankTwoTensor & delta_dp,
1396 : const RankTwoTensor & dstress,
1397 : const std::vector<Real> & dpm,
1398 : const std::vector<Real> & dintnl,
1399 : std::vector<Real> & f,
1400 : RankTwoTensor & epp,
1401 : std::vector<Real> & ic,
1402 : const std::vector<bool> & active,
1403 : const std::vector<bool> & deactivated_due_to_ld,
1404 : bool & linesearch_needed)
1405 : {
1406 : // Line search algorithm straight out of "Numerical Recipes"
1407 :
1408 : bool success =
1409 : true; // return value: will be false if linesearch couldn't reduce the residual-squared
1410 :
1411 : // Aim is to decrease residual2
1412 :
1413 544304 : Real lam = 1.0; // the line-search parameter: 1.0 is a full Newton step
1414 : Real lam_min =
1415 : 1E-10; // minimum value of lam allowed - perhaps this should be dynamically calculated?
1416 544304 : Real f0 = nr_res2; // initial value of residual2
1417 544304 : Real slope = -2 * nr_res2; // "Numerical Recipes" uses -b*A*x, in order to check for roundoff, but
1418 : // i hope the nrStep would warn if there were problems.
1419 : Real tmp_lam; // cached value of lam used in quadratic & cubic line search
1420 : Real f2 = nr_res2; // cached value of f = residual2 used in the cubic in the line search
1421 : Real lam2 = lam; // cached value of lam used in the cubic in the line search
1422 :
1423 : // pm during the line-search
1424 : std::vector<Real> ls_pm;
1425 544304 : ls_pm.resize(pm.size());
1426 :
1427 : // delta_dp during the line-search
1428 544304 : RankTwoTensor ls_delta_dp;
1429 :
1430 : // internal parameter during the line-search
1431 : std::vector<Real> ls_intnl;
1432 544304 : ls_intnl.resize(intnl.size());
1433 :
1434 : // stress during the line-search
1435 544304 : RankTwoTensor ls_stress;
1436 :
1437 : // flow directions (not used in line search, but calculateConstraints returns this parameter)
1438 : std::vector<RankTwoTensor> r;
1439 :
1440 : while (true)
1441 : {
1442 : // update the variables using this line-search parameter
1443 2143444 : for (unsigned alpha = 0; alpha < pm.size(); ++alpha)
1444 1378880 : ls_pm[alpha] = pm[alpha] + dpm[alpha] * lam;
1445 764564 : ls_delta_dp = delta_dp - E_inv * dstress * lam;
1446 1995940 : for (unsigned a = 0; a < intnl.size(); ++a)
1447 1231376 : ls_intnl[a] = intnl[a] + dintnl[a] * lam;
1448 764564 : ls_stress = stress + dstress * lam;
1449 :
1450 : // calculate the new active yield functions, epp and active internal constraints
1451 764564 : calculateConstraints(ls_stress, intnl_old, ls_intnl, ls_pm, ls_delta_dp, f, r, epp, ic, active);
1452 :
1453 : // calculate the new residual-squared
1454 764564 : nr_res2 = residual2(ls_pm, f, epp, ic, active, deactivated_due_to_ld);
1455 :
1456 764564 : if (nr_res2 < f0 + 1E-4 * lam * slope)
1457 : break;
1458 220292 : else if (lam < lam_min)
1459 : {
1460 : success = false;
1461 : // restore plastic multipliers, yield functions, etc to original values
1462 160 : for (unsigned alpha = 0; alpha < pm.size(); ++alpha)
1463 128 : ls_pm[alpha] = pm[alpha];
1464 32 : ls_delta_dp = delta_dp;
1465 160 : for (unsigned a = 0; a < intnl.size(); ++a)
1466 128 : ls_intnl[a] = intnl[a];
1467 32 : ls_stress = stress;
1468 32 : calculateConstraints(
1469 : ls_stress, intnl_old, ls_intnl, ls_pm, ls_delta_dp, f, r, epp, ic, active);
1470 32 : nr_res2 = residual2(ls_pm, f, epp, ic, active, deactivated_due_to_ld);
1471 32 : break;
1472 : }
1473 220260 : else if (lam == 1.0)
1474 : {
1475 : // model as a quadratic
1476 123348 : tmp_lam = -slope / 2.0 / (nr_res2 - f0 - slope);
1477 : }
1478 : else
1479 : {
1480 : // model as a cubic
1481 96912 : Real rhs1 = nr_res2 - f0 - lam * slope;
1482 96912 : Real rhs2 = f2 - f0 - lam2 * slope;
1483 96912 : Real a = (rhs1 / Utility::pow<2>(lam) - rhs2 / Utility::pow<2>(lam2)) / (lam - lam2);
1484 : Real b =
1485 96912 : (-lam2 * rhs1 / Utility::pow<2>(lam) + lam * rhs2 / Utility::pow<2>(lam2)) / (lam - lam2);
1486 96912 : if (a == 0)
1487 0 : tmp_lam = -slope / (2.0 * b);
1488 : else
1489 : {
1490 96912 : Real disc = Utility::pow<2>(b) - 3 * a * slope;
1491 96912 : if (disc < 0)
1492 0 : tmp_lam = 0.5 * lam;
1493 96912 : else if (b <= 0)
1494 4112 : tmp_lam = (-b + std::sqrt(disc)) / (3.0 * a);
1495 : else
1496 92800 : tmp_lam = -slope / (b + std::sqrt(disc));
1497 : }
1498 96912 : if (tmp_lam > 0.5 * lam)
1499 3536 : tmp_lam = 0.5 * lam;
1500 : }
1501 : lam2 = lam;
1502 : f2 = nr_res2;
1503 220260 : lam = std::max(tmp_lam, 0.1 * lam);
1504 220260 : }
1505 :
1506 544304 : if (lam < 1.0)
1507 123348 : linesearch_needed = true;
1508 :
1509 : // assign the quantities found in the line-search
1510 : // back to the originals
1511 1580928 : for (unsigned alpha = 0; alpha < pm.size(); ++alpha)
1512 1036624 : pm[alpha] = ls_pm[alpha];
1513 544304 : delta_dp = ls_delta_dp;
1514 1433424 : for (unsigned a = 0; a < intnl.size(); ++a)
1515 889120 : intnl[a] = ls_intnl[a];
1516 544304 : stress = ls_stress;
1517 :
1518 544304 : return success;
1519 : }
1520 :
1521 : void
1522 0 : ComputeMultiPlasticityStress::buildDumbOrder(const RankTwoTensor & stress,
1523 : const std::vector<Real> & intnl,
1524 : std::vector<unsigned int> & dumb_order)
1525 : {
1526 0 : if (dumb_order.size() != 0)
1527 0 : return;
1528 :
1529 : std::vector<bool> act;
1530 0 : act.assign(_num_surfaces, true);
1531 :
1532 : std::vector<Real> f;
1533 0 : yieldFunction(stress, intnl, act, f);
1534 : std::vector<RankTwoTensor> df_dstress;
1535 0 : dyieldFunction_dstress(stress, intnl, act, df_dstress);
1536 :
1537 : typedef std::pair<Real, unsigned> pair_for_sorting;
1538 0 : std::vector<pair_for_sorting> dist(_num_surfaces);
1539 0 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
1540 : {
1541 0 : dist[surface].first = f[surface] / df_dstress[surface].L2norm();
1542 0 : dist[surface].second = surface;
1543 : }
1544 0 : std::sort(dist.begin(), dist.end()); // sorted in ascending order of f/df_dstress
1545 :
1546 0 : dumb_order.resize(_num_surfaces);
1547 0 : for (unsigned i = 0; i < _num_surfaces; ++i)
1548 0 : dumb_order[i] = dist[_num_surfaces - 1 - i].second;
1549 : // now dumb_order[0] is the surface with the greatest f/df_dstress
1550 : }
1551 :
1552 : void
1553 0 : ComputeMultiPlasticityStress::incrementDumb(int & dumb_iteration,
1554 : const std::vector<unsigned int> & dumb_order,
1555 : std::vector<bool> & act)
1556 : {
1557 0 : dumb_iteration += 1;
1558 0 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
1559 0 : act[dumb_order[surface]] =
1560 0 : (dumb_iteration &
1561 0 : (1 << surface)); // returns true if the surface_th bit of dumb_iteration == 1
1562 0 : }
1563 :
1564 : bool
1565 0 : ComputeMultiPlasticityStress::canIncrementDumb(int dumb_iteration)
1566 : {
1567 : // (1 << _num_surfaces) = 2^_num_surfaces
1568 0 : return ((dumb_iteration + 1) < (1 << _num_surfaces));
1569 : }
1570 :
1571 : unsigned int
1572 315940 : ComputeMultiPlasticityStress::activeCombinationNumber(const std::vector<bool> & act)
1573 : {
1574 : unsigned num = 0;
1575 1227364 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
1576 911424 : if (act[surface])
1577 495028 : num += (1 << surface); // (1 << x) = 2^x
1578 :
1579 315940 : return num;
1580 : }
1581 :
1582 : RankFourTensor
1583 147088 : ComputeMultiPlasticityStress::consistentTangentOperator(const RankTwoTensor & stress,
1584 : const std::vector<Real> & intnl,
1585 : const RankFourTensor & E_ijkl,
1586 : const std::vector<Real> & pm_this_step,
1587 : const std::vector<Real> & cumulative_pm)
1588 : {
1589 :
1590 147088 : if (_tangent_operator_type == elastic)
1591 5568 : return E_ijkl;
1592 :
1593 : // Typically act_at_some_step = act, but it is possible
1594 : // that when subdividing a strain increment, a surface
1595 : // is only active for one sub-step
1596 141520 : std::vector<bool> act_at_some_step(_num_surfaces);
1597 517888 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
1598 376368 : act_at_some_step[surface] = (cumulative_pm[surface] > 0);
1599 :
1600 : // "act" might contain surfaces that are linearly dependent
1601 : // with others. Only the plastic multipliers that are > 0
1602 : // for this strain increment need to be varied to find
1603 : // the consistent tangent operator
1604 141520 : std::vector<bool> act_vary(_num_surfaces);
1605 517888 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
1606 376368 : act_vary[surface] = (pm_this_step[surface] > 0);
1607 :
1608 : std::vector<RankTwoTensor> df_dstress;
1609 141520 : dyieldFunction_dstress(stress, intnl, act_vary, df_dstress);
1610 : std::vector<Real> df_dintnl;
1611 141520 : dyieldFunction_dintnl(stress, intnl, act_vary, df_dintnl);
1612 : std::vector<RankTwoTensor> r;
1613 141520 : flowPotential(stress, intnl, act_vary, r);
1614 : std::vector<RankFourTensor> dr_dstress_at_some_step;
1615 141520 : dflowPotential_dstress(stress, intnl, act_at_some_step, dr_dstress_at_some_step);
1616 : std::vector<RankTwoTensor> dr_dintnl_at_some_step;
1617 141520 : dflowPotential_dintnl(stress, intnl, act_at_some_step, dr_dintnl_at_some_step);
1618 : std::vector<Real> h;
1619 141520 : hardPotential(stress, intnl, act_vary, h);
1620 :
1621 : unsigned ind1;
1622 : unsigned ind2;
1623 :
1624 : // r_minus_stuff[alpha] = r[alpha] -
1625 : // pm_cumulatve[gamma]*dr[gamma]_dintnl[a]_at_some_step*h[a][alpha], with alpha only being in
1626 : // act_vary, but gamma being act_at_some_step
1627 : std::vector<RankTwoTensor> r_minus_stuff;
1628 : ind1 = 0;
1629 517888 : for (unsigned surface1 = 0; surface1 < _num_surfaces; ++surface1)
1630 376368 : if (act_vary[surface1])
1631 : {
1632 179040 : r_minus_stuff.push_back(r[ind1]);
1633 : ind2 = 0;
1634 781824 : for (unsigned surface2 = 0; surface2 < _num_surfaces; ++surface2)
1635 602784 : if (act_at_some_step[surface2])
1636 : {
1637 269408 : if (modelNumber(surface1) == modelNumber(surface2))
1638 : {
1639 : r_minus_stuff.back() -=
1640 224384 : cumulative_pm[surface2] * dr_dintnl_at_some_step[ind2] * h[ind1];
1641 : }
1642 269408 : ind2++;
1643 : }
1644 179040 : ind1++;
1645 : }
1646 :
1647 : unsigned int num_currently_active = 0;
1648 517888 : for (unsigned surface = 0; surface < _num_surfaces; ++surface)
1649 376368 : if (act_vary[surface])
1650 179040 : num_currently_active += 1;
1651 :
1652 : // zzz is a matrix in the form that can be easily
1653 : // inverted by MatrixTools::inverse
1654 : // Eg for num_currently_active = 3
1655 : // (zzz[0] zzz[1] zzz[2])
1656 : // (zzz[3] zzz[4] zzz[5])
1657 : // (zzz[6] zzz[7] zzz[8])
1658 : std::vector<PetscScalar> zzz;
1659 141520 : zzz.assign(num_currently_active * num_currently_active, 0.0);
1660 :
1661 : ind1 = 0;
1662 141520 : RankTwoTensor r2;
1663 517888 : for (unsigned surface1 = 0; surface1 < _num_surfaces; ++surface1)
1664 376368 : if (act_vary[surface1])
1665 : {
1666 : ind2 = 0;
1667 781824 : for (unsigned surface2 = 0; surface2 < _num_surfaces; ++surface2)
1668 602784 : if (act_vary[surface2])
1669 : {
1670 269408 : r2 = df_dstress[ind1] * (E_ijkl * r_minus_stuff[ind2]);
1671 269408 : zzz[ind1 * num_currently_active + ind2] += r2(0, 0) + r2(1, 1) + r2(2, 2);
1672 269408 : if (modelNumber(surface1) == modelNumber(surface2))
1673 224384 : zzz[ind1 * num_currently_active + ind2] += df_dintnl[ind1] * h[ind2];
1674 269408 : ind2++;
1675 : }
1676 179040 : ind1++;
1677 : }
1678 :
1679 141520 : if (num_currently_active > 0)
1680 : {
1681 : // invert zzz, in place. if num_currently_active = 0 then zzz is not needed.
1682 : try
1683 : {
1684 141520 : MatrixTools::inverse(zzz, num_currently_active);
1685 : }
1686 0 : catch (const MooseException & e)
1687 : {
1688 : // in the very rare case of zzz being singular, just return the "elastic" tangent operator
1689 0 : return E_ijkl;
1690 0 : }
1691 : }
1692 :
1693 141520 : RankFourTensor strain_coeff = E_ijkl;
1694 : ind1 = 0;
1695 517888 : for (unsigned surface1 = 0; surface1 < _num_surfaces; ++surface1)
1696 376368 : if (act_vary[surface1])
1697 : {
1698 179040 : RankTwoTensor part1 = E_ijkl * r_minus_stuff[ind1];
1699 : ind2 = 0;
1700 781824 : for (unsigned surface2 = 0; surface2 < _num_surfaces; ++surface2)
1701 602784 : if (act_vary[surface2])
1702 : {
1703 269408 : RankTwoTensor part2 = E_ijkl * df_dstress[ind2];
1704 1077632 : for (unsigned i = 0; i < 3; i++)
1705 3232896 : for (unsigned j = 0; j < 3; j++)
1706 9698688 : for (unsigned k = 0; k < 3; k++)
1707 29096064 : for (unsigned l = 0; l < 3; l++)
1708 21822048 : strain_coeff(i, j, k, l) -=
1709 21822048 : part1(i, j) * part2(k, l) * zzz[ind1 * num_currently_active + ind2];
1710 269408 : ind2++;
1711 : }
1712 179040 : ind1++;
1713 : }
1714 :
1715 141520 : if (_tangent_operator_type == linear)
1716 9024 : return strain_coeff;
1717 :
1718 132496 : RankFourTensor stress_coeff(RankFourTensor::initIdentitySymmetricFour);
1719 :
1720 132496 : RankFourTensor part3;
1721 : ind1 = 0;
1722 478336 : for (unsigned surface1 = 0; surface1 < _num_surfaces; ++surface1)
1723 345840 : if (act_at_some_step[surface1])
1724 : {
1725 332864 : part3 += cumulative_pm[surface1] * E_ijkl * dr_dstress_at_some_step[ind1];
1726 166432 : ind1++;
1727 : }
1728 :
1729 132496 : stress_coeff += part3;
1730 :
1731 132496 : part3 = part3.transposeMajor(); // this is because below i want df_dstress[ind2]*part3, and this
1732 : // equals (part3.transposeMajor())*df_dstress[ind2]
1733 :
1734 : ind1 = 0;
1735 478336 : for (unsigned surface1 = 0; surface1 < _num_surfaces; ++surface1)
1736 345840 : if (act_vary[surface1])
1737 : {
1738 166432 : RankTwoTensor part1 = E_ijkl * r_minus_stuff[ind1];
1739 : ind2 = 0;
1740 720768 : for (unsigned surface2 = 0; surface2 < _num_surfaces; ++surface2)
1741 554336 : if (act_vary[surface2])
1742 : {
1743 249632 : RankTwoTensor part2 = part3 * df_dstress[ind2];
1744 998528 : for (unsigned i = 0; i < 3; i++)
1745 2995584 : for (unsigned j = 0; j < 3; j++)
1746 8986752 : for (unsigned k = 0; k < 3; k++)
1747 26960256 : for (unsigned l = 0; l < 3; l++)
1748 20220192 : stress_coeff(i, j, k, l) -=
1749 20220192 : part1(i, j) * part2(k, l) * zzz[ind1 * num_currently_active + ind2];
1750 249632 : ind2++;
1751 : }
1752 166432 : ind1++;
1753 : }
1754 :
1755 : // need to find the inverse of stress_coeff, but remember
1756 : // stress_coeff does not have the symmetries commonly found
1757 : // in tensor mechanics:
1758 : // stress_coeff(i, j, k, l) = stress_coeff(j, i, k, l) = stress_coeff(i, j, l, k) !=
1759 : // stress_coeff(k, l, i, j)
1760 : // (note the final not-equals). We want s_inv, such that
1761 : // s_inv(i, j, m, n)*stress_coeff(m, n, k, l) = (de_ik*de_jl + de_il*de_jk)/2
1762 : // where de_ij = 1 if i=j, and 0 otherwise.
1763 132496 : RankFourTensor s_inv;
1764 : try
1765 : {
1766 132496 : s_inv = stress_coeff.invSymm();
1767 : }
1768 212 : catch (const MooseException & e)
1769 : {
1770 212 : return strain_coeff; // when stress_coeff is singular (perhaps for incompressible plasticity?)
1771 : // return the "linear" tangent operator
1772 212 : }
1773 :
1774 132284 : return s_inv * strain_coeff;
1775 : }
|