Serac  0.1
Serac is an implicit thermal strucural mechanics simulation code.
solid_material.hpp
Go to the documentation of this file.
1 // Copyright (c) Lawrence Livermore National Security, LLC and
2 // other Serac Project Developers. See the top-level LICENSE file for
3 // details.
4 //
5 // SPDX-License-Identifier: (BSD-3-Clause)
6 
13 #pragma once
14 
16 
18 namespace serac::solid_mechanics {
19 
25  using State = Empty;
26 
38  template <typename T, int dim>
39  SERAC_HOST_DEVICE auto operator()(State& /* state */, const tensor<T, dim, dim>& du_dX) const
40  {
41  auto I = Identity<dim>();
42  auto lambda = K - (2.0 / 3.0) * G;
43  auto epsilon = 0.5 * (transpose(du_dX) + du_dX);
44  return lambda * tr(epsilon) * I + 2.0 * G * epsilon;
45  }
46 
47  double density;
48  double K;
49  double G;
50 };
51 
55 template <typename T, int dim>
56 auto greenStrain(const tensor<T, dim, dim>& grad_u)
57 {
58  return 0.5 * (grad_u + transpose(grad_u) + dot(transpose(grad_u), grad_u));
59 }
60 
63  using State = Empty;
64 
74  template <typename T, int dim>
75  auto operator()(State&, const tensor<T, dim, dim>& grad_u) const
76  {
77  static constexpr auto I = Identity<dim>();
78  auto F = grad_u + I;
79  const auto E = greenStrain(grad_u);
80 
81  // stress
82  const auto S = K * tr(E) * I + 2.0 * G * dev(E);
83  return dot(F, S);
84  }
85 
86  double density;
87  double K;
88  double G;
89 };
90 
95 struct NeoHookean {
96  using State = Empty;
97 
109  template <typename T, int dim>
110  SERAC_HOST_DEVICE auto operator()(State& /* state */, const tensor<T, dim, dim>& du_dX) const
111  {
112  using std::log1p;
113  constexpr auto I = Identity<dim>();
114  auto lambda = K - (2.0 / 3.0) * G;
115  auto B_minus_I = dot(du_dX, transpose(du_dX)) + transpose(du_dX) + du_dX;
116 
117  auto logJ = log1p(detApIm1(du_dX));
118  // Kirchoff stress, in form that avoids cancellation error when F is near I
119  auto TK = lambda * logJ * I + G * B_minus_I;
120 
121  // Pull back to Piola
122  auto F = du_dX + I;
123  return dot(TK, inv(transpose(F)));
124  }
125 
126  double density;
127  double K;
128  double G;
129 };
130 
133  using State = Empty;
134 
142  template <typename T, int dim>
143  SERAC_HOST_DEVICE auto operator()(State& /* state */, const tensor<T, dim, dim>& du_dX) const
144  {
145  using std::pow;
146  constexpr auto I = Identity<dim>();
147  auto F = I + du_dX;
148  auto J = det(F);
149  auto Jm13 = pow(J, -1.0 / 3.0);
150  auto F_bar = Jm13 * F;
151  auto Pdev = G * Jm13 * (F_bar - 1.0 / 3.0 * inner(F_bar, F_bar) * inv(transpose(F_bar)));
152 
153  // any volumetric model could be substituted here
154  using std::log1p;
155  auto logJ = log1p(detApIm1(du_dX));
156  auto Pvol = K * logJ * inv(transpose(F));
157 
158  return Pdev + Pvol;
159  }
160 
161  double density;
162  double K;
163  double G;
164 };
165 
169 template <typename T>
170 auto overstress(double viscosity, T accumulated_plastic_strain_rate)
171 {
172  return viscosity * accumulated_plastic_strain_rate;
173 }
174 
179  double sigma_y;
180  double Hi;
181  double eta;
182 
192  template <typename T1, typename T2>
193  auto operator()(T1 accumulated_plastic_strain, T2 accumulated_plastic_strain_rate) const
194  {
195  return sigma_y + Hi * accumulated_plastic_strain + overstress(eta, accumulated_plastic_strain_rate);
196  };
197 };
198 
203  double sigma_y;
204  double n;
205  double eps0;
206  double eta;
207 
217  template <typename T1, typename T2>
218  auto operator()(T1 accumulated_plastic_strain, T2 accumulated_plastic_strain_rate) const
219  {
220  using std::pow;
221  return sigma_y * pow(1.0 + accumulated_plastic_strain / eps0, 1.0 / n) +
222  overstress(eta, accumulated_plastic_strain_rate);
223  };
224 };
225 
232  double sigma_y;
233  double sigma_sat;
235  double eta;
236 
246  template <typename T1, typename T2>
247  auto operator()(T1 accumulated_plastic_strain, T2 accumulated_plastic_strain_rate) const
248  {
249  using std::exp;
250  auto& epsilon_p = accumulated_plastic_strain;
251  auto Y_eq = sigma_sat - (sigma_sat - sigma_y) * exp(-epsilon_p / strain_constant);
252  auto& epsilon_p_dot = accumulated_plastic_strain_rate;
253  auto Y_vis = overstress(eta, epsilon_p_dot);
254  return Y_eq + Y_vis;
255  };
256 };
257 
259 template <typename HardeningType>
261  static constexpr int dim = 3;
262  static constexpr double tol = 1e-10;
263 
264  double E;
265  double nu;
266  HardeningType hardening;
267  double Hk;
268  double density;
269 
271  struct State {
274  };
275 
277  template <typename T>
278  auto operator()(State& state, double dt, const tensor<T, dim, dim>& du_dX) const
279  {
280  using std::sqrt;
281  constexpr auto I = Identity<dim>();
282  const double K = E / (3.0 * (1.0 - 2.0 * nu));
283  const double G = 0.5 * E / (1.0 + nu);
284 
285  // (i) elastic predictor
286  auto el_strain = sym(du_dX) - state.plastic_strain;
287  auto p = K * tr(el_strain);
288  auto s = 2.0 * G * dev(el_strain);
289  auto sigma_b = 2.0 / 3.0 * Hk * state.plastic_strain;
290  auto eta = s - sigma_b;
291  auto q = sqrt(1.5) * norm(eta);
292 
293  // (ii) admissibility
294  const double eqps_old = state.accumulated_plastic_strain;
295  auto residual = [eqps_old, G, dt, *this](auto delta_eqps, auto trial_q) {
296  auto eqps_dot = dt > 0.0 ? delta_eqps / dt : 0.0 * delta_eqps;
297  return trial_q - (3.0 * G + Hk) * delta_eqps - this->hardening(eqps_old + delta_eqps, eqps_dot);
298  };
299  if (residual(0.0, get_value(q)) > tol * hardening.sigma_y) {
300  // (iii) return mapping
301 
302  // Note the tolerance for convergence is the same as the tolerance for entering the return map.
303  // This ensures that if the constitutive update is called again with the updated internal
304  // variables, the return map won't be repeated.
305  ScalarSolverOptions opts{.xtol = 0, .rtol = tol * hardening.sigma_y, .max_iter = 25};
306  double lower_bound = 0.0;
307  double upper_bound = (get_value(q) - hardening(eqps_old, 0.0)) / (3.0 * G + Hk);
308  auto [delta_eqps, status] = solve_scalar_equation(residual, 0.0, lower_bound, upper_bound, opts, q);
309 
310  auto Np = 1.5 * eta / q;
311 
312  s = s - 2.0 * G * delta_eqps * Np;
313  state.accumulated_plastic_strain += get_value(delta_eqps);
314  state.plastic_strain += get_value(delta_eqps) * get_value(Np);
315  }
316 
317  return s + p * I;
318  }
319 };
320 
322 template <typename HardeningType>
323 struct J2 {
324  static constexpr int dim = 3;
325  static constexpr double tol = 1e-10;
326 
327  double E;
328  double nu;
329  HardeningType hardening;
330  double density;
331 
333  struct State {
334  tensor<double, dim, dim> Fpinv = DenseIdentity<3>();
336  };
337 
339  template <typename T>
340  auto operator()(State& state, double dt, const tensor<T, dim, dim>& du_dX) const
341  {
342  using std::sqrt;
343  constexpr auto I = Identity<dim>();
344  const double K = E / (3.0 * (1.0 - 2.0 * nu));
345  const double G = 0.5 * E / (1.0 + nu);
346 
347  // (i) elastic predictor
348  auto F = du_dX + I;
349  auto Fe = dot(F, state.Fpinv);
350  auto Ee = 0.5 * log_symm(dot(transpose(Fe), Fe));
351  // From this point until the state variable update, the algorithm exactly coincides with the
352  // small strain one.
353  auto p = K * tr(Ee);
354  auto s = 2.0 * G * dev(Ee);
355  double q_val = sqrt(1.5) * norm(get_value(s));
356 
357  // (ii) admissibility
358  const double eqps_old = state.accumulated_plastic_strain;
359  auto residual = [eqps_old, G, dt, *this](auto delta_eqps, auto trial_mises) {
360  auto eqps_dot = dt > 0.0 ? delta_eqps / dt : 0.0 * delta_eqps;
361  return trial_mises - 3.0 * G * delta_eqps - this->hardening(eqps_old + delta_eqps, eqps_dot);
362  };
363  if (residual(0.0, q_val) > tol * hardening.sigma_y) {
364  // (iii) return mapping
365 
366  // Note the tolerance for convergence is the same as the tolerance for entering the return map.
367  // This ensures that if the constitutive update is called again with the updated internal
368  // variables, the return map won't be repeated.
369  ScalarSolverOptions opts{.xtol = 0, .rtol = tol * hardening.sigma_y, .max_iter = 25};
370  double lower_bound = 0.0;
371  double upper_bound = (q_val - hardening(eqps_old, 0.0)) / (3.0 * G);
372  // safe to compute derivative of mises stress now that we're in yielding branch
373  auto q = sqrt(1.5) * norm(s);
374  auto [delta_eqps, status] = solve_scalar_equation(residual, 0.0, lower_bound, upper_bound, opts, q);
375 
376  if (!status.converged) {
377  SLIC_WARNING("J2 solve failed to converge.");
378  }
379 
380  auto Np = 1.5 * s / q;
381 
382  s = s - 2.0 * G * delta_eqps * Np;
383  auto A = exp_symm(-delta_eqps * Np);
384 
385  auto Fpinv = dot(state.Fpinv, A);
386 
387  state.accumulated_plastic_strain += get_value(delta_eqps);
388  state.Fpinv = get_value(Fpinv);
389  // Mandel stress
390  auto M = s + p * I;
391  // convert to Piola
392  Fe = dot(Fe, A);
393  auto P = transpose(dot(dot(Fpinv, M), inv(Fe)));
394  return P;
395  } else {
396  // I want to unify this branch with the yielding branch, but I'd need to declare Fpinv above,
397  // and I don't know how to declare its type. BT 11/5/24
398  // Mandel stress
399  auto M = s + p * I;
400  // convert to Piola
401  auto P = transpose(dot(dot(state.Fpinv, M), inv(Fe)));
402  return P;
403  }
404  }
405 };
406 
418 template <typename T1, typename T2, int dim>
419 auto KirchhoffToPiola(const tensor<T1, dim, dim>& kirchhoff_stress, const tensor<T2, dim, dim>& displacement_gradient)
420 {
421  return transpose(dot(inv(displacement_gradient + Identity<dim>()), kirchhoff_stress));
422 }
423 
435 template <typename T1, typename T2, int dim>
436 auto CauchyToPiola(const tensor<T1, dim, dim>& cauchy_stress, const tensor<T2, dim, dim>& displacement_gradient)
437 {
438  auto kirchhoff_stress = det(displacement_gradient + Identity<dim>()) * cauchy_stress;
439  return KirchhoffToPiola(kirchhoff_stress, displacement_gradient);
440 }
441 
443 template <int dim>
447 
455  template <typename T>
456  SERAC_HOST_DEVICE tensor<double, dim> operator()(const tensor<T, dim>& /* x */, const double /* t */) const
457  {
458  return force_;
459  }
460 };
461 
463 template <int dim>
467 
474  template <typename T>
476  const double /* t */) const
477  {
478  return traction_;
479  }
480 };
481 
491  using State = Empty;
492 
502  template <typename T, int dim, typename Density>
503  SERAC_HOST_DEVICE auto pkStress(State& /* state */, const tensor<T, dim, dim>& du_dX, const Density&) const
504  {
505  using std::log1p;
506  constexpr auto I = Identity<dim>();
507  auto lambda = K - (2.0 / 3.0) * G;
508  auto B_minus_I = dot(du_dX, transpose(du_dX)) + transpose(du_dX) + du_dX;
509 
510  auto logJ = log1p(detApIm1(du_dX));
511  // Kirchoff stress, in form that avoids cancellation error when F is near I
512  auto TK = lambda * logJ * I + G * B_minus_I;
513 
514  // Pull back to Piola
515  auto F = du_dX + I;
516  return dot(TK, inv(transpose(F)));
517  }
518 
520  template <typename Density>
521  SERAC_HOST_DEVICE auto density(const Density& density) const
522  {
523  return get<VALUE>(density);
524  }
525 
526  double K;
527  double G;
528 };
529 
530 } // namespace serac::solid_mechanics
#define SERAC_HOST_DEVICE
Macro that evaluates to __host__ __device__ when compiling with nvcc and does nothing on a host compi...
Definition: accelerator.hpp:38
Implementation of the quadrature-function-based functional enabling rapid development of FEM formulat...
SolidMechanics helper data types.
auto CauchyToPiola(const tensor< T1, dim, dim > &cauchy_stress, const tensor< T2, dim, dim > &displacement_gradient)
Transform the Cauchy stress to the Piola stress.
auto KirchhoffToPiola(const tensor< T1, dim, dim > &kirchhoff_stress, const tensor< T2, dim, dim > &displacement_gradient)
Transform the Kirchhoff stress to the Piola stress.
auto overstress(double viscosity, T accumulated_plastic_strain_rate)
auto greenStrain(const tensor< T, dim, dim > &grad_u)
Compute Green's strain from the displacement gradient.
auto solve_scalar_equation(const function &f, double x0, double lower_bound, double upper_bound, ScalarSolverOptions options, ParamTypes... params)
Solves a nonlinear scalar-valued equation and gives derivatives of solution to parameters.
constexpr SERAC_HOST_DEVICE auto inner(const dual< S > &A, const dual< T > &B)
Definition: dual.hpp:281
constexpr SERAC_HOST_DEVICE auto tr(const isotropic_tensor< T, m, m > &I)
calculate the trace of an isotropic tensor
constexpr SERAC_HOST_DEVICE auto dot(const isotropic_tensor< S, m, m > &I, const tensor< T, m, n... > &A)
dot product between an isotropic and (nonisotropic) tensor
auto exp_symm(tensor< T, 3, 3 > A)
Exponential of a symmetric matrix.
auto log_symm(tensor< T, 3, 3 > A)
Logarithm of a symmetric matrix.
constexpr SERAC_HOST_DEVICE auto detApIm1(const tensor< T, 2, 2 > &A)
computes det(A + I) - 1, where precision is not lost when the entries A_{ij} << 1
Definition: tensor.hpp:1321
SERAC_HOST_DEVICE auto pow(dual< gradient_type > a, dual< gradient_type > b)
implementation of a (dual) raised to the b (dual) power
Definition: dual.hpp:405
constexpr SERAC_HOST_DEVICE auto sym(const isotropic_tensor< T, m, m > &I)
return the symmetric part of an isotropic tensor
SERAC_HOST_DEVICE auto sqrt(dual< gradient_type > x)
implementation of square root for dual numbers
Definition: dual.hpp:308
constexpr SERAC_HOST_DEVICE auto dev(const tensor< T, n, n > &A)
Calculates the deviator of a matrix (rank-2 tensor)
Definition: tensor.hpp:1195
constexpr SERAC_HOST_DEVICE auto get_value(const T &arg)
return the "value" part from a given type. For non-dual types, this is just the identity function
Definition: dual.hpp:445
constexpr SERAC_HOST_DEVICE auto det(const isotropic_tensor< T, m, m > &I)
compute the determinant of an isotropic tensor
constexpr SERAC_HOST_DEVICE auto norm(const isotropic_tensor< T, m, m > &I)
compute the Frobenius norm (sqrt(tr(dot(transpose(I), I)))) of an isotropic tensor
constexpr SERAC_HOST_DEVICE auto inv(const isotropic_tensor< T, m, m > &I)
return the inverse of an isotropic tensor
constexpr SERAC_HOST_DEVICE auto transpose(const isotropic_tensor< T, m, m > &I)
return the transpose of an isotropic tensor
SERAC_HOST_DEVICE auto log1p(dual< gradient_type > a)
implementation of the natural logarithm of one plus the argument function for dual numbers
Definition: dual.hpp:397
SERAC_HOST_DEVICE auto exp(dual< gradient_type > a)
implementation of exponential function for dual numbers
Definition: dual.hpp:381
see Nothing for a complete description of this class and when to use it
Settings for solve_scalar_equation.
double xtol
absolute tolerance on Newton correction
SERAC_HOST_DEVICE tensor< double, dim > operator()(const tensor< T, dim > &, const double) const
Evaluation function for the constant body force model.
tensor< double, dim > force_
The constant body force.
Constant traction boundary condition model.
SERAC_HOST_DEVICE tensor< double, dim > operator()(const tensor< T, dim > &, const tensor< T, dim > &, const double) const
Evaluation function for the constant traction model.
tensor< double, dim > traction_
The constant traction.
variables required to characterize the hysteresis response
double accumulated_plastic_strain
uniaxial equivalent plastic strain
tensor< double, dim, dim > plastic_strain
plastic strain
J2 material with nonlinear isotropic hardening and linear kinematic hardening.
auto operator()(State &state, double dt, const tensor< T, dim, dim > &du_dX) const
calculate the first Piola stress, given the displacement gradient and previous material state
static constexpr int dim
spatial dimension
double Hk
Kinematic hardening modulus.
HardeningType hardening
Flow stress hardening model.
static constexpr double tol
relative tolerance on residual mag to judge convergence of return map
variables required to characterize the hysteresis response
double accumulated_plastic_strain
uniaxial equivalent plastic strain
tensor< double, dim, dim > Fpinv
inverse of plastic distortion tensor
Finite deformation version of J2 material with nonlinear isotropic hardening.
static constexpr int dim
spatial dimension
double E
Young's modulus.
double density
mass density
static constexpr double tol
relative tolerance on residual mag to judge convergence of return map
double nu
Poisson's ratio.
HardeningType hardening
Flow stress hardening model.
auto operator()(State &state, double dt, const tensor< T, dim, dim > &du_dX) const
calculate the first Piola stress, given the displacement gradient and previous material state
Linear isotropic hardening law.
double eta
viscosity for linear rate sensitivity
double Hi
Isotropic hardening modulus.
auto operator()(T1 accumulated_plastic_strain, T2 accumulated_plastic_strain_rate) const
Computes the flow stress.
Linear isotropic elasticity material model.
SERAC_HOST_DEVICE auto operator()(State &, const tensor< T, dim, dim > &du_dX) const
stress calculation for a linear isotropic material model
Neo-Hookean material version with additive split of deviatoric and volumetric behavior.
SERAC_HOST_DEVICE auto operator()(State &, const tensor< T, dim, dim > &du_dX) const
Piola stress calculation.
Neo-Hookean material model This struct differs in style relative to the older materials as it needs t...
SERAC_HOST_DEVICE auto pkStress(State &, const tensor< T, dim, dim > &du_dX, const Density &) const
stress calculation for a NeoHookean material model
SERAC_HOST_DEVICE auto density(const Density &density) const
interpolates density field
Neo-Hookean material model.
SERAC_HOST_DEVICE auto operator()(State &, const tensor< T, dim, dim > &du_dX) const
stress calculation for a NeoHookean material model
Power-law isotropic hardening law.
auto operator()(T1 accumulated_plastic_strain, T2 accumulated_plastic_strain_rate) const
Computes the flow stress.
double n
hardening index in reciprocal form
double eta
viscosity for linear rate sensitivity
double eps0
reference value of accumulated plastic strain
St. Venant Kirchhoff hyperelastic model.
auto operator()(State &, const tensor< T, dim, dim > &grad_u) const
stress calculation for a St. Venant Kirchhoff material model
Voce's isotropic hardening law.
double sigma_sat
saturation value of flow strength
auto operator()(T1 accumulated_plastic_strain, T2 accumulated_plastic_strain_rate) const
Computes the flow stress.
double strain_constant
The constant dictating how fast the exponential decays.
double eta
viscosity for linear rate sensitivity
Arbitrary-rank tensor class.
Definition: tensor.hpp:28