v0.16.3
Loading...
Searching...
No Matches
plastic_local_law_atom.cpp
Go to the documentation of this file.
1/**
2 * @file plastic_local_law_atom.cpp
3 * @brief Pointwise checks of the ADOL-C two-field plastic local law.
4 *
5 * The law is the local density of the Chapter 1 positive-reconstruction
6 * objective in doc/development/proximal_plasticity.tex (label
7 * eq:tao-primal-objective). This atom needs no mesh: it records the tape,
8 * then checks value, gradient and Hessian action against their closed forms
9 * and against centred finite differences, using TestingInterface.
10 */
11
12#include <MoFEM.hpp>
13using namespace MoFEM;
14
16#include <MatrixFunction.hpp>
21using namespace EshelbianPlasticity;
23
24static char help[] = "Pointwise checks of the two-field plastic local law.\n"
25 " -test_nb 1,2,... select the registered checks\n\n";
26
27namespace {
28
29constexpr int nbCoords = plasticLogarithmicStretchCoordinateSize;
30constexpr int nbActive = plasticLocalLawActiveCount;
31using Increment = PlasticLocalLaw::IncrementTensor;
32using Gradient = PlasticLocalLaw::GradientTensor;
33using Direction = PlasticLocalLaw::DirectionTensor;
34using HessianAction = PlasticLocalLaw::HessianActionTensor;
36
37/** Exact first directional data for F_p = exp(H_p). */
38struct PlasticExponentialDirectionalData {
42 FTensor::Tensor2_symmetric<double, SPACE_DIM> directionInversePlasticF;
43};
44
45/** Evaluate exp(H), exp(-H), and their actions for derivative checks. */
46MoFEMErrorCode evaluatePlasticExponentialDirectionalData(
49 PlasticExponentialDirectionalData &data) {
51 static_assert(SPACE_DIM == 3,
52 "Plastic exponential directional data is three-dimensional");
56 t_eigen_vectors(i, j) = t_plastic_h(i, j);
57 if (computeEigenValuesSymmetric(t_eigen_vectors, t_eigen_values) !=
58 MB_SUCCESS)
59 SETERRQ(PETSC_COMM_SELF, MOFEM_OPERATION_UNSUCCESSFUL,
60 "Failed to diagonalise logarithmic plastic deformation for a "
61 "directional exponential action");
62 const int nb_unique = getUniqNb<SPACE_DIM>(t_eigen_values);
63 if (nb_unique < SPACE_DIM)
64 CHKERR sortEigenVals<SPACE_DIM>(t_eigen_values, t_eigen_vectors);
65 const EigenMatrix::Fun<double> exp_fun = [](const double value) {
66 return std::exp(value);
67 };
68 const EigenMatrix::Fun<double> inverse_exp_fun = [](const double value) {
69 return std::exp(-value);
70 };
71 const EigenMatrix::Fun<double> derivative_inverse_exp_fun =
72 [](const double value) { return -std::exp(-value); };
73 const auto t_plastic_f =
74 EigenMatrix::getMat(t_eigen_values, t_eigen_vectors, exp_fun);
75 const auto t_inverse_plastic_f =
76 EigenMatrix::getMat(t_eigen_values, t_eigen_vectors, inverse_exp_fun);
77 const auto t_diff_exp = EigenMatrix::getDiffMat(
78 t_eigen_values, t_eigen_vectors, exp_fun, exp_fun, nb_unique);
79 const auto t_diff_inverse_exp =
80 EigenMatrix::getDiffMat(t_eigen_values, t_eigen_vectors, inverse_exp_fun,
81 derivative_inverse_exp_fun, nb_unique);
82 data.plasticF(i, j) = t_plastic_f(i, j);
83 data.inversePlasticF(i, j) = t_inverse_plastic_f(i, j);
84 data.directionPlasticF(i, j) = t_diff_exp(i, j, k, l) * t_direction(k, l);
85 data.directionInversePlasticF(i, j) =
86 t_diff_inverse_exp(i, j, k, l) * t_direction(k, l);
88}
89
90/** Contract D^2 exp(H^p) with two directions and the conjugate tensor
91 * for the matrix-exponential second-derivative check. */
92MoFEMErrorCode evaluatePlasticExponentialSecondContraction(
94 const FTensor::Tensor2_symmetric<double, SPACE_DIM> &t_first_direction,
95 const FTensor::Tensor2_symmetric<double, SPACE_DIM> &t_second_direction,
97 double &value) {
99 static_assert(SPACE_DIM == 3,
100 "Plastic exponential Hessian action is three-dimensional");
104 t_eigen_vectors(i, j) = t_plastic_h(i, j);
105 if (computeEigenValuesSymmetric(t_eigen_vectors, t_eigen_values) !=
106 MB_SUCCESS)
107 SETERRQ(PETSC_COMM_SELF, MOFEM_OPERATION_UNSUCCESSFUL,
108 "Failed to diagonalise logarithmic plastic deformation for a "
109 "second directional exponential action");
110 const int nb_unique = getUniqNb<SPACE_DIM>(t_eigen_values);
111 if (nb_unique < SPACE_DIM)
112 CHKERR sortEigenVals<SPACE_DIM>(t_eigen_values, t_eigen_vectors);
113 const EigenMatrix::Fun<double> exp_fun = [](const double argument) {
114 return std::exp(argument);
115 };
117 t_mutable_conjugate(i, j) = t_conjugate(i, j);
118 const auto t_contracted_hessian = EigenMatrix::getDiffDiffMat(
119 t_eigen_values, t_eigen_vectors, exp_fun, exp_fun, exp_fun,
120 t_mutable_conjugate, nb_unique);
121 value = t_contracted_hessian(i, j, k, l) * t_first_direction(i, j) *
122 t_second_direction(k, l);
123 if (!std::isfinite(value))
124 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
125 "Contracted second directional plastic exponential is not finite");
127}
128
129/** Closed-form gradient: g_H = f_r (2/3) h / ell_eps,
130 * g_psi = e^psi [f_r + alpha (psi - psi^k)]. */
131Gradient closedFormGradient(const Increment &t_h, double psi,
132 const PlasticLocalLawParameters &p) {
133 const auto terms = evaluatePlasticLocalLawTerms(t_h, psi, p);
134 Gradient t_g;
135 for (int l = 0; l != nbCoords; ++l)
136 t_g(l) = terms.resistanceForce * plasticEquivalentIncrementScaleSquared *
137 t_h(l) / terms.regularisedMagnitude;
138 t_g(nbCoords) =
139 terms.slack *
140 (terms.resistanceForce + p.proximalCoefficient * (psi - p.latentCentre));
141 return t_g;
142}
143
144HessianAction closedFormHessianAction(const Increment &t_h, double psi,
145 const Direction &t_direction,
146 const PlasticLocalLawParameters &p) {
147 const auto terms = evaluatePlasticLocalLawTerms(t_h, psi, p);
148 std::array<double, nbCoords> normal;
149 double delta_ell = 0;
150 for (int l = 0; l != nbCoords; ++l) {
152 terms.regularisedMagnitude;
153 delta_ell += normal[l] * t_direction(l);
154 }
155 const double direction_latent = t_direction(nbCoords);
156 const double delta_eta = delta_ell + terms.slack * direction_latent;
157 const double delta_resistance = p.hardeningModulus * delta_eta;
158 HessianAction t_action;
159 for (int l = 0; l != nbCoords; ++l) {
160 const double delta_normal =
162 terms.regularisedMagnitude -
163 normal[l] * delta_ell / terms.regularisedMagnitude;
164 t_action(l) =
165 delta_resistance * normal[l] + terms.resistanceForce * delta_normal;
166 }
167 t_action(nbCoords) =
168 terms.slack * delta_resistance +
169 terms.slack *
170 (terms.resistanceForce +
171 p.proximalCoefficient * (psi - p.latentCentre + 1.)) *
172 direction_latent;
173 return t_action;
174}
175
176double maxAbs(const Gradient &t_a) {
177 double m = 0;
178 for (int a = 0; a != nbActive; ++a)
179 m = std::max(m, std::abs(t_a(a)));
180 return m;
181}
182
183double maxAbsDifference(const Gradient &t_a, const Gradient &t_b) {
184 double m = 0;
185 for (int a = 0; a != nbActive; ++a)
186 m = std::max(m, std::abs(t_a(a) - t_b(a)));
187 return m;
188}
189
190struct Point {
191 Increment t_h;
192 double psi;
193};
194
195void perturb(const Point &base, const Gradient &t_direction, double step,
196 Point &out) {
197 for (int l = 0; l != nbCoords; ++l)
198 out.t_h(l) = base.t_h(l) + step * t_direction(l);
199 out.psi = base.psi + step * t_direction(nbCoords);
200}
201
202double maxAbsDifference(const SymmetricTensor &t_a,
203 const SymmetricTensor &t_b) {
204 double result = 0;
205 for (int i = 0; i != SPACE_DIM; ++i)
206 for (int j = 0; j != SPACE_DIM; ++j)
207 result = std::max(result, std::abs(t_a(i, j) - t_b(i, j)));
208 return result;
209}
210
211double maxAbs(const SymmetricTensor &t_a) {
212 double result = 0;
213 for (int i = 0; i != SPACE_DIM; ++i)
214 for (int j = 0; j != SPACE_DIM; ++j)
215 result = std::max(result, std::abs(t_a(i, j)));
216 return result;
217}
218
219} // namespace
220
221int main(int argc, char *argv[]) {
222 MoFEM::Core::Initialize(&argc, &argv, nullptr, help);
223 try {
224 moab::Core moab_core;
225 moab::Interface &moab = moab_core;
226 MoFEM::Core mofem_core(moab);
227 MoFEM::Interface &m_field = mofem_core;
228 auto testing = m_field.getInterface<TestingInterface>();
229
230 auto law = createPlasticLocalLaw();
231 CHKERR law->recordTape();
232
233 // Reference parameters: hardening on, plastic slack open.
234 const PlasticLocalLawParameters hardening{/*yieldStress*/ 0.1,
235 /*hardeningModulus*/ 0.02,
236 /*committedKappa*/ 0.03,
237 /*latentCentre*/ std::log(0.01),
238 /*proximalCoefficient*/ 0.1,
239 /*regularisationEpsilon*/ 1e-4};
240 PlasticLocalLawParameters perfect = hardening;
241 perfect.hardeningModulus = 0;
242 const std::array<const PlasticLocalLawParameters *, 2> parameter_sets{
243 &hardening, &perfect};
244
245 const Point active{Increment{0.012, -0.004, 0.007, 0.002, -0.009},
246 std::log(0.003)};
247 const Point elastic{Increment{0., 0., 0., 0., 0.}, std::log(0.0005)};
248
249 const std::array<Gradient, 3> directions{
250 Gradient{0.3, -0.7, 0.2, 0.5, -0.4, 0.},
251 Gradient{0., 0., 0., 0., 0., 1.},
252 Gradient{-0.2, 0.4, 0.6, -0.3, 0.1, 0.8}};
253
254 // 1. Tape value equals the shared formula, and its terms are consistent.
255 CHKERR testing->addTestCommandLine("", 1, "tape value", [&]() {
257 for (const auto *p : parameter_sets) {
258 CHKERR law->setParameters(*p);
259 for (const auto *pt : {&active, &elastic}) {
261 CHKERR law->evaluateValue(pt->t_h, pt->psi, terms);
262 const auto direct =
263 evaluatePlasticLocalLawTerms(pt->t_h, pt->psi, *p);
264 CHKERR testing->checkValue<TestType::SCALAR>(
265 "density tape vs direct", terms.density, direct.density,
266 {1e-15, 1e-13});
267 CHKERR testing->checkValue<TestType::SCALAR>(
268 "density = resistance + proximal", terms.density,
269 terms.resistance + terms.proximal, {1e-15, 1e-13});
270 CHKERR testing->checkError<TestType::SCALAR>(
271 "proximal Bregman term non-negative",
272 std::max(0., -terms.proximal), 1., {0., 0.});
273 }
274 }
276 });
277
278 // 2. Reconstruction admissibility: eta - ell_eps = e^psi > 0, ell_eps >=
279 // ell, and ell_eps(0) = eps.
280 CHKERR testing->addTestCommandLine("", 2, "reconstruction", [&]() {
282 CHKERR law->setParameters(hardening);
284 CHKERR law->evaluateValue(active.t_h, active.psi, terms);
285 CHKERR testing->checkValue<TestType::SCALAR>(
286 "slack equals exp(psi)",
288 std::exp(active.psi), {1e-15, 1e-13});
289 CHKERR testing->checkError<TestType::SCALAR>(
290 "slack positive", terms.slack > 0 ? 0. : 1., 1., {0., 0.});
291 const double exact_norm =
293 CHKERR testing->checkError<TestType::SCALAR>(
294 "ell_eps >= ell",
295 std::max(0., exact_norm - terms.regularisedMagnitude), exact_norm,
296 {0., 0.});
297 CHKERR testing->checkValue<TestType::SCALAR>(
298 "ell_eps^2 = ell^2 + eps^2",
300 exact_norm * exact_norm +
301 hardening.regularisationEpsilon * hardening.regularisationEpsilon,
302 {1e-15, 1e-13});
303 CHKERR law->evaluateValue(elastic.t_h, elastic.psi, terms);
304 CHKERR testing->checkValue<TestType::SCALAR>(
305 "ell_eps at origin equals eps", terms.regularisedMagnitude,
306 hardening.regularisationEpsilon, {1e-15, 1e-13});
307 CHKERR testing->checkValue<TestType::SCALAR>(
308 "f_r at origin", terms.resistanceForce,
309 hardening.yieldStress +
310 hardening.hardeningModulus *
311 (hardening.committedKappa + hardening.regularisationEpsilon +
312 std::exp(elastic.psi)),
313 {1e-15, 1e-13});
315 });
316
317 // 3. Tape gradient equals the closed form of Eq. (tao-primal-gradient)
318 // and centred finite differences of the tape value.
319 CHKERR testing->addTestCommandLine("", 3, "gradient", [&]() {
321 for (const auto *p : parameter_sets) {
322 CHKERR law->setParameters(*p);
323 for (const auto *pt : {&active, &elastic}) {
324 Gradient t_g;
325 CHKERR law->evaluateGradient(pt->t_h, pt->psi, t_g);
326 const auto t_closed = closedFormGradient(pt->t_h, pt->psi, *p);
327 CHKERR testing->checkError<TestType::DERIVATIVE>(
328 "gradient tape vs closed form", maxAbsDifference(t_g, t_closed),
329 std::max(maxAbs(t_g), maxAbs(t_closed)), {1e-16, 1e-12});
330 for (const auto &t_v : directions) {
331 double assembled = 0;
332 for (int a = 0; a != nbActive; ++a)
333 assembled += t_g(a) * t_v(a);
334 for (const double step : {1e-5, 1e-6}) {
335 Point plus, minus;
336 perturb(*pt, t_v, step, plus);
337 perturb(*pt, t_v, -step, minus);
338 PlasticLocalLawTerms terms_plus, terms_minus;
339 CHKERR law->evaluateValue(plus.t_h, plus.psi, terms_plus);
340 CHKERR law->evaluateValue(minus.t_h, minus.psi, terms_minus);
341 const double fd =
342 (terms_plus.density - terms_minus.density) / (2 * step);
343 // Truncation is O(step^2 |phi'''|); phi''' ~ f_r / ell^2.
344 const double tolerance = step == 1e-5 ? 1e-5 : 1e-7;
345 CHKERR testing->checkError<TestType::DERIVATIVE>(
346 "gradient centred difference", std::abs(fd - assembled),
347 std::max(std::abs(fd), std::abs(assembled)),
348 {1e-12, tolerance});
349 }
350 }
351 }
352 }
354 });
355
356 // 4. Perfect plasticity: the latent stationarity point is explicit,
357 // psi = psi^k - sigma_y / alpha, and the tensor gradient does not
358 // depend on psi (Eq. perfect-plasticity-latent-solution).
359 CHKERR testing->addTestCommandLine("", 4, "perfect plasticity", [&]() {
361 CHKERR law->setParameters(perfect);
362 const double psi_star = perfect.latentCentre -
363 perfect.yieldStress / perfect.proximalCoefficient;
364 Gradient t_g_star, t_g_other;
365 CHKERR law->evaluateGradient(active.t_h, psi_star, t_g_star);
366 CHKERR law->evaluateGradient(active.t_h, active.psi, t_g_other);
367 CHKERR testing->checkError<TestType::DERIVATIVE>(
368 "latent gradient vanishes at the explicit root",
369 std::abs(t_g_star(nbCoords)),
370 std::exp(psi_star) * perfect.yieldStress, {1e-16, 1e-12});
371 double tensor_difference = 0, tensor_scale = 0;
372 for (int l = 0; l != nbCoords; ++l) {
373 tensor_difference =
374 std::max(tensor_difference, std::abs(t_g_star(l) - t_g_other(l)));
375 tensor_scale = std::max(tensor_scale, std::abs(t_g_other(l)));
376 }
377 CHKERR testing->checkError<TestType::DERIVATIVE>(
378 "tensor gradient independent of psi", tensor_difference, tensor_scale,
379 {1e-16, 1e-14});
380 // The latent gradient has the sign of sigma_y + alpha (psi - psi^k).
381 Gradient t_g_above;
382 CHKERR law->evaluateGradient(active.t_h, psi_star + 1., t_g_above);
383 CHKERR testing->checkError<TestType::SCALAR>(
384 "latent gradient positive above the root",
385 t_g_above(nbCoords) > 0 ? 0. : 1., 1., {0., 0.});
387 });
388
389 // 5. Hardening couples the fields only through A_h e^psi: closing the
390 // slack changes the tensor gradient by A_h (e^psi_1 - e^psi_2) G.
391 CHKERR testing->addTestCommandLine("", 5, "hardening coupling", [&]() {
393 CHKERR law->setParameters(hardening);
394 const double psi_open = active.psi;
395 const double psi_closed = std::log(1e-7);
396 Gradient t_g_open, t_g_closed;
397 CHKERR law->evaluateGradient(active.t_h, psi_open, t_g_open);
398 CHKERR law->evaluateGradient(active.t_h, psi_closed, t_g_closed);
399 const auto terms =
400 evaluatePlasticLocalLawTerms(active.t_h, psi_open, hardening);
401 const double factor = hardening.hardeningModulus *
402 (std::exp(psi_open) - std::exp(psi_closed));
403 double error = 0, scale = 0;
404 for (int l = 0; l != nbCoords; ++l) {
405 const double predicted = factor *
407 active.t_h(l) / terms.regularisedMagnitude;
408 error = std::max(error,
409 std::abs((t_g_open(l) - t_g_closed(l)) - predicted));
410 scale = std::max(scale, std::abs(t_g_open(l)));
411 }
412 CHKERR testing->checkError<TestType::DERIVATIVE>(
413 "tensor gradient shift equals A_h delta(e^psi) G", error, scale,
414 {1e-16, 1e-12});
416 });
417
418 // 6. Invalid parameters are rejected: eps = 0 and non-positive alpha.
419 CHKERR testing->addTestCommandLine("", 6, "parameter validation", [&]() {
421 auto rejects = [&](PlasticLocalLawParameters bad) {
422 CHKERR PetscPushErrorHandler(PetscIgnoreErrorHandler, nullptr);
423 const auto ierr = law->setParameters(bad);
424 CHKERR PetscPopErrorHandler();
425 return ierr != 0 ? 0. : 1.;
426 };
427 auto bad_eps = hardening;
428 bad_eps.regularisationEpsilon = 0;
429 CHKERR testing->checkError<TestType::SCALAR>(
430 "epsilon zero rejected", rejects(bad_eps), 1., {0., 0.});
431 auto bad_alpha = hardening;
432 bad_alpha.proximalCoefficient = 0;
433 CHKERR testing->checkError<TestType::SCALAR>(
434 "zero proximal coefficient rejected", rejects(bad_alpha), 1.,
435 {0., 0.});
436 auto bad_hardening = hardening;
437 bad_hardening.hardeningModulus = -1;
438 CHKERR testing->checkError<TestType::SCALAR>(
439 "negative hardening rejected", rejects(bad_hardening), 1., {0., 0.});
440 // The valid parameters still apply after a rejected update.
441 CHKERR law->setParameters(hardening);
442 CHKERR testing->checkValue<TestType::SCALAR>(
443 "valid parameters restored",
444 law->getParameters().regularisationEpsilon,
445 hardening.regularisationEpsilon, {0., 0.});
447 });
448
449 // 7. The ADOL-C Hessian-vector product equals the analytical directional
450 // derivative and a centred difference of the pointwise gradient.
451 CHKERR testing->addTestCommandLine("", 7, "hessian action", [&]() {
453 for (const auto *p : parameter_sets) {
454 CHKERR law->setParameters(*p);
455 for (const auto *pt : {&active, &elastic}) {
456 for (const auto &t_v : directions) {
457 HessianAction t_action;
458 CHKERR law->evaluateHessianAction(pt->t_h, pt->psi, t_v, t_action);
459 const auto t_closed =
460 closedFormHessianAction(pt->t_h, pt->psi, t_v, *p);
461 CHKERR testing->checkError<TestType::DERIVATIVE>(
462 "Hessian action tape vs closed form",
463 maxAbsDifference(t_action, t_closed),
464 std::max(maxAbs(t_action), maxAbs(t_closed)), {1e-12, 1e-11});
465
466 // At h = 0 the regularised norm has curvature O(1 / eps), so use
467 // a smaller perturbation to keep the centred-difference truncation
468 // error below the derivative tolerance.
469 const double step = pt == &elastic ? 1e-8 : 1e-7;
470 Point plus, minus;
471 perturb(*pt, t_v, step, plus);
472 perturb(*pt, t_v, -step, minus);
473 Gradient t_gradient_plus, t_gradient_minus;
474 CHKERR law->evaluateGradient(plus.t_h, plus.psi, t_gradient_plus);
475 CHKERR law->evaluateGradient(minus.t_h, minus.psi,
476 t_gradient_minus);
477 HessianAction t_difference;
478 for (int a = 0; a != nbActive; ++a)
479 t_difference(a) =
480 (t_gradient_plus(a) - t_gradient_minus(a)) / (2. * step);
481 CHKERR testing->checkError<TestType::DERIVATIVE>(
482 "Hessian action centred gradient difference",
483 maxAbsDifference(t_action, t_difference),
484 std::max(maxAbs(t_action), maxAbs(t_difference)), {1e-8, 2e-7});
485 }
486 }
487 }
488
489 CHKERR law->setParameters(perfect);
490 HessianAction t_perfect_cross;
491 CHKERR law->evaluateHessianAction(active.t_h, active.psi, directions[1],
492 t_perfect_cross);
493 double tensor_cross_norm = 0;
494 for (int l = 0; l != nbCoords; ++l)
495 tensor_cross_norm =
496 std::max(tensor_cross_norm, std::abs(t_perfect_cross(l)));
497 CHKERR testing->checkError<TestType::DERIVATIVE>(
498 "perfect-plastic tensor-latent Hessian coupling vanishes",
499 tensor_cross_norm, std::abs(t_perfect_cross(nbCoords)),
500 {1e-16, 1e-13});
501
502 CHKERR law->setParameters(hardening);
503 HessianAction t_origin_action;
504 CHKERR law->evaluateHessianAction(elastic.t_h, elastic.psi, directions[0],
505 t_origin_action);
506 const auto origin_terms =
507 evaluatePlasticLocalLawTerms(elastic.t_h, elastic.psi, hardening);
508 double origin_error = 0;
509 double origin_scale = 0;
510 for (int l = 0; l != nbCoords; ++l) {
511 const double expected = origin_terms.resistanceForce *
513 directions[0](l) /
514 hardening.regularisationEpsilon;
515 origin_error =
516 std::max(origin_error, std::abs(t_origin_action(l) - expected));
517 origin_scale = std::max(origin_scale, std::abs(expected));
518 }
519 CHKERR testing->checkError<TestType::DERIVATIVE>(
520 "regularised tensor curvature at the origin", origin_error,
521 origin_scale, {1e-10, 1e-12});
523 });
524
525 // 8. Check first and contracted second matrix-exponential derivatives
526 // against centred finite differences.
527 CHKERR testing->addTestCommandLine(
528 "", 8, "plastic exponential directional actions", [&]() {
530 Increment t_u_coordinates;
531 Increment t_v_coordinates;
532 for (int l = 0; l != nbCoords; ++l) {
533 t_u_coordinates(l) = directions[0](l);
534 t_v_coordinates(l) = directions[2](l);
535 }
536 const auto t_h =
538 const auto t_u =
540 const auto t_v =
542 SymmetricTensor t_s{0.7, -0.2, 0.1, 0.5, 0.3, -0.4};
543 PlasticExponentialDirectionalData data;
544 CHKERR evaluatePlasticExponentialDirectionalData(t_h, t_u, data);
545
546 constexpr double step = 2e-6;
547 SymmetricTensor t_plus_h;
548 SymmetricTensor t_minus_h;
549 for (int i = 0; i != SPACE_DIM; ++i)
550 for (int j = 0; j <= i; ++j) {
551 t_plus_h(i, j) = t_h(i, j) + step * t_u(i, j);
552 t_minus_h(i, j) = t_h(i, j) - step * t_u(i, j);
553 }
554 PlasticExponentialDirectionalData plus_data;
555 PlasticExponentialDirectionalData minus_data;
556 CHKERR evaluatePlasticExponentialDirectionalData(t_plus_h, t_v,
557 plus_data);
558 CHKERR evaluatePlasticExponentialDirectionalData(t_minus_h, t_v,
559 minus_data);
560
561 SymmetricTensor t_first_difference_f;
562 SymmetricTensor t_first_difference_inverse;
563 for (int i = 0; i != SPACE_DIM; ++i)
564 for (int j = 0; j <= i; ++j) {
565 t_first_difference_f(i, j) =
566 (plus_data.plasticF(i, j) - minus_data.plasticF(i, j)) /
567 (2. * step);
568 t_first_difference_inverse(i, j) =
569 (plus_data.inversePlasticF(i, j) -
570 minus_data.inversePlasticF(i, j)) /
571 (2. * step);
572 }
573 CHKERR testing->checkError<TestType::DERIVATIVE>(
574 "D exp(H)[U]",
575 maxAbsDifference(data.directionPlasticF, t_first_difference_f),
576 std::max(maxAbs(data.directionPlasticF),
577 maxAbs(t_first_difference_f)),
578 {1e-10, 2e-8});
579 CHKERR testing->checkError<TestType::DERIVATIVE>(
580 "D exp(-H)[U]",
581 maxAbsDifference(data.directionInversePlasticF,
582 t_first_difference_inverse),
583 std::max(maxAbs(data.directionInversePlasticF),
584 maxAbs(t_first_difference_inverse)),
585 {1e-10, 2e-8});
586
587 double exact_second_contraction = 0;
588 CHKERR evaluatePlasticExponentialSecondContraction(
589 t_h, t_u, t_v, t_s, exact_second_contraction);
590 double difference_second_contraction = 0;
591 for (int i = 0; i != SPACE_DIM; ++i)
592 for (int j = 0; j != SPACE_DIM; ++j)
593 difference_second_contraction +=
594 t_s(i, j) *
595 (plus_data.directionPlasticF(i, j) -
596 minus_data.directionPlasticF(i, j)) /
597 (2. * step);
598 CHKERR testing->checkError<TestType::DERIVATIVE>(
599 "S:D2 exp(H)[U,V]",
600 std::abs(exact_second_contraction -
601 difference_second_contraction),
602 std::max(std::abs(exact_second_contraction),
603 std::abs(difference_second_contraction)),
604 {1e-10, 3e-8});
606 });
607
608 CHKERR testing->addTestCommandLine(
609 "", 9, "local elastic logarithmic sensitivity", [&]() {
611 FTENSOR_INDEXES(3, i, j, k);
612 FTensor::Index<'a', 5> a;
613 FTensor::Index<'b', 5> b;
614 const auto function = [](const SymmetricTensor &tensor,
616 FTENSOR_INDEXES(3, m, n);
619 vectors(m, n) = tensor(m, n);
621 "Diagonalise local sensitivity test tensor");
622 return EigenMatrix::getMat(values, vectors, f);
623 };
624 const EigenMatrix::Fun<double> exp_f = [](double x) {
625 return std::exp(x);
626 };
627 const EigenMatrix::Fun<double> inv_f = [](double x) {
628 return std::exp(-x);
629 };
630 const EigenMatrix::Fun<double> log_f = [](double x) {
631 return .5 * std::log(x);
632 };
633 const std::array<SymmetricTensor, 4> elastic_logs{
634 {{0., 0., 0., 0., 0., 0.},
635 {.12, 0., 0., .12, 0., -.08},
636 {.12, 0., 0., .12 + 1e-12, 0., -.08},
637 {.18, .07, -.04, -.09, .03, .02}}};
638 const std::array<SymmetricTensor, 3> plastic_logs{
639 {{0., 0., 0., 0., 0., 0.},
640 {.1, 0., 0., .1, 0., -.2},
641 {.21, -.09, .04, -.13, .06, -.08}}};
642 double worst_error = 0.;
643 for (const auto &he : elastic_logs) {
644 const auto ue = function(he, exp_f);
645 for (const auto &hp : plastic_logs) {
648 const auto fp = function(hp, exp_f);
650 anchor(i, j) = ue(i, k) * fp(k, j);
651 for (const auto &source : {directions[0], directions[2]}) {
652 Increment v, exact;
653 for (int n = 0; n != 5; ++n)
654 v(n) = source(n);
655 const auto tv =
657 exact(a) = map(a, b) * v(b);
658 for (const double step : {2e-5, 2e-6}) {
659 const auto evaluate = [&](double shift) {
660 SymmetricTensor trial_hp, c;
661 trial_hp(i, j) = hp(i, j) + shift * tv(i, j);
662 const auto inverse = function(trial_hp, inv_f);
664 fe(i, j) = anchor(i, k) * inverse(k, j);
665 metric(i, j) = fe(k, i) * fe(k, j);
666 c(i, j) = (metric(i, j) || metric(j, i)) / 2.;
667 return function(c, log_f);
668 };
669 const auto plus = evaluate(step), minus = evaluate(-step);
670 SymmetricTensor derivative;
671 derivative(i, j) = (plus(i, j) - minus(i, j)) / (2. * step);
672 const auto reference =
674 derivative);
675 double error = 0.;
676 for (int n = 0; n != 5; ++n)
677 error = std::max(error, std::abs(exact(n) - reference(n)));
678 worst_error = std::max(worst_error, error);
679 CHKERR testing->checkError<TestType::DERIVATIVE>(
680 "Direct Ue sensitivity vs fixed-F differences", error,
681 plasticCoordinateNorm(reference), {1e-9, 2e-7});
682 CHKERR testing->checkError<TestType::DERIVATIVE>(
683 "Isochoric elastic logarithmic sensitivity",
684 std::abs(derivative(i, i)), 1., {2e-8, 2e-8});
685 }
686 }
687 }
689 const Material::Parameters parameters{1.7, 8.5};
690 const auto coordinates = Material::getCoordinates(he);
691 Material::Tangent tangent;
692 double energy = 0.;
693 CHKERR Material::evaluateDeviator(parameters, coordinates, energy,
694 nullptr, &tangent);
695 for (int n = 0; n != 5; ++n) {
696 constexpr double step = 2e-6;
697 Increment unit, plus, minus, gp, gm, action;
698 unit(a) = 0.;
699 unit(n) = 1.;
700 plus(a) = coordinates(a) + step * unit(a);
701 minus(a) = coordinates(a) - step * unit(a);
702 CHKERR Material::evaluateDeviator(parameters, plus, energy, &gp);
703 CHKERR Material::evaluateDeviator(parameters, minus, energy, &gm);
704 action(a) = tangent(a, b) * unit(b);
705 double error = 0.;
706 for (int col = 0; col != 5; ++col)
707 error =
708 std::max(error, std::abs(action(col) - (gp(col) - gm(col)) /
709 (2. * step)));
710 CHKERR testing->checkError<TestType::DERIVATIVE>(
711 "Pure logarithmic material tangent", error,
712 plasticCoordinateNorm(action), {2e-8, 2e-7});
713 }
714 }
715 SymmetricTensor zero{0., 0., 0., 0., 0., 0.};
716 SymmetricTensor identity{1., 0., 0., 1., 0., 1.};
719 map);
720 for (int row = 0; row != 5; ++row)
721 for (int col = 0; col != 5; ++col)
722 if (std::abs(map(row, col) + (row == col ? 1. : 0.)) > 1e-12)
723 SETERRQ(PETSC_COMM_SELF, MOFEM_ATOM_TEST_INVALID,
724 "Identity elastic sensitivity is not minus identity");
726 double energy = 0.;
729 energy, nullptr, &tangent);
730 FTensor::Index<'d', 5> d;
731 FTensor::Tensor2<double, 5, 5> tangent_map, estimate;
732 tangent_map(a, b) = tangent(a, d) * map(d, b);
733 estimate(a, b) = map(d, a) * tangent_map(d, b);
734 for (int row = 0; row != 5; ++row)
735 for (int col = 0; col != 5; ++col)
736 if (std::abs(estimate(row, col) - (row == col ? 6.8 : 0.)) >
737 1e-11)
738 SETERRQ(PETSC_COMM_SELF, MOFEM_ATOM_TEST_INVALID,
739 "Identity local material estimate is not 2*mu*I");
740 MOFEM_LOG("WORLD", Sev::inform)
741 << "Local stretch sensitivity maximum FD error " << worst_error;
743 });
744
745 CHKERR testing->runTests();
746 }
749 return 0;
750}
bool active
Eshelbian plasticity interface.
SmartPetscObj< Mat > metric
Neo-Hookean material law in orthonormal logarithmic coordinates.
boost::shared_ptr< DataAtIntegrationPts > data
Shared implementation details for plastic incremental optimization.
PetscErrorCode(* function)(TS, PetscReal, Vec, Vec, Vec, void *)
Plasticity implementation of incremental optimization.
SmartPetscObj< Vec > values
SmartPetscObj< Mat > inverse
#define FTENSOR_INDEXES(DIM,...)
int main()
constexpr double a
constexpr int SPACE_DIM
#define CATCH_ERRORS
Catch errors.
#define CHK_THROW_MESSAGE(err, msg)
Check and throw MoFEM exception.
#define MoFEMFunctionBegin
First executable line of each MoFEM function, used for error handling. Final line of MoFEM functions ...
@ MOFEM_OPERATION_UNSUCCESSFUL
Definition definitions.h:34
@ MOFEM_ATOM_TEST_INVALID
Definition definitions.h:40
@ MOFEM_INVALID_DATA
Definition definitions.h:36
#define MoFEMFunctionReturn(a)
Last executable line of each PETSc function used for error handling. Replaces return()
#define CHKERR
Inline error check.
#define MOFEM_LOG(channel, severity)
Log.
FTensor::Index< 'i', SPACE_DIM > i
PetscBool testing
const double c
speed of light (cm/ns)
const double v
phase velocity of light in medium (cm/ns)
const double n
refractive index of diffusive medium
FTensor::Index< 'l', 3 > l
FTensor::Index< 'j', 3 > j
FTensor::Index< 'k', 3 > k
boost::function< T(const T)> Fun
auto getMat(A &&t_val, B &&t_vec, Fun< double > f)
Get the Mat object.
auto getDiffMat(A &&t_val, B &&t_vec, Fun< double > f, Fun< double > d_f, const int nb)
Get the Diff Mat object.
auto getDiffDiffMat(A &&t_val, B &&t_vec, Fun< double > f, Fun< double > d_f, Fun< double > dd_f, C &&t_S, const int nb)
Get the Diff Diff Mat object.
PlasticLocalLawTermsT< T > evaluatePlasticLocalLawTerms(const FTensor::Tensor1< T, plasticLogarithmicStretchCoordinateSize > &t_increment, const T &latent, const PlasticLocalLawParametersT< S > &p)
MoFEMErrorCode evaluateLocalElasticLogarithmicSensitivity(const FTensor::Tensor2_symmetric< double, 3 > &plastic_h, const FTensor::Tensor2_symmetric< double, 3 > &elastic_stretch, FTensor::Tensor2< double, 5, 5 > &sensitivity)
double plasticCoordinateNorm(const FTensor::Tensor1< T, plasticLogarithmicStretchCoordinateSize > &t_values)
boost::shared_ptr< PlasticLocalLaw > createPlasticLocalLaw(boost::shared_ptr< MatOps::MatOpsData > mat_ops_data_ptr=nullptr)
FTensor::Tensor1< double, plasticLogarithmicStretchCoordinateSize > plasticLogarithmicStretchCoordinatesFromTensor(const FTensor::Tensor2_symmetric< T, SPACE_DIM > &t_values)
FTensor::Tensor2_symmetric< double, SPACE_DIM > plasticLogarithmicStretchTensorFromCoordinates(const FTensor::Tensor1< T, plasticLogarithmicStretchCoordinateSize > &t_coordinates)
static MoFEMErrorCodeGeneric< PetscErrorCode > ierr
PetscErrorCode MoFEMErrorCode
MoFEM/PETSc error code.
implementation of Data Operators for Forces and Sources
Definition Common.hpp:10
MoFEMErrorCode computeEigenValuesSymmetric(const MatrixDouble &mat, VectorDouble &eig, MatrixDouble &eigen_vec)
compute eigenvalues of a symmetric matrix using lapack dsyev
static char help[]
FTensor::Index< 'm', 3 > m
static MoFEMErrorCode evaluateDeviator(const Parameters &parameters, 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 Coordinates getCoordinates(const SymmetricTensor &t_tensor)
Project a symmetric tensor onto the fixed trace-free basis.
FTensor::Tensor1< double, plasticLogarithmicStretchCoordinateSize > IncrementTensor
Core (interface) class.
Definition Core.hpp:83
static MoFEMErrorCode Initialize(int *argc, char ***args, const char file[], const char help[])
Initializes the MoFEM database PETSc, MOAB and MPI.
Definition Core.cpp:68
static MoFEMErrorCode Finalize()
Checks for options to be called at the conclusion of the program.
Definition Core.cpp:123
Deprecated interface functions.
MoFEMErrorCode checkValue(const std::string &check_name, PetscReal actual, PetscReal expected, Tolerance tolerance, Sev error_severity=Sev::error, Sev success_severity=Sev::inform) const
MoFEMErrorCode checkError(const std::string &check_name, PetscReal error, PetscReal scale, Tolerance tolerance, Sev error_severity=Sev::error, Sev success_severity=Sev::inform) const
MoFEMErrorCode getInterface(IFACE *&iface) const
Get interface reference to pointer of interface.
double scale
Definition plastic.cpp:124