10static_assert(std::is_abstract_v<TestingInterface>);
14template <
typename Check>
16expectFailure(Check check,
const std::vector<std::string> &details = {},
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 {
24 *
static_cast<std::string *
>(ctx) += message;
28 const auto error = check();
29 CHKERR PetscPopErrorHandler();
30 if (error != expected_error)
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)
37 "Missing diagnostic '%s' in '%s'", detail.c_str(),
48 [](TS, PetscReal, Vec, Vec rhs,
void *) -> PetscErrorCode {
49 return VecSet(rhs, 1.);
57 "Set final-time policy");
65 std::unique_ptr<TestingInterface>
testing(
68 CHKERR VecZeroEntries(solution);
69 auto ts = createTimeSolver(
comm, solution);
70 PetscInt monitor_calls = 0;
75 CHKERR PetscOptionsSetValue(
nullptr,
"-selection_test_name",
"monitor,missing");
76 CHKERR testing->addTSMonitor(
"selection_", 1,
"monitor", ts, monitor);
79 "Selected monitor is missing from the test registry");
81 auto messages = boost::make_shared<std::ostringstream>();
83 auto log = logging::core::get();
85 const auto selection_error =
testing->runTests();
86 log->remove_sink(sink);
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");
94 "runTests invoked a TS monitor callback");
96 CHKERR PetscOptionsClearValue(
nullptr,
"-selection_test_name");
100 const auto refresh_error =
testing->runTests();
101 log->remove_sink(sink);
103 if (!messages->str().empty())
105 "Refreshed empty selectors still produce a warning");
107 CHKERR TSSolve(ts, solution);
109 "selected monitor execution", monitor_calls, 3., {0., 0.});
115 CHKERR PetscOptionsSetValue(
nullptr,
"-selection_test_nb",
"1");
117 CHKERR testing->addTSMonitor(
"selection_", 1,
"monitor", ts, monitor);
120 "monitor registration retains post-solve hook", hook_calls, 1., {0., 0.});
121 CHKERR PetscOptionsClearValue(
nullptr,
"-selection_test_nb");
128 std::unique_ptr<TestingInterface>
testing(
130 std::vector<int> calls;
131 for (
int number : {3, 1, 2})
137 CHKERR PetscOptionsSetValue(
nullptr,
"-execution_test_nb",
"3,1,3,9,9");
138 auto messages = boost::make_shared<std::ostringstream>();
140 auto log = logging::core::get();
142 const auto run_error =
testing->runTestCommandLine(
"execution_");
143 log->remove_sink(sink);
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()) !=
151 "Selected tests must run once in number order and warn once per "
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");
162 if (calls != std::vector<int>({1, 3}))
164 "Name selection differs from number selection");
166 CHKERR PetscOptionsClearValue(
nullptr,
"-execution_test_name");
172 "Empty refreshed selectors executed a test");
174 if (calls != std::vector<int>({1, 2, 3}))
176 "Refreshing selectors changed existing test registrations");
188 CHKERR MatCreateAIJ(
comm, 1, 1, PETSC_DECIDE, PETSC_DECIDE, 1,
nullptr, 0,
189 nullptr, &matrix_raw);
191 CHKERR MatAssemblyBegin(matrix, MAT_FINAL_ASSEMBLY);
192 CHKERR MatAssemblyEnd(matrix, MAT_FINAL_ASSEMBLY);
193 CHKERR MatShift(matrix, 1.);
197 [&] {
return testing->checkSolver(
ksp,
"unsolved KSP"); });
198 CHKERR KSPSetOperators(
ksp, matrix, matrix);
202 CHKERR PCSetType(pc, PCNONE);
206 CHKERR VecZeroEntries(rhs);
210 CHKERR KSPSetTolerances(
ksp, PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT, 0);
213 [&] {
return testing->checkSolver(
ksp,
"KSP iteration limit"); });
217 [&] {
return testing->checkSolver(snes,
"unsolved SNES"); });
218 CHKERR SNESSetType(snes, SNESNEWTONLS);
221 [](SNES, Vec x, Vec f,
void *) -> PetscErrorCode {
229 snes, matrix, matrix,
230 [](SNES, Vec, Mat, Mat,
void *) -> PetscErrorCode {
231 return PETSC_SUCCESS;
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);
247 CHKERR KSPSetTolerances(inner_ksp, PETSC_DEFAULT, PETSC_DEFAULT,
249 CHKERR KSPSolve(inner_ksp, rhs, solution);
251 [&] {
return testing->checkSolver(snes,
"SNES with failed KSP"); });
252 CHKERR SNESSetTolerances(snes, PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT, 0,
254 CHKERR VecZeroEntries(solution);
255 CHKERR SNESSolve(snes,
nullptr, solution);
257 [&] {
return testing->checkSolver(snes,
"SNES iteration limit"); });
259 auto ts = createTimeSolver(
comm, solution);
260 CHKERR TSSetType(ts, TSBEULER);
263 [](TS, PetscReal, Vec, Mat
a, Mat,
void *) -> PetscErrorCode {
264 return MatZeroEntries(
a);
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);
275 "implicit trajectory with SNES and KSP");
276 CHKERR KSPSetTolerances(inner_ksp, PETSC_DEFAULT, PETSC_DEFAULT,
278 CHKERR KSPSolve(inner_ksp, rhs, solution);
279 CHKERR expectFailure([&] {
280 return testing->checkSolver(ts, 0.375, {1e-12, 0.},
"TS with failed KSP");
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");
288 CHKERR VecZeroEntries(solution);
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");
307 return testing->checkSolver(ts, 0.375, {1e-12, 0.},
"real TS failure");
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]});
314 [&] {
return testing->checkSolver(inner_snes,
"real SNES failure"); },
315 {SNESConvergedReasons[snes_reason],
"real SNES failure: KSP: termination",
316 KSPConvergedReasons[ksp_reason]});
318 CHKERR KSPSetTolerances(inner_ksp, PETSC_DEFAULT, PETSC_DEFAULT,
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");
333 [&] {
return testing->checkSolver(tao,
"unsolved TAO"); });
336 CHKERR VecSet(solution, 2.);
337 CHKERR TaoSetType(tao, TAOLMVM);
338 CHKERR TaoSetSolution(tao, solution);
339 CHKERR TaoSetTolerances(tao, 1e-10, 0., 0.);
342 [](Tao, Vec x, PetscReal *f, Vec
gradient,
void *) -> PetscErrorCode {
351 *
f = 0.25 * PetscRealPart(xg + xx);
355 CHKERR TaoSetMaximumIterations(tao, 1);
357 TaoConvergedReason reason;
358 CHKERR TaoGetConvergedReason(tao, &reason);
361 "Expected TAO iteration limit");
363 [&] {
return testing->checkSolver(tao,
"TAO iteration limit"); },
364 {TaoConvergedReasons[reason]});
365 CHKERR TaoSetMaximumIterations(tao, 20);
379 CHKERR VecZeroEntries(solution);
380 auto ts = createTimeSolver(duplicate, solution);
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");
390 [&] {
return testing->checkSolver(snes,
"congruent SNES"); },
393 [&] {
return testing->checkSolver(
ksp,
"congruent KSP"); },
396 [&] {
return testing->checkSolver(tao,
"congruent TAO"); },
399 CHKERR MPI_Comm_free(&duplicate);
403 auto ts =
createTS(PETSC_COMM_SELF);
410 ts, [](TS, PetscInt, PetscReal, Vec) {
417 return testing->checkSolver(ts, 1., {0., 0.},
"subcommunicator TS");
421 [&] {
return testing->checkSolver(snes,
"subcommunicator SNES"); },
424 [&] {
return testing->checkSolver(
ksp,
"subcommunicator KSP"); },
427 [&] {
return testing->checkSolver(tao,
"subcommunicator TAO"); },
436 [&] {
return testing->checkSolver(
ksp,
"reordered KSP"); },
439 CHKERR MPI_Comm_free(&reordered);
450 CHKERR testing->checkValue<TestType::SCALAR>(
"relative tolerance", 100.001,
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);
459 CHKERR expectFailure([&] {
460 return testing->checkValue<TestType::SCALAR>(
462 last_rank ? std::numeric_limits<PetscReal>::quiet_NaN() : 1., 1.,
465 CHKERR expectFailure([&] {
466 return testing->checkError<TestType::DERIVATIVE>(
"invalid tolerance", 0.,
472 return testing->checkError<TestType::SCALAR>(
473 "rank-dependent bounds", last_rank ? 2. : 100., 0.,
474 {last_rank ? 1. : 200., 0.});
476 {
"max_error=" + std::to_string(m_field.
get_comm_size() > 1 ? 100 : 2),
478 "failing_rank=" +
std::to_string(m_field.get_comm_size() - 1),
479 ", error=2, bound=1,",
"inputs=valid"});
482 return testing->checkError<TestType::SCALAR>(
483 "invalid input diagnostic",
484 std::numeric_limits<PetscReal>::infinity(), 0., {1., 0.});
486 {
"max_error=inf",
"bound=1",
"inputs=invalid/non-finite"});
489 CHKERR VecZeroEntries(solution);
490 auto ts = createTimeSolver(
comm, solution);
491 PetscInt existing_calls = 0;
494 [](TS, PetscInt, PetscReal, Vec,
void *ctx) -> PetscErrorCode {
495 ++*
static_cast<PetscInt *
>(ctx);
496 return PETSC_SUCCESS;
498 &existing_calls,
nullptr);
499 auto checked_steps = boost::make_shared<PetscInt>(0);
500 boost::weak_ptr<PetscInt> monitor_lifetime = checked_steps;
502 ts, [
testing, operators, checked_steps](TS, PetscInt step, PetscReal time,
507 CHKERR VecCopy(state, before);
508 CHKERR VecCopy(state, difference);
509 CHKERR VecShift(difference, -time);
511 CHKERR VecNorm(difference, NORM_INFINITY, &error);
513 "u(t)=t", error, std::abs(time), {1e-12, 1e-12});
517 CHKERR VecSet(direction, 1.);
520 PetscReal finite_difference, derivative;
521 CHKERR operators->checkScalarCentralFiniteDifference(
523 [](Vec x, PetscReal &value) {
526 CHKERR VecDot(x, x, &dot);
527 value = PetscRealPart(dot);
530 finite_difference, derivative);
532 "per-step energy derivative", finite_difference, derivative,
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");
542 CHKERR TSSolve(ts, solution);
543 CHKERR testing->checkSolver(ts,
"complete configured trajectory");
544 CHKERR testing->checkValue<TestType::SCALAR>(
"accepted steps", *checked_steps,
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())
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");
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");
575 CHKERR VecZeroEntries(solution);
576 ts = createTimeSolver(
comm, solution);
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.});
582 CHKERR expectFailure([&] {
return TSSolve(ts, solution); });
583 CHKERR checkSolverCompletion(m_field);
584 CHKERR checkTaoCompletion(m_field);
585 CHKERR checkCommunicators(m_field);
591int main(
int argc,
char *argv[]) {
593 "Numerical testing interface\n");
595 moab::Core moab_core;
597 std::unique_ptr<TestingInterface> owned_testing(
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);
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");
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);
SmartPetscObj< Vec > residual
SmartPetscObj< Vec > gradient
#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
#define MoFEMFunctionReturn(a)
Last executable line of each PETSc function used for error handling. Replaces return()
#define CHKERR
Inline error check.
const FTensor::Tensor2< T, Dim, Dim > Vec
PetscErrorCode MoFEMErrorCode
MoFEM/PETSc error code.
implementation of Data Operators for Forces and Sources
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.
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
static MoFEMErrorCode Initialize(int *argc, char ***args, const char file[], const char help[])
Initializes the MoFEM database PETSc, MOAB and MPI.
static MoFEMErrorCode Finalize()
Checks for options to be called at the conclusion of the program.
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.