https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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
19registerMooseObjectAliased("SolidMechanicsApp", LAROMANCEStressUpdateBase, "LAROMANCEStressUpdate");
20registerMooseObjectAliased("SolidMechanicsApp",
22 "ADLAROMANCEStressUpdate");
23
24template <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
111 params.addRequiredRangeCheckedParam<Real>(
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
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
165template <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
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
281template <bool is_ad>
282void
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
295template <bool is_ad>
296bool
301
302template <bool is_ad>
303void
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
313template <bool is_ad>
314void
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)
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)
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
496template <bool is_ad>
497void
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
515template <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
525template <bool is_ad>
526void
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
538template <bool is_ad>
539void
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
548template <bool is_ad>
549void
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
595template <bool is_ad>
596void
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
748template <bool is_ad>
749bool
750LAROMANCEStressUpdateBaseTempl<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
759template <bool is_ad>
760bool
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
773template <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
924template <bool is_ad>
925void
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
954template <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
995template <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
1013template <bool is_ad>
1014void
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
1031template <bool is_ad>
1032void
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
1048template <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
1074template <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 using std::exp;
1084 if (out_index == _strain_output_index)
1085 {
1086 if (derivative)
1087 return exp(rom_output) * _dt * drom_output;
1088 else
1089 return exp(rom_output) * _dt;
1090 }
1091
1092 if (derivative)
1093 return 0.0;
1094
1095 GenericReal<is_ad> expout = exp(rom_output);
1096 mooseAssert(expout > 0.0, "ROM calculated strain increment must be positive");
1097
1098 if (expout > _cutoff[p])
1099 expout -= _cutoff[p];
1100 else
1101 expout = -_cutoff[p] * _cutoff[p] / expout + _cutoff[p];
1102
1103 return -expout * old_input_values[out_index] * _dt;
1104}
1105
1106template <bool is_ad>
1109 const unsigned int degree,
1110 const bool derivative)
1111{
1112 if (degree == 0)
1113 {
1114 if (derivative)
1115 return 0.0;
1116 return 1.0;
1117 }
1118 else if (degree == 1)
1119 {
1120 if (derivative)
1121 return 1.0;
1122 return value;
1123 }
1124 else if (degree == 2)
1125 {
1126 if (derivative)
1127 return 3.0 * value;
1128 return 1.5 * Utility::pow<2>(value) - 0.5;
1129 }
1130 else
1131 {
1132 if (derivative)
1133 return 7.5 * Utility::pow<2>(value) - 1.5;
1134 return 2.5 * Utility::pow<3>(value) - 1.5 * value;
1135 }
1136}
1137
1138template <bool is_ad>
1139std::vector<std::vector<std::vector<std::vector<Real>>>>
1141 const unsigned int p, const std::vector<std::vector<std::vector<Real>>> limits)
1142{
1143 std::vector<std::vector<std::vector<std::vector<Real>>>> transformed_limits(
1144 _num_tiles[p],
1145 std::vector<std::vector<std::vector<Real>>>(
1146 _num_outputs, std::vector<std::vector<Real>>(_num_inputs, std::vector<Real>(3))));
1147 for (unsigned int t = 0; t < _num_tiles[p]; ++t)
1148 {
1149 for (unsigned int o = 0; o < _num_outputs; ++o)
1150 {
1151 for (unsigned int i = 0; i < _num_inputs; ++i)
1152 {
1153 for (unsigned int k = 0; k < 2; ++k)
1154 {
1155 transformed_limits[t][o][i][k] = limits[t][i][k];
1156 convertValue(
1157 transformed_limits[t][o][i][k], _transform[p][t][o][i], _transform_coefs[p][t][o][i]);
1158 }
1159 transformed_limits[t][o][i][2] =
1160 (transformed_limits[t][o][i][1] - transformed_limits[t][o][i][0]) / 2.0;
1161 }
1162 }
1163 }
1164 return transformed_limits;
1165}
1166
1167template <bool is_ad>
1168std::vector<unsigned int>
1170{
1171 std::vector<unsigned int> v(_num_coefs[p] * _num_inputs);
1172
1173 for (unsigned int numcoeffs = 0; numcoeffs < _num_coefs[p]; ++numcoeffs)
1174 for (unsigned int invar = 0; invar < _num_inputs; ++invar)
1175 v[numcoeffs + _num_coefs[p] * invar] =
1176 numcoeffs / MathUtils::pow(_degree[p], invar) % _degree[p];
1177
1178 return v;
1179}
1180
1181template <bool is_ad>
1182void
1184 const GenericRankTwoTensor<is_ad> & plastic_strain_increment)
1185{
1186 _cell_dislocation_increment = 0.0;
1187 _wall_dislocation_increment = 0.0;
1188 if (_input_values[_stress_input_index])
1189 {
1190 precomputeROM(_cell_output_index);
1191 for (unsigned int p = 0; p < _num_partitions; ++p)
1192 if (_partition_weights[p])
1193 for (unsigned int t = 0; t < _num_tiles[p]; ++t)
1194 if (_weights[p][t])
1195 _cell_dislocation_increment +=
1196 _partition_weights[p] * _weights[p][t] * computeROM(t, p, _cell_output_index);
1197 precomputeROM(_wall_output_index);
1198 for (unsigned int p = 0; p < _num_partitions; ++p)
1199 if (_partition_weights[p])
1200 for (unsigned int t = 0; t < _num_tiles[p]; ++t)
1201 if (_weights[p][t])
1202 _wall_dislocation_increment +=
1203 _partition_weights[p] * _weights[p][t] * computeROM(t, p, _wall_output_index);
1204 }
1205
1206 _cell_rate[_qp] = _cell_dislocation_increment / _dt;
1207 _wall_rate[_qp] = _wall_dislocation_increment / _dt;
1208 _cell_dislocations[_qp] = _old_input_values[_cell_output_index] + _cell_dislocation_increment;
1209 _wall_dislocations[_qp] = _old_input_values[_wall_output_index] + _wall_dislocation_increment;
1210
1211 // For (possibly) substepping.
1212 _plastic_strain_increment += MetaPhysicL::raw_value(plastic_strain_increment);
1213
1214 // Prevent the ROM from calculating and proceeding with negative dislocations
1215 if (_apply_strain && (_cell_dislocations[_qp] < 0.0 || _wall_dislocations[_qp] < 0.0))
1216 mooseException("In ",
1217 _name,
1218 ": Negative disclocation density calculated for cell (old : ",
1219 MetaPhysicL::raw_value(_old_input_values[_cell_output_index]),
1220 " increment: ",
1221 MetaPhysicL::raw_value(_cell_dislocation_increment),
1222 " value: ",
1223 MetaPhysicL::raw_value(_cell_dislocations[_qp]),
1224 ") or wall (old : ",
1225 MetaPhysicL::raw_value(_old_input_values[_wall_output_index]),
1226 " increment: ",
1227 MetaPhysicL::raw_value(_wall_dislocation_increment),
1228 " value: ",
1229 MetaPhysicL::raw_value(_wall_dislocations[_qp]),
1230 ").");
1231
1232 if (_verbose)
1233 {
1234 Moose::err << " Finalized ROM output\n";
1235 Moose::err << " effective creep strain increment: "
1236 << std::sqrt(2.0 / 3.0 *
1237 MetaPhysicL::raw_value(_plastic_strain_increment.doubleContraction(
1238 _plastic_strain_increment)))
1239 << "\n";
1240 Moose::err << " total effective creep strain: "
1241 << std::sqrt(2.0 / 3.0 *
1242 MetaPhysicL::raw_value(this->_creep_strain[_qp].doubleContraction(
1243 this->_creep_strain[_qp])))
1244 << "\n";
1245 Moose::err << " creep rate: " << MetaPhysicL::raw_value(_creep_rate[_qp]) << "\n";
1246 Moose::err << " cell dislocation rate: " << MetaPhysicL::raw_value(_cell_rate[_qp]) << "\n";
1247 Moose::err << " wall dislocation rate: " << MetaPhysicL::raw_value(_wall_rate[_qp]) << "\n";
1248 Moose::err << " new cell dislocations: " << MetaPhysicL::raw_value(_cell_dislocations[_qp])
1249 << "\n";
1250 Moose::err << " new wall dislocations: " << MetaPhysicL::raw_value(_wall_dislocations[_qp])
1251 << "\n"
1252 << std::endl;
1253 }
1254
1256 MetaPhysicL::raw_value(_plastic_strain_increment));
1257}
1258
1259template <bool is_ad>
1260Real
1262{
1264
1265 Real cell_strain_inc = std::abs(MetaPhysicL::raw_value(_cell_dislocation_increment));
1266 if (cell_strain_inc && _old_input_values[_cell_output_index])
1267 limited_dt = std::min(limited_dt,
1268 _dt * _max_cell_increment * _old_input_values[_cell_output_index] /
1269 cell_strain_inc);
1270 Real wall_strain_inc = std::abs(MetaPhysicL::raw_value(_wall_dislocation_increment));
1271 if (wall_strain_inc && _old_input_values[_wall_output_index])
1272 limited_dt = std::min(limited_dt,
1273 _dt * _max_wall_increment * _old_input_values[_wall_output_index] /
1274 wall_strain_inc);
1275
1276 return limited_dt;
1277}
1278
1279template <bool is_ad>
1282 const Real upper,
1283 const GenericReal<is_ad> & val,
1284 const bool derivative)
1285{
1286 using std::exp;
1287
1288 mooseAssert(std::isfinite(MetaPhysicL::raw_value(val)), "Sigmoid value should must be infinite");
1289 mooseAssert(MetaPhysicL::raw_value(val) >= lower, "Input value must be greater than lower limit");
1290 mooseAssert(MetaPhysicL::raw_value(val) <= upper, "Input value must be greater than upper limit");
1291
1292 // normalize val between 0 and 1, then shift between -1 and 1
1293 const GenericReal<is_ad> x = 2.0 * (val - lower) / (upper - lower) - 1.0;
1294
1295 if (x == -1.0)
1296 {
1297 if (derivative)
1298 return 0.0;
1299 else
1300 return 1.0;
1301 }
1302 else if (x == 1.0)
1303 {
1304 if (derivative)
1305 return 0.0;
1306 else
1307 return 0.0;
1308 }
1309 else if (x < 1.0 && x > -1.0)
1310 {
1311 const GenericReal<is_ad> plus = exp(-2.0 / (1.0 + x));
1312 const GenericReal<is_ad> minus = exp(-2.0 / (1.0 - x));
1313
1314 if (!derivative)
1315 return 1.0 - plus / (plus + minus);
1316
1317 const GenericReal<is_ad> dplus_dx = plus * 2.0 / Utility::pow<2>(1.0 + x);
1318 const GenericReal<is_ad> dminus_dx = -minus * 2.0 / Utility::pow<2>(1.0 - x);
1319
1320 return (plus * dminus_dx - dplus_dx * minus) / Utility::pow<2>(plus + minus) * 2.0 /
1321 (upper - lower);
1322 }
1323 else
1324 mooseError("Internal error: Sigmoid, value: x is out of bounds. val=",
1325 val,
1326 " low=",
1327 lower,
1328 " high=",
1329 upper);
1330}
1331
1332template <bool is_ad>
1333void
1335 const unsigned int total_it)
1336{
1337 if (iter_output)
1338 {
1339 *iter_output << "At element " << this->_current_elem->id() << " _qp=" << _qp << " Coordinates "
1340 << _q_point[_qp] << " block=" << this->_current_elem->subdomain_id() << '\n';
1341 *iter_output << " dt " << _dt << " old cell disl: " << _old_input_values[_cell_output_index]
1342 << " old wall disl: " << _old_input_values[_wall_output_index]
1343 << " old effective strain: " << _old_input_values[_strain_output_index] << "\n";
1344
1345 *iter_output << " temp: " << MetaPhysicL::raw_value(_temperature[_qp]) << " environmental: "
1346 << (_environmental ? MetaPhysicL::raw_value((*_environmental)[_qp]) : 0.0)
1347 << " trial stress into rom (MPa): "
1348 << MetaPhysicL::raw_value(_input_values[_stress_input_index])
1349 << " cell: " << MetaPhysicL::raw_value(_input_values[_cell_input_index])
1350 << " wall: " << MetaPhysicL::raw_value(_input_values[_wall_input_index])
1351 << " old strain: "
1352 << MetaPhysicL::raw_value(_input_values[_old_strain_input_index]) << "\n";
1353 *iter_output << " partition 2 weight: "
1354 << MetaPhysicL::raw_value(_second_partition_weight[_qp]) << "\n";
1355 *iter_output << " weights by tile, partition 1: ";
1356 for (unsigned int t = 0; t < _num_tiles[0]; ++t)
1357 *iter_output << " (" << t << ", " << MetaPhysicL::raw_value(_weights[0][t]) << ") ";
1358 *iter_output << "\n";
1359 *iter_output << " weights by tile, partition 2: ";
1360 for (unsigned int t = 0; t < _num_tiles[1]; ++t)
1361 *iter_output << " (" << t << ", " << MetaPhysicL::raw_value(_weights[1][t]) << ") ";
1362 *iter_output << "\n";
1363 *iter_output << " nonstress weights by tile, partition 1: ";
1364 for (unsigned int t = 0; t < _num_tiles[0]; ++t)
1365 *iter_output << " (" << t << ", " << MetaPhysicL::raw_value(_non_stress_weights[0][t])
1366 << ") ";
1367 *iter_output << "\n";
1368 *iter_output << " nonstress weights by tile, partition 2: ";
1369 for (unsigned int t = 0; t < _num_tiles[1]; ++t)
1370 *iter_output << " (" << t << ", " << MetaPhysicL::raw_value(_non_stress_weights[1][t])
1371 << ") ";
1372 *iter_output << "\n";
1373 }
1374
1376}
1377
1378template <bool is_ad>
1379void
1381 std::stringstream * iter_output,
1382 const GenericReal<is_ad> & effective_trial_stress,
1383 const GenericReal<is_ad> & scalar,
1384 const Real reference_residual)
1385{
1387 iter_output, effective_trial_stress, scalar, reference_residual);
1388 if (iter_output)
1389 *iter_output << " derivative: "
1390 << MetaPhysicL::raw_value(computeDerivative(effective_trial_stress, scalar))
1391 << std::endl;
1392}
1393
1394template <bool is_ad>
1395void
1397{
1398 if (!this->isParamValid("model"))
1399 this->paramError("model", "Specify a JSON data filename.");
1400
1401 const auto model_file_name = this->template getParam<DataFileName>("model");
1402 if (_json.empty())
1403 this->paramError("model", "The specified JSON data file '", model_file_name, "' is empty.");
1404 if (!_json.contains(key))
1405 this->paramError(
1406 "model", "The key '", key, "' is missing from the JSON data file '", model_file_name, "'.");
1407}
1408
const std::vector< double > x
const Real p
const double v
registerMooseObjectAliased("SolidMechanicsApp", LAROMANCEStressUpdateBase, "LAROMANCEStressUpdate")
void mooseWarning(Args &&... args)
void mooseError(Args &&... args)
Moose::GenericType< RankFourTensor, is_ad > GenericRankFourTensor
Moose::GenericType< Real, is_ad > GenericReal
Moose::GenericType< RankTwoTensor, is_ad > GenericRankTwoTensor
void addParamNamesToGroup(const std::string &space_delim_names, const std::string group_name)
void addRequiredRangeCheckedParam(const std::string &name, const std::string &parsed_function, const std::string &doc_string)
void addRequiredCoupledVar(const std::string &name, const std::string &doc_string)
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
std::vector< std::pair< R1, R2 > > get(const std::string &param1, const std::string &param2) const
void addClassDescription(const std::string &doc_string)
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
void checkJSONKey(const std::string &key)
check if a JSON file was loaded and if the specified key exists
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...
virtual GenericReal< is_ad > maximumPermissibleValue(const GenericReal< is_ad > &effective_trial_stress) const override
Compute the maximum permissible value of the scalar.
std::vector< std::vector< std::vector< std::vector< Real > > > > getTransformedLimits(const unsigned int p, const std::vector< std::vector< std::vector< Real > > > limits)
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.
virtual Real computeTimeStepLimit() override
Compute the limiting value of the time step for this material.
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.
std::vector< std::string > _index_name
index names for error output
virtual void resetIncrementalMaterialProperties() override
Reset material properties.
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 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.
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 precomputeROM(const unsigned out_index)
Precompute the ROM strain rate information for all inputs except for strain.
const unsigned int _wall_input_index
Index corresponding to the position for the dislocations within the cell wall in the input vector.
virtual bool substeppingCapabilityEnabled() override
Does the model include the infrastructure for substep decomposition of the elastic strain initially u...
const unsigned int _stress_input_index
Index corresponding to the position for the stress in the input vector.
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.
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.
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...
const unsigned int _cell_input_index
Index corresponding to the position for the dislocations with in the cell in the input vector.
virtual void computeStressFinalize(const GenericRankTwoTensor< is_ad > &plastic_strain_increment) override
Perform any necessary steps to finalize state after return mapping iterations.
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...
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.
const unsigned int _old_strain_input_index
Index corresponding to the position for the old strain in the input vector.
virtual void setupUnitConversionFactors(const InputParameters &parameters)
std::vector< unsigned int > getMakeFrameHelper(const unsigned int p) const
virtual void initQpStatefulProperties() override
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...
const GenericMaterialProperty< Real, is_ad > * _environmental
Optionally coupled environmental factor.
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.
const unsigned int _environmental_input_index
Index corresponding to the position for the environmental factor in the input vector.
const unsigned int _temperature_input_index
Index corresponding to the position for the tempeature in the input vector.
bool checkInTile(const unsigned int p, const unsigned int t) const
Checks if the input combination is in a specific tile.
nlohmann::json _json
JSON object constructed from the datafile.
LAROMANCEStressUpdateBaseTempl(const InputParameters &parameters)
std::vector< std::pair< WindowFailure, WindowFailure > > _window_failure
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...
void outputIterationSummary(std::stringstream *iter_output, const unsigned int total_it) override
Output summary information for the convergence history of the model.
const InputParameters & parameters() const
bool isParamValid(const std::string &name) const
void seed(std::size_t i, unsigned int seed)
Real randNormal(std::size_t i, Real mean, Real sigma)
Real convert(Real from_value, const MooseUnits &from_unit) const
This class provides baseline functionallity for creep models based on the stress update material in a...
virtual void computeStressFinalize(const GenericRankTwoTensor< is_ad > &plastic_strain_increment) override
Perform any necessary steps to finalize state after return mapping iterations.
RadialReturnStressUpdate computes the radial return stress increment for an isotropic elastic-viscopl...
virtual void computeStressInitialize(const GenericReal< is_ad > &effective_trial_stress, const GenericRankFourTensor< is_ad > &elasticity_tensor)
Perform any necessary initialization before return mapping iterations.
virtual Real computeTimeStepLimit() override
Compute the limiting value of the time step for this material.
bool _check_range
Whether to check to see whether iterative solution is within admissible range, and set within that ra...
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.
virtual void outputIterationSummary(std::stringstream *iter_output, const unsigned int total_it)
Output summary information for the convergence history of the model.
T pow(T x, int e)
auto raw_value(const Eigen::Map< T > &in)
Real elasticity_tensor(unsigned int i, unsigned int j, unsigned int k, unsigned int l)