v0.16.3
Loading...
Searching...
No Matches
rotation_split_atom.cpp
Go to the documentation of this file.
1/** Regression checks for product L2 rotations, derivatives and TS branching. */
2#include <array>
3#include <numbers>
4#include <MoFEM.hpp>
5using namespace MoFEM;
7#include <L2Rotation.hpp>
10using namespace EshelbianPlasticity;
11
12namespace {
13
14struct OpCheckRotationLayout : VolUserDataOperator {
15 OpCheckRotationLayout(const std::string &field,
16 boost::shared_ptr<PetscInt> visits)
17 : VolUserDataOperator(field, OPROW), visitsPtr(std::move(visits)) {
18 doEntities.fill(false);
19 doEntities[MBTET] = true;
20 }
21
22 MoFEMErrorCode doWork(int, EntityType, EntData &data) override {
24 const int dofs = data.getFieldData().size();
25 if (!dofs)
27 const int points = getGaussPts().size2();
28 if (dofs < 3 || dofs % 3 || points <= 1 || data.getN().size2() < dofs / 3)
29 SETERRQ(PETSC_COMM_SELF, MOFEM_ATOM_TEST_INVALID,
30 "Incomplete tetrahedral L2 rotation block");
31 const auto &field_dofs = data.getFieldDofs();
32 for (int c = 0; c != 3; ++c)
33 if (field_dofs[c]->getDofOrder() != 0 ||
34 field_dofs[c]->getDofCoeffIdx() != c)
35 SETERRQ(PETSC_COMM_SELF, MOFEM_ATOM_TEST_INVALID,
36 "First rotation block is not the constant vector mode");
37 const double phi0 = data.getN()(0, 0);
38 if (!std::isfinite(phi0) || std::abs(phi0) < 1.e-12)
39 SETERRQ(PETSC_COMM_SELF, MOFEM_ATOM_TEST_INVALID,
40 "Constant rotation basis has invalid normalisation");
41 FTENSOR_INDEX(3, i);
42 for (int gg = 0; gg != points; ++gg) {
43 auto gradient = data.getFTensor1DiffN<3>(gg, 0);
44 if (std::abs(data.getN()(gg, 0) - phi0) > 1.e-13 ||
45 gradient(i) * gradient(i) > 1.e-26)
46 SETERRQ(PETSC_COMM_SELF, MOFEM_ATOM_TEST_INVALID,
47 "First L2 rotation basis is not spatially constant");
48 }
49 ++*visitsPtr;
51 }
52
53private:
54 boost::shared_ptr<PetscInt> visitsPtr;
55};
56
57/** Compare a split vector with ordinary interpolation and known c0. */
58struct OpCheckRotationValues : VolUserDataOperator {
59 OpCheckRotationValues(MatrixPtr total, MatrixPtr constant, MatrixPtr higher,
60 MatrixPtr reference, double scale)
61 : VolUserDataOperator(NOSPACE, OPSPACE), totalPtr(total),
62 constantPtr(constant), higherPtr(higher), referencePtr(reference),
63 stateScale(scale) {}
64 MoFEMErrorCode doWork(int, EntityType, EntData &) override {
66 using View = GetFTensor1FromMatType<3, -1, DL>;
67 const int points = getGaussPts().size2();
68 auto t = MatrixSizeHelper<View, DL>::get(*totalPtr, points)();
69 auto a = MatrixSizeHelper<View, DL>::get(*constantPtr, points)();
70 auto q = MatrixSizeHelper<View, DL>::get(*higherPtr, points)();
71 auto r = MatrixSizeHelper<View, DL>::get(*referencePtr, points)();
72 FTENSOR_INDEX(3, i);
73 const FTensor::Tensor1<double, 3> expected(
74 .4 * stateScale, -.3 * stateScale, .2 * stateScale);
75 for (int gg = 0; gg != points; ++gg) {
77 error(i) = a(i) - expected(i);
78 if (error(i) * error(i) > 1.e-24)
79 SETERRQ(PETSC_COMM_SELF, MOFEM_ATOM_TEST_INVALID,
80 "Incorrect constant rotation vector");
81 error(i) = t(i) - r(i);
82 if (error(i) * error(i) > 1.e-24)
83 SETERRQ(PETSC_COMM_SELF, MOFEM_ATOM_TEST_INVALID,
84 "Split total differs from core field evaluation");
85 error(i) = t(i) - a(i) - q(i);
86 if (error(i) * error(i) > 1.e-24)
87 SETERRQ(PETSC_COMM_SELF, MOFEM_ATOM_TEST_INVALID,
88 "Rotation decomposition does not reconstruct the total");
89 ++t;
90 ++a;
91 ++q;
92 ++r;
93 }
95 }
96 MatrixPtr totalPtr, constantPtr, higherPtr, referencePtr;
97 double stateScale;
98};
99
100MoFEMErrorCode checkRotationVectors(EshelbianCore &ep) {
102 const auto make_vector = [&](double scale, SmartPetscObj<Vec> &vector) {
104 vector = createDMVector(ep.dmElastic);
105 CHKERR VecZeroEntries(vector);
106 PetscInt first, last;
107 CHKERR VecGetOwnershipRange(vector, &first, &last);
108 const Problem *problem;
110 const double c0[] = {.4, -.3, .2};
111 for (const auto &dof : *problem->getNumeredRowDofsPtr()) {
112 const auto index = dof->getPetscGlobalDofIdx();
113 if (dof->getName() != ep.rotAxis || index < first || index >= last)
114 continue;
115 const auto component = dof->getDofCoeffIdx();
116 const double value =
117 dof->getDofOrder() == 0 ? c0[component] : .001 * (index % 13 - 6);
118 CHKERR VecSetValue(vector, index, scale * value, INSERT_VALUES);
119 }
120 CHKERR VecAssemblyBegin(vector);
121 CHKERR VecAssemblyEnd(vector);
122 CHKERR VecGhostUpdateBegin(vector, INSERT_VALUES, SCATTER_FORWARD);
123 CHKERR VecGhostUpdateEnd(vector, INSERT_VALUES, SCATTER_FORWARD);
125 };
126 SmartPetscObj<Vec> current, previous, direction;
127 CHKERR make_vector(1., current);
128 CHKERR make_vector(-.7, previous);
129 CHKERR make_vector(.13, direction);
130 CHKERR DMoFEMMeshToLocalVector(ep.dmElastic, current, INSERT_VALUES,
131 SCATTER_REVERSE);
132 auto fe = boost::make_shared<VolumeElementForcesAndSourcesCore>(ep.mField);
133 fe->getUserPolynomialBase() =
134 boost::make_shared<CGGUserPolynomialBase>(nullptr, true);
135 fe->getRuleHook = [](int, int, int order) { return 2 * order + 2; };
136 CHKERR MoFEM::AddHOOps<3, 3, 3>::add(fe->getOpPtrVector(), {L2},
138 for (const auto &state :
139 {std::make_pair(SmartPetscObj<Vec>(), 1.), std::make_pair(previous, -.7),
140 std::make_pair(direction, .13)}) {
141 auto total = boost::make_shared<MatrixDouble>();
142 auto constant = boost::make_shared<MatrixDouble>();
143 auto higher = boost::make_shared<MatrixDouble>();
144 auto reference = boost::make_shared<MatrixDouble>();
145 fe->getOpPtrVector().push_back(new OpCalculateL2RotationValues(
146 ep.rotAxis, total, constant, higher, state.first));
147 fe->getOpPtrVector().push_back(new OpCalculateVectorFieldValues<3>(
148 ep.rotAxis, reference, state.first, MBTET));
149 fe->getOpPtrVector().push_back(new OpCheckRotationValues(
150 total, constant, higher, reference, state.second));
151 }
154}
155
156/** Contract the same constant/higher Hessian blocks used by mechanics. */
157LogarithmicDirectionalDerivatives::Tensor contractRotationHessian(
163 FTENSOR_INDEXES(3, i, j, k, l);
164 const auto aa = rotation.secondTensor(0, 0);
165 const auto aq = rotation.secondTensor(0, 1);
166 const auto qa = rotation.secondTensor(1, 0);
167 const auto qq = rotation.secondTensor(1, 1);
169 result(i, j) =
170 aa(i, j, k, l) * da(k) * ea(l) + aq(i, j, k, l) * da(k) * eq(l) +
171 qa(i, j, k, l) * dq(k) * ea(l) + qq(i, j, k, l) * dq(k) * eq(l);
172 return result;
173}
174
175/** Independent central differences of values/derivatives, through order 3. */
176MoFEMErrorCode checkSplitRotationDerivatives() {
178 using namespace LogarithmicDirectionalDerivatives;
179 FTENSOR_INDEXES(3, i, j);
180 struct Direction {
181 Vector a, q;
182 };
183 const Vector zero(0., 0., 0.);
184 const Direction directions[] = {{Vector(.3, -.2, .1), zero},
185 {zero, Vector(-.1, .4, .2)},
186 {Vector(.2, .1, -.3), Vector(-.3, .2, .1)}};
187 const Direction bases[] = {
188 {zero, zero},
189 {Vector(.7, -.2, .4), Vector(-.3, .5, .1)},
190 {Vector(0., 0., 6.1), Vector(.2, -.1, .08)},
191 {Vector(1.e-8, -2.e-8, 3.e-8), Vector(.1, .2, -.1)}};
192 const double step = 4.e-6;
193 const auto check = [&](const auto &exact, const auto &fd,
194 const char *description) {
196 Tensor error;
197 error(i, j) = exact(i, j) - fd(i, j);
198 if (error(i, j) * error(i, j) > 1.e-14)
199 SETERRQ(PETSC_COMM_SELF, MOFEM_ATOM_TEST_INVALID,
200 "Split rotation %s finite difference failed", description);
202 };
203 for (const auto &base : bases) {
204 const SplitRotation rotation(base.a, base.q);
205 for (const auto &d : directions) {
206 Vector plus_a, minus_a, plus_q, minus_q;
207 plus_a(i) = base.a(i) + step * d.a(i);
208 minus_a(i) = base.a(i) - step * d.a(i);
209 plus_q(i) = base.q(i) + step * d.q(i);
210 minus_q(i) = base.q(i) - step * d.q(i);
211 const SplitRotation plus(plus_a, plus_q), minus(minus_a, minus_q);
212 Tensor fd;
213 fd(i, j) = (plus.value()(i, j) - minus.value()(i, j)) / (2 * step);
214 CHKERR check(rotation.first(d.a, d.q), fd, "first");
215 for (const auto &e : directions) {
216 fd(i, j) = (plus.first(e.a, e.q)(i, j) - minus.first(e.a, e.q)(i, j)) /
217 (2 * step);
218 CHKERR check(contractRotationHessian(rotation, d.a, d.q, e.a, e.q), fd,
219 "second");
220 for (const auto &f : directions) {
222 a[0](i, j) = skew(base.a)(i, j);
223 q[0](i, j) = skew(base.q)(i, j);
224 a[1](i, j) = skew(d.a)(i, j);
225 q[1](i, j) = skew(d.q)(i, j);
226 a[2](i, j) = skew(e.a)(i, j);
227 q[2](i, j) = skew(e.q)(i, j);
228 a[4](i, j) = skew(f.a)(i, j);
229 q[4](i, j) = skew(f.q)(i, j);
230 rotation.fill(a, q, result);
231 fd(i, j) =
232 (contractRotationHessian(plus, e.a, e.q, f.a, f.q)(i, j) -
233 contractRotationHessian(minus, e.a, e.q, f.a, f.q)(i, j)) /
234 (2 * step);
235 CHKERR check(result[7], fd, "third");
236 CHKERR check(result[3],
237 contractRotationHessian(rotation, d.a, d.q, e.a, e.q),
238 "jet second");
239 }
240 }
241 }
242 }
243 const Vector a(0., 0., 6.1), q(.2, -.1, .08);
244 const Vector shortened(0., 0., 6.1 - 2 * std::numbers::pi);
245 CHKERR check(SplitRotation(a, q).value(), SplitRotation(shortened, q).value(),
246 "branch value");
248}
249
250struct OpCheckShortenedRotation : VolUserDataOperator {
251 OpCheckShortenedRotation(std::array<MatrixPtr, 2> constant,
252 std::array<MatrixPtr, 2> higher)
253 : VolUserDataOperator(NOSPACE, OPSPACE), constantPtr(constant),
254 higherPtr(higher) {}
255 MoFEMErrorCode doWork(int, EntityType, EntData &) override {
257 using namespace LogarithmicDirectionalDerivatives;
258 FTENSOR_INDEXES(3, i, j);
259 const int points = getGaussPts().size2();
260 using View = GetFTensor1FromMatType<3, -1, DL>;
261 auto a =
262 std::array{MatrixSizeHelper<View, DL>::get(*constantPtr[0], points)(),
263 MatrixSizeHelper<View, DL>::get(*constantPtr[1], points)()};
264 auto q =
265 std::array{MatrixSizeHelper<View, DL>::get(*higherPtr[0], points)(),
266 MatrixSizeHelper<View, DL>::get(*higherPtr[1], points)()};
267 for (int gg = 0; gg != points; ++gg) {
268 Vector old_a, new_a, old_q, new_q;
269 old_a(i) = a[0](i);
270 new_a(i) = a[1](i);
271 old_q(i) = q[0](i);
272 new_q(i) = q[1](i);
273 Tensor error;
274 error(i, j) = SplitRotation(old_a, old_q).value()(i, j) -
275 SplitRotation(new_a, new_q).value()(i, j);
276 if (error(i, j) * error(i, j) > 1.e-25 || new_a.l2() >= std::numbers::pi)
277 SETERRQ(PETSC_COMM_SELF, MOFEM_ATOM_TEST_INVALID,
278 "Constant branch update changed the product rotation");
279 ++a[0];
280 ++a[1];
281 ++q[0];
282 ++q[1];
283 }
285 }
286 std::array<MatrixPtr, 2> constantPtr, higherPtr;
287};
288
289// Verify that the Schur shift changes only preconditioning, including rebuilds.
290MoFEMErrorCode checkRotationPreconditioner(EshelbianCore &ep, TS ts, Vec state,
291 Vec rates, Mat jacobian,
292 Mat pc_mat) {
294 if (ep.alphaOmegaPrec == 0.)
296 auto exact = vectorDuplicate(state);
297 auto shifted = vectorDuplicate(state);
299 CHKERR MatMult(jacobian, state, exact);
300 CHKERR MatMult(pc_mat, state, shifted);
301 CHKERR TSComputeIFunction(ts, 0., state, rates, residual, PETSC_FALSE);
302 auto schur_shifted = matDuplicate(ep.S, MAT_COPY_VALUES);
303
304 auto &pipeline = ep.elasticFeLhs->getOpPtrVector();
305 if (!dynamic_cast<OpSpatialRotationPrec *>(&pipeline.back()))
307 "Expected rotation preconditioner at the end of the LHS pipeline");
308 pipeline.pop_back();
309 CHKERR TSComputeIJacobian(ts, 0., state, rates, 1., jacobian, pc_mat,
310 PETSC_FALSE);
311 auto base = vectorDuplicate(state);
312 CHKERR MatMult(jacobian, state, base);
313 CHKERR VecAXPY(base, -1., exact);
314 double error;
315 CHKERR VecNorm(base, NORM_2, &error);
316 if (error > 1e-10)
318 "Rotation preconditioning changed the exact Jacobian: %g", error);
319 CHKERR TSComputeIFunction(ts, 0., state, rates, base, PETSC_FALSE);
320 CHKERR VecAXPY(base, -1., residual);
321 CHKERR VecNorm(base, NORM_2, &error);
322 if (error > 1e-12)
324 "Rotation preconditioning changed the residual: %g", error);
325
326 CHKERR MatMult(pc_mat, state, base);
327 CHKERR VecAXPY(base, -1., shifted);
328 double shift_norm, work;
329 CHKERR VecNorm(base, NORM_2, &shift_norm);
330 CHKERR VecDot(state, base, &work);
331 if (!std::isfinite(shift_norm) || shift_norm < 1e-10 || work >= 0.)
333 "Expected a nonzero positive rotation-gradient preconditioner");
334 IS rotation_raw;
336 &rotation_raw);
337 SmartPetscObj<IS> rotation_is(rotation_raw);
338 Vec rotation_sub;
339 CHKERR VecGetSubVector(base, rotation_is, &rotation_sub);
340 CHKERR VecZeroEntries(rotation_sub);
341 CHKERR VecRestoreSubVector(base, rotation_is, &rotation_sub);
342 CHKERR VecNorm(base, NORM_2, &error);
343 if (error > 1e-10)
345 "Rotation preconditioner modified another field: %g", error);
346 CHKERR MatAXPY(schur_shifted, -1., ep.S, SAME_NONZERO_PATTERN);
347 CHKERR MatNorm(schur_shifted, NORM_FROBENIUS, &error);
348 if (!std::isfinite(error) || error < 1e-10)
350 "Schur preconditioner did not include the rotation shift");
351
352 pipeline.push_back(
354 CHKERR TSComputeIJacobian(ts, 0., state, rates, 1., jacobian, pc_mat,
355 PETSC_FALSE);
356 CHKERR MatMult(pc_mat, state, base);
357 CHKERR VecAXPY(base, -1., shifted);
358 CHKERR VecNorm(base, NORM_2, &error);
359 if (error > 1e-10)
361 "Rotation preconditioner changed after identical reassembly: %g",
362 error);
363
364 auto constant = vectorDuplicate(state);
365 CHKERR VecZeroEntries(constant);
366 const Problem *problem;
368 PetscInt first, last;
369 CHKERR VecGetOwnershipRange(constant, &first, &last);
370 for (const auto &dof : *problem->getNumeredRowDofsPtr()) {
371 const auto index = dof->getPetscGlobalDofIdx();
372 if (dof->getName() == ep.rotAxis && dof->getDofOrder() == 0 &&
373 index >= first && index < last)
374 CHKERR VecSetValue(constant, index, 1., INSERT_VALUES);
375 }
376 CHKERR VecAssemblyBegin(constant);
377 CHKERR VecAssemblyEnd(constant);
378 CHKERR MatMult(pc_mat, constant, base);
379 CHKERR MatMult(jacobian, constant, exact);
380 CHKERR VecAXPY(base, -1., exact);
381 CHKERR VecNorm(base, NORM_2, &error);
382 if (error > 1e-10)
384 "Gradient preconditioner penalised constant L2 rotation: %g",
385 error);
386
387 Mat a00;
388 CHKERR MatNestGetSubMat(pc_mat, 1, 1, &a00);
389 Vec x_raw, b_raw;
390 CHKERR MatCreateVecs(a00, &x_raw, &b_raw);
391 SmartPetscObj<Vec> x(x_raw), b(b_raw);
392 auto solution = vectorDuplicate(x);
393 CHKERR VecSet(x, 1.);
394 CHKERR MatMult(a00, x, b);
395 CHKERR MatSolve(a00, b, solution);
396 double rhs_norm;
397 CHKERR VecNorm(b, NORM_2, &rhs_norm);
398 CHKERR MatMult(a00, solution, x);
399 CHKERR VecAXPY(x, -1., b);
400 CHKERR VecNorm(x, NORM_2, &error);
401 error /= std::max(rhs_norm, std::numeric_limits<double>::min());
402 if (!std::isfinite(error) || error > 1e-10)
404 "Shifted A00 solve does not match its operator: %g", error);
405 MOFEM_LOG("EP", Sev::inform)
406 << "Rotation preconditioner: exact residual/Jacobian preserved, shift "
407 << shift_norm << ", relative A00 residual " << error;
409}
410
411MoFEMErrorCode checkConstantBranchUpdate(EshelbianCore &ep) {
414 CHKERR VecZeroEntries(state);
415 const Problem *problem;
417 PetscInt first, last;
418 CHKERR VecGetOwnershipRange(state, &first, &last);
419 for (const auto &dof : *problem->getNumeredRowDofsPtr()) {
420 const auto index = dof->getPetscGlobalDofIdx();
421 if (dof->getName() != ep.rotAxis || index < first || index >= last)
422 continue;
423 const double value = dof->getDofOrder() == 0
424 ? (dof->getDofCoeffIdx() == 2 ? 6.1 : 0.)
425 : .003 * (index % 11 - 5);
426 CHKERR VecSetValue(state, index, value, INSERT_VALUES);
427 }
428 CHKERR VecAssemblyBegin(state);
429 CHKERR VecAssemblyEnd(state);
430 CHKERR VecGhostUpdateBegin(state, INSERT_VALUES, SCATTER_FORWARD);
431 CHKERR VecGhostUpdateEnd(state, INSERT_VALUES, SCATTER_FORWARD);
432 auto old = vectorDuplicate(state);
433 CHKERR VecCopy(state, old);
434 CHKERR VecCopy(state, ep.solTSStep);
435 CHKERR VecScale(ep.solTSStep, .7);
436 auto history = vectorDuplicate(ep.solTSStep);
437 CHKERR VecCopy(ep.solTSStep, history);
438 ep.dataAtPts = boost::make_shared<DataAtIntegrationPts>();
439 auto ts = createTS(ep.mField.get_comm());
440 CHKERR TSSetDM(ts, ep.dmElastic);
441 CHKERR TSSetType(ts, TSBEULER);
442 CHKERR TSSetSolution(ts, state);
445 CHKERR TSPreStep(ts);
446 PetscBool equal;
447 CHKERR VecEqual(state, old, &equal);
448 if (!equal)
450 "Pre-step changed H1 incremental kinematics before transfer");
451 CHKERR VecEqual(ep.solTSStep, history, &equal);
452 if (!equal)
454 "Pre-step changed H1 physical history");
456 }
457 PetscBool check_schur = PETSC_FALSE;
458 CHKERR PetscOptionsGetBool(nullptr, nullptr, "-test_rotation_schur",
459 &check_schur, nullptr);
460 boost::shared_ptr<EshelbianCore::SetUpSchur> schur;
461 if (check_schur) {
464 CHKERR TSSetOptionsPrefix(ts, "elastic_");
465 CHKERR TSSetFromOptions(ts);
467 CHKERR schur->setUp(ts);
468 }
469 const auto check_schur_hierarchy = [&]() {
471 if (!check_schur)
473 SNES snes;
474 KSP ksp, *subksp;
475 PC pc, a00_pc, schur_pc;
476 CHKERR TSGetSNES(ts, &snes);
477 CHKERR SNESGetKSP(snes, &ksp);
478 CHKERR KSPGetPC(ksp, &pc);
479 // Reassemble through the same TS Jacobian callback used by Newton solves.
480 auto rates = vectorDuplicate(state);
481 CHKERR VecZeroEntries(rates);
483 CHKERR TSGetIJacobian(ts, &jacobian, &preconditioner, nullptr, nullptr);
484 CHKERR TSComputeIJacobian(ts, 0., state, rates, 1., jacobian,
485 preconditioner, PETSC_FALSE);
486 CHKERR checkRotationPreconditioner(ep, ts, state, rates, jacobian,
488 CHKERR KSPSetOperators(ksp, jacobian, preconditioner);
489 CHKERR KSPSetUp(ksp);
490 CHKERR PCFieldSplitSchurGetSubKSP(pc, nullptr, &subksp);
491 CHKERR KSPGetPC(subksp[0], &a00_pc);
492 CHKERR KSPGetPC(subksp[1], &schur_pc);
493 CHKERR PetscFree(subksp);
494 PetscBool shell;
495 CHKERR PetscObjectTypeCompare(getPetscObject(a00_pc), PCSHELL, &shell);
496 if (!shell)
498 "Rotation shortening destroyed the Schur A00 shell PC");
499 DM dm;
500 CHKERR PCGetDM(schur_pc, &dm);
501 const Problem *problem;
502 CHKERR DMMoFEMGetProblemPtr(dm, &problem);
503 if (problem->getName() != "SUB_SCHUR")
505 "Rotation shortening lost the Schur DM");
507 };
508 CHKERR check_schur_hierarchy();
509 SNES snes;
510 CHKERR TSGetSNES(ts, &snes);
511 CHKERR SNESSetLagJacobian(snes, 3);
512 CHKERR SNESSetLagPreconditioner(snes, 4);
513 CHKERR VecCopy(old, state);
514 CHKERR TSPreStep(ts);
515 CHKERR check_schur_hierarchy();
516 PetscBool equal;
517 CHKERR VecEqual(state, old, &equal);
518 if (equal)
520 "Expected a constant rotation branch change");
521 auto difference = vectorDuplicate(state);
522 CHKERR VecCopy(state, difference);
523 CHKERR VecAXPY(difference, -1., old);
524 const double *array;
525 CHKERR VecGetArrayRead(difference, &array);
526 for (const auto &dof : *problem->getNumeredRowDofsPtr()) {
527 const auto index = dof->getPetscGlobalDofIdx();
528 if (index < first || index >= last)
529 continue;
530 if ((dof->getName() != ep.rotAxis || dof->getDofOrder() != 0) &&
531 array[index - first] != 0.)
532 SETERRQ(PETSC_COMM_SELF, MOFEM_ATOM_TEST_INVALID,
533 "Branch update changed a higher mode or another field");
534 }
535 CHKERR VecRestoreArrayRead(difference, &array);
536 auto shortened = vectorDuplicate(state);
537 CHKERR VecCopy(state, shortened);
538 CHKERR TSPreStep(ts);
539 CHKERR VecEqual(state, shortened, &equal);
540 if (!equal)
542 "Short constant rotation should not be changed twice");
543 CHKERR check_schur_hierarchy();
544 PetscInt lag;
545 CHKERR SNESGetLagJacobian(snes, &lag);
546 if (lag != 3)
548 "Rotation shortening changed the configured Jacobian lag");
549 CHKERR SNESGetLagPreconditioner(snes, &lag);
550 if (lag != 4)
552 "Rotation shortening changed the configured preconditioner lag");
553 CHKERR VecEqual(ep.solTSStep, history, &equal);
554 if (!equal)
556 "TS pre-step replaced the distinct previous physical state");
557 for (double *penalty : {&ep.alphaR, &ep.alphaViscousR}) {
558 const double saved = *penalty;
559 *penalty = .3;
560 CHKERR VecCopy(old, state);
561 CHKERR TSPreStep(ts);
562 CHKERR VecEqual(state, old, &equal);
563 *penalty = saved;
564 if (!equal)
566 "Raw coordinate penalty must prevent branch shortening");
567 }
568 CHKERR TSPreStep(ts);
569 auto fe = boost::make_shared<VolumeElementForcesAndSourcesCore>(ep.mField);
570 fe->getUserPolynomialBase() =
571 boost::make_shared<CGGUserPolynomialBase>(nullptr, true);
572 fe->getRuleHook = [](int, int, int order) { return 2 * order + 2; };
573 CHKERR MoFEM::AddHOOps<3, 3, 3>::add(fe->getOpPtrVector(), {L2},
575 std::array<MatrixPtr, 2> a, q;
576 for (int mode = 0; mode != 2; ++mode) {
577 a[mode] = boost::make_shared<MatrixDouble>();
578 q[mode] = boost::make_shared<MatrixDouble>();
579 fe->getOpPtrVector().push_back(new OpCalculateL2RotationValues(
580 ep.rotAxis, boost::make_shared<MatrixDouble>(), a[mode], q[mode],
581 mode ? state : old));
582 }
583 fe->getOpPtrVector().push_back(new OpCheckShortenedRotation(a, q));
586}
587
588/** Verify product-rotation tangents with a nontrivial known H1 deformation. */
589MoFEMErrorCode checkH1RotationJacobian(EshelbianCore &ep) {
591 CHKERR ep.mField.getInterface<FieldBlas>()->fieldLambdaOnEntities(
592 [&](boost::shared_ptr<FieldEntity> entity) {
594 auto values = entity->getEntFieldData();
595 for (int c = 0; c != values.size(); ++c)
596 values[c] = 0.;
597 if (entity->getEntType() == MBVERTEX) {
598 double coordinates[3];
599 const auto handle = entity->getEnt();
600 CHKERR ep.mField.get_moab().get_coords(&handle, 1, coordinates);
601 auto x = getFTensor1FromPtr<3>(coordinates);
602 auto displacement = getFTensor1FromPtr<3>(&values[0]);
603 displacement(0) = .04 * x(0) + .02 * x(1);
604 displacement(1) = -.03 * x(1) + .01 * x(2);
605 displacement(2) = .02 * x(2);
606 }
608 },
611 CHKERR VecZeroEntries(state);
612 const Problem *problem;
614 PetscInt first, last;
615 CHKERR VecGetOwnershipRange(state, &first, &last);
616 for (const auto &dof : *problem->getNumeredRowDofsPtr()) {
617 const auto index = dof->getPetscGlobalDofIdx();
618 if (index < first || index >= last)
619 continue;
620 const auto &name = dof->getName();
621 if (name == ep.rotAxis || name == ep.stretchTensor ||
623 CHKERR VecSetValue(state, index, .01 * (index % 13 - 6), INSERT_VALUES);
624 }
625 CHKERR VecAssemblyBegin(state);
626 CHKERR VecAssemblyEnd(state);
627 CHKERR VecGhostUpdateBegin(state, INSERT_VALUES, SCATTER_FORWARD);
628 CHKERR VecGhostUpdateEnd(state, INSERT_VALUES, SCATTER_FORWARD);
629 CHKERR VecCopy(state, ep.solTSStep);
630 auto rates = vectorDuplicate(state);
631 CHKERR VecZeroEntries(rates);
632 boost::shared_ptr<VolumeElementForcesAndSourcesCore> rhs, lhs;
633 CHKERR ep.setVolumeElementOps(1, true, false, rhs, lhs);
634 auto tester = ep.mField.getInterface<OperatorsTester>();
635 auto jacobian = tester->assembleMat(ep.dmElastic, ep.elementVolumeName, lhs,
636 state, rates, rates, 1., 1., {});
637 auto residual = tester->assembleVec(ep.dmElastic, ep.elementVolumeName, rhs,
638 state, rates, rates, 1., 1., {});
640 auto error = vectorDuplicate(state);
642 auto trial = vectorDuplicate(state);
643 const OperatorsTester::VectorFunction evaluate = [&](Vec input, Vec output) {
645 CHKERR VecCopy(input, trial);
646 CHKERR VecGhostUpdateBegin(trial, INSERT_VALUES, SCATTER_FORWARD);
647 CHKERR VecGhostUpdateEnd(trial, INSERT_VALUES, SCATTER_FORWARD);
648 auto value = tester->assembleVec(ep.dmElastic, ep.elementVolumeName, rhs,
649 trial, rates, rates, 1., 1., {});
650 CHKERR VecCopy(value, output);
652 };
653 for (const auto &field : {ep.rotAxis, ep.stretchTensor, ep.piolaStress}) {
654 CHKERR VecCopy(state, direction);
655 // Keep only the selected unknown block to expose each cross coupling.
656 double *values;
657 CHKERR VecGetArray(direction, &values);
658 for (const auto &dof : *problem->getNumeredRowDofsPtr()) {
659 const auto index = dof->getPetscGlobalDofIdx();
660 if (index >= first && index < last && dof->getName() != field)
661 values[index - first] = 0.;
662 }
663 CHKERR VecRestoreArray(direction, &values);
664 CHKERR tester->checkVectorCentralFiniteDifference(
665 state, direction, residual, jacobian, 2.e-6, evaluate, error);
666 CHKERR MatMult(jacobian, direction, action);
667 PetscReal error_norm, action_norm;
668 CHKERR VecNorm(error, NORM_INFINITY, &error_norm);
669 CHKERR VecNorm(action, NORM_INFINITY, &action_norm);
670 if (!std::isfinite(error_norm) || error_norm > 2.e-8 + 2.e-6 * action_norm)
672 "H1 split Jacobian for %s: error %g, action %g", field.c_str(),
673 error_norm, action_norm);
674 CHKERR PetscPrintf(ep.mField.get_comm(), "H1 split Jacobian %s: %.3e\n",
675 field.c_str(), error_norm);
676 }
678}
679
680MoFEMErrorCode runRotationSplitAtom(EshelbianCore &ep) {
682 const auto *field = ep.mField.get_field_structure(ep.rotAxis);
683 if (field->getSpace() != L2 || field->getApproxBase() != USER_BASE ||
684 field->getNbOfCoeffs() != 3)
686 "Rotation split requires three-component USER_BASE L2");
687 auto fe = boost::make_shared<VolumeElementForcesAndSourcesCore>(ep.mField);
688 fe->getUserPolynomialBase() =
689 boost::make_shared<CGGUserPolynomialBase>(nullptr, true);
690 fe->getRuleHook = [](int, int, int order) { return 2 * order + 2; };
691 CHKERR MoFEM::AddHOOps<3, 3, 3>::add(fe->getOpPtrVector(), {L2},
693 auto visits = boost::make_shared<PetscInt>(0);
694 fe->getOpPtrVector().push_back(new OpCheckRotationLayout(ep.rotAxis, visits));
696 PetscInt global_visits;
697 CHKERR MPI_Allreduce(visits.get(), &global_visits, 1, MPIU_INT, MPI_SUM,
698 ep.mField.get_comm());
699 if (!global_visits)
701 "No rotation layout evaluations executed");
702 CHKERR checkRotationVectors(ep);
703 CHKERR checkSplitRotationDerivatives();
704 CHKERR checkConstantBranchUpdate(ep);
705 PetscBool check_h1 = PETSC_FALSE;
706 CHKERR PetscOptionsGetBool(nullptr, nullptr, "-test_rotation_h1_jacobian",
707 &check_h1, nullptr);
708 if (check_h1)
709 CHKERR checkH1RotationJacobian(ep);
710 CHKERR PetscPrintf(ep.mField.get_comm(),
711 "Rotation constant-mode layout verified on %d elements\n",
712 static_cast<int>(global_visits));
714}
715} // namespace
716
717int main(int argc, char *argv[]) {
718 MoFEM::Core::Initialize(&argc, &argv, nullptr,
719 "Verify L2 constant-higher rotation split.\n");
720 auto log = logging::core::get();
721 for (const auto *name : {"EP", "EPSELF", "EPSYNC"}) {
724 }
725 try {
727 CHKERR runAuxiliaryLogarithmicStressTest(runRotationSplitAtom);
728 }
731 return 0;
732}
Shared mesh and problem setup for auxiliary formulation atoms.
SmartPetscObj< KSP > ksp
Mixed directional derivatives of matrix exponentials for the reduced Hessian of the logarithmic plast...
boost::shared_ptr< DataAtIntegrationPts > data
boost::shared_ptr< MatrixDouble > direction
EshelbianCore * ep
Elastic TS pre/post-step registration and H1 transfer lifecycle.
SmartPetscObj< Vec > work
SmartPetscObj< Vec > residual
SmartPetscObj< Vec > gradient
SmartPetscObj< Mat > jacobian
SmartPetscObj< Vec > values
SmartPetscObj< Mat > preconditioner
#define FTENSOR_INDEXES(DIM,...)
#define FTENSOR_INDEX(DIM, I)
std::string name
int main()
constexpr double a
@ ROW
#define CATCH_ERRORS
Catch errors.
@ USER_BASE
user implemented approximation base
Definition definitions.h:68
#define MoFEMFunctionReturnHot(a)
Last executable line of each PETSc function used for error handling. Replaces return()
@ L2
field with C-1 continuity
Definition definitions.h:88
@ NOSPACE
Definition definitions.h:83
#define MoFEMFunctionBegin
First executable line of each MoFEM function, used for error handling. Final line of MoFEM functions ...
@ MOFEM_ATOM_TEST_INVALID
Definition definitions.h:40
#define MoFEMFunctionReturn(a)
Last executable line of each PETSc function used for error handling. Replaces return()
#define CHKERR
Inline error check.
constexpr int order
PetscErrorCode DMMoFEMGetProblemPtr(DM dm, const MoFEM::Problem **problem_ptr)
Get pointer to problem data structure.
Definition DMMoFEM.cpp:451
PetscErrorCode DMoFEMMeshToLocalVector(DM dm, Vec l, InsertMode mode, ScatterMode scatter_mode, RowColData rc=RowColData::COL)
set local (or ghosted) vector values on mesh for partition only
Definition DMMoFEM.cpp:543
MoFEMErrorCode DMRegister_MGViaApproxOrders(const char sname[])
Register DM for Multi-Grid via approximation orders.
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:605
PetscErrorCode DMMoFEMGetFieldIS(DM dm, RowColData rc, const char field_name[], IS *is)
get field is in the problem
Definition DMMoFEM.cpp:1536
auto createDMVector(DM dm, RowColData rc=RowColData::COL)
Get smart vector from DM.
Definition DMMoFEM.hpp:1250
virtual const Field * get_field_structure(const std::string &name, enum MoFEMTypes bh=MF_EXIST) const =0
get field structure
static LoggerType & setLog(const std::string channel)
Set ans resset chanel logger.
#define MOFEM_LOG(channel, severity)
Log.
FTensor::Index< 'i', SPACE_DIM > i
const double c
speed of light (cm/ns)
FTensor::Index< 'l', 3 > l
FTensor::Index< 'j', 3 > j
FTensor::Index< 'k', 3 > k
const FTensor::Tensor2< T, Dim, Dim > Vec
FTensor::Tensor2< FTensor::CursorPtr< double *, 9 >, 3, 3 > View
MoFEMErrorCode runAuxiliaryLogarithmicStressTest(const std::function< MoFEMErrorCode(EshelbianCore &)> &test)
boost::shared_ptr< MatrixDouble > MatrixPtr
PetscErrorCode MoFEMErrorCode
MoFEM/PETSc error code.
implementation of Data Operators for Forces and Sources
Definition Common.hpp:10
PetscErrorCode PetscOptionsGetBool(PetscOptions *, const char pre[], const char name[], PetscBool *bval, PetscBool *set)
SmartPetscObj< Vec > vectorDuplicate(Vec vec)
Create duplicate vector of smart vector.
auto createTS(MPI_Comm comm)
decltype(GetFTensor1FromMatImpl< Tensor_Dim, S, DL, M >::get(std::declval< M & >(), 0, 0)) GetFTensor1FromMatType
PetscObject getPetscObject(T obj)
SmartPetscObj< Mat > matDuplicate(Mat mat, MatDuplicateOption op)
int r
Definition sdf.py:205
constexpr double t
plate stiffness
Definition plate.cpp:58
static double penalty
double q
static boost::shared_ptr< SetUpSchur > createSetUpSchur(MoFEM::Interface &m_field, EshelbianCore *ep_core_ptr)
MoFEMErrorCode setElasticElementOps(const int tag)
MoFEM::Interface & mField
const std::string materialH1Positions
const std::string elementVolumeName
static enum RotSelector gradApproximator
const std::string spatialH1Disp
const std::string piolaStress
MoFEMErrorCode setElasticElementToTs(DM dm)
MoFEMErrorCode setVolumeElementOps(const int tag, const bool add_elastic, const bool add_material, boost::shared_ptr< VolumeElementForcesAndSourcesCore > &fe_rhs, boost::shared_ptr< VolumeElementForcesAndSourcesCore > &fe_lhs)
boost::shared_ptr< VolumeElementForcesAndSourcesCore > elasticFeLhs
const std::string rotAxis
double alphaOmegaPrec
Rotation-gradient shift of the Schur preconditioner.
boost::shared_ptr< DataAtIntegrationPts > dataAtPts
SmartPetscObj< Vec > solTSStep
SmartPetscObj< DM > dmElastic
Elastic problem.
const std::string stretchTensor
void fill(const Matrix< N > &a, const Matrix< N > &q, Matrix< N > &result) const
static MoFEMErrorCode setPreStep(EshelbianCore *ep_ptr, TS ts, bool h1_update=false)
Add operators pushing bases from local to physical configuration.
virtual moab::Interface & get_moab()=0
virtual MPI_Comm & get_comm() const =0
static MoFEMErrorCode Initialize(int *argc, char ***args, const char file[], const char help[])
Initializes the MoFEM database PETSc, MOAB and MPI.
Definition Core.cpp:68
static MoFEMErrorCode Finalize()
Checks for options to be called at the conclusion of the program.
Definition Core.cpp:123
virtual MoFEMErrorCode doWork(int row_side, int col_side, EntityType row_type, EntityType col_type, EntitiesFieldData::EntData &row_data, EntitiesFieldData::EntData &col_data)
Operator for bi-linear form, usually to calculate values on left hand side.
Data on single entity (This is passed as argument to DataOperator::doWork)
Basic algebra on fields.
Definition FieldBlas.hpp:21
MatrixDouble & getGaussPts()
matrix of integration (Gauss) points for Volume Element
static boost::shared_ptr< SinkType > createSink(boost::shared_ptr< std::ostream > stream_ptr, std::string comm_filter)
Create a sink object.
static boost::shared_ptr< std::ostream > getStrmWorld()
Get the strm world object.
Specialization for MatrixDouble vector field values calculation.
Calculate directional derivative of the right hand side and compare it with tangent matrix derivative...
std::function< MoFEMErrorCode(Vec, Vec)> VectorFunction
keeps basic data about problem
intrusive_ptr for managing petsc objects
MoFEMErrorCode getInterface(IFACE *&iface) const
Get interface reference to pointer of interface.
double scale
Definition plastic.cpp:124