21using Material = NeoHookeanLogarithmicMaterial;
27 for (
int aa = 0; aa != 5; ++aa) {
28 if (!std::isfinite(t_values(aa)))
30 "Neo-Hookean logarithmic coordinates must be finite");
37 PrincipalValues &t_eigenvalues,
38 Eigenvectors &t_eigenvectors,
43 CHKERR validateCoordinates(t_values);
45 for (
int aa = 0; aa != 5; ++aa)
46 scale = std::max(
scale, std::abs(t_values(aa)));
48 t_eigenvalues(
i) = 0.;
50 t_eigenvectors(
i,
j) = t_identity(
i,
j);
53 t_scaled(A) = t_values(A) /
scale;
57 t_eigenvalues(0) / 3. + t_eigenvalues(1) / 3. + t_eigenvalues(2) / 3.;
62double getLogSum(
const double a,
const double b) {
63 const double maximum = std::max(
a, b);
64 return maximum + std::log1p(std::exp(std::min(
a, b) - maximum));
68 const PrincipalValues &t_log_values,
72 for (
int aa = 0; aa != 3; ++aa) {
73 const double x = t_log_values(aa);
74 if (std::abs(x) <= 1.e-3) {
76 const double remainder =
77 0.5 + x * (1. / 6. + x * (1. / 24. + x * (1. / 120. + x / 720.)));
78 energy += ((
mu / 2.) * x) * x * remainder;
80 energy += (
mu / 2.) * (std::expm1(x) - x);
83 energy += std::exp(std::log(
mu) + x - std::log(2.) +
84 std::log1p(-(1. + x) * std::exp(-x)));
87 if (!std::isfinite(energy))
89 "Neo-Hookean deviatoric energy is not representable");
94 const int aa,
const int bb) {
97 t_mode(
i,
j) = (t_vectors(aa,
i) * t_vectors(bb,
j) ||
98 t_vectors(bb,
i) * t_vectors(aa,
j)) /
99 (aa == bb ? 2. :
std::sqrt(2.));
105 const double coefficient) {
109 if (!std::isfinite(coefficient) || coefficient <= 0.)
111 "Neo-Hookean material derivative is not representable");
113 t_tangent(A,
B) += coefficient * t_coordinates(A) * t_coordinates(
B);
119 for (
int aa = 0; aa != 5; ++aa)
120 for (
int bb = 0; bb != 5; ++bb)
121 if (!std::isfinite(t_tangent(aa, bb)))
123 "Neo-Hookean material derivative is not representable");
131 if (!std::isfinite(parameters.c10) || parameters.c10 <= 0. ||
132 !std::isfinite(2. * parameters.c10) ||
133 !std::isfinite(parameters.bulkModulus) || parameters.bulkModulus <= 0.)
135 "Neo-Hookean C10 and K must be finite and positive, with finite "
141 const double theta) {
142 return Tensor2SymmetricDeviatorBasis::getTensor(t_coordinates, theta);
147 return Tensor2SymmetricDeviatorBasis::getCoordinates(t_tensor);
151 const Coordinates &t_deviator,
153 Coordinates *stress_ptr,
154 Tangent *hessian_ptr) {
160 CHKERR validateParameters(parameters);
162 const double mu = 2. * parameters.c10;
163 const double log_mu = std::log(
mu);
164 PrincipalValues t_log_values;
165 Eigenvectors t_vectors;
167 CHKERR evaluateEigenvectors(t_deviator, t_log_values, t_vectors,
scale);
168 t_log_values(
i) *= 2. *
scale;
169 for (
int aa = 0; aa != 3; ++aa)
170 if (!std::isfinite(t_log_values(aa)))
172 "Neo-Hookean principal logarithm is not representable");
173 CHKERR evaluateEnergy(
mu, t_log_values, energy);
178 const double maximum =
179 std::max({t_log_values(0), t_log_values(1), t_log_values(2)});
180 PrincipalValues t_deviatoric_values;
181 for (
int aa = 0; aa != 3; ++aa)
182 t_deviatoric_values(aa) = std::expm1(t_log_values(aa) - maximum);
183 const double mean = t_deviatoric_values(0) / 3. +
184 t_deviatoric_values(1) / 3. +
185 t_deviatoric_values(2) / 3.;
186 t_deviatoric_values(
i) -= mean;
187 auto principal_stress = [log_mu, maximum](
const double value) {
188 return value == 0. ? 0.
189 : std::copysign(std::exp(log_mu + maximum +
190 std::log(std::abs(value))),
193 const auto t_stress =
195 *stress_ptr = getCoordinates(t_stress);
196 CHKERR validateCoordinates(*stress_ptr);
200 auto &t_hessian = *hessian_ptr;
201 t_hessian(A,
B) = 0.;
202 for (
int aa = 0; aa != 3; ++aa) {
203 CHKERR addTangentMode(t_hessian, getPrincipalMode(t_vectors, aa, aa),
204 std::exp(std::log(2.) + log_mu + t_log_values(aa)));
205 for (
int bb = 0; bb != aa; ++bb) {
206 const double difference = std::abs(t_log_values(aa) - t_log_values(bb));
207 const double factor =
208 difference == 0. ? 1. : -std::expm1(-difference) / difference;
209 const double coefficient = std::exp(
210 std::log(2.) + log_mu +
211 std::max(t_log_values(aa), t_log_values(bb)) + std::log(factor));
212 CHKERR addTangentMode(t_hessian, getPrincipalMode(t_vectors, aa, bb),
216 CHKERR validateTangent(t_hessian);
223 VolumeState &
state) {
226 CHKERR validateParameters(parameters);
228 if (!std::isfinite(theta))
230 "Neo-Hookean logarithmic volume must be finite");
231 state.jacobian = std::exp(theta);
232 const double jacobian_minus_one = std::expm1(theta);
234 0.5 * (parameters.bulkModulus * jacobian_minus_one) * jacobian_minus_one;
235 const double k_j = parameters.bulkModulus *
state.jacobian;
236 state.firstDerivative = k_j * jacobian_minus_one;
238 if (!std::isfinite(
state.jacobian) ||
state.jacobian <= 0. ||
239 !std::isfinite(
state.energy) || !std::isfinite(
state.firstDerivative) ||
240 !std::isfinite(
state.secondDerivative))
242 "Neo-Hookean volumetric state is not representable");
247 const Coordinates &t_stress,
248 InverseState &
state) {
254 CHKERR validateParameters(parameters);
256 const double mu = 2. * parameters.c10;
257 const double log_mu = std::log(
mu);
258 PrincipalValues t_values;
259 Eigenvectors t_vectors;
261 CHKERR evaluateEigenvectors(t_stress, t_values, t_vectors,
scale);
264 const double minimum = std::min({t_values(0), t_values(1), t_values(2)});
265 PrincipalValues t_log_gaps;
266 double residual_at_zero = 0.;
267 for (
int aa = 0; aa != 3; ++aa) {
268 const double difference = t_values(aa) - minimum;
269 t_log_gaps(aa) = difference == 0. ||
scale == 0.
270 ? -std::numeric_limits<double>::infinity()
272 residual_at_zero += getLogSum(t_log_gaps(aa), 0.);
274 double lower = -residual_at_zero;
276 double z = lower / 3.;
277 PrincipalValues t_log_values;
278 constexpr int maximum_iterations = 100;
279 bool converged =
false;
280 for (
int iteration = 0; iteration != maximum_iterations; ++iteration) {
281 double residual = 0.;
282 double derivative = 0.;
283 double residual_scale = 1.;
284 for (
int aa = 0; aa != 3; ++aa) {
285 t_log_values(aa) = getLogSum(t_log_gaps(aa), z);
286 residual += t_log_values(aa);
287 derivative += std::exp(z - t_log_values(aa));
288 residual_scale = std::max(residual_scale, std::abs(t_log_values(aa)));
290 state.inverseResidual = residual;
291 state.inverseIterations = iteration + 1;
292 if (std::abs(residual) <=
293 32. * std::numeric_limits<double>::epsilon() * residual_scale) {
301 const double newton = z - residual / derivative;
302 z = newton > lower && newton < upper ? newton : (lower + upper) / 2.;
307 "Neo-Hookean auxiliary logarithmic-stress inverse did not converge");
309 const double mean_log =
state.inverseResidual / 3.;
310 t_log_values(
i) -= mean_log;
312 t_log_values, t_vectors, [](
const double value) {
return value / 2.; });
313 state.tMaterialDeviator = getCoordinates(t_material_deviator);
314 CHKERR evaluateEnergy(
mu, t_log_values,
state.deviatoricEnergy);
315 state.conjugateEnergy =
316 t_stress(A) *
state.tMaterialDeviator(A) -
state.deviatoricEnergy;
317 if (!std::isfinite(
state.conjugateEnergy))
319 "Neo-Hookean conjugate energy is not representable");
327 PrincipalValues t_log_weights;
328 t_log_weights(
i) = -log_mu - t_log_values(
i);
329 const double log_weight_sum = getLogSum(
330 getLogSum(t_log_weights(0), t_log_weights(1)), t_log_weights(2));
331 auto &t_compliance =
state.tCompliance;
332 t_compliance(A,
B) = 0.;
333 std::array<int, 3> weight_order{0, 1, 2};
334 std::sort(weight_order.begin(), weight_order.end(),
335 [&t_log_weights](
const int aa,
const int bb) {
336 return t_log_weights(aa) < t_log_weights(bb);
338 const int first = weight_order[0];
339 const int second = weight_order[1];
340 const int third = weight_order[2];
341 const double log_pair_sum =
342 getLogSum(t_log_weights(first), t_log_weights(second));
343 const auto t_first_mode = getPrincipalMode(t_vectors, first, first);
344 const auto t_second_mode = getPrincipalMode(t_vectors, second, second);
345 const auto t_third_mode = getPrincipalMode(t_vectors, third, third);
346 SymmetricTensor t_difference;
347 t_difference(
i,
j) = t_first_mode(
i,
j) - t_second_mode(
i,
j);
348 CHKERR addTangentMode(t_compliance, t_difference,
349 std::exp(-std::log(2.) + t_log_weights(first) +
350 t_log_weights(second) - log_pair_sum));
352 std::exp(t_log_weights(first) - log_pair_sum) * t_first_mode(
i,
j) +
353 std::exp(t_log_weights(second) - log_pair_sum) * t_second_mode(
i,
j) -
355 CHKERR addTangentMode(t_compliance, t_difference,
356 std::exp(-std::log(2.) + log_pair_sum +
357 t_log_weights(third) - log_weight_sum));
358 for (
int aa = 0; aa != 3; ++aa) {
359 for (
int bb = 0; bb != aa; ++bb) {
360 const double difference = std::abs(t_log_values(aa) - t_log_values(bb));
361 const double factor =
362 difference == 0. ? 1. : difference / -std::expm1(-difference);
363 const double shear_coefficient = std::exp(
364 -std::log(2.) - log_mu -
365 std::max(t_log_values(aa), t_log_values(bb)) + std::log(factor));
366 CHKERR addTangentMode(t_compliance, getPrincipalMode(t_vectors, aa, bb),
370 CHKERR validateCoordinates(
state.tMaterialDeviator);
371 CHKERR validateTangent(t_compliance);
376 const Coordinates &t_deviator,
377 const Coordinates &t_stress,
378 const InverseState &inverse,
380 double *energy_ptr) {
383 CHKERR validateCoordinates(t_stress);
385 CHKERR evaluateDeviator(parameters, t_deviator, energy);
386 gap = energy + inverse.conjugateEnergy - t_stress(A) * t_deviator(A);
387 if (!std::isfinite(gap))
389 "Neo-Hookean Fenchel gap is not representable");
391 *energy_ptr = energy;
Neo-Hookean material law in orthonormal logarithmic coordinates.
#define FTENSOR_INDEXES(DIM,...)
#define MoFEMFunctionBegin
First executable line of each MoFEM function, used for error handling. Final line of MoFEM functions ...
@ MOFEM_OPERATION_UNSUCCESSFUL
@ MOFEM_DATA_INCONSISTENCY
#define MoFEMFunctionReturn(a)
Last executable line of each PETSc function used for error handling. Replaces return()
#define CHKERR
Inline error check.
FTensor::Index< 'i', SPACE_DIM > i
FTensor::Index< 'j', 3 > j
auto getMat(A &&t_val, B &&t_vec, Fun< double > f)
Get the Mat object.
PetscErrorCode MoFEMErrorCode
MoFEM/PETSc error code.
implementation of Data Operators for Forces and Sources
MoFEMErrorCode computeEigenValuesSymmetric(const MatrixDouble &mat, VectorDouble &eig, MatrixDouble &eigen_vec)
compute eigenvalues of a symmetric matrix using lapack dsyev
static MoFEMErrorCode evaluateInverse(const Parameters ¶meters, const Coordinates &t_stress, InverseState &state)
Recover Dm(Td), its compliance and conjugate energy; no state is cached.
static MoFEMErrorCode validateParameters(const Parameters ¶meters)
Check finite, strictly positive C10, K and representable mu = 2*C10.
static SymmetricTensor getTensor(const Coordinates &t_coordinates, double theta=0.)
Reconstruct H = D + theta*I/3 from the fixed five-coordinate basis.
static MoFEMErrorCode evaluateDeviator(const Parameters ¶meters, const Coordinates &t_deviator, double &energy, Coordinates *stress_ptr=nullptr, Tangent *hessian_ptr=nullptr)
Evaluate f(D), optionally its five-component gradient and Hessian.
static MoFEMErrorCode evaluateFenchelGap(const Parameters ¶meters, const Coordinates &t_deviator, const Coordinates &t_stress, const InverseState &inverse, double &gap, double *energy_ptr=nullptr)
static Coordinates getCoordinates(const SymmetricTensor &t_tensor)
Project a symmetric tensor onto the fixed trace-free basis.
static MoFEMErrorCode evaluateVolume(const Parameters ¶meters, double theta, VolumeState &state)
Evaluate g(J) = K*(J-1)^2/2 and its logarithmic-volume derivatives.