https://mooseframework.inl.gov
Loading...
Searching...
No Matches
LagrangianObjectiveRate.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 "MooseEnum.h"
12#include "MooseError.h"
13
14#include <tuple>
15
16// ============================================================================
17// File-local helpers (formerly static members of the rate class hierarchy)
18// ============================================================================
19namespace
20{
23updateTensor(const RankTwoTensor & dQ)
24{
25 const auto I = RankTwoTensor::Identity();
26 usingTensorIndices(i, j, k, l);
27 return (1.0 + dQ.trace()) * I.times<i, k, j, l>(I) - dQ.times<i, k, j, l>(I) -
28 I.times<i, k, j, l>(dQ);
29}
30
32std::tuple<RankTwoTensor, RankFourTensor>
33advectStress(const RankTwoTensor & S0, const RankTwoTensor & dQ)
34{
35 const RankFourTensor J = updateTensor(dQ);
36 const RankFourTensor Jinv = J.inverse();
37 const RankTwoTensor S = Jinv * S0;
38 return {S, Jinv};
39}
40
47advectStressSolve(const RankTwoTensor & S0, const RankTwoTensor & dQ)
48{
49 const RankFourTensor J = updateTensor(dQ);
50 // Row/col index (a, b) -> (3a + b); A_{(ij)(kl)} = J_ijkl matches RankFourTensor::operator*.
51 Eigen::Matrix<Real, 9, 9> A;
52 Eigen::Matrix<Real, 9, 1> b;
53 for (const auto i : make_range(3u))
54 for (const auto j : make_range(3u))
55 {
56 b(3 * i + j) = S0(i, j);
57 for (const auto k : make_range(3u))
58 for (const auto l : make_range(3u))
59 A(3 * i + j, 3 * k + l) = J(i, j, k, l);
60 }
61 const Eigen::Matrix<Real, 9, 1> x = A.partialPivLu().solve(b);
63 for (const auto i : make_range(3u))
64 for (const auto j : make_range(3u))
65 S(i, j) = x(3 * i + j);
66 return S;
67}
68
71stressAdvectionDerivative(const RankTwoTensor & S)
72{
73 const auto I = RankTwoTensor::Identity();
74 usingTensorIndices(i, j, k, l);
75 return S.times<i, j, k, l>(I) - I.times<i, k, l, j>(S) - S.times<i, l, j, k>(I);
76}
77
80cauchyJacobian(const RankFourTensor & Jinv,
81 const RankFourTensor & small_jacobian,
82 const RankFourTensor & U)
83{
84 return Jinv * (small_jacobian - U);
85}
86
91rotationFromVorticity(const RankTwoTensor & W, RankFourTensor * dR_dW)
92{
93 // For a skew W in 3D, theta = sqrt((W : W) / 2). R = exp(W) via Rodrigues:
94 // R = I + f(theta) W + g(theta) W^2, f = sin theta / theta, g = (1 - cos theta) / theta^2.
95 // For theta -> 0, fall back to the Taylor expansion: R ~= I + W + W^2/2.
96 const Real theta2 = 0.5 * W.doubleContraction(W);
97 const Real theta = std::sqrt(theta2);
98 const auto I2 = RankTwoTensor::Identity();
99 const RankTwoTensor W2 = W * W;
100
101 Real f, g, df_dth, dg_dth;
102 const Real small_theta = 1.0e-7;
103 if (theta < small_theta)
104 {
105 f = 1.0 - theta2 / 6.0;
106 g = 0.5 - theta2 / 24.0;
107 df_dth = -theta / 3.0;
108 dg_dth = -theta / 12.0;
109 }
110 else
111 {
112 const Real s = std::sin(theta), c = std::cos(theta);
113 f = s / theta;
114 g = (1.0 - c) / theta2;
115 df_dth = (theta * c - s) / theta2;
116 dg_dth = (theta * s - 2.0 * (1.0 - c)) / (theta * theta2);
117 }
118 const RankTwoTensor R = I2 + f * W + g * W2;
119
120 if (!dR_dW)
121 return R;
122
123 // dR_ij/dW_mn = (df/dtheta * dtheta/dW_mn) W_ij + f * delta_im delta_jn
124 // + (dg/dtheta * dtheta/dW_mn) (W^2)_ij + g * (delta_im W_nj + W_im delta_jn)
125 // dtheta/dW_mn = W_mn / (2theta) (from d(theta^2)/dW = W).
126 usingTensorIndices(i_, j_, m_, n_);
127 const RankFourTensor d_W2_dW =
128 I2.template times<i_, m_, n_, j_>(W) + W.template times<i_, m_, j_, n_>(I2);
129 *dR_dW = f * RankFourTensor::IdentityFour() + g * d_W2_dW;
130 if (theta >= small_theta)
131 {
132 const Real inv_2theta = 1.0 / (2.0 * theta);
133 // (df/dtheta * W_mn / (2theta)) * W_ij -> (df/dtheta * inv_2theta) * W (x) W (output indices
134 // ij,mn).
135 *dR_dW += (df_dth * inv_2theta) * W.template times<i_, j_, m_, n_>(W);
136 *dR_dW += (dg_dth * inv_2theta) * W2.template times<i_, j_, m_, n_>(W);
137 }
138 // (For theta < small_theta the dtheta-dependent contributions are O(theta) -> 0; the
139 // identity + W^2 parts above cover the small-angle limit cleanly.)
140
141 return R;
142}
143}
144
146{
147
148// ============================================================================
149// Truesdell
150// ============================================================================
151Outputs
152truesdell(const Inputs & in, bool need_jacobian)
153{
154 Outputs out;
155 if (!need_jacobian)
156 {
157 out.cauchy_stress = advectStressSolve(in.cauchy_stress_old + in.dS, in.dL);
158 return out;
159 }
160
161 auto [S, Jinv] = advectStress(in.cauchy_stress_old + in.dS, in.dL);
162 out.cauchy_stress = S;
163 out.dcauchy_stress_d_eigenstrain = -Jinv * in.small_jacobian;
164
165 const RankFourTensor U = stressAdvectionDerivative(S);
166 out.cauchy_jacobian = cauchyJacobian(Jinv, in.small_jacobian, U);
167 return out;
168}
169
170// ============================================================================
171// Jaumann
172// ============================================================================
173Outputs
174jaumann(const Inputs & in, bool need_jacobian)
175{
176 Outputs out;
177 if (!need_jacobian)
178 {
179 out.cauchy_stress = advectStressSolve(in.cauchy_stress_old + in.dS, in.dW);
180 return out;
181 }
182
183 auto [S, Jinv] = advectStress(in.cauchy_stress_old + in.dS, in.dW);
184 out.cauchy_stress = S;
185 out.dcauchy_stress_d_eigenstrain = -Jinv * in.small_jacobian;
186
187 const RankFourTensor d_dW_d_dL = in.d_dW_d_F * in.d_dL_d_F.inverse();
188 const RankFourTensor U = stressAdvectionDerivative(S) * d_dW_d_dL;
189 out.cauchy_jacobian = cauchyJacobian(Jinv, in.small_jacobian, U);
190 return out;
191}
192
193// ============================================================================
194// Green-Naghdi
195// ============================================================================
196Outputs
197greenNaghdi(const Inputs & in, bool need_jacobian)
198{
199 usingTensorIndices(i, j, k, l, m);
200
202 const RankTwoTensor dR = in.rotation * in.rotation_old.transpose() - I;
203 const RankTwoTensor dO = dR * in.inv_df;
204
205 Outputs out;
206 if (!need_jacobian)
207 {
208 out.cauchy_stress = advectStressSolve(in.cauchy_stress_old + in.dS, dO);
209 return out;
210 }
211
212 auto [S, Jinv] = advectStress(in.cauchy_stress_old + in.dS, dO);
213 out.cauchy_stress = S;
214 out.dcauchy_stress_d_eigenstrain = -Jinv * in.small_jacobian;
215
216 const RankFourTensor & d_R_d_F = in.d_rotation_d_F;
217 const RankFourTensor d_F_d_dL = in.d_dL_d_F.inverse();
218 const RankTwoTensor T = in.rotation_old.transpose() * in.inv_df;
219
220 const RankFourTensor d_invdf_d_F = -in.inv_df.times<i, k, l, j>(in.inv_def_grad);
221 const RankFourTensor d_invdf_d_dL = d_invdf_d_F * d_F_d_dL;
222 const RankFourTensor d_dO_d_invdf = dR.times<i, k, j, l>(I);
223
224 const RankFourTensor d_dO_d_dL =
225 T.times<m, j, i, m, k, l>(d_R_d_F * d_F_d_dL) + d_dO_d_invdf * d_invdf_d_dL;
226 const RankFourTensor U = stressAdvectionDerivative(S) * d_dO_d_dL;
227 out.cauchy_jacobian = cauchyJacobian(Jinv, in.small_jacobian, U);
228 return out;
229}
230
231// ============================================================================
232// Rashid
233// ============================================================================
234Outputs
235rashid(const Inputs & in, bool need_jacobian)
236{
237 usingTensorIndices(i_, j_, k_, l_, m_, n_);
238
239 // r_hat = exp(Deltaw). Skip its R4 derivative when we don't need the Jacobian.
240 RankFourTensor d_rhat_d_dW;
241 const RankTwoTensor rhat = rotationFromVorticity(in.dW, need_jacobian ? &d_rhat_d_dW : nullptr);
242 const RankTwoTensor rhatT = rhat.transpose();
243
244 // sigma_{n+1} = r_hat (sigma_n + Deltasigma) r_hat^T (eq. 22)
245 const RankTwoTensor S_inner = in.cauchy_stress_old + in.dS;
246 Outputs out;
247 out.cauchy_stress = rhat * S_inner * rhatT;
248
249 if (!need_jacobian)
250 return out;
251
252 // J^{-1}_{ijkl} = r_hat_ik r_hat_jl (eq. 24), the rank-4 (r_hat (x) r_hat) sandwich. Used for
253 // the eigenstrain Jacobian and as the "outer" operator for the constitutive piece. We name the
254 // dummy "middle pair" k, l here (output indices i, j, k, l) -- when chained via
255 // RankFourTensor::operator*, the k, l contract against small_jacobian's first pair, as desired.
256 const RankFourTensor J_inv = rhat.times<i_, k_, j_, l_>(rhat);
257
258 // dsigma/d(eigenstrain) = -J^{-1} : small_jacobian.
259 // (mechanical_strain = total_strain - eigenstrain, hence the minus.)
260 out.dcauchy_stress_d_eigenstrain = -(J_inv * in.small_jacobian);
261
262 // Chain rule pieces for dsigma/d(dL):
263 // d(Deltaw)/d(dL) = d(dW)/dF * inverse(d(dL)/dF)
264 // d(Deltad)/d(dL) = I^(4) - d(Deltaw)/d(dL) (Deltad + Deltaw = dL by construction).
265 // d(r_hat)/d(dL) = d(r_hat)/d(Deltaw) * d(Deltaw)/d(dL).
266 const RankFourTensor d_F_d_dL = in.d_dL_d_F.inverse();
267 const RankFourTensor d_dW_d_dL = in.d_dW_d_F * d_F_d_dL;
268 const RankFourTensor d_dD_d_dL = RankFourTensor::IdentityFour() - d_dW_d_dL;
269 const RankFourTensor d_rhat_d_dL = d_rhat_d_dW * d_dW_d_dL;
270
271 // Rotation pieces of dsigma/d(dL):
272 // T1_{ijkl} = (d_rhat_d_dL)_{imkl} * (S r_hat^T)_{mj}
273 // T2_{ijkl} = (r_hat S)_{im} * (d_rhat_d_dL)_{jmkl}
274 const RankTwoTensor SR = S_inner * rhatT; // S r_hat^T (shape (m, j))
275 const RankTwoTensor RS = rhat * S_inner; // r_hat S (shape (i, m))
276 const RankFourTensor T1 = SR.times<m_, j_, i_, m_, k_, l_>(d_rhat_d_dL);
277 const RankFourTensor T2 = RS.times<i_, m_, j_, m_, k_, l_>(d_rhat_d_dL);
278
279 // Constitutive piece: J^{-1} : (small_jacobian * d(Deltad)/d(dL)).
280 const RankFourTensor T3 = J_inv * (in.small_jacobian * d_dD_d_dL);
281
282 out.cauchy_jacobian = T1 + T2 + T3;
283 return out;
284}
285
286// ============================================================================
287// Dispatch
288// ============================================================================
289Outputs
290compute(const MooseEnum & rate, const Inputs & in, bool need_jacobian)
291{
292 const std::string s = rate;
293 if (s == "truesdell")
294 return truesdell(in, need_jacobian);
295 if (s == "jaumann")
296 return jaumann(in, need_jacobian);
297 if (s == "green_naghdi")
298 return greenNaghdi(in, need_jacobian);
299 if (s == "rashid")
300 return rashid(in, need_jacobian);
301 mooseError("Unknown objective_rate value: ", s);
302}
303}
Real f(Real x)
Test function for Brents method.
const std::vector< double > x
const double R
const double T
void mooseError(Args &&... args)
for(PetscInt i=0;i< nvars;++i)
RankFourTensorTempl< T > inverse() const
static RankFourTensorTempl< T > IdentityFour()
RankFourTensorTempl< T > times(const RankTwoTensorTempl< T > &b) const
T doubleContraction(const RankTwoTensorTempl< T > &a) const
RankTwoTensorTempl< T > transpose() const
static RankTwoTensorTempl Identity()
Objective-stress-rate updates as stateless free functions.
Outputs jaumann(const Inputs &in, bool need_jacobian)
sigma_{n+1} = J(dW)^{-1} (sigma_n + Deltasigma). Jaumann rate.
Outputs greenNaghdi(const Inputs &in, bool need_jacobian)
sigma_{n+1} = J(dO)^{-1} (sigma_n + Deltasigma), with dO built from the polar rotation increment.
Outputs truesdell(const Inputs &in, bool need_jacobian)
sigma_{n+1} = J(dL)^{-1} (sigma_n + Deltasigma). Truesdell rate.
Outputs compute(const MooseEnum &rate, const Inputs &in, bool need_jacobian)
Dispatch to the rate selected by the objective_rate enum (truesdell / jaumann / green_naghdi / rashid...
Outputs rashid(const Inputs &in, bool need_jacobian)
sigma_{n+1} = r_hat (sigma_n + Deltasigma) r_hat^T, r_hat = exp(Deltaw).
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)
Per-qp quantities a rate reads.
RankFourTensor d_rotation_d_F
Green-Naghdi only: dR/dF.
RankFourTensor d_dL_d_F
d(dL)/dF and d(dW)/dF from the strain calculator.
RankTwoTensor dL
Spatial velocity gradient increment dL and its vorticity (skew) part dW.
RankTwoTensor rotation
Green-Naghdi only: polar-decomposition rotation R (n+1 and n), the inverse incremental deformation gr...
RankTwoTensor cauchy_stress_old
Cumulative Cauchy stress at step n.
RankTwoTensor dS
Constitutive small-stress increment (_small_stress - _small_stress_old).
RankFourTensor small_jacobian
Small-strain algorithmic tangent.
Per-qp outputs a rate produces.