v0.16.3
Loading...
Searching...
No Matches
testing_interface.cpp
Go to the documentation of this file.
1/** \file testing_interface.cpp
2 * \brief Collective comparisons and accepted-step numerical testing.
3 */
4#include <MoFEM.hpp>
5#include <memory>
6#include <sstream>
7#include <type_traits>
8using namespace MoFEM;
9
10static_assert(std::is_abstract_v<TestingInterface>);
11
12namespace {
13
14template <typename Check>
16expectFailure(Check check, const std::vector<std::string> &details = {},
17 MoFEMErrorCode expected_error = MOFEM_ATOM_TEST_INVALID) {
19 std::string diagnostic;
20 CHKERR PetscPushErrorHandler(
21 [](MPI_Comm, int, const char *, const char *, PetscErrorCode code,
22 PetscErrorType, const char *message, void *ctx) -> PetscErrorCode {
23 if (message)
24 *static_cast<std::string *>(ctx) += message;
25 return code;
26 },
27 &diagnostic);
28 const auto error = check();
29 CHKERR PetscPopErrorHandler();
30 if (error != expected_error)
31 SETERRQ(PETSC_COMM_SELF, MOFEM_ATOM_TEST_INVALID,
32 "Expected error %d, got %d", static_cast<int>(expected_error),
33 static_cast<int>(error));
34 for (const auto &detail : details)
35 if (diagnostic.find(detail) == std::string::npos)
36 SETERRQ(PETSC_COMM_SELF, MOFEM_ATOM_TEST_INVALID,
37 "Missing diagnostic '%s' in '%s'", detail.c_str(),
38 diagnostic.c_str());
40}
41
42SmartPetscObj<TS> createTimeSolver(MPI_Comm comm, Vec solution) {
43 auto ts = createTS(comm);
44 CHK_THROW_MESSAGE(TSSetType(ts, TSEULER), "Set Euler solver");
46 TSSetRHSFunction(
47 ts, nullptr,
48 [](TS, PetscReal, Vec, Vec rhs, void *) -> PetscErrorCode {
49 return VecSet(rhs, 1.);
50 },
51 nullptr),
52 "Set unit rate");
53 CHK_THROW_MESSAGE(TSSetSolution(ts, solution), "Set solution");
54 CHK_THROW_MESSAGE(TSSetTimeStep(ts, 0.125), "Set time step");
55 CHK_THROW_MESSAGE(TSSetMaxTime(ts, 0.375), "Set final time");
56 CHK_THROW_MESSAGE(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_MATCHSTEP),
57 "Set final-time policy");
58 return ts;
59}
60
61MoFEMErrorCode checkSelection(MoFEM::Core &core) {
63 MoFEM::Interface &m_field = core;
64 const auto comm = m_field.get_comm();
65 std::unique_ptr<TestingInterface> testing(
67 auto solution = createGhostVector(comm, 1, PETSC_DECIDE, 0, nullptr);
68 CHKERR VecZeroEntries(solution);
69 auto ts = createTimeSolver(comm, solution);
70 PetscInt monitor_calls = 0;
71 auto monitor = [&](TS, PetscInt, PetscReal, Vec) -> MoFEMErrorCode {
72 ++monitor_calls;
73 return 0;
74 };
75 CHKERR PetscOptionsSetValue(nullptr, "-selection_test_name", "monitor,missing");
76 CHKERR testing->addTSMonitor("selection_", 1, "monitor", ts, monitor);
77 if (!testing->checkTest(1) || !testing->checkTest("monitor"))
79 "Selected monitor is missing from the test registry");
80
81 auto messages = boost::make_shared<std::ostringstream>();
82 auto sink = LogManager::createSink(messages, "TEST_INTERFACE");
83 auto log = logging::core::get();
84 log->add_sink(sink);
85 const auto selection_error = testing->runTests();
86 log->remove_sink(sink);
87 CHKERR selection_error;
88 if (messages->str().find("-selection_test_name missing") == std::string::npos ||
89 messages->str().find("-selection_test_name monitor") != std::string::npos)
91 "Selection diagnostics must identify only the unmatched test");
92 if (monitor_calls)
94 "runTests invoked a TS monitor callback");
95
96 CHKERR PetscOptionsClearValue(nullptr, "-selection_test_name");
97 CHKERR testing->getOptions("selection_");
98 messages->str("");
99 log->add_sink(sink);
100 const auto refresh_error = testing->runTests();
101 log->remove_sink(sink);
102 CHKERR refresh_error;
103 if (!messages->str().empty())
105 "Refreshed empty selectors still produce a warning");
106
107 CHKERR TSSolve(ts, solution);
108 CHKERR testing->checkValue<TestType::SCALAR>(
109 "selected monitor execution", monitor_calls, 3., {0., 0.});
110 int hook_calls = 0;
111 CHKERR testing->addTest(1, "monitor", [&]() -> MoFEMErrorCode {
112 ++hook_calls;
113 return 0;
114 });
115 CHKERR PetscOptionsSetValue(nullptr, "-selection_test_nb", "1");
116 CHKERR testing->getOptions("selection_");
117 CHKERR testing->addTSMonitor("selection_", 1, "monitor", ts, monitor);
118 CHKERR testing->runTestCommandLine("selection_");
119 CHKERR testing->checkValue<TestType::SCALAR>(
120 "monitor registration retains post-solve hook", hook_calls, 1., {0., 0.});
121 CHKERR PetscOptionsClearValue(nullptr, "-selection_test_nb");
123}
124
125MoFEMErrorCode checkSelectedExecution(MoFEM::Core &core) {
127 MoFEM::Interface &m_field = core;
128 std::unique_ptr<TestingInterface> testing(
130 std::vector<int> calls;
131 for (int number : {3, 1, 2})
132 CHKERR testing->addTest(
133 number, "test_" + std::to_string(number), [&, number]() {
134 calls.push_back(number);
135 return MoFEMErrorCode(0);
136 });
137 CHKERR PetscOptionsSetValue(nullptr, "-execution_test_nb", "3,1,3,9,9");
138 auto messages = boost::make_shared<std::ostringstream>();
139 auto sink = LogManager::createSink(messages, "TEST_INTERFACE");
140 auto log = logging::core::get();
141 log->add_sink(sink);
142 const auto run_error = testing->runTestCommandLine("execution_");
143 log->remove_sink(sink);
144 CHKERR run_error;
145 const std::string missing = "-execution_test_nb 9";
146 const auto warning = messages->str().find(missing);
147 if (calls != std::vector<int>({1, 3}) || warning == std::string::npos ||
148 messages->str().find(missing, warning + missing.size()) !=
149 std::string::npos)
150 SETERRQ(m_field.get_comm(), MOFEM_ATOM_TEST_INVALID,
151 "Selected tests must run once in number order and warn once per "
152 "unknown selector");
153
154 CHKERR PetscOptionsSetValue(nullptr, "-execution_test_name", "test_3,test_1,test_3");
155 CHKERR expectFailure([&] { return testing->getOptions("execution_"); },
156 {"Both -execution_test_nb and -execution_test_name"},
158 CHKERR PetscOptionsClearValue(nullptr, "-execution_test_nb");
159 CHKERR testing->getOptions("execution_");
160 calls.clear();
161 CHKERR testing->runTestCommandLine("execution_");
162 if (calls != std::vector<int>({1, 3}))
163 SETERRQ(m_field.get_comm(), MOFEM_ATOM_TEST_INVALID,
164 "Name selection differs from number selection");
165
166 CHKERR PetscOptionsClearValue(nullptr, "-execution_test_name");
167 CHKERR testing->getOptions("execution_");
168 calls.clear();
169 CHKERR testing->runTestCommandLine("execution_");
170 if (!calls.empty())
171 SETERRQ(m_field.get_comm(), MOFEM_ATOM_TEST_INVALID,
172 "Empty refreshed selectors executed a test");
173 CHKERR testing->runTests();
174 if (calls != std::vector<int>({1, 2, 3}))
175 SETERRQ(m_field.get_comm(), MOFEM_ATOM_TEST_INVALID,
176 "Refreshing selectors changed existing test registrations");
178}
179
180MoFEMErrorCode checkSolverCompletion(MoFEM::Interface &m_field) {
182 const auto comm = m_field.get_comm();
183 auto testing = m_field.getInterface<TestingInterface>();
184 auto solution = createGhostVector(comm, 1, PETSC_DECIDE, 0, nullptr);
185 auto rhs = vectorDuplicate(solution);
186 auto residual = vectorDuplicate(solution);
187 Mat matrix_raw;
188 CHKERR MatCreateAIJ(comm, 1, 1, PETSC_DECIDE, PETSC_DECIDE, 1, nullptr, 0,
189 nullptr, &matrix_raw);
190 SmartPetscObj<Mat> matrix(matrix_raw);
191 CHKERR MatAssemblyBegin(matrix, MAT_FINAL_ASSEMBLY);
192 CHKERR MatAssemblyEnd(matrix, MAT_FINAL_ASSEMBLY);
193 CHKERR MatShift(matrix, 1.);
194
195 auto ksp = createKSP(comm);
196 CHKERR expectFailure(
197 [&] { return testing->checkSolver(ksp, "unsolved KSP"); });
198 CHKERR KSPSetOperators(ksp, matrix, matrix);
199 CHKERR KSPSetType(ksp, KSPCG);
200 PC pc;
201 CHKERR KSPGetPC(ksp, &pc);
202 CHKERR PCSetType(pc, PCNONE);
203 CHKERR VecSet(rhs, 1.);
204 CHKERR KSPSolve(ksp, rhs, solution);
205 CHKERR testing->checkSolver(ksp, "converged KSP");
206 CHKERR VecZeroEntries(rhs);
207 CHKERR KSPSolve(ksp, rhs, solution);
208 CHKERR testing->checkSolver(ksp, "KSP converged without iterations");
209 CHKERR VecSet(rhs, 1.);
210 CHKERR KSPSetTolerances(ksp, PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT, 0);
211 CHKERR KSPSolve(ksp, rhs, solution);
212 CHKERR expectFailure(
213 [&] { return testing->checkSolver(ksp, "KSP iteration limit"); });
214
215 auto snes = createSNES(comm);
216 CHKERR expectFailure(
217 [&] { return testing->checkSolver(snes, "unsolved SNES"); });
218 CHKERR SNESSetType(snes, SNESNEWTONLS);
219 CHKERR SNESSetFunction(
220 snes, residual,
221 [](SNES, Vec x, Vec f, void *) -> PetscErrorCode {
223 CHKERR VecCopy(x, f);
224 CHKERR VecShift(f, -1.);
226 },
227 nullptr);
228 CHKERR SNESSetJacobian(
229 snes, matrix, matrix,
230 [](SNES, Vec, Mat, Mat, void *) -> PetscErrorCode {
231 return PETSC_SUCCESS;
232 },
233 nullptr);
234 KSP inner_ksp;
235 CHKERR SNESGetKSP(snes, &inner_ksp);
236 CHKERR KSPSetType(inner_ksp, KSPCG);
237 CHKERR KSPGetPC(inner_ksp, &pc);
238 CHKERR PCSetType(pc, PCNONE);
239 CHKERR VecSet(solution, 1.);
240 CHKERR SNESSolve(snes, nullptr, solution);
241 CHKERR testing->checkSolver(snes, "SNES converged without KSP");
242 CHKERR VecZeroEntries(solution);
243 CHKERR SNESSolve(snes, nullptr, solution);
244 CHKERR testing->checkSolver(snes, "converged SNES and KSP");
245
246 // A positive outer reason must not conceal a failed inner solve.
247 CHKERR KSPSetTolerances(inner_ksp, PETSC_DEFAULT, PETSC_DEFAULT,
248 PETSC_DEFAULT, 0);
249 CHKERR KSPSolve(inner_ksp, rhs, solution);
250 CHKERR expectFailure(
251 [&] { return testing->checkSolver(snes, "SNES with failed KSP"); });
252 CHKERR SNESSetTolerances(snes, PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT, 0,
253 PETSC_DEFAULT);
254 CHKERR VecZeroEntries(solution);
255 CHKERR SNESSolve(snes, nullptr, solution);
256 CHKERR expectFailure(
257 [&] { return testing->checkSolver(snes, "SNES iteration limit"); });
258
259 auto ts = createTimeSolver(comm, solution);
260 CHKERR TSSetType(ts, TSBEULER);
261 CHKERR TSSetRHSJacobian(
262 ts, matrix, matrix,
263 [](TS, PetscReal, Vec, Mat a, Mat, void *) -> PetscErrorCode {
264 return MatZeroEntries(a);
265 },
266 nullptr);
267 SNES inner_snes;
268 CHKERR TSGetSNES(ts, &inner_snes);
269 CHKERR SNESGetKSP(inner_snes, &inner_ksp);
270 CHKERR KSPSetType(inner_ksp, KSPCG);
271 CHKERR KSPGetPC(inner_ksp, &pc);
272 CHKERR PCSetType(pc, PCNONE);
273 CHKERR TSSolve(ts, solution);
274 CHKERR testing->checkSolver(ts, 0.375, {1e-12, 0.},
275 "implicit trajectory with SNES and KSP");
276 CHKERR KSPSetTolerances(inner_ksp, PETSC_DEFAULT, PETSC_DEFAULT,
277 PETSC_DEFAULT, 0);
278 CHKERR KSPSolve(inner_ksp, rhs, solution);
279 CHKERR expectFailure([&] {
280 return testing->checkSolver(ts, 0.375, {1e-12, 0.}, "TS with failed KSP");
281 });
282 CHKERR SNESSetConvergedReason(inner_snes, SNES_DIVERGED_MAX_IT);
283 CHKERR expectFailure([&] {
284 return testing->checkSolver(ts, 0.375, {1e-12, 0.}, "TS with failed SNES");
285 });
286
287 // Fail through the actual TS -> SNES -> KSP solve chain.
288 CHKERR VecZeroEntries(solution);
289 CHKERR TSSetTime(ts, 0.);
290 CHKERR TSSetStepNumber(ts, 0);
291 CHKERR TSSetMaxSNESFailures(ts, 1);
292 CHKERR TSSetErrorIfStepFails(ts, PETSC_FALSE);
293 CHKERR SNESSetErrorIfNotConverged(inner_snes, PETSC_FALSE);
294 CHKERR KSPSetErrorIfNotConverged(inner_ksp, PETSC_FALSE);
295 CHKERR TSSolve(ts, solution);
296 TSConvergedReason ts_reason;
297 SNESConvergedReason snes_reason;
298 KSPConvergedReason ksp_reason;
299 CHKERR TSGetConvergedReason(ts, &ts_reason);
300 CHKERR SNESGetConvergedReason(inner_snes, &snes_reason);
301 CHKERR KSPGetConvergedReason(inner_ksp, &ksp_reason);
302 if (ts_reason >= 0 || snes_reason >= 0 || ksp_reason >= 0)
304 "Expected genuine TS/SNES/KSP divergence");
305 CHKERR expectFailure(
306 [&] {
307 return testing->checkSolver(ts, 0.375, {1e-12, 0.}, "real TS failure");
308 },
309 {TSConvergedReasons[ts_reason], "final time (actual=0, expected=0.375)",
310 "real TS failure: SNES: termination", SNESConvergedReasons[snes_reason],
311 "real TS failure: SNES: KSP: termination",
312 KSPConvergedReasons[ksp_reason]});
313 CHKERR expectFailure(
314 [&] { return testing->checkSolver(inner_snes, "real SNES failure"); },
315 {SNESConvergedReasons[snes_reason], "real SNES failure: KSP: termination",
316 KSPConvergedReasons[ksp_reason]});
317
318 CHKERR KSPSetTolerances(inner_ksp, PETSC_DEFAULT, PETSC_DEFAULT,
319 PETSC_DEFAULT, 100);
320 CHKERR TSSetTime(ts, 0.);
321 CHKERR TSSetStepNumber(ts, 0);
322 CHKERR VecZeroEntries(solution);
323 CHKERR TSSolve(ts, solution);
324 CHKERR testing->checkSolver(ts, 0.375, {1e-12, 0.}, "recovered trajectory");
326}
327
328MoFEMErrorCode checkTaoCompletion(MoFEM::Interface &m_field) {
330 auto testing = m_field.getInterface<TestingInterface>();
331 auto tao = createTao(m_field.get_comm());
332 CHKERR expectFailure(
333 [&] { return testing->checkSolver(tao, "unsolved TAO"); });
334 auto solution =
335 createGhostVector(m_field.get_comm(), 1, PETSC_DECIDE, 0, nullptr);
336 CHKERR VecSet(solution, 2.);
337 CHKERR TaoSetType(tao, TAOLMVM);
338 CHKERR TaoSetSolution(tao, solution);
339 CHKERR TaoSetTolerances(tao, 1e-10, 0., 0.);
341 tao, nullptr,
342 [](Tao, Vec x, PetscReal *f, Vec gradient, void *) -> PetscErrorCode {
344 // Strictly convex quartic: f = sum(x^4/4 + x^2/2).
345 CHKERR VecPointwiseMult(gradient, x, x);
346 CHKERR VecPointwiseMult(gradient, gradient, x);
347 CHKERR VecAXPY(gradient, 1., x);
348 PetscScalar xg, xx;
349 CHKERR VecDot(x, gradient, &xg);
350 CHKERR VecDot(x, x, &xx);
351 *f = 0.25 * PetscRealPart(xg + xx);
353 },
354 nullptr);
355 CHKERR TaoSetMaximumIterations(tao, 1);
356 CHKERR TaoSolve(tao);
357 TaoConvergedReason reason;
358 CHKERR TaoGetConvergedReason(tao, &reason);
359 if (reason >= 0)
360 SETERRQ(m_field.get_comm(), MOFEM_ATOM_TEST_INVALID,
361 "Expected TAO iteration limit");
362 CHKERR expectFailure(
363 [&] { return testing->checkSolver(tao, "TAO iteration limit"); },
364 {TaoConvergedReasons[reason]});
365 CHKERR TaoSetMaximumIterations(tao, 20);
366 CHKERR TaoSolve(tao);
367 CHKERR testing->checkSolver(tao, "converged TAO");
369}
370
371MoFEMErrorCode checkCommunicators(MoFEM::Interface &m_field) {
373 auto testing = m_field.getInterface<TestingInterface>();
374 auto comm = m_field.get_comm();
375 MPI_Comm duplicate;
376 CHKERR MPI_Comm_dup(comm, &duplicate);
377 {
378 auto solution = createGhostVector(duplicate, 1, PETSC_DECIDE, 0, nullptr);
379 CHKERR VecZeroEntries(solution);
380 auto ts = createTimeSolver(duplicate, solution);
381 CHKERR testing->addTSMonitor(
382 ts, [](TS, PetscInt, PetscReal, Vec) { return MoFEMErrorCode(0); });
383 CHKERR TSSolve(ts, solution);
384 CHKERR testing->checkSolver(ts, 0.375, {1e-12, 0.}, "congruent TS");
385 auto snes = createSNES(duplicate);
386 auto ksp = createKSP(duplicate);
387 auto tao = createTao(duplicate);
388 // Unsolved reasons fail, but the duplicated communicator is accepted.
389 CHKERR expectFailure(
390 [&] { return testing->checkSolver(snes, "congruent SNES"); },
391 {"termination"});
392 CHKERR expectFailure(
393 [&] { return testing->checkSolver(ksp, "congruent KSP"); },
394 {"termination"});
395 CHKERR expectFailure(
396 [&] { return testing->checkSolver(tao, "congruent TAO"); },
397 {"termination"});
398 }
399 CHKERR MPI_Comm_free(&duplicate);
400 if (m_field.get_comm_size() > 1) {
401 // Only one Core rank calls: rejection must precede any Core collective.
402 if (m_field.get_comm_rank() == 0) {
403 auto ts = createTS(PETSC_COMM_SELF);
404 auto snes = createSNES(PETSC_COMM_SELF);
405 auto ksp = createKSP(PETSC_COMM_SELF);
406 auto tao = createTao(PETSC_COMM_SELF);
407 CHKERR expectFailure(
408 [&] {
409 return testing->addTSMonitor(
410 ts, [](TS, PetscInt, PetscReal, Vec) {
411 return MoFEMErrorCode(0);
412 });
413 },
414 {"communicator"}, MOFEM_INVALID_DATA);
415 CHKERR expectFailure(
416 [&] {
417 return testing->checkSolver(ts, 1., {0., 0.}, "subcommunicator TS");
418 },
419 {"communicator"}, MOFEM_INVALID_DATA);
420 CHKERR expectFailure(
421 [&] { return testing->checkSolver(snes, "subcommunicator SNES"); },
422 {"communicator"}, MOFEM_INVALID_DATA);
423 CHKERR expectFailure(
424 [&] { return testing->checkSolver(ksp, "subcommunicator KSP"); },
425 {"communicator"}, MOFEM_INVALID_DATA);
426 CHKERR expectFailure(
427 [&] { return testing->checkSolver(tao, "subcommunicator TAO"); },
428 {"communicator"}, MOFEM_INVALID_DATA);
429 }
430 CHKERR MPI_Barrier(comm);
431 MPI_Comm reordered;
432 CHKERR MPI_Comm_split(comm, 0, -m_field.get_comm_rank(), &reordered);
433 {
434 auto ksp = createKSP(reordered);
435 CHKERR expectFailure(
436 [&] { return testing->checkSolver(ksp, "reordered KSP"); },
437 {"communicator"}, MOFEM_INVALID_DATA);
438 }
439 CHKERR MPI_Comm_free(&reordered);
440 }
442}
443
444MoFEMErrorCode runTests(MoFEM::Interface &m_field) {
446 auto testing = m_field.getInterface<TestingInterface>();
447 auto operators = m_field.getInterface<OperatorsTester>();
448 const auto comm = m_field.get_comm();
449 const bool last_rank = m_field.get_comm_rank() == m_field.get_comm_size() - 1;
450 CHKERR testing->checkValue<TestType::SCALAR>("relative tolerance", 100.001,
451 100., {0., 1e-4});
452 CHKERR testing->checkValue<TestType::SCALAR>(
453 "custom success severity", 0., 0., {0., 0.}, Sev::error, Sev::warning);
454 CHKERR expectFailure([&] {
455 return testing->checkValue<TestType::POINTWISE>(
456 "rank-local mismatch", last_rank ? 2. : 1., 1., {1e-8, 0.},
457 Sev::warning, Sev::inform);
458 });
459 CHKERR expectFailure([&] {
460 return testing->checkValue<TestType::SCALAR>(
461 "non-finite value",
462 last_rank ? std::numeric_limits<PetscReal>::quiet_NaN() : 1., 1.,
463 {1e-8, 0.});
464 });
465 CHKERR expectFailure([&] {
466 return testing->checkError<TestType::DERIVATIVE>("invalid tolerance", 0.,
467 1., {-1., 1.});
468 });
469
470 CHKERR expectFailure(
471 [&] {
472 return testing->checkError<TestType::SCALAR>(
473 "rank-dependent bounds", last_rank ? 2. : 100., 0.,
474 {last_rank ? 1. : 200., 0.});
475 },
476 {"max_error=" + std::to_string(m_field.get_comm_size() > 1 ? 100 : 2),
477 "failing_ranks=1",
478 "failing_rank=" + std::to_string(m_field.get_comm_size() - 1),
479 ", error=2, bound=1,", "inputs=valid"});
480 CHKERR expectFailure(
481 [&] {
482 return testing->checkError<TestType::SCALAR>(
483 "invalid input diagnostic",
484 std::numeric_limits<PetscReal>::infinity(), 0., {1., 0.});
485 },
486 {"max_error=inf", "bound=1", "inputs=invalid/non-finite"});
487
488 auto solution = createGhostVector(comm, 1, PETSC_DECIDE, 0, nullptr);
489 CHKERR VecZeroEntries(solution);
490 auto ts = createTimeSolver(comm, solution);
491 PetscInt existing_calls = 0;
492 CHKERR TSMonitorSet(
493 ts,
494 [](TS, PetscInt, PetscReal, Vec, void *ctx) -> PetscErrorCode {
495 ++*static_cast<PetscInt *>(ctx);
496 return PETSC_SUCCESS;
497 },
498 &existing_calls, nullptr);
499 auto checked_steps = boost::make_shared<PetscInt>(0);
500 boost::weak_ptr<PetscInt> monitor_lifetime = checked_steps;
501 CHKERR testing->addTSMonitor(
502 ts, [testing, operators, checked_steps](TS, PetscInt step, PetscReal time,
503 Vec state) {
505 auto difference = vectorDuplicate(state);
506 auto before = vectorDuplicate(state);
507 CHKERR VecCopy(state, before);
508 CHKERR VecCopy(state, difference);
509 CHKERR VecShift(difference, -time);
510 PetscReal error;
511 CHKERR VecNorm(difference, NORM_INFINITY, &error);
512 CHKERR testing->checkError<TestType::POINTWISE>(
513 "u(t)=t", error, std::abs(time), {1e-12, 1e-12});
514
515 auto direction = vectorDuplicate(state);
516 auto gradient = vectorDuplicate(state);
517 CHKERR VecSet(direction, 1.);
518 CHKERR VecCopy(state, gradient);
519 CHKERR VecScale(gradient, 2.);
520 PetscReal finite_difference, derivative;
521 CHKERR operators->checkScalarCentralFiniteDifference(
522 state, direction, gradient, 1e-5,
523 [](Vec x, PetscReal &value) {
525 PetscScalar dot;
526 CHKERR VecDot(x, x, &dot);
527 value = PetscRealPart(dot);
529 },
530 finite_difference, derivative);
531 CHKERR testing->checkValue<TestType::DERIVATIVE>(
532 "per-step energy derivative", finite_difference, derivative,
533 {1e-9, 1e-9});
534 PetscBool unchanged;
535 CHKERR VecEqual(state, before, &unchanged);
536 if (!unchanged || step != ++*checked_steps)
537 SETERRQ(PetscObjectComm(reinterpret_cast<PetscObject>(state)),
539 "Monitor changed the solution or skipped/repeated a step");
541 });
542 CHKERR TSSolve(ts, solution);
543 CHKERR testing->checkSolver(ts, "complete configured trajectory");
544 CHKERR testing->checkValue<TestType::SCALAR>("accepted steps", *checked_steps,
545 3., {0., 0.});
546 CHKERR testing->checkValue<TestType::SCALAR>("existing monitor retained",
547 existing_calls, 4., {0., 0.});
548 checked_steps.reset();
549 if (monitor_lifetime.expired())
551 "TS did not retain its test callback");
552 CHKERR TSMonitorCancel(ts);
553 if (!monitor_lifetime.expired())
554 SETERRQ(comm, MOFEM_ATOM_TEST_INVALID, "TS leaked its test callback");
555
556 CHKERR VecZeroEntries(solution);
557 ts = createTimeSolver(comm, solution);
558 CHKERR TSSetMaxSteps(ts, 1);
559 CHKERR TSSolve(ts, solution);
560 CHKERR expectFailure([&] {
561 return testing->checkSolver(ts, "incomplete configured trajectory");
562 });
563
564 for (auto policy : {TS_EXACTFINALTIME_STEPOVER,
565 TS_EXACTFINALTIME_INTERPOLATE,
566 TS_EXACTFINALTIME_MATCHSTEP}) {
567 CHKERR VecZeroEntries(solution);
568 ts = createTimeSolver(comm, solution);
569 CHKERR TSSetMaxTime(ts, 0.3);
570 CHKERR TSSetExactFinalTime(ts, policy);
571 CHKERR TSSolve(ts, solution);
572 CHKERR testing->checkSolver(ts, "configured final-time policy");
573 }
574
575 CHKERR VecZeroEntries(solution);
576 ts = createTimeSolver(comm, solution);
577 CHKERR testing->addTSMonitor(
578 ts, [testing, last_rank](TS, PetscInt, PetscReal, Vec) {
579 return testing->checkValue<TestType::SCALAR>(
580 "monitor failure propagation", last_rank ? 1. : 0., 0., {0., 0.});
581 });
582 CHKERR expectFailure([&] { return TSSolve(ts, solution); });
583 CHKERR checkSolverCompletion(m_field);
584 CHKERR checkTaoCompletion(m_field);
585 CHKERR checkCommunicators(m_field);
587}
588
589} // namespace
590
591int main(int argc, char *argv[]) {
592 MoFEM::Core::Initialize(&argc, &argv, nullptr,
593 "Numerical testing interface\n");
594 try {
595 moab::Core moab_core;
596 MoFEM::Core mofem_core(moab_core);
597 std::unique_ptr<TestingInterface> owned_testing(
599 if (owned_testing->getInterface<TestingInterface>() != owned_testing.get())
600 SETERRQ(PETSC_COMM_WORLD, MOFEM_ATOM_TEST_INVALID,
601 "Factory-created testing interface cannot be queried");
602 CHKERR owned_testing->checkValue<TestType::SCALAR>(
603 "factory-created interface", 1., 1., {0., 0.});
604 auto calls = std::make_shared<int>(0);
605 auto hook = [calls]() -> MoFEMErrorCode { ++*calls; return 0; };
606 CHKERR PetscOptionsSetValue(nullptr, "-registry_test_nb", "2");
607 CHKERR owned_testing->getOptions("registry_");
608 CHKERR owned_testing->addTestCommandLine("registry_", 1, "first", hook);
609 CHKERR owned_testing->addTestCommandLine("registry_", 2, "second", hook);
610 CHKERR owned_testing->runTests();
611 CHKERR owned_testing->checkValue<TestType::SCALAR>(
612 "number selection", *calls, 1., {0., 0.});
613 CHKERR PetscOptionsClearValue(nullptr, "-registry_test_nb");
614 CHKERR PetscOptionsSetValue(nullptr, "-registry_test_name", "second");
615 // Cached selection remains valid until explicitly refreshed.
616 CHKERR owned_testing->runTestCommandLine("registry_");
617 CHKERR owned_testing->getOptions("registry_");
618 CHKERR owned_testing->runTestCommandLine("registry_");
619 CHKERR owned_testing->checkValue<TestType::SCALAR>(
620 "cached and named selection", *calls, 3., {0., 0.});
621 CHKERR PetscOptionsClearValue(nullptr, "-registry_test_name");
622 CHKERR checkSelection(mofem_core);
623 CHKERR checkSelectedExecution(mofem_core);
624 CHKERR runTests(mofem_core);
625 }
628 return 0;
629}
MPI_Comm comm
SmartPetscObj< KSP > ksp
SmartPetscObj< Vec > residual
SmartPetscObj< Vec > gradient
TestHook hook
int number
int main()
constexpr double a
#define CATCH_ERRORS
Catch errors.
#define CHK_THROW_MESSAGE(err, msg)
Check and throw MoFEM exception.
#define MoFEMFunctionBegin
First executable line of each MoFEM function, used for error handling. Final line of MoFEM functions ...
@ MOFEM_ATOM_TEST_INVALID
Definition definitions.h:40
@ MOFEM_INVALID_DATA
Definition definitions.h:36
#define MoFEMFunctionReturn(a)
Last executable line of each PETSc function used for error handling. Replaces return()
#define CHKERR
Inline error check.
PetscBool testing
const FTensor::Tensor2< T, Dim, Dim > Vec
PetscErrorCode MoFEMErrorCode
MoFEM/PETSc error code.
implementation of Data Operators for Forces and Sources
Definition Common.hpp:10
auto createKSP(MPI_Comm comm)
auto createSNES(MPI_Comm comm)
TestingInterface * createInterface< TestingInterface >(const Core &core)
SmartPetscObj< Vec > vectorDuplicate(Vec vec)
Create duplicate vector of smart vector.
auto createTS(MPI_Comm comm)
auto createGhostVector(MPI_Comm comm, PetscInt n, PetscInt N, PetscInt nghost, const PetscInt ghosts[])
Create smart ghost vector.
PetscErrorCode TaoSetObjectiveAndGradient(Tao tao, Vec x, PetscReal *f, Vec g, void *ctx)
Sets the objective function value and gradient for a TAO optimization solver.
Definition TaoCtx.cpp:386
auto createTao(MPI_Comm comm)
virtual int get_comm_size() const =0
virtual MPI_Comm & get_comm() const =0
virtual int get_comm_rank() const =0
Core (interface) class.
Definition Core.hpp:83
static MoFEMErrorCode Initialize(int *argc, char ***args, const char file[], const char help[])
Initializes the MoFEM database PETSc, MOAB and MPI.
Definition Core.cpp:68
static MoFEMErrorCode Finalize()
Checks for options to be called at the conclusion of the program.
Definition Core.cpp:123
Deprecated interface functions.
static boost::shared_ptr< SinkType > createSink(boost::shared_ptr< std::ostream > stream_ptr, std::string comm_filter)
Create a sink object.
Calculate directional derivative of the right hand side and compare it with tangent matrix derivative...
intrusive_ptr for managing petsc objects
MoFEMErrorCode checkValue(const std::string &check_name, PetscReal actual, PetscReal expected, Tolerance tolerance, Sev error_severity=Sev::error, Sev success_severity=Sev::inform) const
MoFEMErrorCode getInterface(IFACE *&iface) const
Get interface reference to pointer of interface.