v0.16.3
Loading...
Searching...
No Matches
PlasticIncrementalOptimizationTests.cpp
Go to the documentation of this file.
1/**
2 * @file PlasticIncrementalOptimizationTests.cpp
3 * @brief Atom tests for incremental optimization
4 */
5
6#define SINGULARITY
7#include <MoFEM.hpp>
8using namespace MoFEM;
9
11
12#include <Lie.hpp>
13#include <MatrixFunction.hpp>
16
17namespace EshelbianPlasticity {
18
19using namespace PlasticIncrementalOptimizationInternal;
20
21namespace {
22
23struct OpVerifyPlasticControl : public VolUserDataOperator {
24 OpVerifyPlasticControl(boost::shared_ptr<MatrixDouble> values,
25 boost::shared_ptr<VectorDouble> kappa_increments,
26 const double expected_kappa_increment,
27 boost::shared_ptr<PetscInt> element_count)
28 : VolUserDataOperator(NOSPACE, OPSPACE), valuesPtr(std::move(values)),
29 kappaIncrementsPtr(std::move(kappa_increments)),
30 expectedKappaIncrement(expected_kappa_increment),
31 elementCount(std::move(element_count)) {}
32
33 MoFEMErrorCode doWork(int, EntityType, EntData &) override {
35 const int number_of_points = getGaussPts().size2();
36 if (!valuesPtr || valuesPtr->size1() != number_of_points ||
37 valuesPtr->size2() != 6)
38 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
39 "Plastic-flow reconstruction has shape %zu x %zu, expected "
40 "%d x 6",
41 valuesPtr ? valuesPtr->size1() : 0,
42 valuesPtr ? valuesPtr->size2() : 0, number_of_points);
43 if (!kappaIncrementsPtr ||
44 kappaIncrementsPtr->size() != number_of_points)
45 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
46 "Plastic-kappa increment reconstruction has size %zu, "
47 "expected %d",
49 number_of_points);
50 if (number_of_points <= 1)
51 SETERRQ(PETSC_COMM_SELF, MOFEM_ATOM_TEST_INVALID,
52 "Plastic-flow reconstruction must exercise multiple "
53 "quadrature points");
54
55 auto t_flow = getFTensor2SymmetricFromMat<3>(*valuesPtr);
57 t_coordinates(1., 2., 3., 4., 5.);
58 const auto t_expected =
60 for (int gg = 0; gg != number_of_points; ++gg) {
61 for (int row = 0; row != 3; ++row)
62 for (int column = row; column != 3; ++column)
63 if (t_flow(row, column) != t_expected(row, column))
64 SETERRQ(PETSC_COMM_SELF, MOFEM_ATOM_TEST_INVALID,
65 "Trace-free plastic-flow reconstruction failed at "
66 "point %d component (%d,%d): %.17g != %.17g",
67 gg, row, column, t_flow(row, column),
68 t_expected(row, column));
70 SETERRQ(PETSC_COMM_SELF, MOFEM_ATOM_TEST_INVALID,
71 "Vector-backed plastic-kappa increment reconstruction failed "
72 "at point %d: %.17g != %.17g",
74 ++t_flow;
75 }
76 ++*elementCount;
78 }
79
80private:
81 boost::shared_ptr<MatrixDouble> valuesPtr;
82 boost::shared_ptr<VectorDouble> kappaIncrementsPtr;
84 boost::shared_ptr<PetscInt> elementCount;
85};
86
87using TestControlBlockVisitor =
88 std::function<MoFEMErrorCode(PetscInt, PetscScalar *)>;
89using TestOpAssemblePlasticCellMeasure =
91 GAUSS>::OpSource<1, 1>;
92
93MoFEMErrorCode getTestFieldIS(DM dm, const std::string &field_name,
94 SmartPetscObj<IS> &field_is) {
96 if (!dm)
97 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
98 "Cannot create a field IS from a null DM");
99 IS raw_is = nullptr;
100 CHKERR DMMoFEMGetFieldIS(dm, RowColData::ROW, field_name.c_str(), &raw_is);
101 field_is = SmartPetscObj<IS>(raw_is);
103}
104
105MoFEMErrorCode getTestFieldBlockCounts(DM dm,
106 const std::string &field_name,
107 const PetscInt block_size,
108 PetscInt &local_count,
109 PetscInt &global_count) {
111 SmartPetscObj<IS> field_is;
112 CHKERR getTestFieldIS(dm, field_name, field_is);
113 PetscInt local_size = 0;
114 PetscInt global_size = 0;
115 CHKERR ISGetLocalSize(field_is, &local_size);
116 CHKERR ISGetSize(field_is, &global_size);
117 if (block_size <= 0 || local_size % block_size || global_size % block_size)
118 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
119 "Invalid %s field layout: local/global sizes %d/%d, block %d",
120 field_name.c_str(), static_cast<int>(local_size),
121 static_cast<int>(global_size), static_cast<int>(block_size));
122 local_count = local_size / block_size;
123 global_count = global_size / block_size;
125}
126
127MoFEMErrorCode forEachOwnedTestFieldBlock(
128 DM dm, const std::string &field_name, const PetscInt block_size, Vec vec,
129 const TestControlBlockVisitor &visitor) {
131 if (!vec || block_size <= 0)
132 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
133 "Invalid vector or block size for field %s", field_name.c_str());
134 SmartPetscObj<IS> field_is;
135 CHKERR getTestFieldIS(dm, field_name, field_is);
136 Vec field_vec = nullptr;
137 CHKERR VecGetSubVector(vec, field_is, &field_vec);
138 PetscRestoreGuard field_vec_guard(
139 [&] { return VecRestoreSubVector(vec, field_is, &field_vec); });
140 PetscInt local_size = 0;
141 CHKERR VecGetLocalSize(field_vec, &local_size);
142 if (local_size % block_size) {
143 CHKERR field_vec_guard.restore();
144 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
145 "Field %s local size %d is not divisible by block size %d",
146 field_name.c_str(), static_cast<int>(local_size),
147 static_cast<int>(block_size));
148 }
149 PetscScalar *array = nullptr;
150 CHKERR VecGetArray(field_vec, &array);
151 PetscRestoreGuard array_guard(
152 [&] { return VecRestoreArray(field_vec, &array); });
153 MoFEMErrorCode visitor_error = 0;
154 for (PetscInt block = 0; block != local_size / block_size; ++block) {
155 visitor_error = visitor(block, array + block * block_size);
156 if (visitor_error)
157 break;
158 }
159 const MoFEMErrorCode restore_array_error = array_guard.restore();
160 const MoFEMErrorCode restore_vector_error = field_vec_guard.restore();
161 CHKERR visitor_error;
162 CHKERR restore_array_error;
163 CHKERR restore_vector_error;
165}
166
167MoFEMErrorCode getOwnedTestFieldEntities(
168 EshelbianCore &ep, DM dm, const std::string &field_name,
169 const PetscInt block_size, std::vector<EntityHandle> &entities) {
171 entities.clear();
172 SmartPetscObj<IS> field_is;
173 CHKERR getTestFieldIS(dm, field_name, field_is);
174 PetscInt local_size = 0;
175 CHKERR ISGetLocalSize(field_is, &local_size);
176 if (block_size <= 0 || local_size % block_size)
177 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
178 "Invalid owned %s field layout", field_name.c_str());
179 const Problem *problem_ptr = nullptr;
180 CHKERR DMMoFEMGetProblemPtr(dm, &problem_ptr);
181 if (!problem_ptr || !problem_ptr->getNumeredRowDofsPtr())
182 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
183 "Field %s DM has no numbered row DOFs", field_name.c_str());
184 const auto &dofs =
185 problem_ptr->getNumeredRowDofsPtr()->get<PetscGlobalIdx_mi_tag>();
186 const PetscInt *indices = nullptr;
187 CHKERR ISGetIndices(field_is, &indices);
188 PetscRestoreGuard indices_guard(
189 [&] { return ISRestoreIndices(field_is, &indices); });
190 entities.reserve(local_size / block_size);
191 for (PetscInt base = 0; base != local_size; base += block_size) {
192 const auto first_dof = dofs.find(indices[base]);
193 if (first_dof == dofs.end())
195 "Missing field %s DOF %d", field_name.c_str(),
196 static_cast<int>(indices[base]));
197 const EntityHandle entity = (*first_dof)->getEnt();
198 entities.emplace_back(entity);
199 for (PetscInt coefficient = 0; coefficient != block_size; ++coefficient) {
200 const auto dof = dofs.find(indices[base + coefficient]);
201 if (dof == dofs.end() || (*dof)->getEnt() != entity ||
202 (*dof)->getDofOrder() != 0 ||
203 (*dof)->getDofCoeffIdx() != coefficient)
205 "Field %s is not an entity-ordered P0 block layout",
206 field_name.c_str());
207 }
208 }
209 CHKERR indices_guard.restore();
211}
212
213MoFEMErrorCode getOwnedTestKappaValues(EshelbianCore &ep, DM control_dm,
214 std::vector<double> &values) {
216 std::vector<EntityHandle> entities;
217 CHKERR getOwnedTestFieldEntities(ep, control_dm, ep.plasticKappaField, 1,
218 entities);
219 PlasticKappaHistory history;
220 CHKERR savePlasticKappaHistory(ep, history);
221 values.resize(entities.size());
222 for (std::size_t block = 0; block != entities.size(); ++block) {
223 const auto history_it = history.find(entities[block]);
224 if (history_it == history.end())
226 "Missing committed plastic kappa for entity %llu",
227 static_cast<unsigned long long>(entities[block]));
228 values[block] = history_it->second;
229 }
231}
232
233MoFEMErrorCode createTestPlasticCellMeasure(EshelbianCore &ep, DM control_dm,
234 SmartPetscObj<Vec> &measure) {
236 measure = createDMVector(control_dm, RowColData::ROW);
237 CHKERR VecZeroEntries(measure);
238 if (!ep.plasticVolume)
240 auto fe = boost::make_shared<VolumeElementForcesAndSourcesCore>(ep.mField);
241 fe->getUserPolynomialBase() =
242 boost::make_shared<CGGUserPolynomialBase>(nullptr, true);
243 fe->ksp_f = measure;
244 fe->getRuleHook = [](int, int, int order_data) {
245 return 2 * (order_data + 1);
246 };
248 fe->getOpPtrVector(), {NOSPACE}, ep.materialH1Positions);
249 fe->getOpPtrVector().push_back(new TestOpAssemblePlasticCellMeasure(
250 ep.plasticKappaField, [](double, double, double) { return 1.; }));
251 fe->exeTestHook = [&ep](FEMethod *method) {
252 return ep.plasticVolumes->find(method->getFEEntityHandle()) !=
253 ep.plasticVolumes->end();
254 };
256 CHKERR VecAssemblyBegin(measure);
257 CHKERR VecAssemblyEnd(measure);
259}
260
261MoFEMErrorCode getTestPlasticConstraintScales(
262 Vec cell_measure, IS kappa_is, std::vector<double> &scales) {
264 Vec measures = nullptr;
265 CHKERR VecGetSubVector(cell_measure, kappa_is, &measures);
266 PetscInt size = 0;
267 CHKERR VecGetLocalSize(measures, &size);
268 const PetscScalar *values = nullptr;
269 CHKERR VecGetArrayRead(measures, &values);
270 scales.resize(size);
271 for (PetscInt block = 0; block != size; ++block)
272 scales[block] = std::sqrt(PetscRealPart(values[block]));
273 CHKERR VecRestoreArrayRead(measures, &values);
274 CHKERR VecRestoreSubVector(cell_measure, kappa_is, &measures);
276}
277
278MoFEMErrorCode setControlField(EshelbianCore &ep, const std::string &field,
279 Range entities, const std::size_t block_size) {
281 auto set = [&](boost::shared_ptr<FieldEntity> field_entity) {
283 auto data = field_entity->getEntFieldData();
284 if (data.size() != block_size)
286 "Field %s has %zu coefficients on entity %llu; expected %zu",
287 field.c_str(), data.size(),
288 static_cast<unsigned long long>(field_entity->getEnt()),
289 block_size);
290 for (std::size_t coefficient = 0; coefficient != block_size; ++coefficient)
291 data[coefficient] = coefficient + 1.;
293 };
294 CHKERR ep.mField.getInterface<FieldBlas>()->fieldLambdaOnEntities(set, field,
295 &entities);
297}
298
300checkControlLayout(EshelbianCore &ep, DM control_dm,
301 const std::string &field_name, Range entities,
302 const PetscInt expected_block_size) {
304 ParallelComm *parallel =
305 ParallelComm::get_pcomm(&ep.mField.get_moab(), MYPCOMM_INDEX);
306 if (!parallel)
308 "ParallelComm is unavailable while checking control layout");
309 Range owned_entities;
310 CHKERR parallel->filter_pstatus(entities, PSTATUS_NOT_OWNED, PSTATUS_NOT, -1,
311 &owned_entities);
312 PetscInt expected_local = owned_entities.size();
313 PetscInt expected_global = 0;
314 const int mpi_error = MPI_Allreduce(&expected_local, &expected_global, 1,
315 MPIU_INT, MPI_SUM, ep.mField.get_comm());
316 if (mpi_error != MPI_SUCCESS)
318 "MPI reduction of control entity counts failed");
319
320 PetscInt actual_local = 0;
321 PetscInt actual_global = 0;
322 CHKERR getTestFieldBlockCounts(control_dm, field_name, expected_block_size,
323 actual_local, actual_global);
324 if (actual_local != expected_local || actual_global != expected_global)
326 "Field %s layout mismatch: block/local/global = %d/%d/%d, "
327 "expected %d/%d/%d",
328 field_name.c_str(), static_cast<int>(expected_block_size),
329 static_cast<int>(actual_local), static_cast<int>(actual_global),
330 static_cast<int>(expected_block_size),
331 static_cast<int>(expected_local),
332 static_cast<int>(expected_global));
334}
335
336MoFEMErrorCode checkPlasticControlEntityPairing(
337 EshelbianCore &ep, const PlasticIncrementalOptimizationProblem &problem) {
339 const Problem *dm_problem_ptr = nullptr;
341 if (!dm_problem_ptr)
343 "Incremental-optimization DM has no problem");
344
345 SmartPetscObj<IS> plastic_is;
346 SmartPetscObj<IS> kappa_is;
347 CHKERR getTestFieldIS(problem.getControlDM(),
348 problem.getPlasticFlowFieldName(), plastic_is);
349 CHKERR getTestFieldIS(problem.getControlDM(),
350 problem.getPlasticKappaFieldName(), kappa_is);
351 PetscInt plastic_size = 0;
352 PetscInt epigraph_size = 0;
353 CHKERR ISGetLocalSize(plastic_is, &plastic_size);
354 CHKERR ISGetLocalSize(kappa_is, &epigraph_size);
355 if (plastic_size != plasticLogarithmicStretchCoordinateSize * epigraph_size)
357 "Plastic and epigraph local layouts cannot be paired");
358
359 const PetscInt *plastic_indices = nullptr;
360 const PetscInt *epigraph_indices = nullptr;
361 CHKERR ISGetIndices(plastic_is, &plastic_indices);
362 CHKERR ISGetIndices(kappa_is, &epigraph_indices);
363 const auto &dofs_by_global =
364 dm_problem_ptr->getNumeredRowDofsPtr()->get<PetscGlobalIdx_mi_tag>();
365 std::vector<double> entity_measures(epigraph_size);
366 for (PetscInt block = 0; block != epigraph_size; ++block) {
367 const auto epigraph_dof = dofs_by_global.find(epigraph_indices[block]);
368 if (epigraph_dof == dofs_by_global.end())
370 "Missing epigraph DOF %d in the control DM",
371 static_cast<int>(epigraph_indices[block]));
372 const EntityHandle entity = (*epigraph_dof)->getEnt();
373 if ((*epigraph_dof)->getDofCoeffIdx() != 0)
375 "Epigraph block %d does not start at coefficient zero",
376 static_cast<int>(block));
377 for (PetscInt coordinate = 0;
378 coordinate != plasticLogarithmicStretchCoordinateSize; ++coordinate) {
379 const PetscInt offset =
380 block * plasticLogarithmicStretchCoordinateSize + coordinate;
381 const auto plastic_dof = dofs_by_global.find(plastic_indices[offset]);
382 if (plastic_dof == dofs_by_global.end())
384 "Missing plastic DOF %d in the control DM",
385 static_cast<int>(plastic_indices[offset]));
386 if ((*plastic_dof)->getEnt() != entity ||
387 (*plastic_dof)->getDofCoeffIdx() != coordinate)
389 "Plastic block %d and epigraph block %d refer to different "
390 "entities or coefficient orderings",
391 static_cast<int>(block), static_cast<int>(block));
392 }
393
394 const EntityHandle *connectivity = nullptr;
395 int number_of_nodes = 0;
396 std::vector<EntityHandle> connectivity_storage;
397 CHKERR ep.mField.get_moab().get_connectivity(
398 entity, connectivity, number_of_nodes, true, &connectivity_storage);
399 if (number_of_nodes != 4)
401 "Plastic P0 entity %llu is not a four-node tetrahedron",
402 static_cast<unsigned long long>(entity));
403 double coordinates[12];
404 CHKERR ep.mField.get_moab().get_coords(connectivity, number_of_nodes,
405 coordinates);
406 const double a_x = coordinates[3] - coordinates[0];
407 const double a_y = coordinates[4] - coordinates[1];
408 const double a_z = coordinates[5] - coordinates[2];
409 const double b_x = coordinates[6] - coordinates[0];
410 const double b_y = coordinates[7] - coordinates[1];
411 const double b_z = coordinates[8] - coordinates[2];
412 const double c_x = coordinates[9] - coordinates[0];
413 const double c_y = coordinates[10] - coordinates[1];
414 const double c_z = coordinates[11] - coordinates[2];
415 entity_measures[block] =
416 std::abs(a_x * (b_y * c_z - b_z * c_y) - a_y * (b_x * c_z - b_z * c_x) +
417 a_z * (b_x * c_y - b_y * c_x)) /
418 6.;
419 }
420 CHKERR ISRestoreIndices(kappa_is, &epigraph_indices);
421 CHKERR ISRestoreIndices(plastic_is, &plastic_indices);
422
423 SmartPetscObj<Vec> plastic_cell_measure;
424 CHKERR createTestPlasticCellMeasure(ep, problem.getControlDM(),
425 plastic_cell_measure);
426 Vec measures = nullptr;
427 CHKERR VecGetSubVector(plastic_cell_measure, kappa_is, &measures);
428 PetscInt measure_size = 0;
429 CHKERR VecGetLocalSize(measures, &measure_size);
430 if (measure_size != epigraph_size) {
431 CHKERR VecRestoreSubVector(plastic_cell_measure, kappa_is, &measures);
433 "Cell-measure and epigraph entity layouts differ");
434 }
435 const PetscScalar *measure_array = nullptr;
436 CHKERR VecGetArrayRead(measures, &measure_array);
437 double maximum_measure_error_local = 0;
438 double minimum_entity_measure_local = std::numeric_limits<double>::max();
439 double maximum_entity_measure_local = 0;
440 for (PetscInt block = 0; block != measure_size; ++block) {
441 const double assembled_measure = PetscRealPart(measure_array[block]);
442 const double entity_measure = entity_measures[block];
443 if (!std::isfinite(assembled_measure) || !(assembled_measure > 0) ||
444 !std::isfinite(entity_measure) || !(entity_measure > 0))
446 "Epigraph entity block %d has invalid assembled/entity cell "
447 "measure %g/%g",
448 static_cast<int>(block), assembled_measure, entity_measure);
449 maximum_measure_error_local =
450 std::max(maximum_measure_error_local,
451 std::abs(assembled_measure - entity_measure));
452 minimum_entity_measure_local =
453 std::min(minimum_entity_measure_local, entity_measure);
454 maximum_entity_measure_local =
455 std::max(maximum_entity_measure_local, entity_measure);
456 }
457 CHKERR VecRestoreArrayRead(measures, &measure_array);
458 CHKERR VecRestoreSubVector(plastic_cell_measure, kappa_is, &measures);
459 double maximum_measure_error = 0;
460 double minimum_entity_measure = 0;
461 double maximum_entity_measure = 0;
462 CHKERR MPI_Allreduce(&maximum_measure_error_local, &maximum_measure_error, 1,
463 MPI_DOUBLE, MPI_MAX, ep.mField.get_comm());
464 CHKERR MPI_Allreduce(&minimum_entity_measure_local, &minimum_entity_measure,
465 1, MPI_DOUBLE, MPI_MIN, ep.mField.get_comm());
466 CHKERR MPI_Allreduce(&maximum_entity_measure_local, &maximum_entity_measure,
467 1, MPI_DOUBLE, MPI_MAX, ep.mField.get_comm());
468 const double measure_tolerance = 1e-11 * std::max(1., maximum_entity_measure);
469 if (maximum_measure_error > measure_tolerance)
471 "Epigraph measure is not paired with its tetrahedral entity: "
472 "error %g > %g",
473 maximum_measure_error, measure_tolerance);
474 MOFEM_LOG("EP", Sev::inform)
475 << "Entity-paired P0 measure range [" << minimum_entity_measure << ", "
476 << maximum_entity_measure << "], maximum assembly error "
477 << maximum_measure_error;
479}
480
481MoFEMErrorCode checkSyntheticActiveFlowValidation(
482 EshelbianCore &ep,
483 const boost::shared_ptr<IncrementalOptimizationContext> &context) {
486 auto control = vectorDuplicate(context->referenceControl);
487 auto smooth_gradient = vectorDuplicate(context->gradient);
488 auto multipliers = vectorDuplicate(context->inequalityConstraints);
489 auto initial_multipliers = vectorDuplicate(context->inequalityConstraints);
490 CHKERR VecZeroEntries(control);
491 CHKERR VecZeroEntries(smooth_gradient);
492 CHKERR VecZeroEntries(multipliers);
493 CHKERR VecZeroEntries(initial_multipliers);
494 CHKERR problem.initialiseInequalityMultipliers(context->constraintDM,
495 initial_multipliers);
496 SmartPetscObj<IS> plastic_is;
497 SmartPetscObj<IS> kappa_is;
498 CHKERR getTestFieldIS(problem.getControlDM(),
499 problem.getPlasticFlowFieldName(), plastic_is);
500 CHKERR getTestFieldIS(problem.getControlDM(),
501 problem.getPlasticKappaFieldName(), kappa_is);
502 SmartPetscObj<Vec> plastic_cell_measure;
503 CHKERR createTestPlasticCellMeasure(ep, problem.getControlDM(),
504 plastic_cell_measure);
505 std::vector<double> committed_kappa;
506 CHKERR getOwnedTestKappaValues(ep, problem.getControlDM(), committed_kappa);
507
508 Vec plastic = nullptr;
509 Vec epigraph = nullptr;
510 Vec plastic_gradient = nullptr;
511 Vec measures = nullptr;
512 CHKERR VecGetSubVector(control, plastic_is, &plastic);
513 CHKERR VecGetSubVector(control, kappa_is, &epigraph);
514 CHKERR VecGetSubVector(smooth_gradient, plastic_is, &plastic_gradient);
515 CHKERR VecGetSubVector(plastic_cell_measure, kappa_is, &measures);
516 PetscInt plastic_size = 0;
517 PetscInt epigraph_size = 0;
518 PetscInt gradient_size = 0;
519 PetscInt measure_size = 0;
520 PetscInt multiplier_size = 0;
521 CHKERR VecGetLocalSize(plastic, &plastic_size);
522 CHKERR VecGetLocalSize(epigraph, &epigraph_size);
523 CHKERR VecGetLocalSize(plastic_gradient, &gradient_size);
524 CHKERR VecGetLocalSize(measures, &measure_size);
525 CHKERR VecGetLocalSize(multipliers, &multiplier_size);
526 if (plastic_size != plasticLogarithmicStretchCoordinateSize * epigraph_size ||
527 gradient_size != plastic_size || measure_size != epigraph_size ||
528 multiplier_size != epigraph_size ||
529 static_cast<PetscInt>(committed_kappa.size()) != epigraph_size)
531 "Synthetic active-flow vector layouts are inconsistent");
532
533 PetscScalar *plastic_array = nullptr;
534 PetscScalar *epigraph_array = nullptr;
535 PetscScalar *gradient_array = nullptr;
536 PetscScalar *multiplier_array = nullptr;
537 const PetscScalar *initial_multiplier_array = nullptr;
538 const PetscScalar *measure_array = nullptr;
539 CHKERR VecGetArray(plastic, &plastic_array);
540 CHKERR VecGetArray(epigraph, &epigraph_array);
541 CHKERR VecGetArray(plastic_gradient, &gradient_array);
542 CHKERR VecGetArrayRead(measures, &measure_array);
543 CHKERR VecGetArray(multipliers, &multiplier_array);
544 CHKERR VecGetArrayRead(initial_multipliers, &initial_multiplier_array);
546 t_direction(.31, -.17, .23, -.29, .19);
547 const double direction_norm = plasticCoordinateNorm(t_direction);
549 double initial_multiplier_error_local = 0;
550 for (PetscInt block = 0; block != epigraph_size; ++block) {
551 auto t_flow = getFTensor1FromPtr<plasticLogarithmicStretchCoordinateSize>(
552 plastic_array + block * plasticLogarithmicStretchCoordinateSize);
553 auto t_gradient =
554 getFTensor1FromPtr<plasticLogarithmicStretchCoordinateSize>(
555 gradient_array + block * plasticLogarithmicStretchCoordinateSize);
556 const double flow_norm = .02 * (1. + .05 * block);
557 t_flow(L) = flow_norm * t_direction(L) / direction_norm;
558 const double denominator =
559 std::hypot(plasticEquivalentIncrementScale * flow_norm,
560 problem.getDissipationRegularizationEpsilon());
561 const double alpha =
562 denominator - problem.getDissipationRegularizationEpsilon();
563 const double current_yield =
564 problem.getInitialYieldStress() +
565 problem.getIsotropicHardeningModulus() *
566 (committed_kappa[block] + alpha);
567 const double lambda = PetscRealPart(measure_array[block]) * current_yield;
568 epigraph_array[block] = alpha;
569 multiplier_array[block] =
570 lambda / std::sqrt(PetscRealPart(measure_array[block]));
571 const double initial_yield =
572 problem.getInitialYieldStress() +
573 problem.getIsotropicHardeningModulus() * committed_kappa[block];
574 initial_multiplier_error_local = std::max(
575 initial_multiplier_error_local,
576 std::abs(std::sqrt(PetscRealPart(measure_array[block])) *
577 PetscRealPart(initial_multiplier_array[block]) -
578 PetscRealPart(measure_array[block]) * initial_yield));
580 t_flow(L) / denominator;
581 }
582 CHKERR VecRestoreArrayRead(initial_multipliers, &initial_multiplier_array);
583 CHKERR VecRestoreArray(multipliers, &multiplier_array);
584 CHKERR VecRestoreArrayRead(measures, &measure_array);
585 CHKERR VecRestoreArray(plastic_gradient, &gradient_array);
586 CHKERR VecRestoreArray(epigraph, &epigraph_array);
587 CHKERR VecRestoreArray(plastic, &plastic_array);
588 CHKERR VecRestoreSubVector(plastic_cell_measure, kappa_is, &measures);
589 CHKERR VecRestoreSubVector(smooth_gradient, plastic_is, &plastic_gradient);
590 CHKERR VecRestoreSubVector(control, kappa_is, &epigraph);
591 CHKERR VecRestoreSubVector(control, plastic_is, &plastic);
592 CHKERR VecGhostUpdateBegin(control, INSERT_VALUES, SCATTER_FORWARD);
593 CHKERR VecGhostUpdateEnd(control, INSERT_VALUES, SCATTER_FORWARD);
594 CHKERR VecGhostUpdateBegin(smooth_gradient, INSERT_VALUES, SCATTER_FORWARD);
595 CHKERR VecGhostUpdateEnd(smooth_gradient, INSERT_VALUES, SCATTER_FORWARD);
596
597 constexpr PetscReal gradient_tolerance = 1e-10;
598 constexpr PetscReal constraint_tolerance = 1e-10;
599 double initial_multiplier_error = 0;
600 CHKERR MPI_Allreduce(&initial_multiplier_error_local,
601 &initial_multiplier_error, 1, MPI_DOUBLE, MPI_MAX,
602 ep.mField.get_comm());
603 if (!std::isfinite(initial_multiplier_error) ||
604 initial_multiplier_error > gradient_tolerance)
606 "Volume-scaled multiplier initialization violates eta "
607 "stationarity: error %g",
608 initial_multiplier_error);
609
610 auto objective_gradient = vectorDuplicate(smooth_gradient);
611 auto stationarity = vectorDuplicate(smooth_gradient);
612 CHKERR problem.assembleObjectiveGradient(control, smooth_gradient,
613 objective_gradient);
614 CHKERR MatZeroEntries(context->inequalityJacobian);
615 CHKERR problem.assembleInequalityJacobian(control, smooth_gradient,
616 context->inequalityJacobian);
617 CHKERR MatMultTranspose(context->inequalityJacobian, multipliers,
618 stationarity);
619 CHKERR VecAXPY(stationarity, -1., objective_gradient);
620 PetscReal stationarity_norm = 0;
621 CHKERR VecNorm(stationarity, NORM_INFINITY, &stationarity_norm);
622 if (!std::isfinite(stationarity_norm) ||
623 stationarity_norm > gradient_tolerance)
625 "Volume-scaled Jacobian and multipliers violate physical "
626 "stationarity: residual %g",
627 stationarity_norm);
628
629 std::string diagnostics;
630 CHKERR problem.validateSolution(context->constraintDM, control,
631 smooth_gradient, multipliers,
632 gradient_tolerance, constraint_tolerance,
633 diagnostics);
634 if (diagnostics.find("plastic flow active true") == std::string::npos)
636 "Synthetic KKT point passed validation without reporting active "
637 "plastic flow: %s",
638 diagnostics.c_str());
639
640 MOFEM_LOG("EP", Sev::inform)
641 << "Synthetic active-flow validation atom test passed";
643}
644
645} // namespace
646
649 // Orthonormal trace-free coordinate basis in Eq. (1.52), label
650 // eq:sym-basis.
653 t_coordinates(1., 2., 3., 4., 5.);
654 const auto t_trace_free =
656 const auto t_round_trip =
660 t_round_trip_error;
661 t_round_trip_error(L) = t_round_trip(L) - t_coordinates(L);
662 if (std::abs(t_trace_free(i, i)) > 1e-14 ||
663 std::abs(plasticFrobeniusNorm(t_trace_free) -
664 plasticCoordinateNorm(t_coordinates)) > 1e-14 ||
665 plasticCoordinateNorm(t_round_trip_error) > 1e-14)
666 SETERRQ(PETSC_COMM_SELF, MOFEM_ATOM_TEST_INVALID,
667 "Trace-free plastic-coordinate basis is inconsistent");
669}
670
672 Vec state) {
674 // Verify labels eq:history-space through eq:quadrature-weight (Eqs.
675 // (1.49)--(1.54)).
677 if (!ep.plasticVolume || ep.interfaceCrack)
679 "The incremental-optimization layout test currently requires "
680 "-plastic_volume 1 and -cohesive_interface_on 0");
681 CHKERR setControlField(ep, ep.plasticFlowField, *ep.plasticVolumes,
683 CHKERR setControlField(ep, ep.plasticKappaField, *ep.plasticVolumes, 1);
684
686 ep, SmartPetscObj<TS>(ts, true), SmartPetscObj<Vec>(state, true));
688 DM control_dm = problem.getControlDM();
689 SmartPetscObj<IS> kappa_is;
690 CHKERR getTestFieldIS(control_dm, problem.getPlasticKappaFieldName(),
691 kappa_is);
692 if (!context->constraintDM)
694 "Incremental optimization has no constraint DM");
695 PetscBool square_constraint_problem = PETSC_TRUE;
697 &square_constraint_problem);
698 if (square_constraint_problem)
700 "Plastic constraint DM must be rectangular");
701
702 DM constraint_vector_dm = nullptr;
703 DM constraint_matrix_dm = nullptr;
704 CHKERR VecGetDM(context->inequalityConstraints, &constraint_vector_dm);
705 CHKERR MatGetDM(context->inequalityJacobian, &constraint_matrix_dm);
706 if (constraint_vector_dm != context->constraintDM.get() ||
707 constraint_matrix_dm != context->constraintDM.get())
709 "Plastic constraint vector or Jacobian lost its constraint DM");
710
711 PetscInt local_constraint_blocks = 0;
712 PetscInt global_constraint_blocks = 0;
713 CHKERR getTestFieldBlockCounts(control_dm,
714 problem.getPlasticKappaFieldName(), 1,
715 local_constraint_blocks,
716 global_constraint_blocks);
717 PetscInt local_constraint_size = 0;
718 PetscInt global_constraint_size = 0;
719 PetscInt local_control_size = 0;
720 PetscInt global_control_size = 0;
721 PetscInt local_matrix_rows = 0;
722 PetscInt local_matrix_columns = 0;
723 PetscInt global_matrix_rows = 0;
724 PetscInt global_matrix_columns = 0;
725 CHKERR VecGetLocalSize(context->inequalityConstraints,
726 &local_constraint_size);
727 CHKERR VecGetSize(context->inequalityConstraints, &global_constraint_size);
728 CHKERR VecGetLocalSize(context->committedControl, &local_control_size);
729 CHKERR VecGetSize(context->committedControl, &global_control_size);
730 CHKERR MatGetLocalSize(context->inequalityJacobian, &local_matrix_rows,
731 &local_matrix_columns);
732 CHKERR MatGetSize(context->inequalityJacobian, &global_matrix_rows,
733 &global_matrix_columns);
734 if (local_constraint_size != local_constraint_blocks ||
735 global_constraint_size != global_constraint_blocks ||
736 local_matrix_rows != local_constraint_size ||
737 global_matrix_rows != global_constraint_size ||
738 local_matrix_columns != local_control_size ||
739 global_matrix_columns != global_control_size)
741 "Plastic constraint DM vector/matrix dimensions are inconsistent");
742
743 IS raw_sub_row_is = nullptr;
744 CHKERR DMMoFEMGetSubRowIS(context->constraintDM, &raw_sub_row_is);
745 SmartPetscObj<IS> sub_row_is(raw_sub_row_is);
746 PetscBool rows_match_kappa = PETSC_FALSE;
747 CHKERR ISEqual(sub_row_is.get(), kappa_is, &rows_match_kappa);
748 if (!rows_match_kappa)
750 "Constraint-DM rows do not match the plastic kappa field");
751
752 IS raw_sub_col_is = nullptr;
753 CHKERR DMMoFEMGetSubColIS(context->constraintDM, &raw_sub_col_is);
754 SmartPetscObj<IS> sub_col_is(raw_sub_col_is);
755 PetscInt local_sub_columns = 0;
756 PetscInt global_sub_columns = 0;
757 CHKERR ISGetLocalSize(sub_col_is, &local_sub_columns);
758 CHKERR ISGetSize(sub_col_is, &global_sub_columns);
759 if (local_sub_columns != local_control_size ||
760 global_sub_columns != global_control_size)
762 "Constraint-DM columns do not span the plastic control space");
763 PetscInt control_start = 0;
764 PetscInt control_end = 0;
765 CHKERR VecGetOwnershipRange(context->committedControl, &control_start,
766 &control_end);
767 if (control_end - control_start != local_control_size)
769 "TAO control ownership is inconsistent with its local size");
770 const PetscInt *sub_column_indices = nullptr;
771 CHKERR ISGetIndices(sub_col_is, &sub_column_indices);
772 for (PetscInt column = 0; column != local_sub_columns; ++column)
773 if (sub_column_indices[column] != control_start + column)
775 "Constraint-DM column numbering differs from the TAO control");
776 CHKERR ISRestoreIndices(sub_col_is, &sub_column_indices);
777 if (context->candidateControl.get() == context->committedControl.get())
779 "TAO candidate aliases the committed control vector");
780
781 CHKERR checkControlLayout(ep, control_dm,
782 problem.getPlasticFlowFieldName(),
783 *ep.plasticVolumes,
785 CHKERR checkControlLayout(ep, control_dm,
786 problem.getPlasticKappaFieldName(),
787 *ep.plasticVolumes, 1);
788 CHKERR checkPlasticControlEntityPairing(ep, problem);
789
790 auto check_zero_block = [&](const PetscInt block_size, PetscInt,
791 PetscScalar *block) {
793 for (PetscInt component = 0; component != block_size; ++component)
794 if (PetscRealPart(block[component]) != 0)
796 "Incremental TAO control was initialized from mesh history");
798 };
799 const auto check_zero_control = [&](Vec control) {
801 CHKERR forEachOwnedTestFieldBlock(
802 control_dm, problem.getPlasticFlowFieldName(),
804 [&](PetscInt block, PetscScalar *values) {
805 return check_zero_block(plasticLogarithmicStretchCoordinateSize,
806 block, values);
807 });
808 CHKERR forEachOwnedTestFieldBlock(
809 control_dm, problem.getPlasticKappaFieldName(), 1, control,
810 [&](PetscInt block, PetscScalar *values) {
811 return check_zero_block(1, block, values);
812 });
814 };
815 CHKERR check_zero_control(context->committedControl);
816 CHKERR check_zero_control(context->candidateControl);
817 CHKERR check_zero_control(context->referenceControl);
818 PlasticKappaHistory initial_kappa_history;
819 CHKERR savePlasticKappaHistory(ep, initial_kappa_history);
820 for (const auto &[entity, kappa] : initial_kappa_history)
821 if (kappa != 1.)
823 "Committed mesh kappa on entity %llu was not preserved "
824 "independently of the TAO increment vector",
825 static_cast<unsigned long long>(entity));
826
827 constexpr double trial_delta_kappa = 0.25;
828 CHKERR forEachOwnedTestFieldBlock(
829 control_dm, problem.getPlasticFlowFieldName(),
831 [](PetscInt, PetscScalar *values) {
832 for (PetscInt coordinate = 0;
833 coordinate != plasticLogarithmicStretchCoordinateSize;
834 ++coordinate)
835 values[coordinate] = coordinate + 1.;
836 return MoFEMErrorCode(0);
837 });
838 CHKERR forEachOwnedTestFieldBlock(
839 control_dm, problem.getPlasticKappaFieldName(), 1,
840 context->candidateControl,
841 [trial_delta_kappa](PetscInt, PetscScalar *value) {
842 value[0] = trial_delta_kappa;
843 return MoFEMErrorCode(0);
844 });
845 CHKERR VecGhostUpdateBegin(context->candidateControl, INSERT_VALUES,
846 SCATTER_FORWARD);
847 CHKERR VecGhostUpdateEnd(context->candidateControl, INSERT_VALUES,
848 SCATTER_FORWARD);
849
850 PlasticKappaHistory mesh_kappa_before_trial;
851 CHKERR savePlasticKappaHistory(ep, mesh_kappa_before_trial);
852 for (const auto &[entity, kappa] : mesh_kappa_before_trial)
853 if (kappa != 1.)
855 "TAO-vector initialization changed mesh kappa on entity %llu",
856 static_cast<unsigned long long>(entity));
857
858 const int rule = 2 * ep.spaceOrder;
859 if (ep.plasticVolume) {
860 auto values = boost::make_shared<MatrixDouble>();
861 auto kappa_increments = boost::make_shared<VectorDouble>();
862 auto count = boost::make_shared<PetscInt>(0);
863 auto fe = boost::make_shared<VolumeElementForcesAndSourcesCore>(ep.mField);
864 auto bubble_cache = boost::make_shared<CGGUserPolynomialBase::CachePhi>(
865 0, 0, MatrixDouble());
866 fe->getUserPolynomialBase() = boost::shared_ptr<BaseFunction>(
867 new CGGUserPolynomialBase(bubble_cache));
868 fe->getRuleHook = [rule](int, int, int) { return rule; };
869 EshelbianPlasticity::AddHOOps<SPACE_DIM, SPACE_DIM, SPACE_DIM>::add(
870 fe->getOpPtrVector(), {L2}, ep.materialH1Positions, ep.frontAdjEdges);
872 fe->getOpPtrVector(), ep.plasticFlowField, values, MBTET,
873 ep.dmIncrementalOptimization, context->candidateControl);
874 fe->getOpPtrVector().push_back(new OpCalculateScalarFieldValues(
877 kappa_increments, context->candidateControl, MBTET));
878 fe->getOpPtrVector().push_back(new OpVerifyPlasticControl(
879 values, kappa_increments, trial_delta_kappa, count));
880 fe->exeTestHook = [&ep](FEMethod *method) {
881 return ep.plasticVolumes->find(method->getFEEntityHandle()) !=
882 ep.plasticVolumes->end();
883 };
885 ep.elementVolumeName, fe);
886 PetscInt verified_global = 0;
887 const PetscInt verified_local = *count;
888 if (MPI_Allreduce(&verified_local, &verified_global, 1, MPIU_INT, MPI_SUM,
889 ep.mField.get_comm()) != MPI_SUCCESS)
891 "MPI reduction of reconstructed plastic cells failed");
892 PetscInt unused_local = 0;
893 PetscInt global_count = 0;
894 CHKERR getTestFieldBlockCounts(control_dm,
895 problem.getPlasticFlowFieldName(),
897 unused_local, global_count);
898 if (verified_global != global_count)
900 "Plastic reconstruction visited %d blocks; expected %d",
901 static_cast<int>(verified_global),
902 static_cast<int>(global_count));
903 }
904
905 auto committed_before = vectorDuplicate(context->committedControl);
906 CHKERR VecCopy(context->committedControl, committed_before);
907 CHKERR context->rollbackTrial();
908 PetscBool candidate_equal = PETSC_FALSE;
909 PetscBool committed_equal = PETSC_FALSE;
910 CHKERR VecEqual(context->candidateControl, context->committedControl,
911 &candidate_equal);
912 CHKERR VecEqual(context->committedControl, committed_before,
913 &committed_equal);
914 if (!candidate_equal || !committed_equal)
916 "Trial rollback changed committed fields or failed to restore the "
917 "candidate bitwise");
918 CHKERR context->rollbackTrial();
919 CHKERR VecEqual(context->candidateControl, context->committedControl,
920 &candidate_equal);
921 if (!candidate_equal)
923 "Post-commit rollback failed to restore the candidate bitwise");
924
925 PlasticHistory plastic_history_before_commit;
926 PlasticKappaHistory kappa_history_before_commit;
927 CHKERR savePlasticHistory(ep, plastic_history_before_commit);
928 CHKERR savePlasticKappaHistory(ep, kappa_history_before_commit);
929 auto accepted_solution = vectorDuplicate(context->referenceControl);
930 auto accepted_solution_before = vectorDuplicate(context->referenceControl);
931 auto state_before_commit = vectorDuplicate(state);
932 CHKERR VecZeroEntries(accepted_solution);
933 constexpr double accepted_delta_kappa = 0.02;
934 CHKERR forEachOwnedTestFieldBlock(
935 control_dm, problem.getPlasticFlowFieldName(),
937 [accepted_delta_kappa](PetscInt, PetscScalar *values) {
938 for (PetscInt coordinate = 0;
939 coordinate != plasticLogarithmicStretchCoordinateSize;
940 ++coordinate)
941 values[coordinate] = 0.;
942 values[0] = accepted_delta_kappa /
943 plasticEquivalentIncrementScale;
944 return MoFEMErrorCode(0);
945 });
946 CHKERR forEachOwnedTestFieldBlock(
947 control_dm, problem.getPlasticKappaFieldName(), 1, accepted_solution,
948 [accepted_delta_kappa](PetscInt, PetscScalar *value) {
949 value[0] = accepted_delta_kappa;
950 return MoFEMErrorCode(0);
951 });
952 CHKERR VecGhostUpdateBegin(accepted_solution, INSERT_VALUES,
953 SCATTER_FORWARD);
954 CHKERR VecGhostUpdateEnd(accepted_solution, INSERT_VALUES,
955 SCATTER_FORWARD);
956 CHKERR VecCopy(accepted_solution, accepted_solution_before);
957 CHKERR VecCopy(state, state_before_commit);
958 CHKERR problem.commit(accepted_solution, state, context->committedControl,
959 context->candidateControl, context->referenceControl);
960
961 PetscBool accepted_solution_unchanged = PETSC_FALSE;
962 PetscBool state_unchanged = PETSC_FALSE;
963 CHKERR VecEqual(accepted_solution, accepted_solution_before,
964 &accepted_solution_unchanged);
965 CHKERR VecEqual(state, state_before_commit, &state_unchanged);
966 if (!accepted_solution_unchanged || !state_unchanged)
968 "Plastic commit modified its read-only solution or state input");
969 CHKERR check_zero_control(context->committedControl);
970 CHKERR check_zero_control(context->candidateControl);
971 CHKERR check_zero_control(context->referenceControl);
972
973 PlasticHistory plastic_history_after_commit;
974 PlasticKappaHistory kappa_history_after_commit;
975 CHKERR savePlasticHistory(ep, plastic_history_after_commit);
976 CHKERR savePlasticKappaHistory(ep, kappa_history_after_commit);
977 for (const auto &[entity, before] : plastic_history_before_commit) {
978 const auto after = plastic_history_after_commit.find(entity);
979 if (after == plastic_history_after_commit.end())
981 "Committed plastic-H entity %llu disappeared",
982 static_cast<unsigned long long>(entity));
983 for (PetscInt coordinate = 0;
985 ++coordinate) {
986 const double increment =
987 coordinate == 0
988 ? accepted_delta_kappa / plasticEquivalentIncrementScale
989 : 0.;
990 if (std::abs(after->second[coordinate] -
991 (before[coordinate] + increment)) > 1e-14)
993 "Plastic-H additive commit failed on entity %llu",
994 static_cast<unsigned long long>(entity));
995 }
996 }
997 for (const auto &[entity, before] : kappa_history_before_commit) {
998 const auto after = kappa_history_after_commit.find(entity);
999 if (after == kappa_history_after_commit.end() ||
1000 std::abs(after->second - (before + accepted_delta_kappa)) > 1e-14)
1002 "Plastic-kappa additive commit failed on entity %llu",
1003 static_cast<unsigned long long>(entity));
1004 }
1006 auto check_zero_flow = [&](boost::shared_ptr<FieldEntity> field_entity) {
1008 for (const double value : field_entity->getEntFieldData())
1009 if (value != 0.)
1011 "Transient plastic-flow mesh data were not cleared");
1013 };
1014 CHKERR ep.mField.getInterface<FieldBlas>()->fieldLambdaOnEntities(
1015 check_zero_flow, ep.plasticFlowField, ep.plasticVolumes.get());
1016 PlasticKappaHistory kappa_history_after_clear;
1017 CHKERR savePlasticKappaHistory(ep, kappa_history_after_clear);
1018 if (kappa_history_after_clear != kappa_history_after_commit)
1020 "Clearing transient plastic flow erased committed kappa");
1021
1022 PetscInt global_size = 0;
1023 CHKERR VecGetSize(context->committedControl, &global_size);
1024 PetscInt local_blocks = 0;
1025 PetscInt global_blocks = 0;
1026 CHKERR getTestFieldBlockCounts(control_dm,
1027 problem.getPlasticFlowFieldName(),
1029 local_blocks, global_blocks);
1030 if (global_size !=
1031 (plasticLogarithmicStretchCoordinateSize + 1) * global_blocks)
1033 "Linear hardening added an unnecessary TAO control block");
1034 MOFEM_LOG("EP", Sev::inform)
1035 << "Incremental-optimization layout verified: global control size "
1036 << global_size << ", plastic blocks " << ep.plasticVolumes->size();
1038}
1039
1041 Vec state) {
1043 // Centred finite-difference verification of the reduced bulk Helmholtz
1044 // derivative. The state is re-equilibrated at both perturbed controls, so
1045 // the assembled comparison includes the static equilibrium adjoint.
1047 if (!ep.plasticVolume || ep.interfaceCrack)
1049 "The equilibrated-value identity test is plastic-only");
1050
1051 double fd_epsilon = 1e-6;
1052 double relative_tolerance = 1e-4;
1053 double absolute_tolerance = 1e-8;
1054 CHKERR PetscOptionsGetReal(PETSC_NULLPTR, PETSC_NULLPTR,
1055 "-equilibrated_value_fd_epsilon", &fd_epsilon,
1056 PETSC_NULLPTR);
1057 CHKERR PetscOptionsGetReal(PETSC_NULLPTR, PETSC_NULLPTR,
1058 "-equilibrated_value_gradient_rtol",
1059 &relative_tolerance, PETSC_NULLPTR);
1060 CHKERR PetscOptionsGetReal(PETSC_NULLPTR, PETSC_NULLPTR,
1061 "-equilibrated_value_gradient_atol",
1062 &absolute_tolerance, PETSC_NULLPTR);
1063 if (!(fd_epsilon > 0) || !(relative_tolerance >= 0) ||
1064 !(absolute_tolerance >= 0))
1065 SETERRQ(ep.mField.get_comm(), MOFEM_INVALID_DATA,
1066 "Invalid equilibrated-value derivative test tolerances");
1067
1068 constexpr double inv_sqrt_two = 0.70710678118654752440;
1070 inv_sqrt_two, 0., 0., -inv_sqrt_two, 0., 0.);
1071 PlasticHistory history;
1072 CHKERR savePlasticHistory(ep, history);
1073 auto base_state = vectorDuplicate(state);
1074
1075 auto restore = [&]() {
1077 CHKERR setPlasticHistory(ep, history, t_direction, 0);
1078 CHKERR VecCopy(base_state, state);
1081 };
1082 auto solve_equilibrium = [&]() {
1085 CHKERR solveEquilibriumStateTS(ep, ts, state, report);
1088 "Equilibrated-value fixed-control solve did not converge "
1089 "(status %d, error %d)",
1090 static_cast<int>(report.status), report.errorCode);
1092 };
1093 auto solve_at_scale = [&](const double scale, double &value) {
1095 CHKERR setPlasticHistory(ep, history, t_direction, scale);
1096 CHKERR VecCopy(base_state, state);
1098 CHKERR solve_equilibrium();
1101 };
1102
1103 CHKERR solve_equilibrium();
1104 CHKERR VecCopy(state, base_state);
1105
1106 auto reduced_gradient =
1108 CHKERR assembleReducedHelmholtzGradient(ep, ts, reduced_gradient);
1109 auto direction_vector = vectorDuplicate(reduced_gradient);
1110 CHKERR VecZeroEntries(direction_vector);
1111 const auto t_direction_coordinates =
1113 CHKERR forEachOwnedTestFieldBlock(
1116 [&t_direction_coordinates](PetscInt, PetscScalar *values) {
1117 auto t_values =
1118 getFTensor1FromPtr<plasticLogarithmicStretchCoordinateSize>(
1119 values);
1120 FTENSOR_INDEX(plasticLogarithmicStretchCoordinateSize, L);
1121 t_values(L) = t_direction_coordinates(L);
1122 return MoFEMErrorCode(0);
1123 });
1124 PetscScalar assembled_derivative_scalar = 0;
1125 CHKERR VecDot(reduced_gradient, direction_vector,
1126 &assembled_derivative_scalar);
1127 const double assembled_derivative =
1128 PetscRealPart(assembled_derivative_scalar);
1129 double value_plus = 0;
1130 double value_minus = 0;
1131 const MoFEMErrorCode plus_error = solve_at_scale(fd_epsilon, value_plus);
1132 const MoFEMErrorCode minus_error =
1133 plus_error ? 0 : solve_at_scale(-fd_epsilon, value_minus);
1134 const MoFEMErrorCode restore_error = restore();
1135 CHKERR plus_error;
1136 CHKERR minus_error;
1137 CHKERR restore_error;
1138
1139 const double finite_difference =
1140 (value_plus - value_minus) / (2 * fd_epsilon);
1141 const double absolute_error =
1142 std::abs(finite_difference - assembled_derivative);
1143 const double tolerance =
1144 absolute_tolerance +
1145 relative_tolerance *
1146 std::max(std::abs(finite_difference), std::abs(assembled_derivative));
1147 MOFEM_LOG("EP", Sev::inform)
1148 << "Reduced Helmholtz plastic derivative: finite difference "
1149 "= "
1150 << finite_difference << ", assembled = " << assembled_derivative
1151 << ", absolute error = " << absolute_error
1152 << ", tolerance = " << tolerance;
1153 if (!std::isfinite(finite_difference) ||
1154 !std::isfinite(assembled_derivative) || absolute_error > tolerance)
1156 "Reduced Helmholtz value/adjoint-gradient identity is "
1157 "inconsistent: error = %g, tolerance = %g",
1158 absolute_error, tolerance);
1159
1161}
1162
1164 Vec state) {
1166 // Transaction-level checks for Eq. (3.25), label gen:eq:common-tests,
1167 // including Eq. (1.88), label eq:smooth-gradient-check.
1169 ep, SmartPetscObj<TS>(ts, true), SmartPetscObj<Vec>(state, true));
1171 DM control_dm = problem.getControlDM();
1172
1175 value_only);
1177 !context->baselineValid || value_only.cacheHit ||
1178 value_only.stateSolveCount != 1 || value_only.smoothGradient ||
1179 value_only.objectiveGradient || context->cachedEvaluation.smoothGradient ||
1180 context->gradientAssemblySeconds != 0)
1182 "Objective-only transaction must equilibrate without assembling "
1183 "a gradient");
1184 auto value_state = vectorDuplicate(context->equilibratedState);
1185 CHKERR VecCopy(context->equilibratedState, value_state);
1186 PetscReal reference_objective = 0;
1187 IncrementalObjectiveEvaluation cached_value;
1189 context, context->referenceControl, reference_objective, nullptr,
1190 cached_value);
1191 if (!cached_value.cacheHit || cached_value.stateSolveCount != 1 ||
1192 reference_objective != value_only.dissipativeValue ||
1193 cached_value.smoothGradient || cached_value.objectiveGradient ||
1194 context->gradientAssemblySeconds != 0)
1196 "TAO objective-only callback did not reuse the value cache");
1197
1200 context, context->referenceControl, first);
1201 PetscBool state_reused = PETSC_FALSE;
1202 CHKERR VecEqual(value_state, context->equilibratedState, &state_reused);
1204 !first.cacheHit || first.stateSolveCount != 1 || !state_reused ||
1205 first.conservativeValue != value_only.conservativeValue ||
1206 first.dissipativeValue != value_only.dissipativeValue ||
1207 !first.smoothGradient)
1209 "Gradient request did not reuse its objective-only equilibrium");
1210 if (!std::isfinite(first.conservativeValue) ||
1211 !std::isfinite(first.equilibriumNorm) ||
1212 !std::isfinite(first.smoothGradientNorm) ||
1213 first.objectiveGradientNorm != std::numeric_limits<double>::max())
1215 "Transaction returned invalid diagnostics or reported "
1216 "outer stationarity before the complete objective was active");
1217
1218 auto first_gradient = vectorDuplicate(context->gradient);
1219 auto first_state = vectorDuplicate(context->equilibratedState);
1220 CHKERR VecCopy(context->gradient, first_gradient);
1221 CHKERR VecCopy(context->equilibratedState, first_state);
1222 const auto first_gradient_seconds = context->gradientAssemblySeconds;
1223 if (ep.plasticVolume) {
1225 0.31, -0.17, 0.23, -0.24, 0.19, -0.07);
1226 const auto t_direction_coordinates =
1228 auto direction_vector = vectorDuplicate(context->committedControl);
1229 CHKERR VecZeroEntries(direction_vector);
1230 CHKERR forEachOwnedTestFieldBlock(
1231 control_dm, problem.getPlasticFlowFieldName(),
1233 [&t_direction_coordinates](PetscInt, PetscScalar *values) {
1234 auto t_values =
1235 getFTensor1FromPtr<plasticLogarithmicStretchCoordinateSize>(
1236 values);
1237 FTENSOR_INDEX(plasticLogarithmicStretchCoordinateSize, L);
1238 t_values(L) = t_direction_coordinates(L);
1239 return MoFEMErrorCode(0);
1240 });
1241 auto fixed_state_force = vectorDuplicate(context->gradient);
1242 CHKERR assemblePlasticForce(ep, fixed_state_force);
1243 PetscScalar vector_derivative = 0;
1244 CHKERR VecDot(fixed_state_force, direction_vector, &vector_derivative);
1245 double scalar_derivative = 0;
1246 CHKERR evaluatePlasticConjugateWork(ep, t_direction, scalar_derivative);
1247 const double force_error =
1248 std::abs(PetscRealPart(vector_derivative) - scalar_derivative);
1249 const double force_tolerance =
1250 1e-11 * std::max({1., std::abs(PetscRealPart(vector_derivative)),
1251 std::abs(scalar_derivative)});
1252 if (force_error > force_tolerance)
1254 "Fixed-state mixed plastic conjugate does not reproduce the "
1255 "exact discrete directional variation: error %g > %g",
1256 force_error, force_tolerance);
1257 }
1258
1261 context, context->referenceControl, cached);
1262 PetscBool cached_gradient_equal = PETSC_FALSE;
1263 PetscBool cached_state_equal = PETSC_FALSE;
1264 CHKERR VecEqual(context->gradient, first_gradient, &cached_gradient_equal);
1265 CHKERR VecEqual(context->equilibratedState, first_state, &cached_state_equal);
1266 if (!cached.cacheHit || cached.stateSolveCount != 1 ||
1267 cached.conservativeValue != first.conservativeValue ||
1268 !cached_gradient_equal || !cached_state_equal ||
1269 context->gradientAssemblySeconds != first_gradient_seconds)
1271 "Repeated transaction evaluation was not served deterministically "
1272 "from the state/value/gradient cache");
1273
1274 auto first_objective_gradient = vectorDuplicate(context->objectiveGradient);
1275 PetscReal first_objective = 0;
1278 context, context->referenceControl, first_objective,
1279 first_objective_gradient, complete);
1280 // ALMM can replace this workspace with its augmented-Lagrangian gradient.
1281 CHKERR VecShift(context->objectiveGradient, 1);
1282 PetscReal repeated_objective = 0;
1284 context, context->referenceControl, repeated_objective,
1285 context->objectiveGradient, complete);
1286 PetscBool objective_gradient_restored = PETSC_FALSE;
1287 CHKERR VecEqual(context->objectiveGradient, first_objective_gradient,
1288 &objective_gradient_restored);
1289 if (!complete.cacheHit || complete.stateSolveCount != 1 ||
1290 repeated_objective != first_objective || !objective_gradient_restored ||
1291 context->gradientAssemblySeconds != first_gradient_seconds)
1293 "Complete objective gradient was not restored from the cached "
1294 "conservative gradient after TAO modified its workspace");
1295
1296 if (ep.plasticVolume) {
1298 0.31, -0.17, 0.23, -0.24, 0.19, -0.07);
1299 const auto t_direction_coordinates =
1301 auto direction_vector = vectorDuplicate(context->referenceControl);
1302 CHKERR VecZeroEntries(direction_vector);
1303 CHKERR forEachOwnedTestFieldBlock(
1304 control_dm, problem.getPlasticFlowFieldName(),
1306 [&t_direction_coordinates](PetscInt, PetscScalar *values) {
1307 auto t_values =
1308 getFTensor1FromPtr<plasticLogarithmicStretchCoordinateSize>(
1309 values);
1310 FTENSOR_INDEX(plasticLogarithmicStretchCoordinateSize, L);
1311 t_values(L) = t_direction_coordinates(L);
1312 return MoFEMErrorCode(0);
1313 });
1314 constexpr double epsilon = 1e-5;
1315 auto plus_control = vectorDuplicate(context->referenceControl);
1316 auto minus_control = vectorDuplicate(context->referenceControl);
1317 CHKERR VecWAXPY(plus_control, epsilon, direction_vector,
1318 context->referenceControl);
1319 CHKERR VecWAXPY(minus_control, -epsilon, direction_vector,
1320 context->referenceControl);
1321 const auto state_solve = context->stateSolve;
1322 const auto state_count = context->stateSolveCount;
1323 context->stateSolve = [](Vec trial_state, EquilibriumSolveReport &report) {
1325 CHKERR VecSet(trial_state, 17);
1326 report.status = EquilibriumSolveStatus::Diverged;
1327 report.errorCode = MOFEM_OPERATION_UNSUCCESSFUL;
1328 report.equilibriumNorm = 1;
1330 };
1331 PetscReal rejected_objective = 0;
1333 const auto rejection_error =
1335 context, plus_control, rejected_objective, nullptr, rejected);
1336 context->stateSolve = state_solve;
1337 CHKERR rejection_error;
1338 PetscBool control_rolled_back = PETSC_FALSE;
1339 PetscBool state_rolled_back = PETSC_FALSE;
1340 CHKERR VecEqual(context->candidateControl, context->committedControl,
1341 &control_rolled_back);
1342 CHKERR VecEqual(context->equilibratedState, first_state,
1343 &state_rolled_back);
1345 !std::isinf(rejected_objective) || rejected_objective < 0 ||
1346 rejected.stateSolveCount != state_count + 1 || context->cacheValid ||
1347 !control_rolled_back || !state_rolled_back ||
1348 context->gradientAssemblySeconds != first_gradient_seconds)
1350 "Rejected objective-only trial did not restore state and "
1351 "controls without gradient assembly");
1352
1355 CHKERR evaluateIncrementalObjective(context, plus_control, plus);
1356 CHKERR evaluateIncrementalObjective(context, minus_control, minus);
1359 plus.cacheHit || plus.stateSolveCount != state_count + 2 ||
1360 plus.smoothGradient || minus.smoothGradient ||
1361 context->gradientAssemblySeconds != first_gradient_seconds)
1363 "Objective-only plastic-flow trials did not converge without "
1364 "gradient assembly");
1365 PetscScalar assembled = 0;
1366 CHKERR VecDot(first_gradient, direction_vector, &assembled);
1367 const double finite_difference =
1368 (plus.conservativeValue - minus.conservativeValue) / (2 * epsilon);
1369 const double tolerance =
1370 2e-6 + 5e-4 * std::max(std::abs(finite_difference),
1371 std::abs(PetscRealPart(assembled)));
1372 if (std::abs(finite_difference - PetscRealPart(assembled)) > tolerance)
1374 "Direct trial DeltaHp incremental-objective gradient finite "
1375 "difference "
1376 "failed: %g versus %g",
1377 finite_difference, PetscRealPart(assembled));
1380 context, context->referenceControl, restored);
1383 "Could not restore reference state after plastic trials");
1384 }
1385
1387
1388 MOFEM_LOG("EP", Sev::inform)
1389 << "Incremental-optimization transaction/cache atom test passed";
1391}
1392
1394 TS ts,
1395 Vec state) {
1397 PlasticHistory committed_history;
1398 PlasticKappaHistory committed_kappa_history;
1399 CHKERR savePlasticHistory(ep, committed_history);
1400 CHKERR savePlasticKappaHistory(ep, committed_kappa_history);
1401 auto original_state = vectorDuplicate(state);
1402 CHKERR VecCopy(state, original_state);
1404 t_history_coordinates(.13, -.29, .17, .31, -.11);
1405 const auto t_history =
1407 CHKERR setPlasticHistory(ep, committed_history, t_history, 2e-3);
1408 CHKERR setControlField(ep, ep.plasticKappaField, *ep.plasticVolumes, 1);
1410 ep, SmartPetscObj<TS>(ts, true), SmartPetscObj<Vec>(state, true));
1412 DM control_dm = problem.getControlDM();
1413 SmartPetscObj<IS> kappa_is;
1414 CHKERR getTestFieldIS(control_dm, problem.getPlasticKappaFieldName(),
1415 kappa_is);
1416 SmartPetscObj<Vec> plastic_cell_measure;
1417 CHKERR createTestPlasticCellMeasure(ep, control_dm, plastic_cell_measure);
1418 std::vector<double> committed_kappa;
1419 CHKERR getOwnedTestKappaValues(ep, control_dm, committed_kappa);
1420
1421 double epsilon = 1e-5;
1422 double relative_tolerance = 5e-4;
1423 double absolute_tolerance = 2e-6;
1424 CHKERR PetscOptionsGetReal(PETSC_NULLPTR, PETSC_NULLPTR,
1425 "-incremental_optimization_derivative_epsilon",
1426 &epsilon, PETSC_NULLPTR);
1427 CHKERR PetscOptionsGetReal(PETSC_NULLPTR, PETSC_NULLPTR,
1428 "-incremental_optimization_derivative_rtol",
1429 &relative_tolerance, PETSC_NULLPTR);
1430 CHKERR PetscOptionsGetReal(PETSC_NULLPTR, PETSC_NULLPTR,
1431 "-incremental_optimization_derivative_atol",
1432 &absolute_tolerance, PETSC_NULLPTR);
1433
1436 context, context->referenceControl, baseline);
1439 "Objective derivative test could not establish equilibrium");
1440
1441 auto operators_tester = ep.mField.getInterface<OperatorsTester>();
1442 auto centre = vectorDuplicate(context->referenceControl);
1443 CHKERR VecCopy(context->referenceControl, centre);
1445 t_centre(.31, -.17, .23, -.24, .19);
1446 const double centre_norm = plasticCoordinateNorm(t_centre);
1447 CHKERR forEachOwnedTestFieldBlock(
1448 control_dm, problem.getPlasticFlowFieldName(),
1450 [&](PetscInt, PetscScalar *values) {
1451 auto t_values =
1452 getFTensor1FromPtr<plasticLogarithmicStretchCoordinateSize>(values);
1453 FTENSOR_INDEX(plasticLogarithmicStretchCoordinateSize, L);
1454 t_values(L) = 1e-3 * t_centre(L) / centre_norm;
1455 return MoFEMErrorCode(0);
1456 });
1457 CHKERR forEachOwnedTestFieldBlock(
1458 control_dm, problem.getPlasticKappaFieldName(), 1, centre,
1459 [](PetscInt, PetscScalar *value) {
1460 value[0] = 2e-3;
1461 return MoFEMErrorCode(0);
1462 });
1463 CHKERR VecGhostUpdateBegin(centre, INSERT_VALUES, SCATTER_FORWARD);
1464 CHKERR VecGhostUpdateEnd(centre, INSERT_VALUES, SCATTER_FORWARD);
1465
1466 auto gradient = vectorDuplicate(context->objectiveGradient);
1467 PetscReal objective = 0;
1468 IncrementalObjectiveEvaluation centre_evaluation;
1470 context, centre, objective, gradient, centre_evaluation);
1471 if (centre_evaluation.stateStatus != EquilibriumSolveStatus::Converged)
1473 "Objective derivative centre did not equilibrate");
1474
1475 Vec epigraph = nullptr;
1476 Vec epigraph_gradient = nullptr;
1477 Vec measures = nullptr;
1478 CHKERR VecGetSubVector(centre, kappa_is, &epigraph);
1479 CHKERR VecGetSubVector(gradient, kappa_is, &epigraph_gradient);
1480 CHKERR VecGetSubVector(plastic_cell_measure, kappa_is, &measures);
1481 PetscInt epigraph_size = 0;
1482 PetscInt gradient_size = 0;
1483 PetscInt measure_size = 0;
1484 CHKERR VecGetLocalSize(epigraph, &epigraph_size);
1485 CHKERR VecGetLocalSize(epigraph_gradient, &gradient_size);
1486 CHKERR VecGetLocalSize(measures, &measure_size);
1487 if (gradient_size != epigraph_size || measure_size != epigraph_size ||
1488 static_cast<PetscInt>(committed_kappa.size()) != epigraph_size) {
1489 CHKERR VecRestoreSubVector(plastic_cell_measure, kappa_is, &measures);
1490 CHKERR VecRestoreSubVector(gradient, kappa_is, &epigraph_gradient);
1491 CHKERR VecRestoreSubVector(centre, kappa_is, &epigraph);
1493 "Objective test kappa layouts are inconsistent");
1494 }
1495 const PetscScalar *epigraph_array = nullptr;
1496 const PetscScalar *gradient_array = nullptr;
1497 const PetscScalar *measure_array = nullptr;
1498 CHKERR VecGetArrayRead(epigraph, &epigraph_array);
1499 CHKERR VecGetArrayRead(epigraph_gradient, &gradient_array);
1500 CHKERR VecGetArrayRead(measures, &measure_array);
1501 double expected_dissipation_local = 0;
1502 double maximum_epigraph_gradient_error_local = 0;
1503 for (PetscInt block = 0; block != epigraph_size; ++block) {
1504 const double delta_kappa = PetscRealPart(epigraph_array[block]);
1505 const double kappa_n = committed_kappa[block];
1506 const double hardening_modulus =
1507 problem.getIsotropicHardeningModulus();
1508 const double expected_gradient =
1509 PetscRealPart(measure_array[block]) *
1510 (problem.getInitialYieldStress() +
1511 hardening_modulus * (kappa_n + delta_kappa));
1512 expected_dissipation_local += PetscRealPart(measure_array[block]) *
1513 ((problem.getInitialYieldStress() +
1514 hardening_modulus * kappa_n) *
1515 delta_kappa +
1516 0.5 * hardening_modulus * delta_kappa *
1517 delta_kappa);
1518 maximum_epigraph_gradient_error_local = std::max(
1519 maximum_epigraph_gradient_error_local,
1520 std::abs(PetscRealPart(gradient_array[block]) - expected_gradient));
1521 }
1522 CHKERR VecRestoreArrayRead(measures, &measure_array);
1523 CHKERR VecRestoreArrayRead(epigraph_gradient, &gradient_array);
1524 CHKERR VecRestoreArrayRead(epigraph, &epigraph_array);
1525 CHKERR VecRestoreSubVector(plastic_cell_measure, kappa_is, &measures);
1526 CHKERR VecRestoreSubVector(gradient, kappa_is, &epigraph_gradient);
1527 CHKERR VecRestoreSubVector(centre, kappa_is, &epigraph);
1528 double expected_dissipation = 0;
1529 double maximum_epigraph_gradient_error = 0;
1530 CHKERR MPI_Allreduce(&expected_dissipation_local, &expected_dissipation, 1,
1531 MPI_DOUBLE, MPI_SUM, ep.mField.get_comm());
1532 CHKERR MPI_Allreduce(&maximum_epigraph_gradient_error_local,
1533 &maximum_epigraph_gradient_error, 1, MPI_DOUBLE, MPI_MAX,
1534 ep.mField.get_comm());
1535 double assembled_dissipation = 0;
1537 assembled_dissipation);
1538 const double dissipation_tolerance =
1539 1e-13 * std::max(1., std::abs(expected_dissipation));
1540 if (std::abs(assembled_dissipation - expected_dissipation) >
1541 dissipation_tolerance ||
1542 maximum_epigraph_gradient_error > dissipation_tolerance)
1544 "Kappa-increment objective value or gradient is inconsistent "
1545 "with linear isotropic hardening");
1546
1547 auto plastic_direction = vectorDuplicate(context->referenceControl);
1548 auto plastic_direction_two = vectorDuplicate(context->referenceControl);
1549 auto plastic_direction_three = vectorDuplicate(context->referenceControl);
1550 auto epigraph_direction = vectorDuplicate(context->referenceControl);
1551 auto mixed_direction = vectorDuplicate(context->referenceControl);
1552 CHKERR VecZeroEntries(plastic_direction);
1553 CHKERR VecZeroEntries(plastic_direction_two);
1554 CHKERR VecZeroEntries(plastic_direction_three);
1555 CHKERR VecZeroEntries(epigraph_direction);
1557 t_plastic_direction(.37, -.19, .11, .29, -.23);
1559 t_plastic_direction_two(-.07, .43, -.31, .13, .29);
1561 t_plastic_direction_three(.19, .11, -.41, .37, -.17);
1562 CHKERR forEachOwnedTestFieldBlock(
1563 control_dm, problem.getPlasticFlowFieldName(),
1564 plasticLogarithmicStretchCoordinateSize, plastic_direction,
1565 [&t_plastic_direction](PetscInt block, PetscScalar *values) {
1566 auto t_values =
1567 getFTensor1FromPtr<plasticLogarithmicStretchCoordinateSize>(values);
1568 FTENSOR_INDEX(plasticLogarithmicStretchCoordinateSize, L);
1569 t_values(L) = (1. + .125 * block) * t_plastic_direction(L);
1570 return MoFEMErrorCode(0);
1571 });
1572 CHKERR forEachOwnedTestFieldBlock(
1573 control_dm, problem.getPlasticFlowFieldName(),
1574 plasticLogarithmicStretchCoordinateSize, plastic_direction_two,
1575 [&t_plastic_direction_two](PetscInt block, PetscScalar *values) {
1576 auto t_values =
1577 getFTensor1FromPtr<plasticLogarithmicStretchCoordinateSize>(values);
1578 FTENSOR_INDEX(plasticLogarithmicStretchCoordinateSize, L);
1579 const double sign = block % 2 ? -1. : 1.;
1580 t_values(L) = sign * (1. + .075 * block) * t_plastic_direction_two(L);
1581 return MoFEMErrorCode(0);
1582 });
1583 CHKERR forEachOwnedTestFieldBlock(
1584 control_dm, problem.getPlasticFlowFieldName(),
1585 plasticLogarithmicStretchCoordinateSize, plastic_direction_three,
1586 [&t_plastic_direction_three](PetscInt block, PetscScalar *values) {
1587 auto t_values =
1588 getFTensor1FromPtr<plasticLogarithmicStretchCoordinateSize>(values);
1589 FTENSOR_INDEX(plasticLogarithmicStretchCoordinateSize, L);
1590 const double scale[] = {-1.25, .5, 1.75};
1591 t_values(L) = scale[block % 3] * t_plastic_direction_three(L);
1592 return MoFEMErrorCode(0);
1593 });
1594 CHKERR forEachOwnedTestFieldBlock(
1595 control_dm, problem.getPlasticKappaFieldName(), 1, epigraph_direction,
1596 [](PetscInt block, PetscScalar *value) {
1597 value[0] = block % 2 ? -.75 : 1.;
1598 return MoFEMErrorCode(0);
1599 });
1600 PetscReal plastic_direction_norm = 0;
1601 PetscReal plastic_direction_two_norm = 0;
1602 PetscReal plastic_direction_three_norm = 0;
1603 PetscReal epigraph_direction_norm = 0;
1604 CHKERR VecNormalize(plastic_direction, &plastic_direction_norm);
1605 CHKERR VecNormalize(plastic_direction_two, &plastic_direction_two_norm);
1606 CHKERR VecNormalize(plastic_direction_three, &plastic_direction_three_norm);
1607 CHKERR VecNormalize(epigraph_direction, &epigraph_direction_norm);
1608 if (!(plastic_direction_norm > 0) || !(plastic_direction_two_norm > 0) ||
1609 !(plastic_direction_three_norm > 0) || !(epigraph_direction_norm > 0))
1611 "Deterministic objective direction is zero");
1612 PetscScalar dot_one_two = 0;
1613 PetscScalar dot_one_three = 0;
1614 PetscScalar dot_two_three = 0;
1615 CHKERR VecDot(plastic_direction, plastic_direction_two, &dot_one_two);
1616 CHKERR VecDot(plastic_direction, plastic_direction_three, &dot_one_three);
1617 CHKERR VecDot(plastic_direction_two, plastic_direction_three, &dot_two_three);
1618 const double a = PetscRealPart(dot_one_two);
1619 const double b = PetscRealPart(dot_one_three);
1620 const double c = PetscRealPart(dot_two_three);
1621 const double gram_determinant = 1. + 2. * a * b * c - a * a - b * b - c * c;
1622 if (!(gram_determinant > 1e-8))
1624 "Plastic objective directions are not linearly independent: "
1625 "Gram determinant %g",
1626 gram_determinant);
1627 CHKERR VecWAXPY(mixed_direction, 1., epigraph_direction, plastic_direction);
1628 PetscReal mixed_direction_norm = 0;
1629 CHKERR VecNormalize(mixed_direction, &mixed_direction_norm);
1630 if (!(mixed_direction_norm > 0))
1632 "Mixed objective direction is zero");
1633
1634 auto reset_state = [&]() {
1636 context->cacheValid = false;
1637 CHKERR VecCopy(context->baselineState, context->lastValidState);
1638 CHKERR VecCopy(context->baselineState, context->equilibratedState);
1639 CHKERR setStateOnMesh(ep, context->equilibratedState);
1641 };
1642 const OperatorsTester::ScalarFunction evaluate = [&](Vec control,
1643 PetscReal &value) {
1645 CHKERR reset_state();
1646 const auto gradient_seconds = context->gradientAssemblySeconds;
1647 IncrementalObjectiveEvaluation evaluation;
1649 context, control, value, nullptr, evaluation);
1650 if (evaluation.stateStatus != EquilibriumSolveStatus::Converged ||
1651 evaluation.smoothGradient || evaluation.objectiveGradient ||
1652 context->gradientAssemblySeconds != gradient_seconds)
1654 "Perturbed objective did not equilibrate without gradient "
1655 "assembly");
1657 };
1658 double maximum_error = 0;
1659 auto check_direction = [&](const char *name, Vec direction) {
1661 PetscReal finite_difference = 0;
1662 PetscReal assembled = 0;
1663 CHKERR operators_tester->checkScalarCentralFiniteDifference(
1664 centre, direction, gradient, epsilon, evaluate, finite_difference,
1665 assembled);
1666 const double error = std::abs(finite_difference - assembled);
1667 const double tolerance =
1668 absolute_tolerance +
1669 relative_tolerance *
1670 std::max(std::abs(finite_difference), std::abs(assembled));
1671 if (!std::isfinite(finite_difference) || !std::isfinite(assembled) ||
1672 error > tolerance)
1674 "%s objective derivative mismatch: error %g > %g", name, error,
1675 tolerance);
1676 maximum_error = std::max(maximum_error, error);
1677 MOFEM_LOG("EP", Sev::inform)
1678 << "OperatorsTester " << name
1679 << " objective derivative: finite difference " << finite_difference
1680 << ", assembled " << assembled << ", error " << error;
1682 };
1683 CHKERR check_direction("plastic-only", plastic_direction);
1684 CHKERR check_direction("plastic-only-two", plastic_direction_two);
1685 CHKERR check_direction("plastic-only-three", plastic_direction_three);
1686 CHKERR check_direction("epigraph-only", epigraph_direction);
1687 CHKERR check_direction("mixed", mixed_direction);
1688
1689 const FTensor::Tensor2_symmetric<double, SPACE_DIM> t_zero(0., 0., 0., 0., 0.,
1690 0.);
1691 CHKERR setPlasticHistory(ep, committed_history, t_zero, 0.);
1692 CHKERR setPlasticKappaHistory(ep, committed_kappa_history);
1693 CHKERR VecCopy(original_state, state);
1695 MOFEM_LOG("EP", Sev::inform)
1696 << "Maximum objective directional-derivative error " << maximum_error;
1698}
1699
1702 Vec control, Vec smooth_gradient) {
1704 auto coupled_dm = createDM(ep.mField.get_comm(), "DMMOFEM");
1706 "INCREMENTAL_OPTIMIZATION_COUPLED_TEST");
1707 CHKERR DMMoFEMSetDestroyProblem(coupled_dm, PETSC_TRUE);
1708 CHKERR DMMoFEMSetSquareProblem(coupled_dm, PETSC_FALSE);
1709 // The flow rows stand in for another dissipative process. Plasticity must
1710 // populate only its kappa-row subset of this larger constraint system.
1712 ep.plasticVolumes);
1714 ep.plasticVolumes);
1716 ep.plasticVolumes);
1718 ep.plasticVolumes);
1720 CHKERR DMSetUp(coupled_dm);
1721
1722 auto coupled_constraints = createDMVector(coupled_dm, RowColData::ROW);
1723 auto coupled_jacobian = createDMMatrix(coupled_dm);
1724 CHKERR VecZeroEntries(coupled_constraints);
1726 coupled_constraints);
1727 CHKERR MatZeroEntries(coupled_jacobian);
1729 problem, control, smooth_gradient, coupled_jacobian);
1730
1731 IS raw_extra_row_is = nullptr;
1733 ep.plasticFlowField.c_str(), &raw_extra_row_is);
1734 SmartPetscObj<IS> extra_row_is(raw_extra_row_is);
1735 Vec extra_constraints = nullptr;
1736 CHKERR VecGetSubVector(coupled_constraints, extra_row_is,
1737 &extra_constraints);
1738 PetscReal extra_constraint_norm = 0;
1739 CHKERR VecNorm(extra_constraints, NORM_INFINITY, &extra_constraint_norm);
1740 CHKERR VecRestoreSubVector(coupled_constraints, extra_row_is,
1741 &extra_constraints);
1742
1743 auto unit_control = vectorDuplicate(control);
1744 CHKERR VecSet(unit_control, 1);
1745 CHKERR VecGhostUpdateBegin(unit_control, INSERT_VALUES, SCATTER_FORWARD);
1746 CHKERR VecGhostUpdateEnd(unit_control, INSERT_VALUES, SCATTER_FORWARD);
1747 auto coupled_action = createDMVector(coupled_dm, RowColData::ROW);
1748 CHKERR MatMult(coupled_jacobian, unit_control, coupled_action);
1749 Vec extra_action = nullptr;
1750 CHKERR VecGetSubVector(coupled_action, extra_row_is, &extra_action);
1751 PetscReal extra_action_norm = 0;
1752 CHKERR VecNorm(extra_action, NORM_INFINITY, &extra_action_norm);
1753 CHKERR VecRestoreSubVector(coupled_action, extra_row_is, &extra_action);
1754 if (extra_constraint_norm > 1e-14 || extra_action_norm > 1e-14)
1756 "Plastic contribution modified non-plastic constraint rows");
1758}
1759
1762 Vec state) {
1765 ep, SmartPetscObj<TS>(ts, true), SmartPetscObj<Vec>(state, true));
1767 PetscBool active_validation_only = PETSC_FALSE;
1768 CHKERR PetscOptionsGetBool(PETSC_NULLPTR, PETSC_NULLPTR,
1769 "-incremental_optimization_active_validation_only",
1770 &active_validation_only, PETSC_NULLPTR);
1771 if (active_validation_only) {
1772 CHKERR checkSyntheticActiveFlowValidation(ep, context);
1774 }
1775 DM control_dm = problem.getControlDM();
1776 SmartPetscObj<IS> plastic_is;
1777 SmartPetscObj<IS> kappa_is;
1778 CHKERR getTestFieldIS(control_dm, problem.getPlasticFlowFieldName(),
1779 plastic_is);
1780 CHKERR getTestFieldIS(control_dm, problem.getPlasticKappaFieldName(),
1781 kappa_is);
1782 SmartPetscObj<Vec> plastic_cell_measure;
1783 CHKERR createTestPlasticCellMeasure(ep, control_dm, plastic_cell_measure);
1784 const double regularization_epsilon =
1785 problem.getDissipationRegularizationEpsilon();
1786 std::vector<double> constraint_scales;
1787 CHKERR getTestPlasticConstraintScales(plastic_cell_measure, kappa_is,
1788 constraint_scales);
1789 double epsilon = 1e-7;
1790 double relative_tolerance = 1e-7;
1791 double absolute_tolerance = 1e-10;
1792 CHKERR PetscOptionsGetReal(PETSC_NULLPTR, PETSC_NULLPTR,
1793 "-incremental_optimization_derivative_epsilon",
1794 &epsilon, PETSC_NULLPTR);
1795 CHKERR PetscOptionsGetReal(PETSC_NULLPTR, PETSC_NULLPTR,
1796 "-incremental_optimization_derivative_rtol",
1797 &relative_tolerance, PETSC_NULLPTR);
1798 CHKERR PetscOptionsGetReal(PETSC_NULLPTR, PETSC_NULLPTR,
1799 "-incremental_optimization_derivative_atol",
1800 &absolute_tolerance, PETSC_NULLPTR);
1801
1803 context, context->referenceControl, context->inequalityConstraints);
1805 context, context->referenceControl, context->inequalityJacobian);
1806 if (context->stateSolveCount || context->gradientAssemblySeconds != 0 ||
1807 context->cachedEvaluation.smoothGradient)
1809 "Independent plastic constraints requested equilibrium or "
1810 "adjoint evaluation");
1811
1812 auto baseline_objective_gradient =
1813 vectorDuplicate(context->objectiveGradient);
1814 PetscReal baseline_objective = 0;
1817 context, context->referenceControl, baseline_objective,
1818 baseline_objective_gradient, baseline);
1819 if (baseline.stateStatus != EquilibriumSolveStatus::Converged)
1821 "Constraint derivative test could not establish equilibrium");
1822 auto preserved_baseline_gradient = vectorDuplicate(baseline.smoothGradient);
1823 CHKERR VecCopy(baseline.smoothGradient, preserved_baseline_gradient);
1824 baseline.smoothGradient = preserved_baseline_gradient;
1825
1826 Vec baseline_gradient = nullptr;
1827 Vec baseline_measures = nullptr;
1828 CHKERR VecGetSubVector(baseline.smoothGradient, plastic_is,
1829 &baseline_gradient);
1830 CHKERR VecGetSubVector(plastic_cell_measure, kappa_is, &baseline_measures);
1831 const PetscScalar *baseline_gradient_array = nullptr;
1832 const PetscScalar *baseline_measure_array = nullptr;
1833 PetscInt baseline_measure_size = 0;
1834 CHKERR VecGetLocalSize(baseline_measures, &baseline_measure_size);
1835 CHKERR VecGetArrayRead(baseline_gradient, &baseline_gradient_array);
1836 CHKERR VecGetArrayRead(baseline_measures, &baseline_measure_array);
1837 double maximum_baseline_q_local = 0;
1838 for (PetscInt block = 0; block != baseline_measure_size; ++block) {
1839 const double measure = PetscRealPart(baseline_measure_array[block]);
1840 const auto t_coefficient_force =
1841 getFTensor1FromPtr<plasticLogarithmicStretchCoordinateSize>(
1842 baseline_gradient_array +
1846 t_force(L) = -t_coefficient_force(L) / measure;
1847 maximum_baseline_q_local =
1848 std::max(maximum_baseline_q_local,
1850 }
1851 CHKERR VecRestoreArrayRead(baseline_measures, &baseline_measure_array);
1852 CHKERR VecRestoreArrayRead(baseline_gradient, &baseline_gradient_array);
1853 CHKERR VecRestoreSubVector(plastic_cell_measure, kappa_is,
1854 &baseline_measures);
1855 CHKERR VecRestoreSubVector(baseline.smoothGradient, plastic_is,
1856 &baseline_gradient);
1857 double maximum_baseline_q = 0;
1858 CHKERR MPI_Allreduce(&maximum_baseline_q_local, &maximum_baseline_q, 1,
1859 MPI_DOUBLE, MPI_MAX, ep.mField.get_comm());
1860 MOFEM_LOG("EP", Sev::inform)
1861 << "Constraint derivative baseline maximum q " << maximum_baseline_q;
1862
1863 if (regularization_epsilon == 0) {
1864 // The evaluation owns aliases into the context. Preserve its matching
1865 // smooth gradient, overwrite the context workspace, and prove that the
1866 // adapter still assembles the force-selected exact-apex row from the
1867 // explicitly supplied objective transaction.
1868 CHKERR VecZeroEntries(context->gradient);
1870 context, context->referenceControl, baseline,
1871 context->inequalityJacobian);
1872 Vec apex_gradient = nullptr;
1873 Vec apex_measures = nullptr;
1874 CHKERR VecGetSubVector(baseline.smoothGradient, plastic_is,
1875 &apex_gradient);
1876 CHKERR VecGetSubVector(plastic_cell_measure, kappa_is, &apex_measures);
1877 PetscInt apex_measure_size = 0;
1878 CHKERR VecGetLocalSize(apex_measures, &apex_measure_size);
1879 const PetscScalar *apex_gradient_array = nullptr;
1880 const PetscScalar *apex_measure_array = nullptr;
1881 const PetscInt *apex_plastic_indices = nullptr;
1882 const PetscInt *apex_epigraph_indices = nullptr;
1883 CHKERR VecGetArrayRead(apex_gradient, &apex_gradient_array);
1884 CHKERR VecGetArrayRead(apex_measures, &apex_measure_array);
1885 CHKERR ISGetIndices(plastic_is, &apex_plastic_indices);
1886 CHKERR ISGetIndices(kappa_is, &apex_epigraph_indices);
1887 PetscInt apex_row_start = 0;
1888 PetscInt apex_row_end = 0;
1889 CHKERR MatGetOwnershipRange(context->inequalityJacobian, &apex_row_start,
1890 &apex_row_end);
1891 if (apex_row_end - apex_row_start != apex_measure_size)
1893 "Objective-evaluation apex rows do not match cell measures");
1894 double adapter_error_local = 0;
1895 for (PetscInt block = 0; block != apex_measure_size; ++block) {
1896 const double measure = PetscRealPart(apex_measure_array[block]);
1897 const auto t_coefficient_force =
1898 getFTensor1FromPtr<plasticLogarithmicStretchCoordinateSize>(
1899 apex_gradient_array +
1903 t_force(L) = -t_coefficient_force(L) / measure;
1904 const double q =
1906 const double denominator = std::max(problem.getInitialYieldStress(), q);
1907 PetscScalar row_values[plasticLogarithmicStretchCoordinateSize];
1908 const PetscInt row = apex_row_start + block;
1909 CHKERR MatGetValues(context->inequalityJacobian, 1, &row,
1911 apex_plastic_indices +
1913 row_values);
1914 for (PetscInt coordinate = 0;
1915 coordinate != plasticLogarithmicStretchCoordinateSize; ++coordinate)
1916 adapter_error_local =
1917 std::max(adapter_error_local,
1918 std::abs(PetscRealPart(row_values[coordinate]) +
1919 constraint_scales[block] *
1920 t_force(coordinate) / denominator));
1921 PetscScalar epigraph_value = 0;
1922 CHKERR MatGetValues(context->inequalityJacobian, 1, &row, 1,
1923 apex_epigraph_indices + block, &epigraph_value);
1924 adapter_error_local = std::max(
1925 adapter_error_local,
1926 std::abs(PetscRealPart(epigraph_value) - constraint_scales[block]));
1927 }
1928 CHKERR ISRestoreIndices(kappa_is, &apex_epigraph_indices);
1929 CHKERR ISRestoreIndices(plastic_is, &apex_plastic_indices);
1930 CHKERR VecRestoreArrayRead(apex_measures, &apex_measure_array);
1931 CHKERR VecRestoreArrayRead(apex_gradient, &apex_gradient_array);
1932 CHKERR VecRestoreSubVector(plastic_cell_measure, kappa_is,
1933 &apex_measures);
1934 CHKERR VecRestoreSubVector(baseline.smoothGradient, plastic_is,
1935 &apex_gradient);
1936 double adapter_error = 0;
1937 CHKERR MPI_Allreduce(&adapter_error_local, &adapter_error, 1, MPI_DOUBLE,
1938 MPI_MAX, ep.mField.get_comm());
1939 if (adapter_error > 1e-12)
1941 "Same-control objective-evaluation apex adapter row error %g",
1942 adapter_error);
1943 CHKERR VecCopy(preserved_baseline_gradient, context->gradient);
1944 }
1945
1946 auto operators_tester = ep.mField.getInterface<OperatorsTester>();
1947 auto centre = vectorDuplicate(context->referenceControl);
1948 CHKERR VecCopy(context->referenceControl, centre);
1950 t_centre(.31, -.17, .23, -.24, .19);
1951 const double centre_norm = plasticCoordinateNorm(t_centre);
1952 CHKERR forEachOwnedTestFieldBlock(
1953 control_dm, problem.getPlasticFlowFieldName(),
1955 [&](PetscInt block, PetscScalar *values) {
1956 auto t_values =
1957 getFTensor1FromPtr<plasticLogarithmicStretchCoordinateSize>(values);
1958 FTENSOR_INDEX(plasticLogarithmicStretchCoordinateSize, L);
1959 t_values(L) = 1e-2 * (1. + .125 * block) * t_centre(L) / centre_norm;
1960 return MoFEMErrorCode(0);
1961 });
1962 CHKERR forEachOwnedTestFieldBlock(
1963 control_dm, problem.getPlasticKappaFieldName(), 1, centre,
1964 [](PetscInt block, PetscScalar *value) {
1965 value[0] = 1.5e-2 + 5e-4 * block;
1966 return MoFEMErrorCode(0);
1967 });
1968 CHKERR VecGhostUpdateBegin(centre, INSERT_VALUES, SCATTER_FORWARD);
1969 CHKERR VecGhostUpdateEnd(centre, INSERT_VALUES, SCATTER_FORWARD);
1970
1971 const auto baseline_state_count = context->stateSolveCount;
1972 const auto baseline_gradient_seconds = context->gradientAssemblySeconds;
1974 context, centre, context->inequalityJacobian);
1976 context, centre, context->inequalityConstraints);
1977 PetscBool smooth_gradient_unchanged = PETSC_FALSE;
1978 PetscBool objective_gradient_unchanged = PETSC_FALSE;
1979 CHKERR VecEqual(context->gradient, preserved_baseline_gradient,
1980 &smooth_gradient_unchanged);
1981 CHKERR VecEqual(context->objectiveGradient, baseline_objective_gradient,
1982 &objective_gradient_unchanged);
1983 if (context->stateSolveCount != baseline_state_count ||
1984 context->gradientAssemblySeconds != baseline_gradient_seconds ||
1985 !smooth_gradient_unchanged || !objective_gradient_unchanged)
1987 "Plastic constraint trials recomputed or modified objective "
1988 "gradient data");
1989 CHKERR checkPlasticConstraintSubsetAssembly(ep, problem, centre,
1990 context->gradient);
1991
1992 Vec plastic_centre = nullptr;
1993 Vec epigraph_centre = nullptr;
1994 CHKERR VecGetSubVector(centre, plastic_is, &plastic_centre);
1995 CHKERR VecGetSubVector(centre, kappa_is, &epigraph_centre);
1996 PetscInt plastic_size = 0;
1997 PetscInt epigraph_size = 0;
1998 PetscInt constraint_size = 0;
1999 CHKERR VecGetLocalSize(plastic_centre, &plastic_size);
2000 CHKERR VecGetLocalSize(epigraph_centre, &epigraph_size);
2001 CHKERR VecGetLocalSize(context->inequalityConstraints, &constraint_size);
2002 if (plastic_size != plasticLogarithmicStretchCoordinateSize * epigraph_size ||
2003 constraint_size != epigraph_size)
2005 "Constraint test layouts are inconsistent");
2006 const PetscScalar *plastic_array = nullptr;
2007 const PetscScalar *epigraph_array = nullptr;
2008 const PetscScalar *constraint_array = nullptr;
2009 const PetscInt *plastic_indices = nullptr;
2010 const PetscInt *epigraph_indices = nullptr;
2011 CHKERR VecGetArrayRead(plastic_centre, &plastic_array);
2012 CHKERR VecGetArrayRead(epigraph_centre, &epigraph_array);
2013 CHKERR VecGetArrayRead(context->inequalityConstraints, &constraint_array);
2014 CHKERR ISGetIndices(plastic_is, &plastic_indices);
2015 CHKERR ISGetIndices(kappa_is, &epigraph_indices);
2016 PetscInt centre_row_start = 0;
2017 PetscInt centre_row_end = 0;
2018 CHKERR MatGetOwnershipRange(context->inequalityJacobian, &centre_row_start,
2019 &centre_row_end);
2020 if (centre_row_end - centre_row_start != epigraph_size)
2022 "Constraint matrix rows do not pair with epigraph blocks");
2023 double constitutive_error_local = 0;
2024 for (PetscInt block = 0; block != epigraph_size; ++block) {
2025 const PetscInt base = block * plasticLogarithmicStretchCoordinateSize;
2026 const auto t_increment =
2027 getFTensor1FromPtr<plasticLogarithmicStretchCoordinateSize>(
2028 plastic_array + base);
2029 const double increment_norm = plasticCoordinateNorm(t_increment);
2030 const double scaled_norm = plasticEquivalentIncrementScale * increment_norm;
2031 const double expected_equivalent_increment =
2032 regularization_epsilon > 0
2033 ? std::hypot(scaled_norm, regularization_epsilon) -
2034 regularization_epsilon
2035 : scaled_norm;
2036 const double expected_constraint =
2037 constraint_scales[block] *
2038 (PetscRealPart(epigraph_array[block]) - expected_equivalent_increment);
2039 constitutive_error_local = std::max(
2040 constitutive_error_local,
2041 std::abs(PetscRealPart(constraint_array[block]) - expected_constraint));
2042
2043 PetscScalar plastic_values[plasticLogarithmicStretchCoordinateSize];
2044 const PetscInt row = centre_row_start + block;
2045 CHKERR MatGetValues(context->inequalityJacobian, 1, &row,
2047 plastic_indices + base, plastic_values);
2048 const double denominator = std::hypot(scaled_norm, regularization_epsilon);
2049 for (PetscInt coordinate = 0;
2050 coordinate != plasticLogarithmicStretchCoordinateSize; ++coordinate) {
2051 const double expected =
2052 -constraint_scales[block] * plasticEquivalentIncrementScaleSquared *
2053 t_increment(coordinate) / denominator;
2054 constitutive_error_local = std::max(
2055 constitutive_error_local,
2056 std::abs(PetscRealPart(plastic_values[coordinate]) - expected));
2057 }
2058 PetscScalar epigraph_value = 0;
2059 CHKERR MatGetValues(context->inequalityJacobian, 1, &row, 1,
2060 epigraph_indices + block, &epigraph_value);
2061 constitutive_error_local = std::max(
2062 constitutive_error_local,
2063 std::abs(PetscRealPart(epigraph_value) - constraint_scales[block]));
2064 }
2065 CHKERR ISRestoreIndices(kappa_is, &epigraph_indices);
2066 CHKERR ISRestoreIndices(plastic_is, &plastic_indices);
2067 CHKERR VecRestoreArrayRead(context->inequalityConstraints, &constraint_array);
2068 CHKERR VecRestoreArrayRead(epigraph_centre, &epigraph_array);
2069 CHKERR VecRestoreArrayRead(plastic_centre, &plastic_array);
2070 CHKERR VecRestoreSubVector(centre, kappa_is, &epigraph_centre);
2071 CHKERR VecRestoreSubVector(centre, plastic_is, &plastic_centre);
2072 double constitutive_error = 0;
2073 CHKERR MPI_Allreduce(&constitutive_error_local, &constitutive_error, 1,
2074 MPI_DOUBLE, MPI_MAX, ep.mField.get_comm());
2075 if (!std::isfinite(constitutive_error) || constitutive_error > 1e-12)
2077 "Independent constraint/Jacobian formula check failed: error %g",
2078 constitutive_error);
2079
2080 auto plastic_direction = vectorDuplicate(context->referenceControl);
2081 auto epigraph_direction = vectorDuplicate(context->referenceControl);
2082 auto mixed_direction = vectorDuplicate(context->referenceControl);
2083 CHKERR VecZeroEntries(plastic_direction);
2084 CHKERR VecZeroEntries(epigraph_direction);
2086 t_direction(.41, -.13, .23, -.31, .17);
2087 CHKERR forEachOwnedTestFieldBlock(
2088 control_dm, problem.getPlasticFlowFieldName(),
2089 plasticLogarithmicStretchCoordinateSize, plastic_direction,
2090 [&t_direction](PetscInt block, PetscScalar *values) {
2091 auto t_values =
2092 getFTensor1FromPtr<plasticLogarithmicStretchCoordinateSize>(values);
2093 FTENSOR_INDEX(plasticLogarithmicStretchCoordinateSize, L);
2094 t_values(L) = (1. + .1 * block) * t_direction(L);
2095 return MoFEMErrorCode(0);
2096 });
2097 CHKERR forEachOwnedTestFieldBlock(
2098 control_dm, problem.getPlasticKappaFieldName(), 1, epigraph_direction,
2099 [](PetscInt block, PetscScalar *value) {
2100 value[0] = block % 2 ? -.5 : 1.;
2101 return MoFEMErrorCode(0);
2102 });
2103 PetscReal plastic_direction_norm = 0;
2104 PetscReal epigraph_direction_norm = 0;
2105 CHKERR VecNormalize(plastic_direction, &plastic_direction_norm);
2106 CHKERR VecNormalize(epigraph_direction, &epigraph_direction_norm);
2107 if (!(plastic_direction_norm > 0) || !(epigraph_direction_norm > 0))
2109 "Deterministic constraint direction is zero");
2110 CHKERR VecWAXPY(mixed_direction, 1., epigraph_direction, plastic_direction);
2111 PetscReal mixed_direction_norm = 0;
2112 CHKERR VecNormalize(mixed_direction, &mixed_direction_norm);
2113 if (!(mixed_direction_norm > 0))
2115 "Mixed constraint direction is zero");
2116
2117 auto error = vectorDuplicate(context->inequalityConstraints);
2118 const OperatorsTester::VectorFunction evaluate = [&](Vec control,
2119 Vec constraints) {
2121 context, control, constraints);
2122 };
2123 PetscReal error_norm = 0;
2124 double tolerance = 0;
2125 auto check_direction = [&](const char *name, Vec direction) {
2127 CHKERR operators_tester->checkVectorCentralFiniteDifference(
2128 centre, direction, context->inequalityConstraints,
2129 context->inequalityJacobian, epsilon, evaluate, error);
2130 PetscReal direction_error_norm = 0;
2131 CHKERR VecNorm(error, NORM_INFINITY, &direction_error_norm);
2132 auto assembled = vectorDuplicate(context->inequalityConstraints);
2133 CHKERR MatMult(context->inequalityJacobian, direction, assembled);
2134 PetscReal assembled_norm = 0;
2135 CHKERR VecNorm(assembled, NORM_INFINITY, &assembled_norm);
2136 const double direction_tolerance =
2137 absolute_tolerance + relative_tolerance * assembled_norm;
2138 if (!std::isfinite(direction_error_norm) ||
2139 direction_error_norm > direction_tolerance)
2141 "%s constraint Jacobian mismatch: error %g > %g", name,
2142 direction_error_norm, direction_tolerance);
2143 error_norm = std::max(error_norm, direction_error_norm);
2144 tolerance = std::max(tolerance, direction_tolerance);
2146 };
2147 CHKERR check_direction("plastic-only", plastic_direction);
2148 CHKERR check_direction("epigraph-only", epigraph_direction);
2149 CHKERR check_direction("mixed", mixed_direction);
2150
2151 // A rounded cone has the classical zero derivative at its apex. The exact
2152 // cone instead selects a subgradient from the cell force. Exercise both
2153 // the subyield (KKT-cancelling) and overstress (saturated) selections.
2154 if (regularization_epsilon > 0) {
2156 context, context->referenceControl, context->inequalityJacobian);
2157 const PetscInt *apex_plastic_indices = nullptr;
2158 const PetscInt *apex_epigraph_indices = nullptr;
2159 CHKERR ISGetIndices(plastic_is, &apex_plastic_indices);
2160 CHKERR ISGetIndices(kappa_is, &apex_epigraph_indices);
2161 PetscInt row_start = 0;
2162 PetscInt row_end = 0;
2163 CHKERR MatGetOwnershipRange(context->inequalityJacobian, &row_start,
2164 &row_end);
2165 double apex_row_error_local = 0;
2166 for (PetscInt block = 0; block != row_end - row_start; ++block) {
2167 const PetscInt row = row_start + block;
2168 PetscScalar values[plasticLogarithmicStretchCoordinateSize];
2169 CHKERR MatGetValues(context->inequalityJacobian, 1, &row,
2171 apex_plastic_indices +
2173 values);
2174 for (const auto value : values)
2175 apex_row_error_local =
2176 std::max(apex_row_error_local, std::abs(PetscRealPart(value)));
2177 PetscScalar eta_value = 0;
2178 CHKERR MatGetValues(context->inequalityJacobian, 1, &row, 1,
2179 apex_epigraph_indices + block, &eta_value);
2180 apex_row_error_local = std::max(
2181 apex_row_error_local,
2182 std::abs(PetscRealPart(eta_value) - constraint_scales[block]));
2183 }
2184 CHKERR ISRestoreIndices(kappa_is, &apex_epigraph_indices);
2185 CHKERR ISRestoreIndices(plastic_is, &apex_plastic_indices);
2186 double apex_row_error = 0;
2187 CHKERR MPI_Allreduce(&apex_row_error_local, &apex_row_error, 1, MPI_DOUBLE,
2188 MPI_MAX, ep.mField.get_comm());
2189 if (apex_row_error > 1e-12)
2191 "Regularized apex row is not sqrt(V)*[0,...,0,1]: error %g",
2192 apex_row_error);
2193
2194 auto apex_error = vectorDuplicate(context->inequalityConstraints);
2195 CHKERR operators_tester->checkVectorCentralFiniteDifference(
2196 context->referenceControl, mixed_direction,
2197 context->inequalityConstraints, context->inequalityJacobian, epsilon,
2198 evaluate, apex_error);
2199 PetscReal apex_error_norm = 0;
2200 CHKERR VecNorm(apex_error, NORM_INFINITY, &apex_error_norm);
2201 auto apex_assembled = vectorDuplicate(context->inequalityConstraints);
2202 CHKERR MatMult(context->inequalityJacobian, mixed_direction,
2203 apex_assembled);
2204 PetscReal apex_assembled_norm = 0;
2205 CHKERR VecNorm(apex_assembled, NORM_INFINITY, &apex_assembled_norm);
2206 const double apex_tolerance =
2207 absolute_tolerance + relative_tolerance * apex_assembled_norm;
2208 if (!std::isfinite(apex_error_norm) || apex_error_norm > apex_tolerance)
2210 "Regularized apex constraint Jacobian mismatch: error %g > %g",
2211 apex_error_norm, apex_tolerance);
2212 error_norm = std::max(error_norm, apex_error_norm);
2213 } else {
2215 t_force_direction(.31, -.17, .23, -.29, .19);
2216 const double force_direction_norm =
2217 plasticCoordinateNorm(t_force_direction);
2218 auto check_exact_apex = [&](const double yield_ratio) {
2222 const double force_norm =
2223 yield_ratio * problem.getInitialYieldStress() /
2225 t_force(L) = force_norm * t_force_direction(L) / force_direction_norm;
2226
2227 auto synthetic_gradient = vectorDuplicate(context->gradient);
2228 CHKERR VecZeroEntries(synthetic_gradient);
2229 Vec apex_measures = nullptr;
2230 CHKERR VecGetSubVector(plastic_cell_measure, kappa_is, &apex_measures);
2231 const PetscScalar *apex_measure_array = nullptr;
2232 CHKERR VecGetArrayRead(apex_measures, &apex_measure_array);
2233 CHKERR forEachOwnedTestFieldBlock(
2234 control_dm, problem.getPlasticFlowFieldName(),
2235 plasticLogarithmicStretchCoordinateSize, synthetic_gradient,
2236 [&](PetscInt block, PetscScalar *gradient_values) {
2237 auto t_gradient =
2238 getFTensor1FromPtr<plasticLogarithmicStretchCoordinateSize>(
2239 gradient_values);
2240 t_gradient(L) =
2241 -PetscRealPart(apex_measure_array[block]) * t_force(L);
2242 return MoFEMErrorCode(0);
2243 });
2244 CHKERR VecRestoreArrayRead(apex_measures, &apex_measure_array);
2245 CHKERR VecRestoreSubVector(plastic_cell_measure, kappa_is,
2246 &apex_measures);
2247
2248 CHKERR MatZeroEntries(context->inequalityJacobian);
2250 problem, context->referenceControl, synthetic_gradient,
2251 context->inequalityJacobian);
2252 const PetscInt *apex_plastic_indices = nullptr;
2253 const PetscInt *apex_epigraph_indices = nullptr;
2254 CHKERR ISGetIndices(plastic_is, &apex_plastic_indices);
2255 CHKERR ISGetIndices(kappa_is, &apex_epigraph_indices);
2256 CHKERR VecGetSubVector(plastic_cell_measure, kappa_is, &apex_measures);
2257 CHKERR VecGetArrayRead(apex_measures, &apex_measure_array);
2258 PetscInt row_start = 0;
2259 PetscInt row_end = 0;
2260 CHKERR MatGetOwnershipRange(context->inequalityJacobian, &row_start,
2261 &row_end);
2262 double selection_error_local = 0;
2263 double kkt_error_local = 0;
2264 const double q = plasticYieldFunctionScale * force_norm;
2265 const double slope_denominator =
2266 std::max(problem.getInitialYieldStress(), q);
2267 for (PetscInt block = 0; block != row_end - row_start; ++block) {
2268 const PetscInt row = row_start + block;
2269 PetscScalar values[plasticLogarithmicStretchCoordinateSize];
2270 CHKERR MatGetValues(context->inequalityJacobian, 1, &row,
2272 apex_plastic_indices +
2274 values);
2275 double kkt_norm_squared = 0;
2276 const double measure = PetscRealPart(apex_measure_array[block]);
2277 for (PetscInt coordinate = 0;
2279 ++coordinate) {
2280 const double expected_row =
2281 -constraint_scales[block] * t_force(coordinate) /
2282 slope_denominator;
2283 selection_error_local = std::max(
2284 selection_error_local,
2285 std::abs(PetscRealPart(values[coordinate]) - expected_row));
2286 const double kkt_component = -measure * t_force(coordinate) -
2287 measure / constraint_scales[block] *
2288 problem.getInitialYieldStress() *
2289 PetscRealPart(values[coordinate]);
2290 kkt_norm_squared += kkt_component * kkt_component;
2291 }
2292 const double expected_kkt_norm =
2293 measure * std::max(0., yield_ratio - 1.) *
2294 problem.getInitialYieldStress() / plasticYieldFunctionScale;
2295 kkt_error_local =
2296 std::max(kkt_error_local,
2297 std::abs(std::sqrt(kkt_norm_squared) - expected_kkt_norm));
2298 PetscScalar eta_value = 0;
2299 CHKERR MatGetValues(context->inequalityJacobian, 1, &row, 1,
2300 apex_epigraph_indices + block, &eta_value);
2301 selection_error_local = std::max(
2302 selection_error_local,
2303 std::abs(PetscRealPart(eta_value) - constraint_scales[block]));
2304 }
2305 CHKERR VecRestoreArrayRead(apex_measures, &apex_measure_array);
2306 CHKERR VecRestoreSubVector(plastic_cell_measure, kappa_is,
2307 &apex_measures);
2308 CHKERR ISRestoreIndices(kappa_is, &apex_epigraph_indices);
2309 CHKERR ISRestoreIndices(plastic_is, &apex_plastic_indices);
2310 double selection_error = 0;
2311 double kkt_error = 0;
2312 CHKERR MPI_Allreduce(&selection_error_local, &selection_error, 1,
2313 MPI_DOUBLE, MPI_MAX, ep.mField.get_comm());
2314 CHKERR MPI_Allreduce(&kkt_error_local, &kkt_error, 1, MPI_DOUBLE, MPI_MAX,
2315 ep.mField.get_comm());
2316 const double apex_tolerance =
2317 1e-12 * std::max(1., problem.getInitialYieldStress());
2318 if (selection_error > apex_tolerance || kkt_error > apex_tolerance)
2320 "Exact apex force selection at q/sigma_y=%g failed: row "
2321 "error %g, KKT error %g",
2322 yield_ratio, selection_error, kkt_error);
2324 };
2325 CHKERR check_exact_apex(.5);
2326 CHKERR check_exact_apex(2.);
2327 }
2328 MOFEM_LOG("EP", Sev::inform)
2329 << "OperatorsTester constraint derivative error " << error_norm
2330 << ", tolerance " << tolerance << ", dissipation epsilon "
2331 << regularization_epsilon;
2333}
2335 Vec state,
2336 int,
2337 double) {
2338 return testIncrementalOptimizationLayout(*this, ts, state);
2339}
2340
2341} // namespace EshelbianPlasticity
boost::shared_ptr< AuxiliaryLogarithmicStressData > valuesPtr
Eshelbian plasticity interface.
boost::shared_ptr< IncrementalOptimizationContext > context
Lie algebra implementation.
Shared implementation details for plastic incremental optimization.
boost::shared_ptr< PetscInt > elementCount
boost::shared_ptr< VectorDouble > kappaIncrementsPtr
Plasticity implementation of incremental optimization.
#define FTENSOR_INDEX(DIM, I)
constexpr double a
constexpr int SPACE_DIM
@ ROW
#define MoFEMFunctionReturnHot(a)
Last executable line of each PETSc function used for error handling. Replaces return()
@ NOSPACE
Definition definitions.h:83
#define MYPCOMM_INDEX
default communicator number PCOMM
#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_DATA_INCONSISTENCY
Definition definitions.h:31
@ MOFEM_INVALID_DATA
Definition definitions.h:36
@ MOFEM_NOT_IMPLEMENTED
Definition definitions.h:32
#define MoFEMFunctionReturn(a)
Last executable line of each PETSc function used for error handling. Replaces return()
#define CHKERR
Inline error check.
double kappa
PetscErrorCode DMMoFEMCreateSubDM(DM subdm, DM dm, const char problem_name[])
Must be called by user to set Sub DM MoFEM data structures.
Definition DMMoFEM.cpp:215
PetscErrorCode DMMoFEMAddElement(DM dm, std::string fe_name)
add element to dm
Definition DMMoFEM.cpp:488
PetscErrorCode DMMoFEMSetSquareProblem(DM dm, PetscBool square_problem)
set squared problem
Definition DMMoFEM.cpp:450
PetscErrorCode DMMoFEMGetProblemPtr(DM dm, const MoFEM::Problem **problem_ptr)
Get pointer to problem data structure.
Definition DMMoFEM.cpp:422
PetscErrorCode DMMoFEMAddSubFieldRow(DM dm, const char field_name[])
Definition DMMoFEM.cpp:238
PetscErrorCode DMMoFEMGetSquareProblem(DM dm, PetscBool *square_problem)
get squared problem
Definition DMMoFEM.cpp:480
PetscErrorCode DMoFEMLoopFiniteElements(DM dm, const char fe_name[], MoFEM::FEMethod *method, CacheTupleWeakPtr cache_ptr=CacheTupleSharedPtr())
Executes FEMethod for finite elements in DM.
Definition DMMoFEM.cpp:576
PetscErrorCode DMMoFEMGetFieldIS(DM dm, RowColData rc, const char field_name[], IS *is)
get field is in the problem
Definition DMMoFEM.cpp:1507
auto createDMVector(DM dm, RowColData rc=RowColData::COL)
Get smart vector from DM.
Definition DMMoFEM.hpp:1237
PetscErrorCode DMMoFEMAddSubFieldCol(DM dm, const char field_name[])
Definition DMMoFEM.cpp:280
auto createDMMatrix(DM dm)
Get smart matrix from DM.
Definition DMMoFEM.hpp:1194
@ GAUSS
Gaussian quadrature integration.
#define MOFEM_LOG(channel, severity)
Log.
FTensor::Index< 'i', SPACE_DIM > i
static double lambda
const double c
speed of light (cm/ns)
const FTensor::Tensor2< T, Dim, Dim > Vec
MoFEMErrorCode assemblePlasticInequalityJacobian(PlasticIncrementalOptimizationProblem &problem, Vec control, Vec smooth_gradient, Mat jacobian)
MoFEMErrorCode setPlasticHistory(EshelbianCore &ep, const PlasticHistory &history, const FTensor::Tensor2_symmetric< double, SPACE_DIM > &t_direction, const double scale)
MoFEMErrorCode setPlasticKappaHistory(EshelbianCore &ep, const PlasticKappaHistory &history)
MoFEMErrorCode savePlasticHistory(EshelbianCore &ep, PlasticHistory &history)
MoFEMErrorCode evaluatePlasticInequalityConstraints(const PlasticIncrementalOptimizationProblem &problem, Vec control, Vec constraints)
MoFEMErrorCode savePlasticKappaHistory(EshelbianCore &ep, PlasticKappaHistory &history)
double plasticCoordinateNorm(const FTensor::Tensor1< T, plasticLogarithmicStretchCoordinateSize > &t_values)
MoFEMErrorCode evaluatePlasticIncrementalResistanceValue(const PlasticIncrementalOptimizationProblem &problem, Vec control, double &value)
FTensor::Tensor1< double, plasticLogarithmicStretchCoordinateSize > plasticLogarithmicStretchCoordinatesFromTensor(const FTensor::Tensor2_symmetric< T, SPACE_DIM > &t_values)
MoFEMErrorCode assembleReducedHelmholtzGradient(EshelbianCore &ep, TS ts, Vec gradient)
FTensor::Tensor2_symmetric< double, SPACE_DIM > plasticLogarithmicStretchTensorFromCoordinates(const FTensor::Tensor1< T, plasticLogarithmicStretchCoordinateSize > &t_coordinates)
double plasticFrobeniusNorm(const FTensor::Tensor2_symmetric< T, SPACE_DIM > &t_values)
MoFEMErrorCode validateEquilibratedMechanicalValueScope(EshelbianCore &ep)
boost::shared_ptr< IncrementalOptimizationContext > createPlasticIncrementalOptimizationContext(EshelbianCore &ep, SmartPetscObj< TS > ts, SmartPetscObj< Vec > state)
MoFEMErrorCode testIncrementalOptimizationLayout(EshelbianCore &ep, TS ts, Vec state)
VolumeElementForcesAndSourcesCore::UserDataOperator VolUserDataOperator
MoFEMErrorCode solveEquilibriumStateTS(TopologicalTAOCtxImpl *ctx_impl_ptr)
MoFEMErrorCode testIncrementalOptimizationConstraintDerivative(EshelbianCore &ep, TS ts, Vec state)
MoFEMErrorCode checkPlasticConstraintSubsetAssembly(EshelbianCore &ep, PlasticIncrementalOptimizationProblem &problem, Vec control, Vec smooth_gradient)
MoFEMErrorCode testIncrementalOptimizationTransaction(EshelbianCore &ep, TS ts, Vec state)
MoFEMErrorCode evaluateEquilibratedMechanicalValue(EshelbianCore &ep, TS ts, double &value)
MoFEMErrorCode evaluateIncrementalObjectiveAndGradient(const boost::shared_ptr< IncrementalOptimizationContext > &context, Vec control, IncrementalObjectiveEvaluation &evaluation)
MoFEMErrorCode testIncrementalOptimizationObjectiveDerivative(EshelbianCore &ep, TS ts, Vec state)
MoFEMErrorCode evaluatePlasticConjugateWork(EshelbianCore &ep, const FTensor::Tensor2_symmetric< double, SPACE_DIM > &t_direction, double &conjugate_work)
MoFEMErrorCode assembleIncrementalOptimizationInequalityJacobianFromEvaluation(const boost::shared_ptr< IncrementalOptimizationContext > &context, Vec control, const IncrementalObjectiveEvaluation &evaluation, Mat jacobian)
MoFEMErrorCode testEquilibratedMechanicalValue(EshelbianCore &ep, TS ts, Vec state)
MoFEMErrorCode evaluateIncrementalOptimizationTAOObjectiveAndGradient(const boost::shared_ptr< IncrementalOptimizationContext > &context, Vec control, PetscReal &objective, Vec objective_gradient, IncrementalObjectiveEvaluation &evaluation)
PlasticIncrementalOptimizationProblem & getPlasticIncrementalOptimizationProblem(const boost::shared_ptr< IncrementalOptimizationContext > &context)
MoFEMErrorCode addCalculatePlasticLogarithmicStretchFieldValues(boost::ptr_deque< ForcesAndSourcesCore::UserDataOperator > &pipeline, const std::string &field_name, boost::shared_ptr< MatrixDouble > tensor_values, const EntityType zero_type, SmartPetscObj< DM > data_dm, SmartPetscObj< Vec > data_vector)
MoFEMErrorCode evaluateIncrementalObjective(const boost::shared_ptr< IncrementalOptimizationContext > &context, Vec control, IncrementalObjectiveEvaluation &evaluation)
MoFEMErrorCode assembleIncrementalOptimizationInequalityJacobian(const boost::shared_ptr< IncrementalOptimizationContext > &context, Vec control, Mat jacobian)
MoFEMErrorCode evaluateIncrementalOptimizationInequalityConstraints(const boost::shared_ptr< IncrementalOptimizationContext > &context, Vec control, Vec constraints)
PetscErrorCode MoFEMErrorCode
MoFEM/PETSc error code.
UBlasMatrix< double > MatrixDouble
Definition Types.hpp:77
implementation of Data Operators for Forces and Sources
Definition Common.hpp:10
PetscErrorCode DMMoFEMSetDestroyProblem(DM dm, PetscBool destroy_problem)
Definition DMMoFEM.cpp:434
PetscErrorCode PetscOptionsGetReal(PetscOptions *, const char pre[], const char name[], PetscReal *dval, PetscBool *set)
PetscErrorCode PetscOptionsGetBool(PetscOptions *, const char pre[], const char name[], PetscBool *bval, PetscBool *set)
PetscErrorCode DMMoFEMGetSubRowIS(DM dm, IS *is)
get sub problem is
Definition DMMoFEM.cpp:330
SmartPetscObj< Vec > vectorDuplicate(Vec vec)
Create duplicate vector of smart vector.
PetscErrorCode DMMoFEMGetSubColIS(DM dm, IS *is)
get sub problem is
Definition DMMoFEM.cpp:347
auto createDM(MPI_Comm comm, const std::string dm_type_name)
Creates smart DM object.
constexpr AssemblyType A
constexpr auto field_name
double q
CGG User Polynomial Base.
boost::shared_ptr< Range > frontAdjEdges
MoFEM::Interface & mField
boost::shared_ptr< Range > plasticVolumes
const std::string materialH1Positions
const std::string elementVolumeName
MoFEMErrorCode solveTestIncrementalOptimizationLayout(TS ts, Vec x, int start_step, double start_time)
const std::string plasticFlowField
static PetscBool plasticVolume
static PetscBool interfaceCrack
const std::string plasticKappaField
SmartPetscObj< DM > dmIncrementalOptimization
Incremental-optimization control problem.
Add operators pushing bases from local to physical configuration.
virtual moab::Interface & get_moab()=0
virtual MPI_Comm & get_comm() const =0
Data on single entity (This is passed as argument to DataOperator::doWork)
Structure for user loop methods on finite elements.
Basic algebra on fields.
Definition FieldBlas.hpp:21
@ OPROW
operator doWork function is executed on FE rows
Specialization for double precision scalar field values calculation.
Calculate directional derivative of the right hand side and compare it with tangent matrix derivative...
keeps basic data about problem
auto & getNumeredRowDofsPtr() const
get access to numeredRowDofsPtr storing DOFs on rows
intrusive_ptr for managing petsc objects
MoFEMErrorCode getInterface(IFACE *&iface) const
Get interface reference to pointer of interface.
double scale
Definition plastic.cpp:123