NEML2 1.4.0
Loading...
Searching...
No Matches
GTNYieldFunction.cxx
1// Copyright 2023, UChicago Argonne, LLC
2// All Rights Reserved
3// Software Name: NEML2 -- the New Engineering material Model Library, version 2
4// By: Argonne National Laboratory
5// OPEN SOURCE LICENSE (MIT)
6//
7// Permission is hereby granted, free of charge, to any person obtaining a copy
8// of this software and associated documentation files (the "Software"), to deal
9// in the Software without restriction, including without limitation the rights
10// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11// copies of the Software, and to permit persons to whom the Software is
12// furnished to do so, subject to the following conditions:
13//
14// The above copyright notice and this permission notice shall be included in
15// all copies or substantial portions of the Software.
16//
17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23// THE SOFTWARE.
24
25#include "neml2/models/solid_mechanics/GTNYieldFunction.h"
26
27namespace neml2
28{
29register_NEML2_object(GTNYieldFunction);
30
33{
35 options.doc() =
36 "Gurson-Tvergaard-Needleman yield function for poroplasticity. The yield function is defined "
37 "as \\f$ f = \\left( \\frac{\\bar{\\sigma}}{\\sigma_y + k} \\right)^2 + 2 q_1 \\phi \\cosh "
38 "\\left( \\frac{1}{2} q_2 \\frac{3\\sigma_h-\\sigma_s}{\\sigma_y + k} \\right) - \\left( q_3 "
39 "\\phi^2 + 1 \\right) \\f$, where \\f$ \\bar{\\sigma} \\f$ is the von Mises stress, \\f$ "
40 "\\sigma_y \\f$ is the yield stress, \\f$ k \\f$ is isotropic hardening, \\f$ \\phi \\f$ is "
41 "the porosity, \\f$ \\sigma_h \\f$ is the hydrostatic stress, and \\f$ \\sigma_s \\f$ is the "
42 "void growth back stress (sintering stress). \\f$ q_1 \\f$, \\f$ q_2 \\f$, and \\f$ q_3 \\f$ "
43 "are parameters controlling the yield mechanisms.";
44
45 options.set<CrossRef<Scalar>>("yield_stress");
46 options.set("yield_stress").doc() = "Yield stress";
47
48 options.set<CrossRef<Scalar>>("q1");
49 options.set("q1").doc() =
50 "Parameter controlling the balance/competition between plastic flow and void evolution.";
51
52 options.set<CrossRef<Scalar>>("q2");
53 options.set("q2").doc() = "Void evolution rate";
54
55 options.set<CrossRef<Scalar>>("q3");
56 options.set("q3").doc() = "Pore pressure";
57
58 options.set<VariableName>("flow_invariant") = VariableName("state", "internal", "se");
59 options.set("flow_invariant").doc() = "Effective stress driving plastic flow";
60
61 options.set<VariableName>("poro_invariant") = VariableName("state", "internal", "sp");
62 options.set("poro_invariant").doc() = "Effective stress driving porous flow";
63
64 options.set<VariableName>("isotropic_hardening");
65 options.set("isotropic_hardening").doc() = "Isotropic hardening";
66
67 options.set<VariableName>("void_fraction") = VariableName("state", "internal", "f");
68 options.set("void_fraction").doc() = "Void fraction (porosity)";
69
70 options.set<VariableName>("yield_function") = VariableName("state", "internal", "fp");
71 options.set("yield_function").doc() = "Yield function";
72
73 return options;
74}
75
77 : Model(options),
78 _f(declare_output_variable<Scalar>("yield_function")),
79 _se(declare_input_variable<Scalar>("flow_invariant")),
80 _sp(declare_input_variable<Scalar>("poro_invariant")),
81 _phi(declare_input_variable<Scalar>("void_fraction")),
82 _h(options.get<VariableName>("isotropic_hardening").empty()
83 ? nullptr
84 : &declare_input_variable<Scalar>("isotropic_hardening")),
85 _s0(declare_parameter<Scalar>("sy", "yield_stress")),
86 _q1(declare_parameter<Scalar>("q1", "q1")),
87 _q2(declare_parameter<Scalar>("q2", "q2")),
88 _q3(declare_parameter<Scalar>("q3", "q3"))
89{
90}
91
92void
94{
95 // Flow stress (depending on whether isotropic hardening is provided)
96 const auto sf = _h ? _s0 + (*_h) : _s0;
97
98 if (out)
99 _f = math::pow(_se / sf, 2.0) + 2 * _q1 * _phi * math::cosh(_q2 / 2.0 * _sp / sf) -
100 (1.0 + _q3 * math::pow(Scalar(_phi), 2.0));
101
102 if (dout_din)
103 {
104 _f.d(_se) = 2.0 * _se / math::pow(sf, 2.0);
105 _f.d(_sp) = _q1 * _phi * _q2 / sf * math::sinh(_q2 / 2.0 * _sp / sf);
106 _f.d(_phi) = 2.0 * _q1 * math::cosh(_q2 / 2.0 * _sp / sf) - 2.0 * _q3 * _phi;
107 if (_h)
108 _f.d(*_h) = -2 * math::pow(Scalar(_se), 2.0) / math::pow(sf, 3.0) -
109 _q1 * _phi * _q2 * _sp / math::pow(sf, 2.0) * math::sinh(_q2 / 2.0 * _sp / sf);
110
111 // Handle the case of nonlinear parameters
112 if (const auto sy = nl_param("sy"))
113 _f.d(*sy) = -2 * math::pow(Scalar(_se), 2.0) / math::pow(sf, 3.0) -
114 _q1 * _phi * _q2 * _sp / math::pow(sf, 2.0) * math::sinh(_q2 / 2.0 * _sp / sf);
115
116 if (const auto q1 = nl_param("q1"))
117 _f.d(*q1) = 2.0 * _phi * math::cosh(_q2 / 2.0 * _sp / sf);
118
119 if (const auto q2 = nl_param("q2"))
120 _f.d(*q2) = _q1 * _phi * _sp / sf * math::sinh(_q2 / 2.0 * _sp / sf);
121
122 if (const auto q3 = nl_param("q3"))
123 _f.d(*q3) = -math::pow(Scalar(_phi), 2.0);
124 }
125
126 if (d2out_din2)
127 {
128 const auto sy = nl_param("sy");
129 const auto q1 = nl_param("q1");
130 const auto q2 = nl_param("q2");
131 const auto q3 = nl_param("q3");
132
134 //
135 // The GTN yield function can be expressed as
136 //
137 // f(se, sp, phi, h; sy, q1, q2, q3)
138 //
139 // - Arguments before the semicolon are variables
140 // - Arguments after the semicolon are (nonlinear) parameters
141 // - Derivatives w.r.t. the first three arguments se, sp, and phi are mandatory
142 // - Derivatives w.r.t. the rest of the arguments are optional
143 //
145
147 //
148 // The second derivative is nothing but a big matrix. We will fill out the matrix row by row, in
149 // the order of the arguments listed above.
150 //
151 // Rows will separated by big fences like this.
152 //
154
156 //
157 // f(se, sp, phi, h; sy, q1, q2, q3)
158 //
159 // se: Flow invariant
160 //
162 _f.d(_se, _se) = 2.0 / math::pow(sf, 2.0);
163
164 if (_h)
165 _f.d(_se, *_h) = -4.0 * _se / math::pow(sf, 3.0);
166
167 if (sy)
168 _f.d(_se, *sy) = -4.0 * _se / math::pow(sf, 3.0);
169
171 //
172 // f(se, sp, phi, h; sy, q1, q2, q3)
173 //
174 // sp: Poro invariant
175 //
177 _f.d(_sp, _sp) = _phi * _q1 * math::pow(_q2, 2.0) / (2.0 * math::pow(sf, 2.0)) *
178 math::cosh(_q2 / 2.0 * _sp / sf);
179
180 _f.d(_sp, _phi) = _q1 * _q2 * math::sinh(_q2 / 2.0 * _sp / sf) / sf;
181
182 if (_h)
183 _f.d(_sp, *_h) = -_phi * _q1 * _q2 *
184 (_q2 * _sp * math::cosh(_q2 / 2.0 * _sp / sf) +
185 2 * sf * math::sinh(_q2 / 2.0 * _sp / sf)) /
186 (2 * math::pow(sf, 3.0));
187 if (sy)
188 _f.d(_sp, *sy) = -_phi * _q1 * _q2 *
189 (_q2 * _sp * math::cosh(_q2 / 2.0 * _sp / sf) +
190 2 * sf * math::sinh(_q2 / 2.0 * _sp / sf)) /
191 (2 * math::pow(sf, 3.0));
192
193 if (q1)
194 _f.d(_sp, *q1) = _phi * _q2 * math::sinh(_q2 / 2.0 * _sp / sf) / sf;
195
196 if (q2)
197 _f.d(_sp, *q2) = _phi * _q1 *
198 (_q2 * _sp * math::cosh(_q2 / 2.0 * _sp / sf) +
199 2.0 * sf * math::sinh(_q2 / 2.0 * _sp / sf)) /
200 (2.0 * math::pow(sf, 2.0));
201
203 //
204 // f(se, sp, phi, h; sy, q1, q2, q3)
205 //
206 // phi: Void fraction
207 //
209 _f.d(_phi, _sp) = _q1 * _q2 * math::sinh(_q2 / 2.0 * _sp / sf) / sf;
210
211 _f.d(_phi, _phi) = -2.0 * _q3;
212
213 if (_h)
214 _f.d(_phi, *_h) = -_q1 * _q2 * _sp * math::sinh(_q2 / 2.0 * _sp / sf) / math::pow(sf, 2.0);
215
216 if (sy)
217 _f.d(_phi, *sy) = -_q1 * _q2 * _sp * math::sinh(_q2 / 2.0 * _sp / sf) / math::pow(sf, 2.0);
218
219 if (q1)
220 _f.d(_phi, *q1) = 2 * math::cosh(_q2 / 2.0 * _sp / sf);
221
222 if (q2)
223 _f.d(_phi, *q2) = _q1 * _sp * math::sinh(_q2 / 2.0 * _sp / sf) / sf;
224
225 if (q3)
226 _f.d(_phi, *q3) = -2.0 * _phi;
227
229 //
230 // f(se, sp, phi, h; sy, q1, q2, q3)
231 //
232 // h: (Optional) isotropic hardening
233 //
235 if (_h)
236 {
237 _f.d(*_h, _se) = -4.0 * _se / math::pow(sf, 3.0);
238
239 _f.d(*_h, _sp) = -_phi * _q1 * _q2 *
240 (_q2 * _sp * math::cosh(_q2 / 2.0 * _sp / sf) +
241 2.0 * sf * math::sinh(_q2 / 2.0 * _sp / sf)) /
242 (2.0 * math::pow(sf, 3.0));
243
244 _f.d(*_h, _phi) = -_q1 * _q2 * _sp * math::sinh(_q2 / 2.0 * _sp / sf) / math::pow(sf, 2.0);
245
246 _f.d(*_h, *_h) =
247 (12 * math::pow(Scalar(_se), 2.0) + _phi * _q1 * _q2 * _sp *
248 (_q2 * _sp * math::cosh(_q2 / 2.0 * _sp / sf) +
249 4.0 * sf * math::sinh(_q2 / 2.0 * _sp / sf))) /
250 (2 * math::pow(sf, 4.0));
251
252 if (sy)
253 _f.d(*_h, *sy) =
254 (12 * math::pow(Scalar(_se), 2.0) + _phi * _q1 * _q2 * _sp *
255 (_q2 * _sp * math::cosh(_q2 / 2.0 * _sp / sf) +
256 4.0 * sf * math::sinh(_q2 / 2.0 * _sp / sf))) /
257 (2 * math::pow(sf, 4.0));
258
259 if (q1)
260 _f.d(*_h, *q1) = -_phi * _q2 * _sp * math::sinh(_q2 / 2.0 * _sp / sf) / math::pow(sf, 2.0);
261
262 if (q2)
263 _f.d(*_h, *q2) = -_phi * _q1 * _sp *
264 (_q2 * _sp * math::cosh(_q2 / 2.0 * _sp / sf) +
265 2.0 * sf * math::sinh(_q2 / 2.0 * _sp / sf)) /
266 (2 * math::pow(sf, 3.0));
267 }
268
270 //
271 // f(se, sp, phi, h; sy, q1, q2, q3)
272 //
273 // sy: (Optionally nonlinear) yield stress
274 //
276 if (sy)
277 {
278 _f.d(*sy, _se) = -4.0 * _se / math::pow(sf, 3.0);
279
280 _f.d(*sy, _phi) = -_q1 * _q2 * _sp * math::sinh(_q2 / 2.0 * _sp / sf) / math::pow(sf, 2.0);
281
282 if (_h)
283 _f.d(*sy, *_h) =
284 (12 * math::pow(Scalar(_se), 2.0) + _phi * _q1 * _q2 * _sp *
285 (_q2 * _sp * math::cosh(_q2 / 2.0 * _sp / sf) +
286 4.0 * sf * math::sinh(_q2 / 2.0 * _sp / sf))) /
287 (2 * math::pow(sf, 4.0));
288
289 _f.d(*sy, *sy) =
290 (12 * math::pow(Scalar(_se), 2.0) + _phi * _q1 * _q2 * _sp *
291 (_q2 * _sp * math::cosh(_q2 / 2.0 * _sp / sf) +
292 4.0 * sf * math::sinh(_q2 / 2.0 * _sp / sf))) /
293 (2 * math::pow(sf, 4.0));
294
295 if (q1)
296 _f.d(*sy, *q1) = -_phi * _q2 * _sp * math::sinh(_q2 / 2.0 * _sp / sf) / math::pow(sf, 2.0);
297
298 if (q2)
299 _f.d(*sy, *q2) = -_phi * _q1 * _sp *
300 (_q2 * _sp * math::cosh(_q2 / 2.0 * _sp / sf) +
301 2.0 * sf * math::sinh(_q2 / 2.0 * _sp / sf)) /
302 (2 * math::pow(sf, 3.0));
303 }
304
306 //
307 // f(se, sp, phi, h; sy, q1, q2, q3)
308 //
309 // q1: (Optionally nonlinear) GTN parameter q1
310 //
312 if (q1)
313 {
314 _f.d(*q1, _sp) = _phi * _q2 * math::sinh(_q2 / 2.0 * _sp / sf) / sf;
315
316 _f.d(*q1, _phi) = 2.0 * math::cosh(_q2 / 2.0 * _sp / sf);
317
318 if (_h)
319 _f.d(*q1, *_h) = -_phi * _q2 * _sp * math::sinh(_q2 / 2.0 * _sp / sf) / math::pow(sf, 2.0);
320
321 if (sy)
322 _f.d(*q1, *sy) = -_phi * _q2 * _sp * math::sinh(_q2 / 2.0 * _sp / sf) / math::pow(sf, 2.0);
323
324 if (q2)
325 _f.d(*q1, *q2) = _phi * _sp * math::sinh(_q2 / 2.0 * _sp / sf) / sf;
326 }
327
329 //
330 // f(se, sp, phi, h; sy, q1, q2, q3)
331 //
332 // q2: (Optionally nonlinear) GTN parameter q2
333 //
335 if (q2)
336 {
337 _f.d(*q2, _sp) = _phi * _q1 *
338 (_q2 * _sp * math::cosh(_q2 / 2.0 * _sp / sf) +
339 2 * sf * math::sinh(_q2 / 2.0 * _sp / sf)) /
340 (2 * math::pow(sf, 2.0));
341
342 _f.d(*q2, _phi) = _q1 * _sp * math::sinh(_q2 / 2.0 * _sp / sf) / sf;
343
344 if (_h)
345 _f.d(*q2, *_h) = -_phi * _q1 * _sp *
346 (_q2 * _sp * math::cosh(_q2 / 2.0 * _sp / sf) +
347 2 * sf * math::sinh(_q2 / 2.0 * _sp / sf)) /
348 (2 * math::pow(sf, 3.0));
349
350 if (sy)
351 _f.d(*q2, *sy) = -_phi * _q1 * _sp *
352 (_q2 * _sp * math::cosh(_q2 / 2.0 * _sp / sf) +
353 2 * sf * math::sinh(_q2 / 2.0 * _sp / sf)) /
354 (2 * math::pow(sf, 3.0));
355
356 if (q1)
357 _f.d(*q2, *q1) = _phi * _sp * math::sinh(_q2 / 2.0 * _sp / sf) / sf;
358
359 _f.d(*q2, *q2) = _phi * _q1 * math::pow(Scalar(_sp), 2.0) * math::cosh(_q2 / 2.0 * _sp / sf) /
360 (2 * math::pow(sf, 2.0));
361 }
362
364 //
365 // f(se, sp, phi, h; sy, q1, q2, q3)
366 //
367 // q3: (Optionally nonlinear) GTN parameter q3
368 //
370 if (q3)
371 {
372 _f.d(*q3, _phi) = -2.0 * _phi;
373 }
374 }
375}
376} // namespace neml2
The wrapper (decorator) for cross-referencing unresolved values at parse time.
Definition CrossRef.h:52
Definition GTNYieldFunction.h:32
Variable< Scalar > & _f
Yield function.
Definition GTNYieldFunction.h:43
const Scalar & _q1
GTN q1 parameter.
Definition GTNYieldFunction.h:61
const Variable< Scalar > * _h
Isotropic hardening.
Definition GTNYieldFunction.h:55
const Variable< Scalar > & _se
Flow invariant.
Definition GTNYieldFunction.h:46
const Scalar & _s0
Yield stress.
Definition GTNYieldFunction.h:58
const Scalar & _q2
GTN q2 parameter.
Definition GTNYieldFunction.h:64
const Scalar & _q3
GTN q3 parameter.
Definition GTNYieldFunction.h:67
static OptionSet expected_options()
Definition GTNYieldFunction.cxx:32
const Variable< Scalar > & _phi
Void fraction.
Definition GTNYieldFunction.h:52
const Variable< Scalar > & _sp
Poro invariant.
Definition GTNYieldFunction.h:49
void set_value(bool out, bool dout_din, bool d2out_din2) override
The value of the yield function.
Definition GTNYieldFunction.cxx:93
GTNYieldFunction(const OptionSet &options)
Definition GTNYieldFunction.cxx:76
The accessor containing all the information needed to access an item in a LabeledAxis.
Definition LabeledAxisAccessor.h:44
The base class for all constitutive models.
Definition Model.h:53
const torch::TensorOptions & options() const
This model's tensor options.
Definition Model.h:116
static OptionSet expected_options()
Definition Model.cxx:33
A custom map-like data structure. The keys are strings, and the values can be nonhomogeneously typed.
Definition OptionSet.h:59
const VariableBase * nl_param(const std::string &) const
Query the existence of a nonlinear parameter.
Definition ParameterStore.cxx:56
The (logical) scalar.
Definition Scalar.h:38
Derivative d(const VariableBase &x)
Create a wrapper representing the derivative dy/dx.
Definition Variable.cxx:102
Derived sinh(const Derived &a)
Definition BatchTensorBase.h:378
Derived cosh(const Derived &a)
Definition BatchTensorBase.h:369
Derived pow(const Derived &a, const Real &n)
Definition BatchTensorBase.h:332
Definition CrossRef.cxx:32
LabeledAxisAccessor VariableName
Definition Variable.h:35