Line data Source code
1 : // Copyright 2024, 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/SR2Invariant.h"
26 : #include "neml2/tensors/Scalar.h"
27 : #include "neml2/tensors/SR2.h"
28 : #include "neml2/tensors/SSR4.h"
29 : #include "neml2/base/EnumSelection.h"
30 :
31 : namespace neml2
32 : {
33 : register_NEML2_object(SR2Invariant);
34 :
35 : OptionSet
36 2 : SR2Invariant::expected_options()
37 : {
38 2 : OptionSet options = Model::expected_options();
39 2 : options.doc() = "Calculate the invariant of a symmetric second order tensor (of type SR2).";
40 :
41 4 : options.set<bool>("define_second_derivatives") = true;
42 :
43 4 : options.set_input("tensor");
44 4 : options.set("tensor").doc() = "SR2 which is used to calculate the invariant of";
45 :
46 4 : options.set_output("invariant");
47 4 : options.set("invariant").doc() = "Invariant";
48 :
49 : EnumSelection type_selection({"I1", "I2", "VONMISES", "EFFECTIVE_STRAIN", "INVALID"},
50 : {static_cast<int>(SR2Invariant::IType::I1),
51 : static_cast<int>(SR2Invariant::IType::I2),
52 : static_cast<int>(SR2Invariant::IType::VONMISES),
53 : static_cast<int>(SR2Invariant::IType::EFFECTIVE_STRAIN),
54 : static_cast<int>(SR2Invariant::IType::INVALID)},
55 12 : "INVALID");
56 2 : options.set<EnumSelection>("invariant_type") = type_selection;
57 4 : options.set("invariant_type").doc() =
58 6 : "Type of invariant. Options are: " + type_selection.candidates_str();
59 :
60 4 : return options;
61 2 : }
62 :
63 21 : SR2Invariant::SR2Invariant(const OptionSet & options)
64 : : Model(options),
65 21 : _type(options.get<EnumSelection>("invariant_type").as<IType>()),
66 21 : _A(declare_input_variable<SR2>("tensor")),
67 42 : _invariant(declare_output_variable<Scalar>("invariant"))
68 : {
69 21 : }
70 :
71 : void
72 52 : SR2Invariant::set_value(bool out, bool dout_din, bool d2out_din2)
73 : {
74 52 : auto A = SR2(_A);
75 :
76 52 : if (_type == IType::I1)
77 : {
78 8 : if (out)
79 6 : _invariant = A.tr();
80 :
81 8 : if (!_A.is_dependent())
82 0 : return;
83 :
84 8 : if (dout_din)
85 4 : _invariant.d(_A) = SR2::identity(_A.options());
86 :
87 : if (d2out_din2)
88 : {
89 : // zero
90 : }
91 : }
92 44 : else if (_type == IType::I2)
93 : {
94 3 : if (out)
95 1 : _invariant = (A.tr() * A.tr() - A.inner(A)) / 2.0;
96 :
97 3 : if (!_A.is_dependent())
98 0 : return;
99 :
100 3 : if (dout_din || d2out_din2)
101 : {
102 2 : auto I2 = SR2::identity(_A.options());
103 :
104 2 : if (dout_din)
105 1 : _invariant.d(_A) = A.tr() * I2 - A;
106 :
107 2 : if (d2out_din2)
108 : {
109 1 : auto I2xI2 = SSR4::identity(_A.options());
110 1 : auto I4sym = SSR4::identity_sym(_A.options());
111 1 : _invariant.d(_A, _A) = I2xI2 - I4sym;
112 1 : }
113 2 : }
114 : }
115 41 : else if (_type == IType::VONMISES)
116 : {
117 38 : const auto eps = machine_precision(A.scalar_type());
118 38 : auto S = A.dev();
119 38 : Scalar vm = std::sqrt(3.0 / 2.0) * S.norm(eps);
120 :
121 38 : if (out)
122 36 : _invariant = vm;
123 :
124 38 : if (!_A.is_dependent())
125 0 : return;
126 :
127 38 : if (dout_din || d2out_din2)
128 : {
129 27 : auto dvm_dA = 3.0 / 2.0 * S / vm;
130 :
131 27 : if (dout_din)
132 26 : _invariant.d(_A) = dvm_dA;
133 :
134 27 : if (d2out_din2)
135 : {
136 12 : auto I = SSR4::identity_sym(_A.options());
137 12 : auto J = SSR4::identity_dev(_A.options());
138 12 : _invariant.d(_A, _A) = 3.0 / 2.0 * (I - 2.0 / 3.0 * dvm_dA.outer(dvm_dA)) * J / vm;
139 12 : }
140 27 : }
141 38 : }
142 3 : else if (_type == IType::EFFECTIVE_STRAIN)
143 : {
144 3 : const auto eps = machine_precision(A.scalar_type());
145 3 : Scalar r = std::sqrt(2.0 / 3.0) * A.norm(eps);
146 :
147 3 : if (out)
148 1 : _invariant = r;
149 :
150 3 : if (!_A.is_dependent())
151 0 : return;
152 :
153 3 : if (dout_din || d2out_din2)
154 : {
155 2 : auto d = 2.0 / 3.0 * A / r;
156 :
157 2 : if (dout_din)
158 1 : _invariant.d(_A) = 2.0 / 3.0 * A / r;
159 :
160 2 : if (d2out_din2)
161 2 : _invariant.d(_A, _A) =
162 3 : 2.0 / 3.0 * (SSR4::identity_sym(_A.options()) - 3.0 / 2.0 * d.outer(d)) / r;
163 2 : }
164 3 : }
165 : else
166 0 : throw NEMLException("Unsupported invariant type: " +
167 0 : std::string(input_options().get<EnumSelection>("invariant_type")));
168 52 : }
169 : } // namespace neml2
|