14#include <boost/multi_index/hashed_index.hpp>
18namespace mi = boost::multi_index;
22template <TestType TYPE>
struct TestTraits;
25 static constexpr const char *
name =
"scalar";
28 static constexpr const char *
name =
"pointwise";
31 static constexpr const char *
name =
"derivative";
34 static constexpr const char *
name =
"solver";
37struct TestingInterfaceImpl final : TestingInterface {
38 explicit TestingInterfaceImpl(
const Core &core);
39 MoFEMErrorCode query_interface(boost::typeindex::type_index type_index,
40 UnknownInterface **iface)
const override;
41 MoFEMErrorCode getOptions(
const std::string &prefix)
const override;
44 const std::string &
name, TS ts,
45 TSCheck check)
override;
46 using TestingInterface::addFieldEvaluator;
48 addFieldEvaluator(
int dimension,
const std::string &problem,
49 const std::string &finite_element,
50 const std::array<double, 3> &field_eval_coords,
51 FieldEvaluatorHook
hook,
double distance,
52 bool *point_found)
override;
53 using TestingInterface::checkError;
55 PetscReal error, PetscReal
scale,
56 Tolerance tolerance, Sev error_severity,
57 Sev success_severity)
const override;
59 const std::string &check_name)
const override;
62 const std::string &check_name)
const override;
64 const std::string &check_name)
const override;
66 const std::string &check_name)
const override;
68 const std::string &check_name)
const override;
70 TestHook
hook)
override;
74 bool checkTest(
int number)
const override;
75 bool checkTest(
const std::string &
name)
const override;
76 MoFEMErrorCode runTestCommandLine(
const std::string &prefix)
const override;
78 const std::string &
name,
79 TestHook
hook)
override;
92 typedef boost::multi_index_container<
95 mi::ordered_unique<mi::member<TestEntry, int, &TestEntry::number>>,
97 mi::member<TestEntry, std::string, &TestEntry::name>>>>
100 MoFEMErrorCode registerTest(
int number,
const std::string &
name,
107 using Selectors = boost::multi_index_container<
110 mi::hashed_non_unique<mi::member<Selector, int, &Selector::number>>,
111 mi::hashed_non_unique<
112 mi::member<Selector, std::string, &Selector::name>>>>;
114 bool isSelected(
const std::string &prefix,
int number,
115 const std::string &
name)
const {
116 const auto &selection =
selectors.at(prefix);
117 auto n = selection.get<0>().equal_range(
number);
118 for (
auto it =
n.first; it !=
n.second; ++it)
121 auto names = selection.get<1>().equal_range(
name);
122 for (
auto it = names.first; it != names.second; ++it)
130 const std::string &check_name, PetscReal error,
131 PetscReal
scale, Tolerance tolerance,
133 Sev error_severity = Sev::error,
134 Sev success_severity = Sev::inform)
const;
135 MoFEMErrorCode reportReason(
const std::string &check_name,
int reason,
136 const char *reason_name,
137 CheckReport &report)
const;
138 MoFEMErrorCode reportFinished(SNES snes,
const std::string &check_name,
139 CheckReport &report)
const;
141 CheckReport &report)
const;
143 MPI_Comm getComm()
const;
145 MoFEMErrorCode addFieldEvaluatorImpl(
146 const std::string &problem,
const std::string &finite_element,
147 const std::array<double, 3> &field_eval_coords, FieldEvaluatorHook
hook,
148 double distance,
bool *point_found);
149 MoFEMErrorCode checkErrorImpl(
const char *category,
150 const std::string &check_name, PetscReal error,
151 PetscReal
scale, Tolerance tolerance,
152 Sev error_severity, Sev success_severity)
const;
153 MoFEMErrorCode ensureOptions(
const std::string &prefix)
const;
154 MoFEMErrorCode checkSelectors(
const std::string &prefix)
const;
157TestingInterfaceImpl::TestingInterfaceImpl(
const Core &core)
159 if (!LogManager::checkIfChannelExist(
"TEST_INTERFACE")) {
160 auto core_log = logging::core::get();
162 LogManager::createSink(LogManager::getStrmWorld(),
"TEST_INTERFACE"));
163 LogManager::setLog(
"TEST_INTERFACE");
169TestingInterfaceImpl::query_interface(boost::typeindex::type_index type_index,
170 UnknownInterface **iface)
const {
171 *iface =
const_cast<TestingInterfaceImpl *
>(
this);
175MoFEMErrorCode TestingInterfaceImpl::addTSMonitor(TS ts, TSCheck check) {
177 CHKERR checkComm(
reinterpret_cast<PetscObject
>(ts));
180 "TS testing monitor requires a callback");
181 auto check_ptr = std::make_unique<TSCheck>(std::move(check));
182 auto monitor = [](TS ts, PetscInt step, PetscReal time,
Vec solution,
183 void *ctx) -> PetscErrorCode {
186 CHKERR (*
static_cast<TSCheck *
>(ctx))(ts, step, time, solution);
189 auto destroy = [](
void **ctx) -> PetscErrorCode {
190 delete static_cast<TSCheck *
>(*ctx);
192 return PETSC_SUCCESS;
194 CHKERR TSMonitorSet(ts, monitor, check_ptr.get(), destroy);
199MoFEMErrorCode TestingInterfaceImpl::addTSMonitor(
const std::string &prefix,
201 const std::string &
name,
202 TS ts, TSCheck check) {
204 CHKERR ensureOptions(prefix);
206 CHKERR addTSMonitor(ts, std::move(check));
214 const std::string &problem,
const std::string &finite_element,
215 const std::array<double, 3> &field_eval_coords, FieldEvaluatorHook
hook,
216 double distance,
bool *point_found) {
220 VolumeElementForcesAndSourcesCore>;
223 "Field evaluator requires a pipeline hook");
224 auto evaluator = m_field.
getInterface<FieldEvaluatorInterface>();
225 auto data = evaluator->getData<
Ele>(field_eval_coords.data(), 1);
226 auto fe = data->feMethodPtr;
227 fe->getRuleHook = [](
int,
int,
int) {
return -1; };
229 CHKERR evaluator->buildTree<DIM>(data, finite_element);
230 CHKERR evaluator->evalFEAtThePoint<DIM>(
231 field_eval_coords.data(), distance, problem, finite_element, data,
234 const bool found = fe->gaussPts.size2() == 1;
236 *point_found = found;
239 "Evaluation point (%g, %g, %g) not found in local problem %s, "
241 field_eval_coords[0], field_eval_coords[1], field_eval_coords[2],
242 problem.c_str(), finite_element.c_str());
247 int dimension,
const std::string &problem,
248 const std::string &finite_element,
249 const std::array<double, 3> &field_eval_coords, FieldEvaluatorHook
hook,
250 double distance,
bool *point_found) {
254 CHKERR addFieldEvaluatorImpl<2>(problem, finite_element, field_eval_coords,
255 std::move(
hook), distance, point_found);
258 CHKERR addFieldEvaluatorImpl<3>(problem, finite_element, field_eval_coords,
259 std::move(
hook), distance, point_found);
263 "Field evaluation requires dimension 2 or 3");
269TestingInterfaceImpl::checkError(TestType
type,
const std::string &check_name,
270 PetscReal error, PetscReal
scale,
271 Tolerance tolerance, Sev error_severity,
272 Sev success_severity)
const {
274 const char *category;
276 case TestType::SCALAR:
277 category = TestTraits<TestType::SCALAR>::name;
279 case TestType::POINTWISE:
280 category = TestTraits<TestType::POINTWISE>::name;
282 case TestType::DERIVATIVE:
283 category = TestTraits<TestType::DERIVATIVE>::name;
285 case TestType::SOLVER:
286 category = TestTraits<TestType::SOLVER>::name;
291 CHKERR checkErrorImpl(category, check_name, error,
scale, tolerance,
292 error_severity, success_severity);
296MPI_Comm TestingInterfaceImpl::getComm()
const {
301MoFEMErrorCode TestingInterfaceImpl::checkComm(PetscObject solver)
const {
304 CHKERR MPI_Comm_compare(PetscObjectComm(solver), getComm(), &relation);
305 if (relation != MPI_IDENT && relation != MPI_CONGRUENT)
307 "Solver communicator must be identical or congruent to the Core "
313TestingInterfaceImpl::checkReport(
const CheckReport &report)
const {
321 const char *category,
const std::string &check_name, PetscReal error,
322 PetscReal
scale, Tolerance tolerance, CheckReport &report,
323 Sev error_severity, Sev success_severity)
const {
325 const PetscReal bound = tolerance.absolute + tolerance.relative *
scale;
327 std::isfinite(error) && error >= 0 && std::isfinite(
scale) &&
328 scale >= 0 && std::isfinite(tolerance.absolute) &&
329 tolerance.absolute >= 0 && std::isfinite(tolerance.relative) &&
330 tolerance.relative >= 0 && std::isfinite(bound);
331 const int local_failed = !valid || error > bound;
332 int failed_ranks = 0;
333 CHKERR MPI_Allreduce(&local_failed, &failed_ranks, 1, MPI_INT, MPI_SUM,
335 const PetscReal local_error =
336 valid ? error : std::numeric_limits<PetscReal>::infinity();
337 PetscReal max_error = 0;
338 CHKERR MPI_Allreduce(&local_error, &max_error, 1, MPIU_REAL, MPI_MAX,
340 std::ostringstream message;
341 message << std::setprecision(std::numeric_limits<PetscReal>::max_digits10)
342 <<
"[test:" << category <<
"] " << check_name <<
": "
343 << (failed_ranks ?
"FAIL" :
"PASS") <<
", max_error=" << max_error
344 <<
", failing_ranks=" << failed_ranks;
348 CHKERR MPI_Comm_rank(getComm(), &rank);
349 const int candidate = local_failed ? rank : std::numeric_limits<int>::max();
351 CHKERR MPI_Allreduce(&candidate, &failing_rank, 1, MPI_INT, MPI_MIN,
354 error, bound,
scale, tolerance.absolute, tolerance.relative,
356 CHKERR MPI_Bcast(
values, 6, MPIU_REAL, failing_rank, getComm());
357 message <<
", failing_rank=" << failing_rank <<
", error=" <<
values[0]
359 <<
", absolute=" <<
values[3] <<
", relative=" <<
values[4]
360 <<
", inputs=" << (
values[5] ?
"valid" :
"invalid/non-finite");
362 message <<
", local_bound=" << bound;
364 MOFEM_LOG(
"TEST_INTERFACE", failed_ranks ? error_severity : success_severity)
366 report.failed = report.failed || failed_ranks;
367 report.text += message.str() +
"\n";
372 const char *category,
const std::string &check_name, PetscReal error,
373 PetscReal
scale, Tolerance tolerance, Sev error_severity,
374 Sev success_severity)
const {
377 CHKERR reportError(category, check_name, error,
scale, tolerance, report,
378 error_severity, success_severity);
379 CHKERR checkReport(report);
383MoFEMErrorCode TestingInterfaceImpl::reportReason(
const std::string &check_name,
385 const char *reason_name,
386 CheckReport &report)
const {
388 CHKERR reportError(TestTraits<TestType::SOLVER>::name,
389 check_name +
": termination (" + reason_name +
390 ", reason " + std::to_string(reason) +
")",
391 reason > 0 ? 0. : 1., 0., {0., 0.}, report);
396TestingInterfaceImpl::checkSolver(TS ts,
const std::string &check_name)
const {
398 PetscReal final_time, solve_time;
399 TSExactFinalTimeOption policy;
400 CHKERR TSGetMaxTime(ts, &final_time);
401 CHKERR TSGetSolveTime(ts, &solve_time);
402 CHKERR TSGetExactFinalTime(ts, &policy);
404 const PetscReal expected =
405 policy == TS_EXACTFINALTIME_STEPOVER && solve_time >= final_time
408 CHKERR checkSolver(ts, expected, {1e-12, 1e-12}, check_name);
413TestingInterfaceImpl::checkSolver(TS ts, PetscReal expected_time,
415 const std::string &check_name)
const {
417 CHKERR checkComm(
reinterpret_cast<PetscObject
>(ts));
419 TSConvergedReason reason;
421 CHKERR TSGetConvergedReason(ts, &reason);
422 CHKERR TSGetSolveTime(ts, &time);
423 CHKERR reportReason(check_name, reason, TSConvergedReasons[reason], report);
424 std::ostringstream time_label;
425 time_label << std::setprecision(std::numeric_limits<PetscReal>::max_digits10)
426 << check_name <<
": final time (actual=" << time
427 <<
", expected=" << expected_time <<
")";
428 CHKERR reportError(TestTraits<TestType::SOLVER>::name, time_label.str(),
429 std::abs(time - expected_time), std::abs(expected_time),
432 CHKERR TSGetSNES(ts, &snes);
434 SNESConvergedReason snes_reason;
435 CHKERR SNESGetConvergedReason(snes, &snes_reason);
436 if (snes_reason != SNES_CONVERGED_ITERATING)
437 CHKERR reportFinished(snes, check_name +
": SNES", report);
439 CHKERR checkReport(report);
444TestingInterfaceImpl::reportFinished(SNES snes,
const std::string &check_name,
445 CheckReport &report)
const {
447 CHKERR checkComm(
reinterpret_cast<PetscObject
>(snes));
448 SNESConvergedReason reason;
449 CHKERR SNESGetConvergedReason(snes, &reason);
450 CHKERR reportReason(check_name, reason, SNESConvergedReasons[reason], report);
454 KSPConvergedReason ksp_reason;
455 CHKERR KSPGetConvergedReason(
ksp, &ksp_reason);
456 if (ksp_reason != KSP_CONVERGED_ITERATING)
457 CHKERR reportFinished(
ksp, check_name +
": KSP", report);
463TestingInterfaceImpl::reportFinished(KSP
ksp,
const std::string &check_name,
464 CheckReport &report)
const {
466 CHKERR checkComm(
reinterpret_cast<PetscObject
>(
ksp));
467 KSPConvergedReason reason;
468 CHKERR KSPGetConvergedReason(
ksp, &reason);
469 CHKERR reportReason(check_name, reason, KSPConvergedReasons[reason], report);
474TestingInterfaceImpl::checkSolver(SNES snes,
475 const std::string &check_name)
const {
478 CHKERR reportFinished(snes, check_name, report);
479 CHKERR checkReport(report);
484TestingInterfaceImpl::checkSolver(KSP
ksp,
485 const std::string &check_name)
const {
488 CHKERR reportFinished(
ksp, check_name, report);
489 CHKERR checkReport(report);
494TestingInterfaceImpl::checkSolver(Tao tao,
495 const std::string &check_name)
const {
497 CHKERR checkComm(
reinterpret_cast<PetscObject
>(tao));
499 TaoConvergedReason reason;
500 CHKERR TaoGetConvergedReason(tao, &reason);
501 CHKERR reportReason(check_name, reason, TaoConvergedReasons[reason], report);
502 CHKERR checkReport(report);
507 const std::string &
name,
518 const std::string &
name,
522 auto existing = by_number.find(
number);
524 if (existing != by_number.end() && existing->name ==
name &&
525 (!
hook || !existing->hook)) {
527 by_number.modify(existing,
528 [&](TestEntry &entry) { entry.hook = std::move(
hook); });
531 auto replace_test = [&](
auto &index,
const auto &
key) {
532 auto it = index.find(
key);
533 if (it != index.end()) {
534 MOFEM_LOG(
"TEST_INTERFACE", Sev::warning)
535 <<
"Test " << it->number <<
" ('" << it->name
536 <<
"') is replaced by test " <<
number <<
" ('" <<
name <<
"')";
549 auto it = by_number.find(
number);
550 if (it == by_number.end()) {
551 MOFEM_LOG(
"TEST_INTERFACE", Sev::warning)
552 <<
"No test registered with number " <<
number <<
"; test skipped";
553 }
else if (it->hook) {
562 auto it = by_name.find(
name);
563 if (it == by_name.end()) {
564 MOFEM_LOG(
"TEST_INTERFACE", Sev::warning)
565 <<
"No test registered with name '" <<
name <<
"'; test skipped";
566 }
else if (it->hook) {
572bool TestingInterfaceImpl::checkTest(
int number)
const {
574 return by_number.find(
number) != by_number.end();
577bool TestingInterfaceImpl::checkTest(
const std::string &
name)
const {
579 return by_name.find(
name) != by_name.end();
585 CHKERR checkSelectors(selection.first);
593TestingInterfaceImpl::getOptions(
const std::string &prefix)
const {
595 const char *pre = prefix.empty() ? PETSC_NULLPTR : prefix.c_str();
596 constexpr PetscInt max_selectors = 64;
597 PetscInt raw_numbers[max_selectors];
598 PetscInt n_numbers = max_selectors;
599 PetscBool has_number = PETSC_FALSE;
601 &n_numbers, &has_number);
602 char *raw_names[max_selectors];
603 PetscInt n_names = max_selectors;
604 PetscBool has_name = PETSC_FALSE;
605 CHKERR PetscOptionsGetStringArray(PETSC_NULLPTR, pre,
"-test_name", raw_names,
606 &n_names, &has_name);
607 if (has_number && has_name) {
608 for (PetscInt
i = 0;
i < n_names; ++
i)
609 CHKERR PetscFree(raw_names[
i]);
611 "Both -%stest_nb and -%stest_name were given; only one test "
612 "selector is allowed",
613 prefix.c_str(), prefix.c_str());
616 for (PetscInt
i = 0;
i < n_numbers; ++
i)
617 if (!selection.get<0>().count(raw_numbers[
i]))
618 selection.insert({
static_cast<int>(raw_numbers[
i]),
"",
true});
619 for (PetscInt
i = 0;
i < n_names; ++
i) {
620 if (!selection.get<1>().count(raw_names[
i]))
621 selection.insert({0, raw_names[
i],
false});
622 CHKERR PetscFree(raw_names[
i]);
624 selectors[prefix] = std::move(selection);
629TestingInterfaceImpl::ensureOptions(
const std::string &prefix)
const {
632 CHKERR getOptions(prefix);
637TestingInterfaceImpl::checkSelectors(
const std::string &prefix)
const {
639 for (
const auto &entry :
selectors.at(prefix))
642 <<
"No test registered for -" << prefix
643 << (entry.
byNumber ?
"test_nb " :
"test_name ")
650TestingInterfaceImpl::runTestCommandLine(
const std::string &prefix)
const {
652 CHKERR ensureOptions(prefix);
653 CHKERR checkSelectors(prefix);
661TestingInterfaceImpl::addTestCommandLine(
const std::string &prefix,
int number,
662 const std::string &
name,
665 CHKERR ensureOptions(prefix);
675 return new TestingInterfaceImpl(core);
680 const std::string &problem,
const std::string &finite_element,
682 double distance,
bool *point_found) {
683 return addFieldEvaluator(2, problem, finite_element, field_eval_coords,
684 std::move(
hook), distance, point_found);
689 const std::string &problem,
const std::string &finite_element,
691 double distance,
bool *point_found) {
692 return addFieldEvaluator(3, problem, finite_element, field_eval_coords,
693 std::move(
hook), distance, point_found);
698 const std::string &check_name, PetscReal error, PetscReal
scale,
699 Tolerance tolerance,
Sev error_severity,
Sev success_severity)
const {
700 return checkError(TestType::SCALAR, check_name, error,
scale, tolerance,
701 error_severity, success_severity);
706 const std::string &check_name, PetscReal error, PetscReal
scale,
707 Tolerance tolerance,
Sev error_severity,
Sev success_severity)
const {
708 return checkError(TestType::POINTWISE, check_name, error,
scale, tolerance,
709 error_severity, success_severity);
714 const std::string &check_name, PetscReal error, PetscReal
scale,
715 Tolerance tolerance,
Sev error_severity,
Sev success_severity)
const {
716 return checkError(TestType::DERIVATIVE, check_name, error,
scale, tolerance,
717 error_severity, success_severity);
722 const std::string &check_name, PetscReal error, PetscReal
scale,
723 Tolerance tolerance,
Sev error_severity,
Sev success_severity)
const {
724 return checkError(TestType::SOLVER, check_name, error,
scale, tolerance,
725 error_severity, success_severity);
730 const std::string &check_name, PetscReal actual, PetscReal expected,
731 Tolerance tolerance,
Sev error_severity,
Sev success_severity)
const {
732 return checkError<TestType::SCALAR>(check_name, std::abs(actual - expected),
733 std::abs(expected), tolerance,
734 error_severity, success_severity);
739 const std::string &check_name, PetscReal actual, PetscReal expected,
740 Tolerance tolerance,
Sev error_severity,
Sev success_severity)
const {
741 return checkError<TestType::POINTWISE>(
742 check_name, std::abs(actual - expected), std::abs(expected), tolerance,
743 error_severity, success_severity);
748 const std::string &check_name, PetscReal actual, PetscReal expected,
749 Tolerance tolerance,
Sev error_severity,
Sev success_severity)
const {
750 return checkError<TestType::DERIVATIVE>(
751 check_name, std::abs(actual - expected), std::abs(expected), tolerance,
752 error_severity, success_severity);
757 const std::string &check_name, PetscReal actual, PetscReal expected,
758 Tolerance tolerance,
Sev error_severity,
Sev success_severity)
const {
759 return checkError<TestType::SOLVER>(check_name, std::abs(actual - expected),
760 std::abs(expected), tolerance,
761 error_severity, success_severity);
SmartPetscObj< Vec > values
TestRegistry testRegistry
std::map< std::string, Selectors > selectors
#define MoFEMFunctionReturnHot(a)
Last executable line of each PETSc function used for error handling. Replaces return()
#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.
#define MOFEM_LOG(channel, severity)
Log.
SeverityLevel
Severity levels.
#define MOFEM_LOG_TAG(channel, tag)
Tag channel.
FTensor::Index< 'i', SPACE_DIM > i
MoFEM::PipelineManager::ElementsAndOpsByDim< SPACE_DIM >::DomainEle Ele
const double n
refractive index of diffusive medium
const FTensor::Tensor2< T, Dim, Dim > Vec
PetscErrorCode MoFEMErrorCode
MoFEM/PETSc error code.
implementation of Data Operators for Forces and Sources
PetscErrorCode PetscOptionsGetIntArray(PetscOptions options, const char pre[], const char name[], PetscInt dvalue[], PetscInt *nmax, PetscBool *set)
MoFEM::LogManager::SeverityLevel Sev
virtual MPI_Comm & get_comm() const =0
virtual int get_comm_rank() const =0
Deprecated interface functions.
std::function< MoFEMErrorCode(boost::ptr_deque< ForcesAndSourcesCore::UserDataOperator > &)> FieldEvaluatorHook
MoFEMErrorCode getInterface(IFACE *&iface) const
Get interface reference to pointer of interface.