Serac  0.1
Serac is an implicit thermal strucural mechanics simulation code.
liquid_crystal_elastomer.hpp
Go to the documentation of this file.
1 // Copyright (c) 2019-2024, 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 
17 #pragma once
18 
23 
24 namespace serac {
25 
31  static constexpr int dim = 3;
32 
34  struct State {
37  double temperature;
38  };
39 
51  LiquidCrystElastomerBrighenti(double rho, double shear_modulus, double bulk_modulus, double order_constant,
52  double order_parameter, double transition_temperature, double N_b_squared)
53  : density(rho),
54  shear_modulus_(shear_modulus),
55  bulk_modulus_(bulk_modulus),
56  order_constant_(order_constant),
57  initial_order_parameter_(order_parameter),
58  transition_temperature_(transition_temperature),
59  N_b_squared_(N_b_squared)
60  {
61  SLIC_ERROR_ROOT_IF(density <= 0.0, "Density must be positive in the LCE material model.");
62  SLIC_ERROR_ROOT_IF(shear_modulus_ <= 0.0, "Shear modulus must be positive in the LCE material model.");
63  SLIC_ERROR_ROOT_IF(bulk_modulus_ <= 0.0, "Bulk modulus must be positive in the LCE material model.");
64  SLIC_ERROR_ROOT_IF(order_constant_ <= 0.0, "Order constant must be positive in the LCE material model.");
65  SLIC_ERROR_ROOT_IF(transition_temperature_ <= 0.0,
66  "The transition temperature must be positive in the LCE material model.");
67  }
68 
82  template <typename DispGradType, typename OrderParamType, typename GammaAngleType>
83  SERAC_HOST_DEVICE auto operator()(State& state, const tensor<DispGradType, dim, dim>& displacement_grad,
84  OrderParamType temperature_tuple, GammaAngleType gamma_tuple) const
85  {
86  using std::cos, std::sin;
87 
88  // get the values from the packed value/gradient tuples
89  auto temperature = get<0>(temperature_tuple);
90  auto gamma = get<0>(gamma_tuple);
91 
92  auto I = Identity<dim>();
93  double q0 = initial_order_parameter_;
94  tensor normal{{cos(gamma), sin(gamma), 0.0 * gamma}};
95 
96  if (norm(state.deformation_gradient) == 0) {
97  state.distribution_tensor = get_value((N_b_squared_ / 3.0) * ((1 - q0) * I) + (3 * q0 * outer(normal, normal)));
98  state.deformation_gradient = get_value(displacement_grad) + I;
99  state.temperature = get_value(temperature);
100  }
101 
102  // kinematics
103  auto F = displacement_grad + I;
104  auto F_old = state.deformation_gradient;
105  auto F_hat = dot(F, inv(F_old));
106  auto J = det(F);
107 
108  // Distribution tensor function of nematic order tensor
109  auto mu = calculateDistributionTensor(state, F_hat, temperature, normal);
110  auto mu0 = (N_b_squared_ / 3.0) * ((1 - q0) * I) + (3 * q0 * outer(normal, normal));
111 
112  // stress output
113  // Note to Jorge-Luis: the paper is omits a prefactor of 1/J in the
114  // Cauchy stress equation because they assume J = 1 strictly
115  // (the incompressible limit). It needs to be retained for this
116  // compressible model.
117  const double lambda = bulk_modulus_ - (2.0 / 3.0) * shear_modulus_;
118  using std::log;
119  auto stress = ((3 * shear_modulus_ / N_b_squared_) * (mu - mu0) + lambda * log(J) * I) / J;
120 
121  // update state variables
122  state.deformation_gradient = get_value(F);
123  state.temperature = get_value(temperature);
124  state.distribution_tensor = get_value(mu);
125 
126  return stress;
127  }
128 
130 
145  template <typename S, typename T, typename U>
146  auto calculateDistributionTensor(const State& state, const tensor<S, dim, dim>& F_hat, const T theta,
147  const tensor<U, dim>& normal) const
148  {
149  // Nematic order scalar
150  using std::exp;
151  auto theta_old = state.temperature;
152  auto q_old = initial_order_parameter_ / (1 + exp((theta_old - transition_temperature_) / order_constant_));
154 
155  // Nematic order tensor
156  constexpr auto I = Identity<dim>();
157  auto n_dyad = outer(normal, normal);
158  // BT: These are different than what Jorge-Luis had. I found the papers
159  // to be confusing on this point. I'm extrapolating from Eq (7)
160  // in https://doi.org/10.1016/j.mechrescom.2022.103858
161  // Well-defined validation problems would help to confirm.
162  auto Q_old = 0.5 * ((1.0 - q_old) * I + 3.0 * q_old * n_dyad);
163  auto Q = 0.5 * ((1.0 - q) * I + 3.0 * q * n_dyad);
164 
165  // Polar decomposition of incremental deformation gradient
166  auto U_hat = matrix_sqrt(transpose(F_hat) * F_hat);
167  auto R_hat = F_hat * inv(U_hat);
168 
169  // Distribution tensor (using 'Strang Splitting' approach)
170  double alpha = 2.0 * N_b_squared_ / 3.0;
171  auto mu_old = state.distribution_tensor;
172  auto mu_hat = mu_old + alpha * (Q - Q_old);
173  auto mu_a = dot(F_hat, dot(mu_hat, transpose(F_hat)));
174  auto mu_b = alpha * (Q - dot(R_hat, dot(Q, transpose(R_hat))));
175 
176  return mu_a + mu_b;
177  }
178 
180 
181  // Sam: please forgive some of the tautological
182  // explanations below, I'm not knowledgeable enough
183  // about this model to write meaningful descriptions,
184  // so these placeholders really only exist to satisfy
185  // our doxygen requirements
186  //
187  // suggestions are welcome
188 
189  double density;
190  double shear_modulus_;
191  double bulk_modulus_;
195 
196  // BT: I think this can be removed - it looks like it cancels out every place it appears.
197  double N_b_squared_;
198 };
199 
203 
211  using State = Empty;
212 
214  static constexpr int dim = 3;
215 
225  LiquidCrystalElastomerBertoldi(double rho, double young_modulus, double poisson_ratio, double initial_order_parameter,
226  double beta_parameter)
227  : density(rho),
228  young_modulus_(young_modulus),
229  poisson_ratio_(poisson_ratio),
230  initial_order_parameter_(initial_order_parameter),
231  beta_parameter_(beta_parameter)
232  {
233  SLIC_ERROR_ROOT_IF(density <= 0.0, "Density must be positive in the LCE material model.");
234  SLIC_ERROR_ROOT_IF(young_modulus_ <= 0.0, "Bulk modulus must be positive in the LCE material model.");
235  SLIC_ERROR_ROOT_IF(poisson_ratio_ <= 0.0, "Poisson ratio must be positive in the LCE material model.");
236  SLIC_ERROR_ROOT_IF(initial_order_parameter_ <= 0.0,
237  "Initial order parameter must be positive in the LCE material model.");
238  SLIC_ERROR_ROOT_IF(beta_parameter_ <= 0.0, "The beta parameter must be positive in the LCE material model.");
239  }
240 
242 
257  template <typename DispGradType, typename OrderParamType, typename GammaAngleType, typename EtaAngleType>
258  SERAC_HOST_DEVICE auto operator()(State& /*state*/, const tensor<DispGradType, dim, dim>& displacement_grad,
259  OrderParamType inst_order_param_tuple, GammaAngleType gamma_tuple,
260  EtaAngleType eta_tuple) const
261  {
262  using std::cos, std::sin;
263 
264  // Compute the normal
265  auto gamma = get<0>(gamma_tuple);
266  auto eta = get<0>(eta_tuple);
267  tensor normal{{cos(gamma) * cos(eta), sin(gamma) * cos(eta), 0.0 * gamma + sin(eta)}};
268 
269  // Get order parameters
270  auto St = get<0>(inst_order_param_tuple);
271  double S0 = initial_order_parameter_;
272 
273  const double lambda = poisson_ratio_ * young_modulus_ / (1.0 + poisson_ratio_) / (1.0 - 2.0 * poisson_ratio_);
274  const double mu = young_modulus_ / 2.0 / (1.0 + poisson_ratio_);
275 
276  // kinematics
277  auto I = Identity<dim>();
278  auto F = displacement_grad + I;
279  auto C = dot(transpose(F), F);
280  auto E = 0.5 * (C - I);
281  auto J = det(F);
282 
283  // Compute the second Piola-Kirchhoff stress, i.e., \partial strain_energy / \partial E
284  auto S_stress_1 = lambda * tr(E) * I;
285  auto S_stress_2 = 2 * mu * E;
286  auto S_stress_3 = -0.5 * beta_parameter_ * (St - S0) * (3 * outer(normal, normal) - I);
287 
288  // transform from second Piola-Lichhoff to Cauchy stress
289  auto stress = 1.0 / J * F * (S_stress_1 + S_stress_2 + S_stress_3) * transpose(F);
290 
291  return stress;
292  }
293 
295 
311  template <typename DispGradType, typename orderParamType, typename GammaAngleType, typename EtaAngleType>
312  auto calculateStrainEnergy(const State& /*state*/, const tensor<DispGradType, dim, dim>& displacement_grad,
313  orderParamType inst_order_param_tuple, GammaAngleType gamma_tuple,
314  EtaAngleType eta_tuple) const
315  {
316  using std::cos, std::sin;
317 
318  // Compute the normal
319  auto gamma = get<0>(gamma_tuple);
320  auto eta = get<0>(eta_tuple);
321  tensor normal{{cos(gamma) * cos(eta), sin(gamma) * cos(eta), 0.0 * gamma + sin(eta)}};
322 
323  // Get order parameters
324  auto St = get<0>(inst_order_param_tuple);
325  double S0 = initial_order_parameter_;
326 
327  const double lambda = poisson_ratio_ * young_modulus_ / (1.0 + poisson_ratio_) / (1.0 - 2.0 * poisson_ratio_);
328  const double mu = young_modulus_ / 2.0 / (1.0 + poisson_ratio_);
329 
330  // kinematics
331  auto I = Identity<dim>();
332  auto F = displacement_grad + I;
333  auto C = dot(transpose(F), F);
334  auto E = 0.5 * (C - I);
335 
336  // Compute the second Piola-Kirchhoff stress, i.e., \partial strain_energy / \partial E
337  auto strain_energy_1 = 0.5 * lambda * tr(E) * tr(E);
338  auto strain_energy_2 = mu * inner(E, E);
339  auto strain_energy_3 = -0.5 * beta_parameter_ * (St - S0) * inner(3 * outer(normal, normal) - I, E);
340 
341  auto strain_energy = strain_energy_1 + strain_energy_2 + strain_energy_3;
342 
343  return strain_energy;
344  }
345 
346  double density;
347  double young_modulus_;
348  double poisson_ratio_;
351 };
352 
353 } // namespace serac
#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
This file contains the declaration of a dual number class.
Accelerator functionality.
Definition: serac.cpp:38
SERAC_HOST_DEVICE auto sin(dual< gradient_type > a)
implementation of sine for dual numbers
Definition: dual.hpp:295
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
SERAC_HOST_DEVICE auto cos(dual< gradient_type > a)
implementation of cosine for dual numbers
Definition: dual.hpp:287
constexpr SERAC_HOST_DEVICE auto inner(const tensor< S, m, n > &A, const tensor< T, m, n > &B)
this function contracts over all indices of the two tensor arguments
Definition: tensor.hpp:668
auto matrix_sqrt(const tensor< T, dim, dim > &A)
compute the matrix square root of a square, real-valued, symmetric matrix i.e. given A,...
Definition: tensor.hpp:1278
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:416
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 log(dual< gradient_type > a)
implementation of the natural logarithm function for dual numbers
Definition: dual.hpp:360
constexpr SERAC_HOST_DEVICE auto outer(double A, tensor< T, n > B)
Definition: tensor.hpp:599
SERAC_HOST_DEVICE auto exp(dual< gradient_type > a)
implementation of exponential function for dual numbers
Definition: dual.hpp:352
The material and load types for the solid functional physics module.
see Nothing for a complete description of this class and when to use it
internal variables for the liquid crystal elastomer model
tensor< double, dim, dim > distribution_tensor
mu from the last timestep
tensor< double, dim, dim > deformation_gradient
F from the last timestep.
double temperature
temperature at the last timestep
Brighenti's liquid crystal elastomer model.
double bulk_modulus_
bulk modulus in stress-free configuration
static constexpr int dim
this model is only intended to be used in 3D
double shear_modulus_
shear modulus in stress-free configuration
auto calculateDistributionTensor(const State &state, const tensor< S, dim, dim > &F_hat, const T theta, const tensor< U, dim > &normal) const
Compute the distribution tensor using Brighenti's model.
double transition_temperature_
Transition temperature.
double N_b_squared_
Kuhn segment parameters.
double initial_order_parameter_
initial value of order parameter
SERAC_HOST_DEVICE auto operator()(State &state, const tensor< DispGradType, dim, dim > &displacement_grad, OrderParamType temperature_tuple, GammaAngleType gamma_tuple) const
Material response.
LiquidCrystElastomerBrighenti(double rho, double shear_modulus, double bulk_modulus, double order_constant, double order_parameter, double transition_temperature, double N_b_squared)
Constructor.
Bertoldi's liquid crystal elastomer model Paper: Li, S., Librandi, G., Yao, Y., Richard,...
auto calculateStrainEnergy(const State &, const tensor< DispGradType, dim, dim > &displacement_grad, orderParamType inst_order_param_tuple, GammaAngleType gamma_tuple, EtaAngleType eta_tuple) const
Compute the strain energy.
SERAC_HOST_DEVICE auto operator()(State &, const tensor< DispGradType, dim, dim > &displacement_grad, OrderParamType inst_order_param_tuple, GammaAngleType gamma_tuple, EtaAngleType eta_tuple) const
Material response.
double initial_order_parameter_
initial value of order parameter
static constexpr int dim
this model is only intended to be used in 3D
double beta_parameter_
Degree of coupling between elastic and nematic energies.
double young_modulus_
Young's modulus in stress-free configuration.
LiquidCrystalElastomerBertoldi(double rho, double young_modulus, double poisson_ratio, double initial_order_parameter, double beta_parameter)
Constructor.
Implementation of the tensor class used by Functional.
Implements a std::tuple-like object that works in CUDA kernels.