https://mooseframework.inl.gov
LAROMANCEStressUpdateBase.C
Go to the documentation of this file.
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 
11 #include "Function.h"
12 #include "MathUtils.h"
13 #include "MooseUtils.h"
14 #include "MooseRandom.h"
15 #include "Units.h"
16 
17 #include <iostream>
18 
19 registerMooseObjectAliased("SolidMechanicsApp", LAROMANCEStressUpdateBase, "LAROMANCEStressUpdate");
20 registerMooseObjectAliased("SolidMechanicsApp",
22  "ADLAROMANCEStressUpdate");
23 
24 template <bool is_ad>
27 {
29  params.addClassDescription("Base class to calculate the effective creep strain based on the "
30  "rates predicted by a material specific Los Alamos Reduced Order "
31  "Model derived from a Visco-Plastic Self Consistent calculations.");
32 
33  params.addRequiredCoupledVar("temperature", "The coupled temperature (K)");
34  params.addParam<MaterialPropertyName>("environmental_factor",
35  "Optional coupled environmental factor");
36 
37  MooseEnum error_lower_limit_behavior("ERROR EXCEPTION WARN IGNORE DONTHING USELIMIT",
38  "EXCEPTION");
39  // Only allow ERROR and EXCEPTION on upper bounds
40  MooseEnum error_upper_limit_behavior("ERROR EXCEPTION", "EXCEPTION");
41  params.addParam<MooseEnum>(
42  "cell_input_window_low_failure",
43  error_lower_limit_behavior,
44  "What to do if cell dislocation concentration is outside the lower global "
45  "window of applicability.");
46  params.addParam<MooseEnum>(
47  "cell_input_window_high_failure",
48  error_upper_limit_behavior,
49  "What to do if cell dislocation concentration is outside the upper global "
50  "window of applicability.");
51  params.addParam<MooseEnum>("wall_input_window_low_failure",
52  error_lower_limit_behavior,
53  "What to do if wall dislocation concentration is outside the "
54  "lower global window of applicability.");
55  params.addParam<MooseEnum>("wall_input_window_high_failure",
56  error_upper_limit_behavior,
57  "What to do if wall dislocation concentration is outside the "
58  "upper global window of applicability.");
59  params.addParam<MooseEnum>(
60  "old_strain_input_window_low_failure",
61  error_lower_limit_behavior,
62  "What to do if old strain is outside the lower global window of applicability.");
63  params.addParam<MooseEnum>(
64  "old_strain_input_window_high_failure",
65  error_upper_limit_behavior,
66  "What to do if old strain is outside the upper global window of applicability.");
67 
68  MooseEnum extrapolated_lower_limit_behavior(
69  "ERROR EXCEPTION WARN IGNORE DONOTHING USELIMIT EXTRAPOLATE", "EXTRAPOLATE");
70  params.addParam<MooseEnum>(
71  "stress_input_window_low_failure",
72  extrapolated_lower_limit_behavior,
73  "What to do if stress is outside the lower global window of applicability.");
74  params.addParam<MooseEnum>(
75  "stress_input_window_high_failure",
76  error_upper_limit_behavior,
77  "What to do if stress is outside the upper global window of applicability.");
78  params.addParam<MooseEnum>(
79  "temperature_input_window_low_failure",
80  extrapolated_lower_limit_behavior,
81  "What to do if temperature is outside the lower global window of applicability.");
82  params.addParam<MooseEnum>(
83  "temperature_input_window_high_failure",
84  error_upper_limit_behavior,
85  "What to do if temperature is outside the upper global window of applicability.");
86  params.addParam<MooseEnum>(
87  "environment_input_window_low_failure",
88  extrapolated_lower_limit_behavior,
89  "What to do if environmental factor is outside the lower global window of applicability.");
90  params.addParam<MooseEnum>(
91  "environment_input_window_high_failure",
92  error_upper_limit_behavior,
93  "What to do if environmental factor is outside the upper global window of applicability.");
94 
96  "initial_cell_dislocation_density",
97  "initial_cell_dislocation_density >= 0.0",
98  "Initial density of cell (glissile) dislocations (1/m^2)");
99  params.addRangeCheckedParam<Real>(
100  "cell_dislocations_normal_distribution_width",
101  0.0,
102  "cell_dislocations_normal_distribution_width >= 0.0",
103  "Width of the normal distribution to assign to the initial cell dislocation value. This is "
104  "given as a fraction of the initial_cell_dislocation_density.");
105  params.addRangeCheckedParam<Real>(
106  "max_relative_cell_dislocation_increment",
107  0.5,
108  "max_relative_cell_dislocation_increment > 0.0",
109  "Maximum increment of density of cell (glissile) dislocations.");
110 
112  "initial_wall_dislocation_density",
113  "initial_wall_dislocation_density >= 0.0",
114  "wall (locked) dislocation density initial value (1/m^2).");
115  params.addRangeCheckedParam<Real>(
116  "wall_dislocations_normal_distribution_width",
117  0.0,
118  "wall_dislocations_normal_distribution_width >= 0.0",
119  "Width of the normal distribution to assign to the initial wall dislocation value. This is "
120  "given as a fraction of the initial_wall_dislocation_density.");
121  params.addRangeCheckedParam<Real>(
122  "max_relative_wall_dislocation_increment",
123  0.5,
124  "max_relative_wall_dislocation_increment > 0.0",
125  "Maximum increment of wall (locked) dislocation density initial value (1/m^2).");
126 
127  params.addParam<bool>("verbose", false, "Flag to output verbose information.");
128 
129  params.addParam<FunctionName>(
130  "cell_dislocation_density_forcing_function",
131  "Optional forcing function for wall dislocation. If provided, the wall dislocation "
132  "density will be reset to the function value at the beginning of the timestep. Used for "
133  "testing purposes only.");
134  params.addParam<FunctionName>(
135  "wall_dislocation_density_forcing_function",
136  "Optional forcing function for wall dislocation. If provided, the wall dislocation "
137  "density will be reset to the function value at the beginning of the timestep. Used for "
138  "testing purposes only.");
139  params.addParam<FunctionName>(
140  "old_creep_strain_forcing_function",
141  "Optional forcing function for the creep strain from the previous timestep. If provided, "
142  "the old creep strain will be reset to the function value at the beginning of the "
143  "timestep. Used for testing purposes only.");
144  params.addParam<FunctionName>(
145  "effective_stress_forcing_function",
146  "Optional forcing function for the effective stress. If provided, the effective stress will "
147  "be reset to the function value at the beginning of the timestep. Used for testing purposes "
148  "only.");
149 
150  params.addParam<unsigned int>("seed", 0, "Random number generator seed");
151  params.addParam<std::string>("stress_unit", "Pa", "unit of stress");
152 
153  // use std::string here to avoid automatic absolute path expansion
154  params.addParam<DataFileName>("model", "LaRomance model JSON datafile");
155  params.addParam<FileName>("export_model", "Write LaRomance model to JSON datafile");
156 
157  params.addParamNamesToGroup(
158  "cell_dislocation_density_forcing_function wall_dislocation_density_forcing_function "
159  "old_creep_strain_forcing_function effective_stress_forcing_function seed stress_unit",
160  "Advanced");
161 
162  return params;
163 }
164 
165 template <bool is_ad>
167  const InputParameters & parameters)
168  : RadialReturnCreepStressUpdateBaseTempl<is_ad>(parameters),
169  _temperature(this->template coupledGenericValue<is_ad>("temperature")),
170  _environmental(
171  this->isParamValid("environmental_factor")
172  ? &this->template getGenericMaterialProperty<Real, is_ad>("environmental_factor")
173  : nullptr),
174  _window_failure(_environmental ? 6 : 5),
175 
176  _verbose(this->template getParam<bool>("verbose")),
177  _cell_dislocations(
178  this->template declareGenericProperty<Real, is_ad>(this->_base_name + "cell_dislocations")),
179  _cell_dislocations_old(
180  this->template getMaterialPropertyOld<Real>(this->_base_name + "cell_dislocations")),
181  _max_cell_increment(this->template getParam<Real>("max_relative_cell_dislocation_increment")),
182  _cell_function(this->isParamValid("cell_dislocation_density_forcing_function")
183  ? &this->getFunction("cell_dislocation_density_forcing_function")
184  : NULL),
185  _cell_dislocation_increment(0.0),
186  _wall_dislocations(
187  this->template declareGenericProperty<Real, is_ad>(this->_base_name + "wall_dislocations")),
188  _wall_dislocations_old(
189  this->template getMaterialPropertyOld<Real>(this->_base_name + "wall_dislocations")),
190  _max_wall_increment(this->template getParam<Real>("max_relative_wall_dislocation_increment")),
191  _wall_function(this->isParamValid("wall_dislocation_density_forcing_function")
192  ? &this->getFunction("wall_dislocation_density_forcing_function")
193  : NULL),
194  _stress_function(this->isParamValid("effective_stress_forcing_function")
195  ? &this->getFunction("effective_stress_forcing_function")
196  : NULL),
197  _wall_dislocation_increment(0.0),
198  _cell_input_index(0),
199  _wall_input_index(1),
200  _stress_input_index(2),
201  _old_strain_input_index(3),
202  _temperature_input_index(4),
203  _environmental_input_index(5),
204  _cell_output_index(0),
205  _wall_output_index(1),
206  _strain_output_index(2),
207 
208  _creep_strain_old_forcing_function(this->isParamValid("old_creep_strain_forcing_function")
209  ? &this->getFunction("old_creep_strain_forcing_function")
210  : NULL),
211 
212  _creep_rate(
213  this->template declareGenericProperty<Real, is_ad>(this->_base_name + "creep_rate")),
214  _cell_rate(this->template declareGenericProperty<Real, is_ad>(this->_base_name +
215  "cell_dislocation_rate")),
216  _wall_rate(this->template declareGenericProperty<Real, is_ad>(this->_base_name +
217  "wall_dislocation_rate")),
218  _extrapolation(this->template declareProperty<Real>("ROM_extrapolation")),
219  _second_partition_weight(
220  this->template declareGenericProperty<Real, is_ad>("partition_weight")),
221 
222  _derivative(0.0),
223  _old_input_values(3),
224  _wall_dislocations_step(this->template declareGenericProperty<Real, is_ad>(
225  this->_base_name + "wall_dislocations_step")),
226  _cell_dislocations_step(this->template declareGenericProperty<Real, is_ad>(
227  this->_base_name + "cell_dislocations_step")),
228  _plastic_strain_increment(),
229  _number_of_substeps(
230  this->template declareProperty<Real>(this->_base_name + "number_of_substeps")),
231  _index_name(_window_failure.size())
232 {
233  this->_check_range = true; // this may not be necessary?
234 
235  _index_name[_cell_input_index] = "cell";
236  _index_name[_wall_input_index] = "wall";
237  _index_name[_stress_input_index] = "stress";
238  _index_name[_old_strain_input_index] = "old strain";
239  _index_name[_temperature_input_index] = "temperature";
240 
242  parameters.get<MooseEnum>("cell_input_window_low_failure").getEnum<WindowFailure>();
244  parameters.get<MooseEnum>("cell_input_window_high_failure").getEnum<WindowFailure>();
246  parameters.get<MooseEnum>("wall_input_window_low_failure").getEnum<WindowFailure>();
248  parameters.get<MooseEnum>("wall_input_window_high_failure").getEnum<WindowFailure>();
250  parameters.get<MooseEnum>("stress_input_window_low_failure").getEnum<WindowFailure>();
252  parameters.get<MooseEnum>("stress_input_window_high_failure").getEnum<WindowFailure>();
254  parameters.get<MooseEnum>("old_strain_input_window_low_failure").getEnum<WindowFailure>();
256  parameters.get<MooseEnum>("old_strain_input_window_high_failure").getEnum<WindowFailure>();
258  parameters.get<MooseEnum>("temperature_input_window_low_failure").getEnum<WindowFailure>();
260  parameters.get<MooseEnum>("temperature_input_window_high_failure").getEnum<WindowFailure>();
261  if (_environmental)
262  {
263  _index_name[_environmental_input_index] = "environmental";
265  parameters.get<MooseEnum>("environment_input_window_low_failure").getEnum<WindowFailure>();
267  parameters.get<MooseEnum>("environment_input_window_high_failure").getEnum<WindowFailure>();
268  }
269 
270  // load JSON datafile
271  if (this->isParamValid("model"))
272  {
273  const auto model_file_name = this->template getParam<DataFileName>("model");
274  std::ifstream model_file(model_file_name.c_str());
275  model_file >> _json;
276  }
277 
279 }
280 
281 template <bool is_ad>
282 void
284 {
285  _json["strain_cutoff"] = getStrainCutoff();
286  _json["transform"] = getTransform();
287  _json["transform_coefs"] = getTransformCoefs();
288  _json["input_limits"] = getInputLimits();
289  _json["normalization_limits"] = getNormalizationLimits();
290  _json["coefs"] = getCoefs();
291  _json["tiling"] = getTilings();
292  _json["cutoff"] = getStrainCutoff();
293 }
294 
295 template <bool is_ad>
296 bool
298 {
299  return this->_use_substepping != RadialReturnStressUpdateTempl<is_ad>::SubsteppingType::NONE;
300 }
301 
302 template <bool is_ad>
303 void
305  const InputParameters & parameters)
306 {
307  // Stress unit conversion factor
308  const MooseUnits stress_unit_to("MPa");
309  const MooseUnits stress_unit_from(parameters.get<std::string>("stress_unit"));
310  _stress_ucf = stress_unit_to.convert(1, stress_unit_from);
311 }
312 
313 template <bool is_ad>
314 void
316 {
317  // export models that are compiled in
318  if (this->isParamValid("export_model"))
319  {
320  exportJSON();
321  std::ofstream out(this->template getParam<FileName>("export_model").c_str());
322  out << _json;
323  }
324 
325  // Pull in relevant ROM information and do sanity checks
326  _transform = getTransform();
327  _transform_coefs = getTransformCoefs();
328  _input_limits = getInputLimits();
329  _normalization_limits = getNormalizationLimits();
330  _coefs = getCoefs();
331  _tiling = getTilings();
332  _cutoff = getStrainCutoff();
333  // resize containers to be filled later based on partition dimension
334  // and immediately run some sanity checks
335  _num_partitions = _transform.size();
336  if (_num_partitions < 1 || _num_partitions > 2)
337  mooseError(
338  "In ", _name, ": First dimension of getTransform must be either size one or size two");
339  if (_transform[0].size() < 1)
340  mooseError("In ", _name, ": Transform is not the correct shape");
341  _num_outputs = _transform[0][0].size();
342  if (_num_outputs != 3)
343  mooseError("In ",
344  _name,
345  ": ",
346  _num_outputs,
347  " outputs detected. Three and only three outputs are currently supported.");
348 
349  _num_inputs = _transform[0][0][0].size();
350  if (_num_inputs != 5 && _num_inputs != 6)
351  mooseError("In ",
352  _name,
353  ": ",
354  _num_inputs,
355  " inputs detected. Only five or six inputs currently supported.");
356  if (_num_inputs == 6 && !_environmental)
357  this->paramError(
358  "environmental_factor",
359  "Number of ROM inputs indicate environmental factor is required to be coupled.");
360  if (_num_inputs != 6 && _environmental)
361  this->paramError("environmental_factor",
362  "Number of ROM inputs indicate environmental factor is not implemented, but "
363  "environmental factor coupled.");
364  _num_tiles.resize(_num_partitions);
365  _num_coefs.resize(_num_partitions);
366  _degree.resize(_num_partitions);
367  _precomputed_vals.resize(_num_partitions);
368  _rom_inputs.resize(_num_partitions);
369  _polynomial_inputs.resize(_num_partitions);
370  _non_stress_weights.resize(_num_partitions);
371  _weights.resize(_num_partitions);
372  _partition_weights.resize(_num_partitions);
373  _dpartition_weight_dstress.resize(_num_partitions);
374  _transformed_normalization_limits.resize(_num_partitions);
375  _makeframe_helper.resize(_num_partitions);
376  _global_limits.resize(_num_inputs);
377  // temporarily fill global limits with extreme numerical values, to later update
378  for (unsigned int i = 0; i < _num_inputs; ++i)
379  _global_limits[i] = {std::numeric_limits<Real>::max(), 0.0};
380 
381  // start loop over partitions to perform sanity checks, set global limits,
382  // and print global configurations
383  if (_transform.size() != _num_partitions || _transform_coefs.size() != _num_partitions ||
384  _input_limits.size() != _num_partitions || _normalization_limits.size() != _num_partitions ||
385  _coefs.size() != _num_partitions || _tiling.size() != _num_partitions ||
386  _cutoff.size() != _num_partitions)
387  mooseError(
388  "In ", _name, ": one of the ROM inputs does not have the correct number of partitions");
389 
390  for (unsigned int p = 0; p < _num_partitions; ++p)
391  {
392  _num_tiles[p] = _transform[p].size();
393  if (!_num_tiles[p])
394  mooseError("In ", _name, ": No tiles detected. Double check your ROM input");
395 
396  bool correct_shape = true;
397  if (_transform[p].size() != _num_tiles[p] || _transform_coefs[p].size() != _num_tiles[p] ||
398  _input_limits[p].size() != _num_tiles[p] ||
399  _normalization_limits[p].size() != _num_tiles[p] || _coefs[p].size() != _num_tiles[p])
400  correct_shape = false;
401  if (_tiling[p].size() != _num_inputs)
402  correct_shape = false;
403  if (_coefs[p][0].size() == 0)
404  correct_shape = false;
405  _num_coefs[p] = _coefs[p][0][0].size();
406 
407  // start loop over tiles to perform sanity checks.
408  for (unsigned int t = 0; t < _num_tiles[p]; ++t)
409  {
410  if (_transform[p][t].size() != _num_outputs ||
411  _transform_coefs[p][t].size() != _num_outputs || _coefs[p][t].size() != _num_outputs)
412  correct_shape = false;
413  for (unsigned int o = 0; o < _num_outputs; ++o)
414  if (_transform[p][t][o].size() != _num_inputs ||
415  _transform_coefs[p][t][o].size() != _num_inputs ||
416  _coefs[p][t][o].size() != _num_coefs[p])
417  correct_shape = false;
418  if (_input_limits[p][t].size() != _num_inputs ||
419  _normalization_limits[p][t].size() != _num_inputs)
420  correct_shape = false;
421  for (unsigned int i = 0; i < _num_inputs; ++i)
422  if (_input_limits[p][t][i].size() != 2 || _normalization_limits[p][t][i].size() != 2)
423  correct_shape = false;
424  }
425 
426  if (!correct_shape)
427  mooseError("In ", _name, ": ROM data is not the right shape.");
428 
429  _degree[p] = std::pow(_num_coefs[p], 1.0 / _num_inputs);
430  if (!_degree[p] || _degree[p] > 4)
431  mooseError("In ", _name, ": degree must be 1, 2, 3 or 4.");
432 
433  // Check input limits and find global limits of all partitions. Note that this will return the
434  // extremes of the model! If the model is not flat along one input limit, then global limits
435  // will not truely capture the mutli-dimensionality of the problem. Consequently, the user may
436  // find input values that result in errors in the partition weight computation.
437  for (unsigned int i = 0; i < _num_inputs; ++i)
438  {
439  for (unsigned int t = 0; t < _num_tiles[p]; ++t)
440  {
441  if (_input_limits[p][t][i][0] >= _input_limits[p][t][i][1])
442  mooseError("In ", _name, ": Input limits are ordered incorrectly");
443  _global_limits[i].first = std::min(_global_limits[i].first, _input_limits[p][t][i][0]);
444  _global_limits[i].second = std::max(_global_limits[i].second, _input_limits[p][t][i][1]);
445  }
446  }
447 
448  // Precompute helper containers
449  _transformed_normalization_limits[p] = getTransformedLimits(p, _normalization_limits[p]);
450  _makeframe_helper[p] = getMakeFrameHelper(p);
451 
452  // Prepare containers for each partition
453  _precomputed_vals[p].resize(_num_tiles[p], std::vector<GenericReal<is_ad>>(_num_coefs[p]));
454  _rom_inputs[p].resize(_num_tiles[p], std::vector<GenericReal<is_ad>>(_num_inputs));
455  _polynomial_inputs[p].resize(_num_tiles[p],
456  std::vector<std::vector<GenericReal<is_ad>>>(
457  _num_inputs, std::vector<GenericReal<is_ad>>(_degree[p])));
458  _non_stress_weights[p].resize(_num_tiles[p]);
459  _weights[p].resize(_num_tiles[p], 0);
460  }
461  // Prepare containers independent of partition
462  _input_values.resize(_num_inputs);
463 
464  if (_verbose)
465  {
466  Moose::err << "ROM model info: " << _name << "\n";
467  Moose::err << " number of tiles, partition 1: " << _num_tiles[0] << "\n";
468  if (_num_partitions > 1)
469  Moose::err << " number of tiles, partition 2: " << _num_tiles[1] << "\n";
470  Moose::err << " number of outputs: " << _num_outputs << "\n";
471  Moose::err << " number of inputs: " << _num_inputs << "\n";
472  Moose::err << " degree (max Legendre degree + constant), partition 1: " << _degree[0] << "\n";
473  if (_num_partitions > 1)
474  Moose::err << " degree (max Legendre degree + constant), partition 2: " << _degree[1] << "\n";
475  Moose::err << " number of coefficients, partition 1: " << _num_coefs[0] << "\n";
476  if (_num_partitions > 1)
477  Moose::err << " number of coefficients, partition 2: " << _num_coefs[1] << "\n";
478  Moose::err << " Global limits:\n cell dislocations ("
479  << _global_limits[_cell_input_index].first << " - "
480  << _global_limits[_cell_input_index].second << ")\n";
481  Moose::err << " wall dislocations (" << _global_limits[_wall_input_index].first << " - "
482  << _global_limits[_wall_input_index].second << ")\n";
483  Moose::err << " Stress (" << _global_limits[_stress_input_index].first << " - "
484  << _global_limits[_stress_input_index].second << ")\n";
485  Moose::err << " Old strain (" << _global_limits[_old_strain_input_index].first << " - "
486  << _global_limits[_old_strain_input_index].second << ")\n";
487  Moose::err << " Temperature (" << _global_limits[_temperature_input_index].first << " - "
488  << _global_limits[_temperature_input_index].second << ")\n";
489  if (_environmental)
490  Moose::err << " Environmental factor (" << _global_limits[_environmental_input_index].first
491  << " - " << _global_limits[_environmental_input_index].second << ")\n";
492  Moose::err << std::endl;
493  }
494 }
495 
496 template <bool is_ad>
497 void
499 {
500  MooseRandom rng;
501  rng.seed(0, this->template getParam<unsigned int>("seed"));
502 
503  _cell_dislocations[_qp] = rng.randNormal(
504  this->template getParam<Real>("initial_cell_dislocation_density"),
505  this->template getParam<Real>("initial_cell_dislocation_density") *
506  this->template getParam<Real>("cell_dislocations_normal_distribution_width"));
507  _wall_dislocations[_qp] = rng.randNormal(
508  this->template getParam<Real>("initial_wall_dislocation_density"),
509  this->template getParam<Real>("initial_wall_dislocation_density") *
510  this->template getParam<Real>("wall_dislocations_normal_distribution_width"));
511 
513 }
514 
515 template <bool is_ad>
518  const GenericReal<is_ad> & effective_trial_stress) const
519 {
520  // Make maximum allowed scalar a little bit less than the deformation that would reduce the
521  // trial stress to zero. This prevents negative trial stresses.
522  return effective_trial_stress / this->_three_shear_modulus * 0.999999;
523 }
524 
525 template <bool is_ad>
526 void
528 {
529  _cell_dislocation_increment = 0.0;
530  _wall_dislocation_increment = 0.0;
531 
532  _plastic_strain_increment.zero();
533 
534  _wall_dislocations_step[_qp] = 0.0;
535  _cell_dislocations_step[_qp] = 0.0;
536 }
537 
538 template <bool is_ad>
539 void
541  const unsigned int total_number_of_substeps)
542 {
543  _wall_dislocations_step[_qp] += _wall_dislocation_increment;
544  _cell_dislocations_step[_qp] += _cell_dislocation_increment;
545  _number_of_substeps[_qp] = total_number_of_substeps;
546 }
547 
548 template <bool is_ad>
549 void
551  const GenericReal<is_ad> & effective_trial_stress,
552  const GenericRankFourTensor<is_ad> & elasticity_tensor)
553 {
556  // Previous substep creep strain
557  RankTwoTensor creep_strain_substep = this->_creep_strain_old[_qp] + _plastic_strain_increment;
558 
559  // Prepare old values
560  _old_input_values[_cell_output_index] =
561  _cell_function
562  ? _cell_function->value(_t, _q_point[_qp])
563  : (_cell_dislocations_old[_qp] + MetaPhysicL::raw_value(_cell_dislocations_step[_qp]));
564  _old_input_values[_wall_output_index] =
565  _wall_function
566  ? _wall_function->value(_t, _q_point[_qp])
567  : (_wall_dislocations_old[_qp] + MetaPhysicL::raw_value(_wall_dislocations_step[_qp]));
568  _old_input_values[_strain_output_index] =
569  _creep_strain_old_forcing_function
570  ? _creep_strain_old_forcing_function->value(_t, _q_point[_qp])
572  std::sqrt((creep_strain_substep).doubleContraction(creep_strain_substep) / 1.5));
573 
574  // Prepare input
575  _input_values[_cell_input_index] = _old_input_values[_cell_output_index];
576  _input_values[_wall_input_index] = _old_input_values[_wall_output_index];
577  _input_values[_stress_input_index] = _stress_function ? _stress_function->value(_t, _q_point[_qp])
578  : effective_trial_stress * _stress_ucf;
579  _input_values[_old_strain_input_index] = _old_input_values[_strain_output_index];
580  _input_values[_temperature_input_index] = _temperature[_qp];
581  if (_environmental)
582  _input_values[_environmental_input_index] = (*_environmental)[_qp];
583 
584  // Determine tile weight and check if input is in range
585  for (unsigned int p = 0; p < _num_partitions; ++p)
586  std::fill(_non_stress_weights[p].begin(), _non_stress_weights[p].end(), 1.0);
587  for (unsigned int i = 0; i < _num_inputs; i++)
588  if (i != _stress_input_index)
589  computeTileWeight(_non_stress_weights, _input_values[i], i);
590 
591  // Precompute transformed input and prebuild polynomials for inputs other than strain
592  precomputeROM(_strain_output_index);
593 }
594 
595 template <bool is_ad>
596 void
598  std::vector<std::vector<GenericReal<is_ad>>> & weights,
599  GenericReal<is_ad> & input,
600  const unsigned int in_index,
601  const bool derivative)
602 {
603  mooseAssert(std::isfinite(MetaPhysicL::raw_value(input)),
604  "computeTileWeight input must be finite");
605 
606  for (unsigned int p = 0; p < _num_partitions; ++p)
607  {
608  for (unsigned int t = 0; t < _num_tiles[p]; ++t)
609  {
610  // If input is within a specfic tile's window of applicability
611  if (input >= _input_limits[p][t][in_index][0] && input <= _input_limits[p][t][in_index][1])
612  {
613  // If there is not more than one tile for this input, set derivative of
614  // weight to zero
615  if (_tiling[p][in_index] == 1)
616  {
617  if (derivative)
618  weights[p][t] = 0.0;
619  }
620  else
621  {
622  // Flag to ensure weights are applied only once
623  bool overlap = false;
624  for (unsigned int tt = 0; tt < _num_tiles[p]; ++tt)
625  {
626  if (!overlap && t != tt)
627  {
628  // If input is within another tile's window of applicability, check to see if inputs
629  // place us in that tile and ensure the two tiles are different in the dimension of
630  // interest
631  if (areTilesNotIdentical(p, t, tt, in_index) && checkInTile(p, tt))
632  {
633  overlap = true;
634 
635  // If current tile is below the second tile's window of applicability
636  if (_input_limits[p][t][in_index][0] < _input_limits[p][tt][in_index][0] &&
637  _input_limits[p][t][in_index][1] > _input_limits[p][tt][in_index][0])
638  {
639  weights[p][t] *= sigmoid(_input_limits[p][tt][in_index][0],
640  _input_limits[p][t][in_index][1],
641  input,
642  derivative);
643  }
644  // If current tile is above the second tile's window of applicability
645  else if (_input_limits[p][t][in_index][0] > _input_limits[p][tt][in_index][0] &&
646  _input_limits[p][t][in_index][0] < _input_limits[p][tt][in_index][1])
647  {
648  if (derivative)
649  weights[p][t] *= -sigmoid(_input_limits[p][t][in_index][0],
650  _input_limits[p][tt][in_index][1],
651  input,
652  derivative);
653  else
654  weights[p][t] *= (1.0 - sigmoid(_input_limits[p][t][in_index][0],
655  _input_limits[p][tt][in_index][1],
656  input));
657  }
658  }
659  }
660  }
661 
662  // If not overlapping, weight is not updated, and the derivative of tile weight is set to
663  // zero
664  if (!overlap && derivative)
665  weights[p][t] = 0.0;
666  }
667  }
668  // If input is below the lower tile limit
669  else if (input < _input_limits[p][t][in_index][0])
670  {
671  // If the lower tile limit equals the lower global limit
672  if (_input_limits[p][t][in_index][0] == _global_limits[in_index].first)
673  {
674  if (_window_failure[in_index].first == WindowFailure::EXTRAPOLATE)
675  {
676  if (derivative)
677  weights[p][t] *= -sigmoid(0.0, _input_limits[p][t][in_index][0], input, derivative);
678  else
679  weights[p][t] *= (1.0 - sigmoid(0.0, _input_limits[p][t][in_index][0], input));
680  input = _input_limits[p][t][in_index][0];
681  }
682  else if (_window_failure[in_index].first == WindowFailure::USELIMIT)
683  input = _input_limits[p][t][in_index][0];
684  else
685  {
686  weights[p][t] = 0.0;
687  std::stringstream msg;
688  msg << "In " << _name << ": " << _index_name[in_index]
689  << " input parameter with value (" << MetaPhysicL::raw_value(input)
690  << ") is out of lower global range (" << _input_limits[p][t][in_index][0] << ")";
691 
692  // Set input to edge of limit so it is found later in computePartitionWeights
693  input = _input_limits[p][t][in_index][0];
694 
695  if (_window_failure[in_index].first == WindowFailure::WARN)
696  mooseWarning(msg.str());
697  else if (_window_failure[in_index].first == WindowFailure::ERROR)
698  mooseError(msg.str());
699  else if (_window_failure[in_index].first == WindowFailure::EXCEPTION)
700  mooseException(msg.str());
701  // if (_window_failure[in_index].first == WindowFailure::DONOTHING) <- nothing is done
702  }
703  }
704  // if input below tile limit, update weight of tile to be zero
705  else
706  weights[p][t] = 0.0;
707  }
708  // If input is above the upper tile limit
709  else if (input > _input_limits[p][t][in_index][1])
710  {
711  if (_input_limits[p][t][in_index][1] == _global_limits[in_index].second)
712  {
713  if (_window_failure[in_index].second == WindowFailure::EXTRAPOLATE)
714  mooseError("Internal error. Extrapolate not appropriate for upper bound");
715  else if (_window_failure[in_index].second == WindowFailure::USELIMIT)
716  input = _input_limits[p][t][in_index][1];
717  else
718  {
719  weights[p][t] = 0.0;
720  std::stringstream msg;
721  msg << "In " << _name << ": " << _index_name[in_index]
722  << " input parameter with value (" << MetaPhysicL::raw_value(input)
723  << ") is out of upper global range (" << _input_limits[p][t][in_index][1] << ")";
724 
725  // Set input to edge of limit so it is found later in computePartitionWeights
726  input = _input_limits[p][t][in_index][1];
727 
728  if (_window_failure[in_index].second == WindowFailure::WARN)
729  mooseWarning(msg.str());
730  else if (_window_failure[in_index].second == WindowFailure::ERROR)
731  mooseError(msg.str());
732  else if (_window_failure[in_index].second == WindowFailure::EXCEPTION)
733  mooseException(msg.str());
734  // if (_window_failure[in_index].second == WindowFailure::DONOTHING) <- nothing is done
735  }
736  }
737  // if input above tile limit, update weight of tile to be zero
738  else
739  weights[p][t] = 0.0;
740  }
741  // If input is outside window of applicability, weight is zero
742  else
743  mooseError("In ", _name, ": Internal error. Outside input limits, input=", input);
744  }
745  }
746 }
747 
748 template <bool is_ad>
749 bool
750 LAROMANCEStressUpdateBaseTempl<is_ad>::checkInTile(const unsigned int p, const unsigned int t) const
751 {
752  for (unsigned int i = 0; i < _num_inputs; ++i)
753  if (_input_values[i] < _input_limits[p][t][i][0] ||
754  _input_values[i] > _input_limits[p][t][i][1])
755  return false;
756  return true;
757 }
758 
759 template <bool is_ad>
760 bool
762  const unsigned int t,
763  const unsigned int tt,
764  const unsigned int in_index)
765 {
766  if (_input_limits[p][t][in_index][0] != _input_limits[p][tt][in_index][0] &&
767  _input_limits[p][t][in_index][1] != _input_limits[p][tt][in_index][1])
768  return true;
769  else
770  return false;
771 }
772 
773 template <bool is_ad>
776  const GenericReal<is_ad> & effective_trial_stress, const GenericReal<is_ad> & scalar)
777 {
778  mooseAssert(std::isfinite(MetaPhysicL::raw_value(effective_trial_stress)),
779  "computeResidual: effective_trial_stress must be finite");
780  mooseAssert(std::isfinite(MetaPhysicL::raw_value(scalar)),
781  "computeResidual: scalar must be finite!");
782  // Update new stress
783  GenericReal<is_ad> trial_stress_mpa = _stress_function
784  ? _stress_function->value(_t, _q_point[_qp])
785  : effective_trial_stress * _stress_ucf;
786  GenericReal<is_ad> dtrial_stress_dscalar = 0.0;
787 
788  // Update stress if strain is being applied, i.e. non-testing simulation
789  if (this->_apply_strain)
790  {
791  trial_stress_mpa -= this->_three_shear_modulus * scalar * _stress_ucf;
792  dtrial_stress_dscalar -= this->_three_shear_modulus * _stress_ucf;
793  }
794  _input_values[_stress_input_index] = trial_stress_mpa;
795 
796  // Update weights for each partition with new stress
797  for (unsigned int p = 0; p < _num_partitions; ++p)
798  _weights[p] = _non_stress_weights[p];
799  computeTileWeight(_weights, _input_values[_stress_input_index], _stress_input_index);
800  auto dweights_dstress = _non_stress_weights;
801  computeTileWeight(
802  dweights_dstress, _input_values[_stress_input_index], _stress_input_index, true);
803 
804  computePartitionWeights(_partition_weights, _dpartition_weight_dstress);
805 
806  // Save extrapolation as a material property in order quantify adequate tiling range
807  _extrapolation[_qp] = 0.0;
808  for (unsigned int p = 0; p < _num_partitions; ++p)
809  for (unsigned int t = 0; t < _num_tiles[p]; ++t)
810  _extrapolation[_qp] += MetaPhysicL::raw_value(_weights[p][t] * _partition_weights[p]);
811 
812  GenericReal<is_ad> total_rom_effective_strain_inc = 0.0;
813  GenericReal<is_ad> dtotal_rom_effective_strain_inc_dstress = 0.0;
814 
815  // Run ROM if all values are within windows.
816  for (unsigned int p = 0; p < _num_partitions; p++)
817  {
818  if (_partition_weights[p])
819  {
820  // compute weight normalizing factor
821  GenericReal<is_ad> weight_normalizer = 0;
822  unsigned int number_of_active_tiles = 0;
823  for (unsigned int t = 0; t < _num_tiles[p]; ++t)
824  {
825  // count number of active tiles
826  number_of_active_tiles += checkInTile(p, t);
827  if (_weights[p][t])
828  {
829  // tile normalization factor (sum of tile weights)
830  weight_normalizer += _weights[p][t];
831  }
832  }
833 
834  // normalize weights only when 3 tiles overlap
835  if (number_of_active_tiles == 3)
836  for (unsigned int t = 0; t < _num_tiles[p]; ++t)
837  {
838  _weights[p][t] /= weight_normalizer;
839  dweights_dstress[p][t] /= weight_normalizer;
840  }
841 
842  for (unsigned int t = 0; t < _num_tiles[p]; ++t)
843  if (_weights[p][t])
844  {
845  const GenericReal<is_ad> rom = computeROM(t, p, _strain_output_index);
846  if (rom == std::numeric_limits<float>::infinity())
847  mooseError("In ", _name, ": Output for strain increment reaches infinity: ", rom);
848 
849  total_rom_effective_strain_inc += _partition_weights[p] * _weights[p][t] * rom;
850 
851  dtotal_rom_effective_strain_inc_dstress +=
852  _partition_weights[p] * _weights[p][t] * computeROM(t, p, _strain_output_index, true);
853  if (_dpartition_weight_dstress[p])
854  dtotal_rom_effective_strain_inc_dstress +=
855  _dpartition_weight_dstress[p] * _weights[p][t] * rom;
856  if (dweights_dstress[p][t])
857  dtotal_rom_effective_strain_inc_dstress +=
858  _partition_weights[p] * dweights_dstress[p][t] * rom;
859  }
860  }
861  }
862 
863  if (_verbose)
864  {
865  Moose::err << std::setprecision(9);
866  GenericReal<is_ad> environmental = 0.0;
867  if (_environmental)
868  environmental = (*_environmental)[_qp];
869  Moose::err << "Verbose information from " << _name << ": \n";
870  Moose::err << " dt: " << _dt << "\n";
871  Moose::err << " old cell disl: " << _old_input_values[_cell_output_index] << "\n";
872  Moose::err << " old wall disl: " << _old_input_values[_wall_output_index] << "\n";
873  Moose::err << " initial stress (MPa): "
874  << MetaPhysicL::raw_value(effective_trial_stress) * _stress_ucf << "\n";
875  Moose::err << " temperature: " << MetaPhysicL::raw_value(_temperature[_qp]) << "\n";
876  Moose::err << " environmental factor: " << MetaPhysicL::raw_value(environmental) << "\n";
877  Moose::err << " calculated scalar strain value: " << MetaPhysicL::raw_value(scalar) << "\n";
878  Moose::err << " trial stress into rom (MPa): " << MetaPhysicL::raw_value(trial_stress_mpa)
879  << "\n";
880  Moose::err << " old effective strain: " << _old_input_values[_strain_output_index] << "\n";
881  Moose::err << " extrapolation: " << MetaPhysicL::raw_value(_extrapolation[_qp]) << "\n";
882  Moose::err << " partition 2 weight: " << MetaPhysicL::raw_value(_second_partition_weight[_qp])
883  << "\n";
884  Moose::err << " weights by tile, partition 1: ";
885  for (unsigned int t = 0; t < _num_tiles[0]; ++t)
886  Moose::err << " (" << t << ", " << MetaPhysicL::raw_value(_weights[0][t]) << ") ";
887  Moose::err << "\n";
888  if (_num_partitions > 1)
889  {
890  Moose::err << " weights by tile, partition 2: ";
891  for (unsigned int t = 0; t < _num_tiles[1]; ++t)
892  Moose::err << " (" << t << ", " << MetaPhysicL::raw_value(_weights[1][t]) << ") ";
893  }
894  Moose::err << "\n";
895  Moose::err << " nonstress weights by tile, partition 1: ";
896  for (unsigned int t = 0; t < _num_tiles[0]; ++t)
897  Moose::err << " (" << t << ", " << MetaPhysicL::raw_value(_non_stress_weights[0][t]) << ") ";
898  Moose::err << "\n";
899  if (_num_partitions > 1)
900  {
901  Moose::err << " nonstress weights by tile, partition 2: ";
902  for (unsigned int t = 0; t < _num_tiles[1]; ++t)
903  Moose::err << " (" << t << ", " << MetaPhysicL::raw_value(_non_stress_weights[1][t])
904  << ") ";
905  }
906  Moose::err << "\n";
907  Moose::err << " effective strain increment: "
908  << MetaPhysicL::raw_value(total_rom_effective_strain_inc) << std::endl;
909  }
910 
911  _creep_rate[_qp] = total_rom_effective_strain_inc / _dt;
912  _derivative = dtotal_rom_effective_strain_inc_dstress * dtrial_stress_dscalar - 1.0;
913 
914  if (!this->_apply_strain)
915  {
916  if (_verbose)
917  Moose::err << " Strain not applied due to apply_strain input parameter!" << std::endl;
918  _derivative = 1.0;
919  return 0.0;
920  }
921  return total_rom_effective_strain_inc - scalar;
922 }
923 
924 template <bool is_ad>
925 void
927 {
928  for (unsigned int p = 0; p < _num_partitions; ++p)
929  {
930  for (unsigned int t = 0; t < _num_tiles[p]; ++t)
931  {
932  // Only precompute for tiles that don't have zero weight
933  if (_non_stress_weights[p][t])
934  {
935  for (unsigned int i = 0; i < _num_inputs; ++i)
936  {
937  if (i != _stress_input_index)
938  {
939  _rom_inputs[p][t][i] =
940  normalizeInput(_input_values[i],
941  _transform[p][t][out_index][i],
942  _transform_coefs[p][t][out_index][i],
943  _transformed_normalization_limits[p][t][out_index][i]);
944  buildPolynomials(p, _rom_inputs[p][t][i], _polynomial_inputs[p][t][i]);
945  }
946  }
947  precomputeValues(
948  p, _coefs[p][t][out_index], _polynomial_inputs[p][t], _precomputed_vals[p][t]);
949  }
950  }
951  }
952 }
953 
954 template <bool is_ad>
957  const unsigned int p,
958  const unsigned out_index,
959  const bool derivative)
960 {
961  // Update due to new stress
962  _rom_inputs[p][t][_stress_input_index] =
963  normalizeInput(_input_values[_stress_input_index],
964  _transform[p][t][out_index][_stress_input_index],
965  _transform_coefs[p][t][out_index][_stress_input_index],
966  _transformed_normalization_limits[p][t][out_index][_stress_input_index]);
967 
968  buildPolynomials(
969  p, _rom_inputs[p][t][_stress_input_index], _polynomial_inputs[p][t][_stress_input_index]);
970 
971  // Compute ROM values
972  const GenericReal<is_ad> rom_output =
973  computeValues(p, _precomputed_vals[p][t], _polynomial_inputs[p][t]);
974 
975  // Return converted output if not derivative
976  if (!derivative)
977  return convertOutput(p, _old_input_values, rom_output, out_index);
978 
979  const GenericReal<is_ad> drom_input =
980  normalizeInput(_input_values[_stress_input_index],
981  _transform[p][t][out_index][_stress_input_index],
982  _transform_coefs[p][t][out_index][_stress_input_index],
983  _transformed_normalization_limits[p][t][out_index][_stress_input_index],
984  derivative);
985 
986  std::vector<GenericReal<is_ad>> dpolynomial_inputs(_degree[p], 0.0);
987  buildPolynomials(
988  p, _rom_inputs[p][t][_stress_input_index], dpolynomial_inputs, drom_input, derivative);
989 
990  const GenericReal<is_ad> drom_output = computeValues(
991  p, _precomputed_vals[p][t], _polynomial_inputs[p][t], dpolynomial_inputs, derivative);
992  return convertOutput(p, _old_input_values, rom_output, out_index, drom_output, derivative);
993 }
994 
995 template <bool is_ad>
998  const ROMInputTransform transform,
999  const Real transform_coef,
1000  const std::vector<Real> & transformed_limits,
1001  const bool derivative)
1002 {
1003  GenericReal<is_ad> x = input;
1004  convertValue(x, transform, transform_coef, derivative);
1005 
1006  // transformed_limits[2] = transformed_limits[1] - transformed_limits[0]
1007  if (derivative)
1008  return x / transformed_limits[2];
1009  else
1010  return (x - transformed_limits[0]) / transformed_limits[2] - 1.0;
1011 }
1012 
1013 template <bool is_ad>
1014 void
1016  const unsigned int p,
1017  const GenericReal<is_ad> & rom_input,
1018  std::vector<GenericReal<is_ad>> & polynomial_inputs,
1019  const GenericReal<is_ad> & drom_input,
1020  const bool derivative)
1021 {
1022  for (unsigned int d = 0; d < _degree[p]; ++d)
1023  {
1024  polynomial_inputs[d] = computePolynomial(rom_input, d);
1025 
1026  if (derivative)
1027  polynomial_inputs[d] = drom_input * computePolynomial(rom_input, d, derivative);
1028  }
1029 }
1030 
1031 template <bool is_ad>
1032 void
1034  const unsigned int p,
1035  const std::vector<Real> & coefs,
1036  const std::vector<std::vector<GenericReal<is_ad>>> & polynomial_inputs,
1037  std::vector<GenericReal<is_ad>> & precomputed)
1038 {
1039  for (unsigned int c = 0; c < _num_coefs[p]; ++c)
1040  {
1041  precomputed[c] = coefs[c];
1042  for (unsigned int i = 0; i < _num_inputs; ++i)
1043  if (i != _stress_input_index)
1044  precomputed[c] *= polynomial_inputs[i][_makeframe_helper[p][c + _num_coefs[p] * i]];
1045  }
1046 }
1047 
1048 template <bool is_ad>
1051  const unsigned int p,
1052  const std::vector<GenericReal<is_ad>> & precomputed,
1053  const std::vector<std::vector<GenericReal<is_ad>>> & polynomial_inputs,
1054  const std::vector<GenericReal<is_ad>> & dpolynomial_inputs,
1055  const bool derivative)
1056 {
1057  GenericReal<is_ad> rom_output = 0.0;
1058  for (unsigned int c = 0; c < _num_coefs[p]; ++c)
1059  {
1060  if (!derivative)
1061  rom_output +=
1062  precomputed[c] *
1063  polynomial_inputs[_stress_input_index]
1064  [_makeframe_helper[p][c + _num_coefs[p] * _stress_input_index]];
1065 
1066  else
1067  rom_output +=
1068  precomputed[c] *
1069  dpolynomial_inputs[_makeframe_helper[p][c + _num_coefs[p] * _stress_input_index]];
1070  }
1071  return rom_output;
1072 }
1073 
1074 template <bool is_ad>
1077  const std::vector<Real> & old_input_values,
1078  const GenericReal<is_ad> & rom_output,
1079  const unsigned out_index,
1080  const GenericReal<is_ad> & drom_output,
1081  const bool derivative)
1082 {
1083  if (out_index == _strain_output_index)
1084  {
1085  if (derivative)
1086  return std::exp(rom_output) * _dt * drom_output;
1087  else
1088  return std::exp(rom_output) * _dt;
1089  }
1090 
1091  if (derivative)
1092  return 0.0;
1093 
1094  GenericReal<is_ad> expout = std::exp(rom_output);
1095  mooseAssert(expout > 0.0, "ROM calculated strain increment must be positive");
1096 
1097  if (expout > _cutoff[p])
1098  expout -= _cutoff[p];
1099  else
1100  expout = -_cutoff[p] * _cutoff[p] / expout + _cutoff[p];
1101 
1102  return -expout * old_input_values[out_index] * _dt;
1103 }
1104 
1105 template <bool is_ad>
1108  const unsigned int degree,
1109  const bool derivative)
1110 {
1111  if (degree == 0)
1112  {
1113  if (derivative)
1114  return 0.0;
1115  return 1.0;
1116  }
1117  else if (degree == 1)
1118  {
1119  if (derivative)
1120  return 1.0;
1121  return value;
1122  }
1123  else if (degree == 2)
1124  {
1125  if (derivative)
1126  return 3.0 * value;
1127  return 1.5 * Utility::pow<2>(value) - 0.5;
1128  }
1129  else
1130  {
1131  if (derivative)
1132  return 7.5 * Utility::pow<2>(value) - 1.5;
1133  return 2.5 * Utility::pow<3>(value) - 1.5 * value;
1134  }
1135 }
1136 
1137 template <bool is_ad>
1138 std::vector<std::vector<std::vector<std::vector<Real>>>>
1140  const unsigned int p, const std::vector<std::vector<std::vector<Real>>> limits)
1141 {
1142  std::vector<std::vector<std::vector<std::vector<Real>>>> transformed_limits(
1143  _num_tiles[p],
1144  std::vector<std::vector<std::vector<Real>>>(
1145  _num_outputs, std::vector<std::vector<Real>>(_num_inputs, std::vector<Real>(3))));
1146  for (unsigned int t = 0; t < _num_tiles[p]; ++t)
1147  {
1148  for (unsigned int o = 0; o < _num_outputs; ++o)
1149  {
1150  for (unsigned int i = 0; i < _num_inputs; ++i)
1151  {
1152  for (unsigned int k = 0; k < 2; ++k)
1153  {
1154  transformed_limits[t][o][i][k] = limits[t][i][k];
1155  convertValue(
1156  transformed_limits[t][o][i][k], _transform[p][t][o][i], _transform_coefs[p][t][o][i]);
1157  }
1158  transformed_limits[t][o][i][2] =
1159  (transformed_limits[t][o][i][1] - transformed_limits[t][o][i][0]) / 2.0;
1160  }
1161  }
1162  }
1163  return transformed_limits;
1164 }
1165 
1166 template <bool is_ad>
1167 std::vector<unsigned int>
1169 {
1170  std::vector<unsigned int> v(_num_coefs[p] * _num_inputs);
1171 
1172  for (unsigned int numcoeffs = 0; numcoeffs < _num_coefs[p]; ++numcoeffs)
1173  for (unsigned int invar = 0; invar < _num_inputs; ++invar)
1174  v[numcoeffs + _num_coefs[p] * invar] =
1175  numcoeffs / MathUtils::pow(_degree[p], invar) % _degree[p];
1176 
1177  return v;
1178 }
1179 
1180 template <bool is_ad>
1181 void
1183  const GenericRankTwoTensor<is_ad> & plastic_strain_increment)
1184 {
1185  _cell_dislocation_increment = 0.0;
1186  _wall_dislocation_increment = 0.0;
1187  if (_input_values[_stress_input_index])
1188  {
1189  precomputeROM(_cell_output_index);
1190  for (unsigned int p = 0; p < _num_partitions; ++p)
1191  if (_partition_weights[p])
1192  for (unsigned int t = 0; t < _num_tiles[p]; ++t)
1193  if (_weights[p][t])
1194  _cell_dislocation_increment +=
1195  _partition_weights[p] * _weights[p][t] * computeROM(t, p, _cell_output_index);
1196  precomputeROM(_wall_output_index);
1197  for (unsigned int p = 0; p < _num_partitions; ++p)
1198  if (_partition_weights[p])
1199  for (unsigned int t = 0; t < _num_tiles[p]; ++t)
1200  if (_weights[p][t])
1201  _wall_dislocation_increment +=
1202  _partition_weights[p] * _weights[p][t] * computeROM(t, p, _wall_output_index);
1203  }
1204 
1205  _cell_rate[_qp] = _cell_dislocation_increment / _dt;
1206  _wall_rate[_qp] = _wall_dislocation_increment / _dt;
1207  _cell_dislocations[_qp] = _old_input_values[_cell_output_index] + _cell_dislocation_increment;
1208  _wall_dislocations[_qp] = _old_input_values[_wall_output_index] + _wall_dislocation_increment;
1209 
1210  // For (possibly) substepping.
1211  _plastic_strain_increment += MetaPhysicL::raw_value(plastic_strain_increment);
1212 
1213  // Prevent the ROM from calculating and proceeding with negative dislocations
1214  if (_apply_strain && (_cell_dislocations[_qp] < 0.0 || _wall_dislocations[_qp] < 0.0))
1215  mooseException("In ",
1216  _name,
1217  ": Negative disclocation density calculated for cell (old : ",
1218  MetaPhysicL::raw_value(_old_input_values[_cell_output_index]),
1219  " increment: ",
1220  MetaPhysicL::raw_value(_cell_dislocation_increment),
1221  " value: ",
1222  MetaPhysicL::raw_value(_cell_dislocations[_qp]),
1223  ") or wall (old : ",
1224  MetaPhysicL::raw_value(_old_input_values[_wall_output_index]),
1225  " increment: ",
1226  MetaPhysicL::raw_value(_wall_dislocation_increment),
1227  " value: ",
1228  MetaPhysicL::raw_value(_wall_dislocations[_qp]),
1229  ").");
1230 
1231  if (_verbose)
1232  {
1233  Moose::err << " Finalized ROM output\n";
1234  Moose::err << " effective creep strain increment: "
1235  << std::sqrt(2.0 / 3.0 *
1236  MetaPhysicL::raw_value(_plastic_strain_increment.doubleContraction(
1237  _plastic_strain_increment)))
1238  << "\n";
1239  Moose::err << " total effective creep strain: "
1240  << std::sqrt(2.0 / 3.0 *
1241  MetaPhysicL::raw_value(this->_creep_strain[_qp].doubleContraction(
1242  this->_creep_strain[_qp])))
1243  << "\n";
1244  Moose::err << " creep rate: " << MetaPhysicL::raw_value(_creep_rate[_qp]) << "\n";
1245  Moose::err << " cell dislocation rate: " << MetaPhysicL::raw_value(_cell_rate[_qp]) << "\n";
1246  Moose::err << " wall dislocation rate: " << MetaPhysicL::raw_value(_wall_rate[_qp]) << "\n";
1247  Moose::err << " new cell dislocations: " << MetaPhysicL::raw_value(_cell_dislocations[_qp])
1248  << "\n";
1249  Moose::err << " new wall dislocations: " << MetaPhysicL::raw_value(_wall_dislocations[_qp])
1250  << "\n"
1251  << std::endl;
1252  }
1253 
1255  MetaPhysicL::raw_value(_plastic_strain_increment));
1256 }
1257 
1258 template <bool is_ad>
1259 Real
1261 {
1263 
1264  Real cell_strain_inc = std::abs(MetaPhysicL::raw_value(_cell_dislocation_increment));
1265  if (cell_strain_inc && _old_input_values[_cell_output_index])
1266  limited_dt = std::min(limited_dt,
1267  _dt * _max_cell_increment * _old_input_values[_cell_output_index] /
1268  cell_strain_inc);
1269  Real wall_strain_inc = std::abs(MetaPhysicL::raw_value(_wall_dislocation_increment));
1270  if (wall_strain_inc && _old_input_values[_wall_output_index])
1271  limited_dt = std::min(limited_dt,
1272  _dt * _max_wall_increment * _old_input_values[_wall_output_index] /
1273  wall_strain_inc);
1274 
1275  return limited_dt;
1276 }
1277 
1278 template <bool is_ad>
1281  const Real upper,
1282  const GenericReal<is_ad> & val,
1283  const bool derivative)
1284 {
1285  mooseAssert(std::isfinite(MetaPhysicL::raw_value(val)), "Sigmoid value should must be infinite");
1286  mooseAssert(MetaPhysicL::raw_value(val) >= lower, "Input value must be greater than lower limit");
1287  mooseAssert(MetaPhysicL::raw_value(val) <= upper, "Input value must be greater than upper limit");
1288 
1289  // normalize val between 0 and 1, then shift between -1 and 1
1290  const GenericReal<is_ad> x = 2.0 * (val - lower) / (upper - lower) - 1.0;
1291 
1292  if (x == -1.0)
1293  {
1294  if (derivative)
1295  return 0.0;
1296  else
1297  return 1.0;
1298  }
1299  else if (x == 1.0)
1300  {
1301  if (derivative)
1302  return 0.0;
1303  else
1304  return 0.0;
1305  }
1306  else if (x < 1.0 && x > -1.0)
1307  {
1308  const GenericReal<is_ad> plus = std::exp(-2.0 / (1.0 + x));
1309  const GenericReal<is_ad> minus = std::exp(-2.0 / (1.0 - x));
1310 
1311  if (!derivative)
1312  return 1.0 - plus / (plus + minus);
1313 
1314  const GenericReal<is_ad> dplus_dx = plus * 2.0 / Utility::pow<2>(1.0 + x);
1315  const GenericReal<is_ad> dminus_dx = -minus * 2.0 / Utility::pow<2>(1.0 - x);
1316 
1317  return (plus * dminus_dx - dplus_dx * minus) / Utility::pow<2>(plus + minus) * 2.0 /
1318  (upper - lower);
1319  }
1320  else
1321  mooseError("Internal error: Sigmoid, value: x is out of bounds. val=",
1322  val,
1323  " low=",
1324  lower,
1325  " high=",
1326  upper);
1327 }
1328 
1329 template <bool is_ad>
1330 void
1332  const unsigned int total_it)
1333 {
1334  if (iter_output)
1335  {
1336  *iter_output << "At element " << this->_current_elem->id() << " _qp=" << _qp << " Coordinates "
1337  << _q_point[_qp] << " block=" << this->_current_elem->subdomain_id() << '\n';
1338  *iter_output << " dt " << _dt << " old cell disl: " << _old_input_values[_cell_output_index]
1339  << " old wall disl: " << _old_input_values[_wall_output_index]
1340  << " old effective strain: " << _old_input_values[_strain_output_index] << "\n";
1341 
1342  *iter_output << " temp: " << MetaPhysicL::raw_value(_temperature[_qp]) << " environmental: "
1343  << (_environmental ? MetaPhysicL::raw_value((*_environmental)[_qp]) : 0.0)
1344  << " trial stress into rom (MPa): "
1345  << MetaPhysicL::raw_value(_input_values[_stress_input_index])
1346  << " cell: " << MetaPhysicL::raw_value(_input_values[_cell_input_index])
1347  << " wall: " << MetaPhysicL::raw_value(_input_values[_wall_input_index])
1348  << " old strain: "
1349  << MetaPhysicL::raw_value(_input_values[_old_strain_input_index]) << "\n";
1350  *iter_output << " partition 2 weight: "
1351  << MetaPhysicL::raw_value(_second_partition_weight[_qp]) << "\n";
1352  *iter_output << " weights by tile, partition 1: ";
1353  for (unsigned int t = 0; t < _num_tiles[0]; ++t)
1354  *iter_output << " (" << t << ", " << MetaPhysicL::raw_value(_weights[0][t]) << ") ";
1355  *iter_output << "\n";
1356  *iter_output << " weights by tile, partition 2: ";
1357  for (unsigned int t = 0; t < _num_tiles[1]; ++t)
1358  *iter_output << " (" << t << ", " << MetaPhysicL::raw_value(_weights[1][t]) << ") ";
1359  *iter_output << "\n";
1360  *iter_output << " nonstress weights by tile, partition 1: ";
1361  for (unsigned int t = 0; t < _num_tiles[0]; ++t)
1362  *iter_output << " (" << t << ", " << MetaPhysicL::raw_value(_non_stress_weights[0][t])
1363  << ") ";
1364  *iter_output << "\n";
1365  *iter_output << " nonstress weights by tile, partition 2: ";
1366  for (unsigned int t = 0; t < _num_tiles[1]; ++t)
1367  *iter_output << " (" << t << ", " << MetaPhysicL::raw_value(_non_stress_weights[1][t])
1368  << ") ";
1369  *iter_output << "\n";
1370  }
1371 
1373 }
1374 
1375 template <bool is_ad>
1376 void
1378  std::stringstream * iter_output,
1379  const GenericReal<is_ad> & effective_trial_stress,
1380  const GenericReal<is_ad> & scalar,
1381  const Real reference_residual)
1382 {
1384  iter_output, effective_trial_stress, scalar, reference_residual);
1385  if (iter_output)
1386  *iter_output << " derivative: "
1387  << MetaPhysicL::raw_value(computeDerivative(effective_trial_stress, scalar))
1388  << std::endl;
1389 }
1390 
1391 template <bool is_ad>
1392 void
1394 {
1395  if (!this->isParamValid("model"))
1396  this->paramError("model", "Specify a JSON data filename.");
1397 
1398  const auto model_file_name = this->template getParam<DataFileName>("model");
1399  if (_json.empty())
1400  this->paramError("model", "The specified JSON data file '", model_file_name, "' is empty.");
1401  if (!_json.contains(key))
1402  this->paramError(
1403  "model", "The key '", key, "' is missing from the JSON data file '", model_file_name, "'.");
1404 }
1405 
nlohmann::json _json
JSON object constructed from the datafile.
Moose::GenericType< Real, is_ad > GenericReal
void outputIterationSummary(std::stringstream *iter_output, const unsigned int total_it) override
Output summary information for the convergence history of the model.
GenericReal< is_ad > computePolynomial(const GenericReal< is_ad > &value, const unsigned int degree, const bool derivative=false)
Calculate the value or derivative of Legendre polynomial up to 3rd order.
void addRequiredRangeCheckedParam(const std::string &name, const std::string &parsed_function, const std::string &doc_string)
virtual void outputIterationStep(std::stringstream *iter_output, const GenericReal< is_ad > &effective_trial_stress, const GenericReal< is_ad > &scalar, const Real reference_residual)
Output information for a single iteration step to build the convergence history of the model...
const unsigned int _old_strain_input_index
Index corresponding to the position for the old strain in the input vector.
void buildPolynomials(const unsigned int p, const GenericReal< is_ad > &rom_input, std::vector< GenericReal< is_ad >> &polynomial_inputs, const GenericReal< is_ad > &drom_input=0, const bool derivative=false)
Assemble the array of Legendre polynomials to be multiplied by the ROM coefficients.
LAROMANCEStressUpdateBaseTempl(const InputParameters &parameters)
GenericReal< is_ad > normalizeInput(const GenericReal< is_ad > &input, const ROMInputTransform transform, const Real transform_coef, const std::vector< Real > &transformed_limits, const bool derivative=false)
Convert the input variables into the form expected by the ROM Legendre polynomials to have a normaliz...
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
virtual GenericReal< is_ad > maximumPermissibleValue(const GenericReal< is_ad > &effective_trial_stress) const override
Compute the maximum permissible value of the scalar.
const GenericMaterialProperty< Real, is_ad > * _environmental
Optionally coupled environmental factor.
void mooseError(Args &&... args)
std::vector< std::pair< R1, R2 > > get(const std::string &param1, const std::string &param2) const
void mooseWarning(Args &&... args)
bool checkInTile(const unsigned int p, const unsigned int t) const
Checks if the input combination is in a specific tile.
virtual void computeStressInitialize(const GenericReal< is_ad > &effective_trial_stress, const GenericRankFourTensor< is_ad > &elasticity_tensor)
Perform any necessary initialization before return mapping iterations.
void seed(std::size_t i, unsigned int seed)
std::vector< unsigned int > getMakeFrameHelper(const unsigned int p) const
GenericReal< is_ad > sigmoid(const Real lower, const Real upper, const GenericReal< is_ad > &val, const bool derivative=false)
Calculate the sigmoid function weighting for the input based on the limits.
auto raw_value(const Eigen::Map< T > &in)
RadialReturnStressUpdate computes the radial return stress increment for an isotropic elastic-viscopl...
virtual void outputIterationStep(std::stringstream *iter_output, const GenericReal< is_ad > &effective_trial_stress, const GenericReal< is_ad > &scalar, const Real reference_residual) override
Output information for a single iteration step to build the convergence history of the model...
registerMooseObjectAliased("SolidMechanicsApp", LAROMANCEStressUpdateBase, "LAROMANCEStressUpdate")
virtual bool substeppingCapabilityEnabled() override
Does the model include the infrastructure for substep decomposition of the elastic strain initially u...
Moose::GenericType< RankFourTensor, is_ad > GenericRankFourTensor
const unsigned int _wall_input_index
Index corresponding to the position for the dislocations within the cell wall in the input vector...
Real randNormal(std::size_t i, Real mean, Real sigma)
const unsigned int _stress_input_index
Index corresponding to the position for the stress in the input vector.
void precomputeROM(const unsigned out_index)
Precompute the ROM strain rate information for all inputs except for strain.
bool isParamValid(const std::string &name) const
std::vector< std::pair< WindowFailure, WindowFailure > > _window_failure
Real elasticity_tensor(unsigned int i, unsigned int j, unsigned int k, unsigned int l)
void checkJSONKey(const std::string &key)
check if a JSON file was loaded and if the specified key exists
std::vector< std::vector< std::vector< std::vector< Real > > > > getTransformedLimits(const unsigned int p, const std::vector< std::vector< std::vector< Real >>> limits)
Real value(unsigned n, unsigned alpha, unsigned beta, Real x)
const std::vector< double > x
const unsigned int _temperature_input_index
Index corresponding to the position for the tempeature in the input vector.
GenericReal< is_ad > computeValues(const unsigned int p, const std::vector< GenericReal< is_ad >> &precomputed, const std::vector< std::vector< GenericReal< is_ad >>> &polynomial_inputs, const std::vector< GenericReal< is_ad >> &dpolynomial_inputs={}, const bool derivative=false)
Arranges the calculated Legendre polynomials into the proper oder and multiplies the Legendre polynom...
virtual void outputIterationSummary(std::stringstream *iter_output, const unsigned int total_it)
Output summary information for the convergence history of the model.
std::vector< std::string > _index_name
index names for error output
const unsigned int _environmental_input_index
Index corresponding to the position for the environmental factor in the input vector.
virtual Real computeTimeStepLimit() override
Compute the limiting value of the time step for this material.
virtual GenericReal< is_ad > computeResidual(const GenericReal< is_ad > &effective_trial_stress, const GenericReal< is_ad > &scalar) override
Compute the residual for a predicted value of the scalar.
virtual void setupUnitConversionFactors(const InputParameters &parameters)
virtual Real computeTimeStepLimit() override
Compute the limiting value of the time step for this material.
virtual GenericReal< is_ad > convertOutput(const unsigned int p, const std::vector< Real > &old_input_values, const GenericReal< is_ad > &rom_output, const unsigned out_index, const GenericReal< is_ad > &drom_output=0.0, const bool derivative=false)
Computes the output variable increments from the ROM predictions by bringing out of the normalized ma...
GenericReal< is_ad > computeROM(const unsigned int tile, const unsigned int partition, const unsigned out_index, const bool derivative=false)
Computes the ROM calculated increment for a given output and tile.
void addRequiredCoupledVar(const std::string &name, const std::string &doc_string)
This class provides baseline functionallity for creep models based on the stress update material in a...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
static const std::string v
Definition: NS.h:84
virtual void computeStressInitialize(const GenericReal< is_ad > &effective_trial_stress, const GenericRankFourTensor< is_ad > &elasticity_tensor) override
Perform any necessary initialization before return mapping iterations.
OStreamProxy out
Real convert(Real from_value, const MooseUnits &from_unit) const
void addClassDescription(const std::string &doc_string)
const InputParameters & parameters() const
virtual void resetIncrementalMaterialProperties() override
Reset material properties.
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
virtual void computeStressFinalize(const GenericRankTwoTensor< is_ad > &plastic_strain_increment) override
Perform any necessary steps to finalize state after return mapping iterations.
const unsigned int _cell_input_index
Index corresponding to the position for the dislocations with in the cell in the input vector...
T pow(T x, int e)
void computeTileWeight(std::vector< std::vector< GenericReal< is_ad >>> &weights, GenericReal< is_ad > &input, const unsigned int in_index, const bool derivative=false)
Compute the contribution (weight) of each tile in each partition, based on the input and tile boundar...
MooseUnits pow(const MooseUnits &, int)
virtual void storeIncrementalMaterialProperties(const unsigned int total_number_substeps) override
Properly set up the incremental calculation storage of the stateful material properties in the inheri...
bool _check_range
Whether to check to see whether iterative solution is within admissible range, and set within that ra...
static const std::string k
Definition: NS.h:130
bool areTilesNotIdentical(const unsigned int p, const unsigned int t, const unsigned int tt, const unsigned int in_index)
Checks if two tile domains are equal.
void precomputeValues(const unsigned int p, const std::vector< Real > &coefs, const std::vector< std::vector< GenericReal< is_ad >>> &polynomial_inputs, std::vector< GenericReal< is_ad >> &precomputed)
Arranges the calculated Legendre polynomials into the proper oder and multiplies the Legendre polynom...
virtual void initQpStatefulProperties() override
Moose::GenericType< RankTwoTensor, is_ad > GenericRankTwoTensor
void addParamNamesToGroup(const std::string &space_delim_names, const std::string group_name)
virtual void computeStressFinalize(const GenericRankTwoTensor< is_ad > &plastic_strain_increment) override
Perform any necessary steps to finalize state after return mapping iterations.