Line data Source code
1 : //* This file is part of the MOOSE framework
2 : //* https://mooseframework.inl.gov
3 : //*
4 : //* All rights reserved, see COPYRIGHT for full restrictions
5 : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 : //*
7 : //* Licensed under LGPL 2.1, please see LICENSE for details
8 : //* https://www.gnu.org/licenses/lgpl-2.1.html
9 :
10 : #include "AbaqusUMATStress.h"
11 : #include "AnalysisStepUserObject.h"
12 : #include "Factory.h"
13 : #include "MooseMesh.h"
14 : #include "RankTwoTensor.h"
15 : #include "RankFourTensor.h"
16 : #include "libmesh/int_range.h"
17 : #include <string.h>
18 : #include <algorithm>
19 :
20 : #define QUOTE(macro) stringifyName(macro)
21 :
22 : registerMooseObject("SolidMechanicsApp", AbaqusUMATStress);
23 :
24 : InputParameters
25 1488 : AbaqusUMATStress::validParams()
26 : {
27 1488 : InputParameters params = ComputeGeneralStressBase::validParams();
28 1488 : params.addClassDescription("Coupling material to use Abaqus UMAT models in MOOSE");
29 2976 : params.addRequiredParam<FileName>(
30 : "plugin", "The path to the compiled dynamic library for the plugin you want to use");
31 2976 : params.addRequiredParam<bool>(
32 : "use_one_based_indexing",
33 : "Parameter to control whether indexing for element and integration points as presented to "
34 : "UMAT models is based on 1 (true) or 0 (false). This does not affect internal MOOSE "
35 : "numbering. The option to use 0-based numbering is deprecated and will be removed soon.");
36 2976 : params.addRequiredParam<std::vector<Real>>(
37 : "constant_properties", "Constant mechanical and thermal material properties (PROPS)");
38 2976 : params.addRequiredParam<unsigned int>("num_state_vars",
39 : "The number of state variables this UMAT is going to use");
40 2976 : params.addCoupledVar("temperature", 0.0, "Coupled temperature");
41 2976 : params.addCoupledVar("external_fields",
42 : "The external fields that can be used in the UMAT subroutine");
43 2976 : params.addParam<std::vector<MaterialPropertyName>>("external_properties", {}, "");
44 2976 : params.addParam<MooseEnum>("decomposition_method",
45 2976 : ComputeFiniteStrain::decompositionType(),
46 : "Method to calculate the strain kinematics.");
47 2976 : params.addParam<bool>(
48 : "use_displaced_mesh",
49 2976 : false,
50 : "Whether or not this object should use the "
51 : "displaced mesh for computing displacements and quantities based on the deformed state.");
52 2976 : params.addParam<UserObjectName>(
53 : "analysis_step_user_object",
54 : "The AnalysisStepUserObject that provides times from simulation loading steps.");
55 2976 : params.addParam<RealVectorValue>(
56 : "orientation",
57 : "Euler angles that describe the orientation of the local material coordinate system.");
58 1488 : return params;
59 0 : }
60 :
61 : #ifndef METHOD
62 : #error "The METHOD preprocessor symbol must be supplied by the build system."
63 : #endif
64 :
65 1116 : AbaqusUMATStress::AbaqusUMATStress(const InputParameters & parameters)
66 : : ComputeGeneralStressBase(parameters),
67 2232 : _plugin(getParam<FileName>("plugin")),
68 2232 : _library(_plugin + std::string("-") + QUOTE(METHOD) + ".plugin"),
69 1116 : _umat(_library.getFunction<umat_t>("umat_")),
70 2232 : _aqNSTATV(getParam<unsigned int>("num_state_vars")),
71 1116 : _aqSTATEV(_aqNSTATV),
72 3348 : _aqPROPS(getParam<std::vector<Real>>("constant_properties")),
73 1116 : _aqNPROPS(_aqPROPS.size()),
74 2232 : _stress_old(getMaterialPropertyOld<RankTwoTensor>(_base_name + "stress")),
75 2232 : _total_strain_old(getMaterialPropertyOld<RankTwoTensor>(_base_name + "total_strain")),
76 2232 : _strain_increment(getOptionalMaterialProperty<RankTwoTensor>(_base_name + "strain_increment")),
77 1116 : _jacobian_mult(declareProperty<RankFourTensor>(_base_name + "Jacobian_mult")),
78 2232 : _Fbar(getOptionalMaterialProperty<RankTwoTensor>(_base_name + "deformation_gradient")),
79 2232 : _Fbar_old(getOptionalMaterialPropertyOld<RankTwoTensor>(_base_name + "deformation_gradient")),
80 1116 : _state_var(declareProperty<std::vector<Real>>(_base_name + "state_var")),
81 2232 : _state_var_old(getMaterialPropertyOld<std::vector<Real>>(_base_name + "state_var")),
82 1116 : _elastic_strain_energy(declareProperty<Real>(_base_name + "elastic_strain_energy")),
83 2232 : _elastic_strain_energy_old(getMaterialPropertyOld<Real>(_base_name + "elastic_strain_energy")),
84 1116 : _plastic_dissipation(declareProperty<Real>(_base_name + "plastic_dissipation")),
85 2232 : _plastic_dissipation_old(getMaterialPropertyOld<Real>(_base_name + "plastic_dissipation")),
86 1116 : _creep_dissipation(declareProperty<Real>(_base_name + "creep_dissipation")),
87 2232 : _creep_dissipation_old(getMaterialPropertyOld<Real>(_base_name + "creep_dissipation")),
88 1116 : _material_timestep(declareProperty<Real>(_base_name + "material_timestep_limit")),
89 1116 : _rotation_increment(
90 1116 : getOptionalMaterialProperty<RankTwoTensor>(_base_name + "rotation_increment")),
91 1116 : _rotation_increment_old(
92 1116 : getOptionalMaterialPropertyOld<RankTwoTensor>(_base_name + "rotation_increment")),
93 1116 : _temperature(coupledValue("temperature")),
94 1116 : _temperature_old(coupledValueOld("temperature")),
95 2232 : _external_fields(isCoupled("external_fields") ? coupledValues("external_fields")
96 : : std::vector<const VariableValue *>{}),
97 2232 : _external_fields_old(isCoupled("external_fields") ? coupledValuesOld("external_fields")
98 : : std::vector<const VariableValue *>{}),
99 1116 : _number_external_fields(_external_fields.size()),
100 3348 : _external_property_names(getParam<std::vector<MaterialPropertyName>>("external_properties")),
101 1116 : _number_external_properties(_external_property_names.size()),
102 1116 : _external_properties(_number_external_properties),
103 1116 : _external_properties_old(_number_external_properties),
104 2232 : _use_one_based_indexing(getParam<bool>("use_one_based_indexing")),
105 2232 : _use_orientation(isParamValid("orientation")),
106 2232 : _R(_use_orientation ? getParam<RealVectorValue>("orientation") : RealVectorValue(0.0)),
107 1116 : _total_rotation(declareProperty<RankTwoTensor>("total_rotation")),
108 2232 : _total_rotation_old(getMaterialPropertyOld<RankTwoTensor>("total_rotation")),
109 1116 : _decomposition_method(
110 2232 : getParam<MooseEnum>("decomposition_method").getEnum<ComputeFiniteStrain::DecompMethod>())
111 : {
112 1116 : if (!_use_one_based_indexing)
113 42 : mooseDeprecated(
114 : "AbaqusUMATStress has transitioned to 1-based indexing in the element (NOEL) and "
115 : "integration point (NPT) numbers to ensure maximum compatibility with legacy UMAT files. "
116 : "Please ensure that any new UMAT plugins using these quantities are using the correct "
117 : "indexing. 0-based indexing will be deprecated soon.");
118 :
119 : // get material properties
120 1158 : for (std::size_t i = 0; i < _number_external_properties; ++i)
121 : {
122 42 : _external_properties[i] = &getMaterialProperty<Real>(_external_property_names[i]);
123 42 : _external_properties_old[i] = &getMaterialPropertyOld<Real>(_external_property_names[i]);
124 : }
125 :
126 : // Read mesh dimension and size UMAT arrays (we always size for full 3D)
127 1116 : _aqNTENS = 6; // Size of the stress or strain component array (NDI+NSHR)
128 1116 : _aqNSHR = 3; // Number of engineering shear stress components
129 1116 : _aqNDI = 3; // Number of direct stress components (always 3)
130 :
131 1116 : _aqDDSDDT.resize(_aqNTENS);
132 1116 : _aqDRPLDE.resize(_aqNTENS);
133 1116 : _aqSTRAN.resize(_aqNTENS);
134 1116 : _aqDFGRD0.resize(9);
135 1116 : _aqDFGRD1.resize(9);
136 1116 : _aqDROT.resize(9);
137 1116 : _aqSTRESS.resize(_aqNTENS);
138 1116 : _aqDDSDDE.resize(_aqNTENS * _aqNTENS);
139 1116 : _aqDSTRAN.resize(_aqNTENS);
140 1116 : _aqPREDEF.resize(_number_external_fields + _number_external_properties);
141 1116 : _aqDPRED.resize(_number_external_fields + _number_external_properties);
142 1116 : }
143 :
144 : void
145 1112 : AbaqusUMATStress::initialSetup()
146 : {
147 : // The _Fbar, _Fbar_old, and _rotation_increment optional properties are only available when an
148 : // incremental strain formulation is used. If they are not avaliable we advide the user to
149 : // select an incremental formulation.
150 1112 : if (!_strain_increment || !_Fbar || !_Fbar_old || !_rotation_increment)
151 2 : mooseError("AbaqusUMATStress '",
152 : name(),
153 : "': Incremental strain quantities are not available. You likely are using a total "
154 : "strain formulation. Specify `incremental = true` in the tensor mechanics action, "
155 : "or use ComputeIncrementalStrain in your input file.");
156 :
157 : // Let's automatically detect uos and identify the one we are interested in.
158 : // If there is more than one, we assume something is off and error out.
159 2220 : if (!isParamSetByUser("analysis_step_user_object"))
160 1089 : getAnalysisStepUserObject(_fe_problem, _step_user_object, name());
161 : else
162 21 : _step_user_object = &getUserObject<AnalysisStepUserObject>("analysis_step_user_object");
163 1110 : }
164 :
165 : void
166 60386 : AbaqusUMATStress::initQpStatefulProperties()
167 : {
168 60386 : ComputeGeneralStressBase::initQpStatefulProperties();
169 :
170 : // Initialize state variable vector
171 60386 : _state_var[_qp].resize(_aqNSTATV);
172 285066 : for (const auto i : make_range(_aqNSTATV))
173 224680 : _state_var[_qp][i] = 0.0;
174 :
175 : // Initialize total rotation tensor
176 60386 : _total_rotation[_qp] = _R;
177 60386 : }
178 :
179 : void
180 565373 : AbaqusUMATStress::computeProperties()
181 : {
182 : // current element "number"
183 565373 : _aqNOEL = _current_elem->id() + (_use_one_based_indexing ? 1 : 0);
184 :
185 : // characteristic element length
186 565373 : _aqCELENT = std::pow(_current_elem->volume(), 1.0 / _current_elem->dim());
187 :
188 565373 : if (!_step_user_object)
189 : {
190 : // Value of total time at the beginning of the current increment
191 563411 : _aqTIME[0] = _t - _dt;
192 : }
193 : else
194 : {
195 1962 : const unsigned int start_time_step = _step_user_object->getStep(_t - _dt);
196 1962 : const Real step_time = _step_user_object->getStartTime(start_time_step);
197 : // Value of step time at the beginning of the current increment
198 1962 : _aqTIME[0] = step_time;
199 : }
200 : // Value of total time at the beginning of the current increment
201 565373 : _aqTIME[1] = _t - _dt;
202 :
203 : // Time increment
204 565373 : _aqDTIME = _dt;
205 :
206 : // Fill unused characters with spaces (Fortran)
207 565373 : std::fill(_aqCMNAME, _aqCMNAME + 80, ' ');
208 565373 : std::memcpy(_aqCMNAME, name().c_str(), name().size());
209 :
210 565373 : ComputeGeneralStressBase::computeProperties();
211 565373 : }
212 :
213 : void
214 2623176 : AbaqusUMATStress::computeQpStress()
215 : {
216 : // C uses row-major, whereas Fortran uses column major
217 : // therefore, all unsymmetric matrices must be transposed before passing them to Fortran
218 2623176 : RankTwoTensor FBar_old_fortran = _Fbar_old[_qp].transpose();
219 2623176 : RankTwoTensor FBar_fortran = _Fbar[_qp].transpose();
220 :
221 : // DROT needed by UMAT will depend on kinematics and whether or not an intermediate configuration
222 : // is used
223 2623176 : RankTwoTensor DROT_fortran;
224 2623176 : if (_use_orientation)
225 : {
226 38232 : DROT_fortran = RankTwoTensor::Identity();
227 : }
228 : else
229 : {
230 2584944 : if (_decomposition_method == ComputeFiniteStrain::DecompMethod::HughesWinget)
231 16696 : DROT_fortran = _rotation_increment[_qp].transpose();
232 : else
233 2568248 : DROT_fortran = _rotation_increment_old[_qp].transpose();
234 : }
235 :
236 : const Real * myDFGRD0 = &(FBar_old_fortran(0, 0));
237 : const Real * myDFGRD1 = &(FBar_fortran(0, 0));
238 : const Real * myDROT = &(DROT_fortran(0, 0));
239 :
240 : // More local copies of materials so we can (optionally) rotate
241 2623176 : RankTwoTensor stress_old = _stress_old[_qp];
242 2623176 : RankTwoTensor total_strain_old = _total_strain_old[_qp];
243 2623176 : RankTwoTensor strain_increment = _strain_increment[_qp];
244 :
245 : // check if we need to rotate to intermediate configuration
246 2623176 : if (_use_orientation)
247 : {
248 : // keep track of total rotation
249 38232 : _total_rotation[_qp] = _rotation_increment[_qp] * _total_rotation_old[_qp];
250 : // rotate stress/strain/increment from reference configuration to intermediate configuration
251 38232 : stress_old.rotate(_total_rotation_old[_qp].transpose());
252 38232 : total_strain_old.rotate(_total_rotation_old[_qp].transpose());
253 :
254 38232 : if (_decomposition_method == ComputeFiniteStrain::DecompMethod::HughesWinget)
255 29808 : strain_increment.rotate(_total_rotation[_qp].transpose());
256 : else
257 8424 : strain_increment.rotate(_total_rotation_old[_qp].transpose());
258 : }
259 2584944 : else if (_decomposition_method == ComputeFiniteStrain::DecompMethod::HughesWinget)
260 : // rotate old stress to reference configuration
261 16696 : stress_old.rotate(_rotation_increment[_qp]);
262 :
263 : // copy because UMAT does not guarantee constness
264 26231760 : for (const auto i : make_range(9))
265 : {
266 23608584 : _aqDFGRD0[i] = myDFGRD0[i];
267 23608584 : _aqDFGRD1[i] = myDFGRD1[i];
268 23608584 : _aqDROT[i] = myDROT[i];
269 : }
270 :
271 : // Recover "old" state variables
272 10262580 : for (const auto i : make_range(_aqNSTATV))
273 7639404 : _aqSTATEV[i] = _state_var_old[_qp][i];
274 :
275 : // Recover "old" energy quantities
276 2623176 : _elastic_strain_energy[_qp] = _elastic_strain_energy_old[_qp];
277 2623176 : _plastic_dissipation[_qp] = _plastic_dissipation_old[_qp];
278 2623176 : _creep_dissipation[_qp] = _creep_dissipation_old[_qp];
279 :
280 : // Pass through updated stress, total strain, and strain increment arrays
281 : static const std::array<Real, 6> strain_factor{{1, 1, 1, 2, 2, 2}};
282 : // Account for difference in vector order convention: yz, xz, xy (MOOSE) vs xy, xz, yz
283 : // (commercial software)
284 : static const std::array<std::pair<unsigned int, unsigned int>, 6> component{
285 : {{0, 0}, {1, 1}, {2, 2}, {0, 1}, {0, 2}, {1, 2}}};
286 :
287 18362232 : for (const auto i : make_range(_aqNTENS))
288 : {
289 15739056 : const auto a = component[i].first;
290 15739056 : const auto b = component[i].second;
291 15739056 : _aqSTRESS[i] = stress_old(a, b);
292 15739056 : _aqSTRAN[i] = total_strain_old(a, b) * strain_factor[i];
293 15739056 : _aqDSTRAN[i] = strain_increment(a, b) * strain_factor[i];
294 : }
295 :
296 : // current coordinates
297 10492704 : for (const auto i : make_range(Moose::dim))
298 7869528 : _aqCOORDS[i] = _q_point[_qp](i);
299 :
300 : // zero out Jacobian contribution
301 97057512 : for (const auto i : make_range(_aqNTENS * _aqNTENS))
302 94434336 : _aqDDSDDE[i] = 0.0;
303 :
304 : // Set PNEWDT initially to a large value
305 2623176 : _aqPNEWDT = std::numeric_limits<Real>::max();
306 :
307 : // Temperature
308 2623176 : _aqTEMP = _temperature_old[_qp];
309 :
310 : // Temperature increment
311 2623176 : _aqDTEMP = _temperature[_qp] - _temperature_old[_qp];
312 :
313 3422978 : for (const auto i : make_range(_number_external_fields))
314 : {
315 : // External field at this step
316 799802 : _aqPREDEF[i] = (*_external_fields_old[i])[_qp];
317 :
318 : // External field increments
319 799802 : _aqDPRED[i] = (*_external_fields[i])[_qp] - (*_external_fields_old[i])[_qp];
320 : }
321 :
322 2729960 : for (const auto i : make_range(_number_external_properties))
323 : {
324 : // External property at this step
325 106784 : _aqPREDEF[i + _number_external_fields] = (*_external_properties_old[i])[_qp];
326 :
327 : // External property increments
328 106784 : _aqDPRED[i + _number_external_fields] =
329 106784 : (*_external_properties[i])[_qp] - (*_external_properties_old[i])[_qp];
330 : }
331 :
332 : // Layer number (not supported)
333 2623176 : _aqLAYER = -1;
334 :
335 : // Section point number within the layer (not supported)
336 2623176 : _aqKSPT = -1;
337 :
338 : // Increment number
339 2623176 : _aqKINC = _t_step;
340 2623176 : _aqKSTEP = 1;
341 :
342 : // integration point number
343 2623176 : _aqNPT = _qp + (_use_one_based_indexing ? 1 : 0);
344 :
345 : // Connection to extern statement
346 2623176 : _umat(_aqSTRESS.data(),
347 : _aqSTATEV.data(),
348 : _aqDDSDDE.data(),
349 : &_elastic_strain_energy[_qp],
350 : &_plastic_dissipation[_qp],
351 : &_creep_dissipation[_qp],
352 : &_aqRPL,
353 : _aqDDSDDT.data(),
354 : _aqDRPLDE.data(),
355 : &_aqDRPLDT,
356 : _aqSTRAN.data(),
357 : _aqDSTRAN.data(),
358 : _aqTIME.data(),
359 : &_aqDTIME,
360 : &_aqTEMP,
361 : &_aqDTEMP,
362 : _aqPREDEF.data(),
363 : _aqDPRED.data(),
364 2623176 : _aqCMNAME,
365 : &_aqNDI,
366 : &_aqNSHR,
367 : &_aqNTENS,
368 : &_aqNSTATV,
369 : _aqPROPS.data(),
370 : &_aqNPROPS,
371 : _aqCOORDS.data(),
372 : _aqDROT.data(),
373 : &_aqPNEWDT,
374 : &_aqCELENT,
375 : _aqDFGRD0.data(),
376 : _aqDFGRD1.data(),
377 : &_aqNOEL,
378 : &_aqNPT,
379 : &_aqLAYER,
380 : &_aqKSPT,
381 : &_aqKSTEP,
382 : &_aqKINC);
383 :
384 : // Update state variables
385 10262580 : for (int i = 0; i < _aqNSTATV; ++i)
386 7639404 : _state_var[_qp][i] = _aqSTATEV[i];
387 :
388 : // Here, we apply UMAT convention: Always multiply _dt by PNEWDT to determine the material time
389 : // step MOOSE time stepper will choose the most limiting of all material time step increments
390 : // provided
391 2623176 : _material_timestep[_qp] = _aqPNEWDT * _dt;
392 :
393 : // Get new stress tensor - UMAT should update stress
394 : // Account for difference in vector order convention: yz, xz, xy (MOOSE) vs xy, xz, yz
395 : // (commercial software)
396 2623176 : _stress[_qp] = RankTwoTensor(
397 : _aqSTRESS[0], _aqSTRESS[1], _aqSTRESS[2], _aqSTRESS[5], _aqSTRESS[4], _aqSTRESS[3]);
398 :
399 : // Build Jacobian matrix from UMAT's Voigt non-standard order to fourth order tensor.
400 : const unsigned int N = Moose::dim;
401 : const unsigned int ntens = N * (N + 1) / 2;
402 : const int nskip = N - 1;
403 :
404 10492704 : for (const auto i : make_range(N))
405 31478112 : for (const auto j : make_range(N))
406 94434336 : for (const auto k : make_range(N))
407 283303008 : for (const auto l : make_range(N))
408 : {
409 212477256 : if (i == j)
410 70825752 : _jacobian_mult[_qp](i, j, k, l) =
411 70825752 : k == l ? _aqDDSDDE[i * ntens + k] : _aqDDSDDE[i * ntens + k + nskip + l];
412 : else
413 : // i!=j
414 141651504 : _jacobian_mult[_qp](i, j, k, l) =
415 141651504 : k == l ? _aqDDSDDE[(nskip + i + j) * ntens + k]
416 94434336 : : _aqDDSDDE[(nskip + i + j) * ntens + k + nskip + l];
417 : }
418 :
419 : // check if we need to rotate from intermediate reference frame
420 2623176 : if (_use_orientation)
421 : {
422 : // rotate to current configuration
423 38232 : _stress[_qp].rotate(_total_rotation[_qp]);
424 38232 : _jacobian_mult[_qp].rotate(_total_rotation[_qp]);
425 : }
426 2584944 : else if (_decomposition_method != ComputeFiniteStrain::DecompMethod::HughesWinget)
427 2568248 : _stress[_qp].rotate(_rotation_increment[_qp]);
428 2623176 : }
|