221int main(
int argc,
char *argv[]) {
224 moab::Core moab_core;
225 moab::Interface &moab = moab_core;
242 const std::array<const PlasticLocalLawParameters *, 2> parameter_sets{
243 &hardening, &perfect};
245 const Point
active{Increment{0.012, -0.004, 0.007, 0.002, -0.009},
247 const Point elastic{Increment{0., 0., 0., 0., 0.}, std::log(0.0005)};
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}};
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);
265 "density tape vs direct", terms.
density, direct.density,
268 "density = resistance + proximal", terms.
density,
271 "proximal Bregman term non-negative",
272 std::max(0., -terms.
proximal), 1., {0., 0.});
280 CHKERR testing->addTestCommandLine(
"", 2,
"reconstruction", [&]() {
282 CHKERR law->setParameters(hardening);
286 "slack equals exp(psi)",
288 std::exp(
active.psi), {1e-15, 1e-13});
290 "slack positive", terms.
slack > 0 ? 0. : 1., 1., {0., 0.});
291 const double exact_norm =
298 "ell_eps^2 = ell^2 + eps^2",
300 exact_norm * exact_norm +
301 hardening.regularisationEpsilon * hardening.regularisationEpsilon,
303 CHKERR law->evaluateValue(elastic.t_h, elastic.psi, terms);
306 hardening.regularisationEpsilon, {1e-15, 1e-13});
309 hardening.yieldStress +
310 hardening.hardeningModulus *
311 (hardening.committedKappa + hardening.regularisationEpsilon +
312 std::exp(elastic.psi)),
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}) {
325 CHKERR law->evaluateGradient(pt->t_h, pt->psi, t_g);
326 const auto t_closed = closedFormGradient(pt->t_h, pt->psi, *p);
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}) {
336 perturb(*pt, t_v, step, plus);
337 perturb(*pt, t_v, -step, minus);
339 CHKERR law->evaluateValue(plus.t_h, plus.psi, terms_plus);
340 CHKERR law->evaluateValue(minus.t_h, minus.psi, terms_minus);
344 const double tolerance = step == 1e-5 ? 1e-5 : 1e-7;
346 "gradient centred difference", std::abs(fd - assembled),
347 std::max(std::abs(fd), std::abs(assembled)),
359 CHKERR testing->addTestCommandLine(
"", 4,
"perfect plasticity", [&]() {
361 CHKERR law->setParameters(perfect);
364 Gradient t_g_star, t_g_other;
365 CHKERR law->evaluateGradient(
active.t_h, psi_star, t_g_star);
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) {
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)));
378 "tensor gradient independent of psi", tensor_difference, tensor_scale,
382 CHKERR law->evaluateGradient(
active.t_h, psi_star + 1., t_g_above);
384 "latent gradient positive above the root",
385 t_g_above(nbCoords) > 0 ? 0. : 1., 1., {0., 0.});
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);
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));
413 "tensor gradient shift equals A_h delta(e^psi) G", error,
scale,
419 CHKERR testing->addTestCommandLine(
"", 6,
"parameter validation", [&]() {
422 CHKERR PetscPushErrorHandler(PetscIgnoreErrorHandler,
nullptr);
423 const auto ierr = law->setParameters(bad);
424 CHKERR PetscPopErrorHandler();
425 return ierr != 0 ? 0. : 1.;
427 auto bad_eps = hardening;
428 bad_eps.regularisationEpsilon = 0;
430 "epsilon zero rejected", rejects(bad_eps), 1., {0., 0.});
431 auto bad_alpha = hardening;
432 bad_alpha.proximalCoefficient = 0;
434 "zero proximal coefficient rejected", rejects(bad_alpha), 1.,
436 auto bad_hardening = hardening;
437 bad_hardening.hardeningModulus = -1;
439 "negative hardening rejected", rejects(bad_hardening), 1., {0., 0.});
441 CHKERR law->setParameters(hardening);
443 "valid parameters restored",
444 law->getParameters().regularisationEpsilon,
445 hardening.regularisationEpsilon, {0., 0.});
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);
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});
469 const double step = pt == &elastic ? 1e-8 : 1e-7;
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,
477 HessianAction t_difference;
478 for (
int a = 0;
a != nbActive; ++
a)
480 (t_gradient_plus(
a) - t_gradient_minus(
a)) / (2. * step);
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});
489 CHKERR law->setParameters(perfect);
490 HessianAction t_perfect_cross;
493 double tensor_cross_norm = 0;
494 for (
int l = 0;
l != nbCoords; ++
l)
496 std::max(tensor_cross_norm, std::abs(t_perfect_cross(
l)));
498 "perfect-plastic tensor-latent Hessian coupling vanishes",
499 tensor_cross_norm, std::abs(t_perfect_cross(nbCoords)),
502 CHKERR law->setParameters(hardening);
503 HessianAction t_origin_action;
504 CHKERR law->evaluateHessianAction(elastic.t_h, elastic.psi, directions[0],
506 const auto origin_terms =
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 *
514 hardening.regularisationEpsilon;
516 std::max(origin_error, std::abs(t_origin_action(
l) - expected));
517 origin_scale = std::max(origin_scale, std::abs(expected));
520 "regularised tensor curvature at the origin", origin_error,
521 origin_scale, {1e-10, 1e-12});
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);
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);
546 constexpr double step = 2e-6;
547 SymmetricTensor t_plus_h;
548 SymmetricTensor t_minus_h;
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);
554 PlasticExponentialDirectionalData plus_data;
555 PlasticExponentialDirectionalData minus_data;
556 CHKERR evaluatePlasticExponentialDirectionalData(t_plus_h, t_v,
558 CHKERR evaluatePlasticExponentialDirectionalData(t_minus_h, t_v,
561 SymmetricTensor t_first_difference_f;
562 SymmetricTensor t_first_difference_inverse;
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)) /
568 t_first_difference_inverse(
i,
j) =
569 (plus_data.inversePlasticF(
i,
j) -
570 minus_data.inversePlasticF(
i,
j)) /
575 maxAbsDifference(
data.directionPlasticF, t_first_difference_f),
576 std::max(maxAbs(
data.directionPlasticF),
577 maxAbs(t_first_difference_f)),
581 maxAbsDifference(
data.directionInversePlasticF,
582 t_first_difference_inverse),
583 std::max(maxAbs(
data.directionInversePlasticF),
584 maxAbs(t_first_difference_inverse)),
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;
593 difference_second_contraction +=
595 (plus_data.directionPlasticF(
i,
j) -
596 minus_data.directionPlasticF(
i,
j)) /
600 std::abs(exact_second_contraction -
601 difference_second_contraction),
602 std::max(std::abs(exact_second_contraction),
603 std::abs(difference_second_contraction)),
609 "", 9,
"local elastic logarithmic sensitivity", [&]() {
614 const auto function = [](
const SymmetricTensor &tensor,
619 vectors(
m,
n) = tensor(
m,
n);
621 "Diagonalise local sensitivity test tensor");
631 return .5 * std::log(x);
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]}) {
653 for (
int n = 0;
n != 5; ++
n)
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);
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 =
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);
680 "Direct Ue sensitivity vs fixed-F differences", error,
683 "Isochoric elastic logarithmic sensitivity",
684 std::abs(derivative(
i,
i)), 1., {2e-8, 2e-8});
695 for (
int n = 0;
n != 5; ++
n) {
696 constexpr double step = 2e-6;
697 Increment unit, plus, minus, gp, gm, action;
700 plus(
a) = coordinates(
a) + step * unit(
a);
701 minus(
a) = coordinates(
a) - step * unit(
a);
704 action(
a) = tangent(
a, b) * unit(b);
706 for (
int col = 0; col != 5; ++col)
708 std::max(error, std::abs(action(col) - (gp(col) - gm(col)) /
711 "Pure logarithmic material tangent", error,
715 SymmetricTensor zero{0., 0., 0., 0., 0., 0.};
716 SymmetricTensor identity{1., 0., 0., 1., 0., 1.};
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)
724 "Identity elastic sensitivity is not minus identity");
729 energy,
nullptr, &tangent);
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.)) >
739 "Identity local material estimate is not 2*mu*I");
741 <<
"Local stretch sensitivity maximum FD error " << worst_error;