143 Vec smooth_gradient, Vec objective_gradient) {
145 CHKERR VecCopy(smooth_gradient, objective_gradient);
148 auto fe = createPlasticIncrementalElement(problem);
149 fe->f = objective_gradient;
154 auto kappa_increment_ptr = boost::make_shared<VectorDouble>();
155 auto committed_kappa_ptr = boost::make_shared<VectorDouble>();
158 control_data_dm, PlasticIncrementalOp::OPCOL,
159 kappa_field, kappa_increment_ptr, control_vec,
162 kappa_field, committed_kappa_ptr, MBTET));
165 new PlasticIncrementalOp(kappa_field, PlasticIncrementalOp::OPROW);
166 kappa_op->doWorkRhsHook =
170 if (row_data.getIndices().empty())
174 auto *op_ptr =
static_cast<PlasticIncrementalOp *
>(base_op_ptr);
178 const double local_gradient =
181 (t_committed_kappa + t_kappa_increment));
182 CHKERR VecSetValues<AssemblyTypeSelector<PETSC>>(
183 op_ptr->getKSPf(), row_data, &local_gradient, ADD_VALUES);
186 addTetrahedronOperator(fe, kappa_op);
190 CHKERR VecAssemblyBegin(objective_gradient);
191 CHKERR VecAssemblyEnd(objective_gradient);
192 CHKERR VecGhostUpdateBegin(objective_gradient, INSERT_VALUES,
194 CHKERR VecGhostUpdateEnd(objective_gradient, INSERT_VALUES, SCATTER_FORWARD);
260 DM constraint_dm =
nullptr;
261 CHKERR VecGetDM(constraints, &constraint_dm);
264 "Plastic inequality vector has no constraint DM");
266 auto fe = createPlasticIncrementalElement(problem);
270 auto plastic_increment_ptr = boost::make_shared<MatrixDouble>();
271 auto kappa_increment_ptr = boost::make_shared<VectorDouble>();
275 control_dm, PlasticIncrementalOp::OPCOL,
276 plastic_field, plastic_increment_ptr,
277 control_vec, MBTET));
279 control_dm, PlasticIncrementalOp::OPCOL,
280 kappa_field, kappa_increment_ptr, control_vec,
283 const double regularization_epsilon =
286 auto *constraint_op =
287 new PlasticIncrementalOp(kappa_field, PlasticIncrementalOp::OPROW);
288 constraint_op->doWorkRhsHook =
292 if (row_data.getIndices().empty())
294 auto *op_ptr =
static_cast<PlasticIncrementalOp *
>(base_op_ptr);
295 const int nb_integration_pts = op_ptr->getGaussPts().size2();
299 DL>::get(*plastic_increment_ptr, nb_integration_pts);
300 const auto t_increment = get_increment();
302 const double constraint_scale = std::sqrt(getCellMeasure(*op_ptr));
304 const double local_constraint =
308 regularization_epsilon));
309 CHKERR VecSetValues<AssemblyTypeSelector<PETSC>>(
310 op_ptr->getKSPf(), row_data, &local_constraint, INSERT_VALUES);
313 addTetrahedronOperator(fe, constraint_op);
317 CHKERR VecAssemblyBegin(constraints);
318 CHKERR VecAssemblyEnd(constraints);
319 CHKERR VecGhostUpdateBegin(constraints, INSERT_VALUES, SCATTER_FORWARD);
320 CHKERR VecGhostUpdateEnd(constraints, INSERT_VALUES, SCATTER_FORWARD);
326 Vec smooth_gradient, Mat jacobian) {
330 DM constraint_dm =
nullptr;
331 CHKERR MatGetDM(jacobian, &constraint_dm);
334 "Plastic inequality Jacobian has no constraint DM");
336 auto fe = createPlasticIncrementalElement(problem);
337 fe->ksp_B = jacobian;
338 const double regularization_epsilon =
344 auto increment_ptr = boost::make_shared<MatrixDouble>();
345 auto gradient_ptr = boost::make_shared<MatrixDouble>();
346 auto committed_kappa_ptr = boost::make_shared<VectorDouble>();
348 addTetrahedronOperator(fe,
351 control_dm, PlasticIncrementalOp::OPCOL,
352 plastic_field, increment_ptr, control_vec, MBTET));
353 addTetrahedronOperator(fe,
356 control_dm, PlasticIncrementalOp::OPCOL,
357 plastic_field, gradient_ptr, gradient_vec, MBTET));
359 kappa_field, committed_kappa_ptr, MBTET));
361 auto *flow_op =
new PlasticIncrementalOp(
362 kappa_field, plastic_field, PlasticIncrementalOp::OPROWCOL,
false);
363 flow_op->doWorkLhsHook =
364 [&](
DataOperator *base_op_ptr, int, int, EntityType, EntityType,
368 if (row_data.getIndices().empty() || col_data.getIndices().empty())
370 auto *op_ptr =
static_cast<PlasticIncrementalOp *
>(base_op_ptr);
371 const int nb_integration_pts = op_ptr->getGaussPts().size2();
375 DL>::get(*increment_ptr, nb_integration_pts);
378 DL>::get(*gradient_ptr, nb_integration_pts);
379 const auto t_increment = get_increment();
380 const auto t_gradient = get_gradient();
388 const double gradient_denominator =
390 regularization_epsilon);
394 if (gradient_denominator > 0) {
396 t_increment(
L) / gradient_denominator;
398 const double cell_measure = getCellMeasure(*op_ptr);
399 if (!(cell_measure > 0))
401 "Plastic P0 block has no positive cell measure");
404 t_cell_force(
L) = -t_gradient(
L) / cell_measure;
407 const double current_yield =
410 t_constraint_slope(
L) = t_cell_force(
L) / std::max(current_yield,
q);
413 const double constraint_scale = std::sqrt(getCellMeasure(*op_ptr));
415 auto t_local_matrix =
416 getFTensor1FromPtr<plasticLogarithmicStretchCoordinateSize>(
417 local_matrix.data().data());
418 t_local_matrix(
L) = -constraint_scale * t_constraint_slope(
L);
419 CHKERR MatSetValues<AssemblyTypeSelector<PETSC>>(
420 op_ptr->getKSPB(), row_data, col_data, local_matrix.data().data(),
424 addTetrahedronOperator(fe, flow_op);
426 auto *kappa_op =
new PlasticIncrementalOp(
427 kappa_field, kappa_field, PlasticIncrementalOp::OPROWCOL,
false);
428 kappa_op->doWorkLhsHook =
429 [&](
DataOperator *base_op_ptr, int, int, EntityType, EntityType,
433 if (row_data.getIndices().empty() || col_data.getIndices().empty())
435 auto *op_ptr =
static_cast<PlasticIncrementalOp *
>(base_op_ptr);
436 const double constraint_scale = std::sqrt(getCellMeasure(*op_ptr));
438 local_matrix(0, 0) = constraint_scale;
439 CHKERR MatSetValues<AssemblyTypeSelector<PETSC>>(
440 op_ptr->getKSPB(), row_data, col_data, local_matrix.data().data(),
444 addTetrahedronOperator(fe, kappa_op);
448 CHKERR MatAssemblyBegin(jacobian, MAT_FINAL_ASSEMBLY);
449 CHKERR MatAssemblyEnd(jacobian, MAT_FINAL_ASSEMBLY);
455 Vec control, Vec smooth_gradient, Vec inequality_multipliers,
456 PetscReal gradient_tolerance, PetscReal constraint_tolerance,
457 std::string &diagnostics) {
459 if (!constraint_dm || !control || !smooth_gradient ||
460 !inequality_multipliers)
462 "Plastic solution validation requires control and constraint DMs "
463 "with control, gradient, and multiplier vectors");
473 auto plastic_increment_ptr = boost::make_shared<MatrixDouble>();
474 auto kappa_increment_ptr = boost::make_shared<VectorDouble>();
475 auto gradient_ptr = boost::make_shared<MatrixDouble>();
476 auto multiplier_ptr = boost::make_shared<VectorDouble>();
477 auto committed_kappa_ptr = boost::make_shared<VectorDouble>();
478 auto metrics_ptr = boost::make_shared<PlasticValidationMetrics>();
480 auto fe = createPlasticIncrementalElement(problem);
483 control_dm, PlasticIncrementalOp::OPCOL,
484 plastic_field, plastic_increment_ptr,
485 control_vec, MBTET));
487 control_dm, PlasticIncrementalOp::OPCOL,
488 kappa_field, kappa_increment_ptr, control_vec,
490 addTetrahedronOperator(fe,
493 control_dm, PlasticIncrementalOp::OPCOL,
494 plastic_field, gradient_ptr, gradient_vec, MBTET));
497 PlasticIncrementalOp::OPROW, kappa_field,
498 multiplier_ptr, multiplier_vec, MBTET));
500 kappa_field, committed_kappa_ptr, MBTET));
502 auto *validation_op =
503 new PlasticIncrementalOp(kappa_field, PlasticIncrementalOp::OPROW);
504 validation_op->doWorkRhsHook =
505 [&, metrics_ptr](
DataOperator *base_op_ptr, int, EntityType,
508 if (row_data.getIndices().empty())
510 auto *op_ptr =
static_cast<PlasticIncrementalOp *
>(base_op_ptr);
511 const int nb_integration_pts = op_ptr->getGaussPts().size2();
515 DL>::get(*plastic_increment_ptr, nb_integration_pts);
518 DL>::get(*gradient_ptr, nb_integration_pts);
519 const auto t_increment = get_increment();
520 const auto t_gradient = get_gradient();
523 const double cell_measure = getCellMeasure(*op_ptr);
525 const double multiplier =
527 const double current_yield =
533 std::isfinite(cell_measure) && std::isfinite(delta_kappa) &&
534 std::isfinite(kappa_n) && kappa_n >= 0 &&
535 std::isfinite(current_yield) &&
536 current_yield > 0 && std::isfinite(multiplier) &&
540 metrics_ptr->nonfinite = 1;
543 if (!(cell_measure > std::numeric_limits<double>::epsilon())) {
544 metrics_ptr->invalidMeasure = 1;
550 t_force_coordinates(
L) = -t_gradient(
L) / cell_measure;
556 const double work = cell_measure * t_force(
i,
j) * t_flow(
i,
j);
562 const double constraint = delta_kappa - equivalent_increment;
564 const double gradient_denominator = std::hypot(
567 if (gradient_denominator > 0)
569 gradient_denominator;
572 t_force_coordinates(
L) / std::max(current_yield,
q);
576 t_kkt_residual(
L) = t_gradient(
L) + multiplier * t_slope(
L);
577 const double eta_stationarity_residual =
578 std::abs(cell_measure * current_yield - multiplier);
579 const double complementarity_residual =
580 std::abs(multiplier * constraint);
582 metrics_ptr->minWork = std::min(metrics_ptr->minWork, work);
583 metrics_ptr->maxYieldFunction =
584 std::max(metrics_ptr->maxYieldFunction,
q);
585 metrics_ptr->maxYieldExcess =
586 std::max(metrics_ptr->maxYieldExcess,
q - current_yield);
587 metrics_ptr->minConstraint =
588 std::min(metrics_ptr->minConstraint, constraint);
589 metrics_ptr->minMultiplier =
590 std::min(metrics_ptr->minMultiplier, multiplier);
591 metrics_ptr->maxActiveConstraint =
592 std::max(metrics_ptr->maxActiveConstraint, std::abs(constraint));
593 metrics_ptr->maxEtaStationarityResidual =
594 std::max(metrics_ptr->maxEtaStationarityResidual,
595 eta_stationarity_residual);
596 metrics_ptr->maxComplementarityResidual =
597 std::max(metrics_ptr->maxComplementarityResidual,
598 complementarity_residual);
599 metrics_ptr->maxKktResidual =
600 std::max(metrics_ptr->maxKktResidual,
602 metrics_ptr->maxEquivalentIncrement =
603 std::max(metrics_ptr->maxEquivalentIncrement, equivalent_increment);
606 addTetrahedronOperator(fe, validation_op);
611 PlasticValidationMetrics global_metrics;
612 const MPI_Comm comm =
613 PetscObjectComm(
reinterpret_cast<PetscObject
>(constraint_dm));
614 const auto reduce_max = [&](
const double local,
double &global) {
615 return MPI_Allreduce(&local, &global, 1, MPI_DOUBLE, MPI_MAX, comm);
617 const auto reduce_min = [&](
const double local,
double &global) {
618 return MPI_Allreduce(&local, &global, 1, MPI_DOUBLE, MPI_MIN, comm);
620 CHKERR reduce_min(metrics_ptr->minWork, global_metrics.minWork);
621 CHKERR reduce_max(metrics_ptr->maxYieldFunction,
622 global_metrics.maxYieldFunction);
623 CHKERR reduce_max(metrics_ptr->maxYieldExcess,
624 global_metrics.maxYieldExcess);
625 CHKERR reduce_min(metrics_ptr->minConstraint, global_metrics.minConstraint);
626 CHKERR reduce_min(metrics_ptr->minMultiplier, global_metrics.minMultiplier);
627 CHKERR reduce_max(metrics_ptr->maxActiveConstraint,
628 global_metrics.maxActiveConstraint);
629 CHKERR reduce_max(metrics_ptr->maxEtaStationarityResidual,
630 global_metrics.maxEtaStationarityResidual);
631 CHKERR reduce_max(metrics_ptr->maxComplementarityResidual,
632 global_metrics.maxComplementarityResidual);
633 CHKERR reduce_max(metrics_ptr->maxKktResidual,
634 global_metrics.maxKktResidual);
635 CHKERR reduce_max(metrics_ptr->maxEquivalentIncrement,
636 global_metrics.maxEquivalentIncrement);
637 CHKERR MPI_Allreduce(&metrics_ptr->nonfinite, &global_metrics.nonfinite, 1,
638 MPI_INT, MPI_MAX, comm);
639 CHKERR MPI_Allreduce(&metrics_ptr->invalidMeasure,
640 &global_metrics.invalidMeasure, 1, MPI_INT, MPI_MAX,
643 const double kkt_tolerance =
644 10 * std::max<double>(gradient_tolerance, 1e-10);
645 const double feasibility_tolerance =
646 10 * std::max<double>(constraint_tolerance, 1e-10);
647 const bool plastic_flow_active =
648 global_metrics.maxEquivalentIncrement > feasibility_tolerance;
649 if (global_metrics.nonfinite || global_metrics.invalidMeasure ||
650 global_metrics.minWork < -kkt_tolerance ||
651 global_metrics.minConstraint < -feasibility_tolerance ||
652 global_metrics.minMultiplier < -kkt_tolerance ||
653 global_metrics.maxActiveConstraint > feasibility_tolerance ||
654 global_metrics.maxEtaStationarityResidual > kkt_tolerance ||
655 global_metrics.maxComplementarityResidual > kkt_tolerance ||
656 global_metrics.maxKktResidual > kkt_tolerance)
658 "Incremental-optimization plastic acceptance failed: non-finite "
659 "%d, invalid measure %d, minimum work %g, max "
660 "yield excess %g, minimum constraint %g, minimum multiplier %g, "
661 "active residual %g, eta stationarity %g, complementarity %g, "
662 "plastic KKT residual %g",
663 global_metrics.nonfinite, global_metrics.invalidMeasure,
664 global_metrics.minWork,
665 global_metrics.maxYieldExcess, global_metrics.minConstraint,
666 global_metrics.minMultiplier, global_metrics.maxActiveConstraint,
667 global_metrics.maxEtaStationarityResidual,
668 global_metrics.maxComplementarityResidual,
669 global_metrics.maxKktResidual);
671 std::ostringstream stream;
672 stream <<
"max yield function " << global_metrics.maxYieldFunction
673 <<
", max yield excess " << global_metrics.maxYieldExcess
674 <<
", min plastic work " << global_metrics.minWork
675 <<
", min constraint " << global_metrics.minConstraint
676 <<
", min multiplier " << global_metrics.minMultiplier
677 <<
", active residual " << global_metrics.maxActiveConstraint
678 <<
", eta stationarity "
679 << global_metrics.maxEtaStationarityResidual
680 <<
", complementarity "
681 << global_metrics.maxComplementarityResidual
682 <<
", plastic KKT residual " << global_metrics.maxKktResidual
683 <<
", max equivalent plastic increment "
684 << global_metrics.maxEquivalentIncrement <<
", plastic flow active "
685 << (plastic_flow_active ?
"true" :
"false")
686 <<
", dissipation epsilon "
688 diagnostics = stream.str();