v0.16.3
Loading...
Searching...
No Matches
TestingInterface.cpp
Go to the documentation of this file.
1/** \file TestingInterface.cpp
2 * \brief Numerical testing interface registration and solver completion checks.
3 */
4
5#include "FieldEvaluator.hpp"
6#include <algorithm>
7#include <cmath>
8#include <iomanip>
9#include <limits>
10#include <map>
11#include <memory>
12#include <sstream>
13#include <type_traits>
14#include <boost/multi_index/hashed_index.hpp>
15
16namespace MoFEM {
17
18namespace mi = boost::multi_index;
19
20namespace {
21
22template <TestType TYPE> struct TestTraits;
23
24template <> struct TestTraits<TestType::SCALAR> {
25 static constexpr const char *name = "scalar";
26};
27template <> struct TestTraits<TestType::POINTWISE> {
28 static constexpr const char *name = "pointwise";
29};
30template <> struct TestTraits<TestType::DERIVATIVE> {
31 static constexpr const char *name = "derivative";
32};
33template <> struct TestTraits<TestType::SOLVER> {
34 static constexpr const char *name = "solver";
35};
36
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;
42 MoFEMErrorCode addTSMonitor(TS ts, TSCheck check) override;
43 MoFEMErrorCode addTSMonitor(const std::string &prefix, int number,
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;
54 MoFEMErrorCode checkError(TestType type, const std::string &check_name,
55 PetscReal error, PetscReal scale,
56 Tolerance tolerance, Sev error_severity,
57 Sev success_severity) const override;
58 MoFEMErrorCode checkSolver(TS ts,
59 const std::string &check_name) const override;
60 MoFEMErrorCode checkSolver(TS ts, PetscReal expected_time,
61 Tolerance tolerance,
62 const std::string &check_name) const override;
63 MoFEMErrorCode checkSolver(SNES snes,
64 const std::string &check_name) const override;
65 MoFEMErrorCode checkSolver(KSP ksp,
66 const std::string &check_name) const override;
67 MoFEMErrorCode checkSolver(Tao tao,
68 const std::string &check_name) const override;
69 MoFEMErrorCode addTest(int number, const std::string &name,
70 TestHook hook) override;
71 MoFEMErrorCode runTest(int number) const override;
72 MoFEMErrorCode runTest(const std::string &name) const override;
73 MoFEMErrorCode runTests() const 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;
77 MoFEMErrorCode addTestCommandLine(const std::string &prefix, int number,
78 const std::string &name,
79 TestHook hook) override;
80
81private:
82 struct CheckReport {
83 bool failed = false;
84 std::string text;
85 };
86 struct TestEntry {
87 int number;
88 std::string name;
89 // TS owns monitor callbacks; only post-solve tests have a hook here.
90 TestHook hook;
91 };
92 typedef boost::multi_index_container<
93 TestEntry,
94 mi::indexed_by<
95 mi::ordered_unique<mi::member<TestEntry, int, &TestEntry::number>>,
96 mi::ordered_unique<
97 mi::member<TestEntry, std::string, &TestEntry::name>>>>
98 TestRegistry;
99 TestRegistry testRegistry;
100 MoFEMErrorCode registerTest(int number, const std::string &name,
101 TestHook hook);
102 struct Selector {
103 int number;
104 std::string name;
106 };
107 using Selectors = boost::multi_index_container<
108 Selector,
109 mi::indexed_by<
110 mi::hashed_non_unique<mi::member<Selector, int, &Selector::number>>,
111 mi::hashed_non_unique<
112 mi::member<Selector, std::string, &Selector::name>>>>;
113 mutable std::map<std::string, Selectors> selectors;
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)
119 if (it->byNumber)
120 return true;
121 auto names = selection.get<1>().equal_range(name);
122 for (auto it = names.first; it != names.second; ++it)
123 if (!it->byNumber)
124 return true;
125 return false;
126 }
127 MoFEMErrorCode checkComm(PetscObject solver) const;
128 MoFEMErrorCode checkReport(const CheckReport &report) const;
129 MoFEMErrorCode reportError(const char *category,
130 const std::string &check_name, PetscReal error,
131 PetscReal scale, Tolerance tolerance,
132 CheckReport &report,
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;
140 MoFEMErrorCode reportFinished(KSP ksp, const std::string &check_name,
141 CheckReport &report) const;
142 Core &cOre;
143 MPI_Comm getComm() const;
144 template <int DIM>
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;
155};
156
157TestingInterfaceImpl::TestingInterfaceImpl(const Core &core)
158 : cOre(const_cast<Core &>(core)) {
159 if (!LogManager::checkIfChannelExist("TEST_INTERFACE")) {
160 auto core_log = logging::core::get();
161 core_log->add_sink(
162 LogManager::createSink(LogManager::getStrmWorld(), "TEST_INTERFACE"));
163 LogManager::setLog("TEST_INTERFACE");
164 MOFEM_LOG_TAG("TEST_INTERFACE", "TestingInterface");
165 }
166}
167
169TestingInterfaceImpl::query_interface(boost::typeindex::type_index type_index,
170 UnknownInterface **iface) const {
171 *iface = const_cast<TestingInterfaceImpl *>(this);
172 return 0;
173}
174
175MoFEMErrorCode TestingInterfaceImpl::addTSMonitor(TS ts, TSCheck check) {
177 CHKERR checkComm(reinterpret_cast<PetscObject>(ts));
178 if (!check)
179 SETERRQ(getComm(), MOFEM_INVALID_DATA,
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 {
185 if (step > 0)
186 CHKERR (*static_cast<TSCheck *>(ctx))(ts, step, time, solution);
188 };
189 auto destroy = [](void **ctx) -> PetscErrorCode {
190 delete static_cast<TSCheck *>(*ctx);
191 *ctx = nullptr;
192 return PETSC_SUCCESS;
193 };
194 CHKERR TSMonitorSet(ts, monitor, check_ptr.get(), destroy);
195 check_ptr.release();
197}
198
199MoFEMErrorCode TestingInterfaceImpl::addTSMonitor(const std::string &prefix,
200 int number,
201 const std::string &name,
202 TS ts, TSCheck check) {
204 CHKERR ensureOptions(prefix);
205 if (isSelected(prefix, number, name)) {
206 CHKERR addTSMonitor(ts, std::move(check));
207 CHKERR registerTest(number, name, {});
208 }
210}
211
212template <int DIM>
213MoFEMErrorCode TestingInterfaceImpl::addFieldEvaluatorImpl(
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) {
218 MoFEM::Interface &m_field = cOre;
219 using Ele = std::conditional_t<DIM == 2, FaceElementForcesAndSourcesCore,
220 VolumeElementForcesAndSourcesCore>;
221 if (!hook)
222 SETERRQ(getComm(), MOFEM_INVALID_DATA,
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; };
228 CHKERR hook(fe->getOpPtrVector());
229 CHKERR evaluator->buildTree<DIM>(data, finite_element);
230 CHKERR evaluator->evalFEAtThePoint<DIM>(
231 field_eval_coords.data(), distance, problem, finite_element, data,
232 m_field.get_comm_rank(), m_field.get_comm_rank(), nullptr, MF_EXIST,
233 QUIET);
234 const bool found = fe->gaussPts.size2() == 1;
235 if (point_found)
236 *point_found = found;
237 else if (!found)
238 SETERRQ(getComm(), MOFEM_ATOM_TEST_INVALID,
239 "Evaluation point (%g, %g, %g) not found in local problem %s, "
240 "finite element %s",
241 field_eval_coords[0], field_eval_coords[1], field_eval_coords[2],
242 problem.c_str(), finite_element.c_str());
244}
245
246MoFEMErrorCode TestingInterfaceImpl::addFieldEvaluator(
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) {
252 switch (dimension) {
253 case 2:
254 CHKERR addFieldEvaluatorImpl<2>(problem, finite_element, field_eval_coords,
255 std::move(hook), distance, point_found);
256 break;
257 case 3:
258 CHKERR addFieldEvaluatorImpl<3>(problem, finite_element, field_eval_coords,
259 std::move(hook), distance, point_found);
260 break;
261 default:
262 SETERRQ(getComm(), MOFEM_INVALID_DATA,
263 "Field evaluation requires dimension 2 or 3");
264 }
266}
267
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;
275 switch (type) {
276 case TestType::SCALAR:
277 category = TestTraits<TestType::SCALAR>::name;
278 break;
279 case TestType::POINTWISE:
280 category = TestTraits<TestType::POINTWISE>::name;
281 break;
282 case TestType::DERIVATIVE:
283 category = TestTraits<TestType::DERIVATIVE>::name;
284 break;
285 case TestType::SOLVER:
286 category = TestTraits<TestType::SOLVER>::name;
287 break;
288 default:
289 SETERRQ(getComm(), MOFEM_INVALID_DATA, "Unknown test category");
290 }
291 CHKERR checkErrorImpl(category, check_name, error, scale, tolerance,
292 error_severity, success_severity);
294}
295
296MPI_Comm TestingInterfaceImpl::getComm() const {
297 MoFEM::Interface &m_field = cOre;
298 return m_field.get_comm();
299}
300
301MoFEMErrorCode TestingInterfaceImpl::checkComm(PetscObject solver) const {
303 int relation;
304 CHKERR MPI_Comm_compare(PetscObjectComm(solver), getComm(), &relation);
305 if (relation != MPI_IDENT && relation != MPI_CONGRUENT)
306 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
307 "Solver communicator must be identical or congruent to the Core "
308 "communicator");
310}
311
313TestingInterfaceImpl::checkReport(const CheckReport &report) const {
315 if (report.failed)
316 SETERRQ(getComm(), MOFEM_ATOM_TEST_INVALID, "%s", report.text.c_str());
318}
319
320MoFEMErrorCode TestingInterfaceImpl::reportError(
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;
326 const bool valid =
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,
334 getComm());
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,
339 getComm());
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;
345 if (failed_ranks) {
346 // Preserve a failing rank's error/bound pair when tolerances differ by rank.
347 int rank;
348 CHKERR MPI_Comm_rank(getComm(), &rank);
349 const int candidate = local_failed ? rank : std::numeric_limits<int>::max();
350 int failing_rank;
351 CHKERR MPI_Allreduce(&candidate, &failing_rank, 1, MPI_INT, MPI_MIN,
352 getComm());
353 PetscReal values[] = {
354 error, bound, scale, tolerance.absolute, tolerance.relative,
355 valid ? 1. : 0.};
356 CHKERR MPI_Bcast(values, 6, MPIU_REAL, failing_rank, getComm());
357 message << ", failing_rank=" << failing_rank << ", error=" << values[0]
358 << ", bound=" << values[1] << ", scale=" << values[2]
359 << ", absolute=" << values[3] << ", relative=" << values[4]
360 << ", inputs=" << (values[5] ? "valid" : "invalid/non-finite");
361 } else {
362 message << ", local_bound=" << bound;
363 }
364 MOFEM_LOG("TEST_INTERFACE", failed_ranks ? error_severity : success_severity)
365 << message.str();
366 report.failed = report.failed || failed_ranks;
367 report.text += message.str() + "\n";
369}
370
371MoFEMErrorCode TestingInterfaceImpl::checkErrorImpl(
372 const char *category, const std::string &check_name, PetscReal error,
373 PetscReal scale, Tolerance tolerance, Sev error_severity,
374 Sev success_severity) const {
376 CheckReport report;
377 CHKERR reportError(category, check_name, error, scale, tolerance, report,
378 error_severity, success_severity);
379 CHKERR checkReport(report);
381}
382
383MoFEMErrorCode TestingInterfaceImpl::reportReason(const std::string &check_name,
384 int reason,
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);
393}
394
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);
403 // STEPOVER permits passing the endpoint; it must never accept stopping short.
404 const PetscReal expected =
405 policy == TS_EXACTFINALTIME_STEPOVER && solve_time >= final_time
406 ? solve_time
407 : final_time;
408 CHKERR checkSolver(ts, expected, {1e-12, 1e-12}, check_name);
410}
411
413TestingInterfaceImpl::checkSolver(TS ts, PetscReal expected_time,
414 Tolerance tolerance,
415 const std::string &check_name) const {
417 CHKERR checkComm(reinterpret_cast<PetscObject>(ts));
418 CheckReport report;
419 TSConvergedReason reason;
420 PetscReal time;
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),
430 tolerance, report);
431 SNES snes;
432 CHKERR TSGetSNES(ts, &snes);
433 if (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);
438 }
439 CHKERR checkReport(report);
441}
442
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);
451 KSP ksp;
452 CHKERR SNESGetKSP(snes, &ksp);
453 if (ksp) {
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);
458 }
460}
461
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);
471}
472
474TestingInterfaceImpl::checkSolver(SNES snes,
475 const std::string &check_name) const {
477 CheckReport report;
478 CHKERR reportFinished(snes, check_name, report);
479 CHKERR checkReport(report);
481}
482
484TestingInterfaceImpl::checkSolver(KSP ksp,
485 const std::string &check_name) const {
487 CheckReport report;
488 CHKERR reportFinished(ksp, check_name, report);
489 CHKERR checkReport(report);
491}
492
494TestingInterfaceImpl::checkSolver(Tao tao,
495 const std::string &check_name) const {
497 CHKERR checkComm(reinterpret_cast<PetscObject>(tao));
498 CheckReport report;
499 TaoConvergedReason reason;
500 CHKERR TaoGetConvergedReason(tao, &reason);
501 CHKERR reportReason(check_name, reason, TaoConvergedReasons[reason], report);
502 CHKERR checkReport(report);
504}
505
506MoFEMErrorCode TestingInterfaceImpl::addTest(int number,
507 const std::string &name,
508 TestHook hook) {
510 if (!hook)
511 SETERRQ(getComm(), MOFEM_INVALID_DATA, "Test '%s' requires a hook",
512 name.c_str());
513 CHKERR registerTest(number, name, std::move(hook));
515}
516
517MoFEMErrorCode TestingInterfaceImpl::registerTest(int number,
518 const std::string &name,
519 TestHook hook) {
521 auto &by_number = testRegistry.get<0>();
522 auto existing = by_number.find(number);
523 // A monitor and a post-solve check can share the same test identity.
524 if (existing != by_number.end() && existing->name == name &&
525 (!hook || !existing->hook)) {
526 if (hook)
527 by_number.modify(existing,
528 [&](TestEntry &entry) { entry.hook = std::move(hook); });
530 }
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 << "')";
537 index.erase(it);
538 }
539 };
540 replace_test(testRegistry.get<0>(), number);
541 replace_test(testRegistry.get<1>(), name);
542 testRegistry.emplace(TestEntry{number, name, std::move(hook)});
544}
545
546MoFEMErrorCode TestingInterfaceImpl::runTest(int number) const {
548 auto &by_number = testRegistry.get<0>();
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) {
554 CHKERR it->hook();
555 }
557}
558
559MoFEMErrorCode TestingInterfaceImpl::runTest(const std::string &name) const {
561 auto &by_name = testRegistry.get<1>();
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) {
567 CHKERR it->hook();
568 }
570}
571
572bool TestingInterfaceImpl::checkTest(int number) const {
573 auto &by_number = testRegistry.get<0>();
574 return by_number.find(number) != by_number.end();
575}
576
577bool TestingInterfaceImpl::checkTest(const std::string &name) const {
578 auto &by_name = testRegistry.get<1>();
579 return by_name.find(name) != by_name.end();
580}
581
582MoFEMErrorCode TestingInterfaceImpl::runTests() const {
584 for (const auto &selection : selectors)
585 CHKERR checkSelectors(selection.first);
586 for (const auto &entry : testRegistry.get<0>())
587 if (entry.hook)
588 CHKERR entry.hook();
590}
591
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;
600 CHKERR PetscOptionsGetIntArray(PETSC_NULLPTR, pre, "-test_nb", raw_numbers,
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]);
610 SETERRQ(getComm(), MOFEM_INVALID_DATA,
611 "Both -%stest_nb and -%stest_name were given; only one test "
612 "selector is allowed",
613 prefix.c_str(), prefix.c_str());
614 }
615 Selectors selection;
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]);
623 }
624 selectors[prefix] = std::move(selection);
626}
627
629TestingInterfaceImpl::ensureOptions(const std::string &prefix) const {
631 if (!selectors.count(prefix))
632 CHKERR getOptions(prefix);
634}
635
637TestingInterfaceImpl::checkSelectors(const std::string &prefix) const {
639 for (const auto &entry : selectors.at(prefix))
640 if (entry.byNumber ? !checkTest(entry.number) : !checkTest(entry.name))
641 MOFEM_LOG("TEST_INTERFACE", Sev::warning)
642 << "No test registered for -" << prefix
643 << (entry.byNumber ? "test_nb " : "test_name ")
644 << (entry.byNumber ? std::to_string(entry.number) : entry.name)
645 << "; test skipped";
647}
648
650TestingInterfaceImpl::runTestCommandLine(const std::string &prefix) const {
652 CHKERR ensureOptions(prefix);
653 CHKERR checkSelectors(prefix);
654 for (const auto &entry : testRegistry.get<0>())
655 if (entry.hook && isSelected(prefix, entry.number, entry.name))
656 CHKERR entry.hook();
658}
659
661TestingInterfaceImpl::addTestCommandLine(const std::string &prefix, int number,
662 const std::string &name,
663 TestHook hook) {
665 CHKERR ensureOptions(prefix);
666 if (isSelected(prefix, number, name))
667 CHKERR addTest(number, name, std::move(hook));
669}
670
671} // namespace
672
673template <>
674TestingInterface *createInterface<TestingInterface>(const Core &core) {
675 return new TestingInterfaceImpl(core);
676}
677
678template <>
679MoFEMErrorCode TestingInterface::addFieldEvaluator<2>(
680 const std::string &problem, const std::string &finite_element,
681 const std::array<double, 3> &field_eval_coords, FieldEvaluatorHook hook,
682 double distance, bool *point_found) {
683 return addFieldEvaluator(2, problem, finite_element, field_eval_coords,
684 std::move(hook), distance, point_found);
685}
686
687template <>
688MoFEMErrorCode TestingInterface::addFieldEvaluator<3>(
689 const std::string &problem, const std::string &finite_element,
690 const std::array<double, 3> &field_eval_coords, FieldEvaluatorHook hook,
691 double distance, bool *point_found) {
692 return addFieldEvaluator(3, problem, finite_element, field_eval_coords,
693 std::move(hook), distance, point_found);
694}
695
696template <>
697MoFEMErrorCode TestingInterface::checkError<TestType::SCALAR>(
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);
702}
703
704template <>
705MoFEMErrorCode TestingInterface::checkError<TestType::POINTWISE>(
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);
710}
711
712template <>
713MoFEMErrorCode TestingInterface::checkError<TestType::DERIVATIVE>(
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);
718}
719
720template <>
721MoFEMErrorCode TestingInterface::checkError<TestType::SOLVER>(
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);
726}
727
728template <>
729MoFEMErrorCode TestingInterface::checkValue<TestType::SCALAR>(
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);
735}
736
737template <>
738MoFEMErrorCode TestingInterface::checkValue<TestType::POINTWISE>(
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);
744}
745
746template <>
747MoFEMErrorCode TestingInterface::checkValue<TestType::DERIVATIVE>(
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);
753}
754
755template <>
756MoFEMErrorCode TestingInterface::checkValue<TestType::SOLVER>(
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);
762}
763
764} // namespace MoFEM
SmartPetscObj< KSP > ksp
Field Evaluator.
std::string type
std::string key
SmartPetscObj< Vec > values
std::string text
TestHook hook
bool failed
TestRegistry testRegistry
int number
std::map< std::string, Selectors > selectors
std::string name
bool byNumber
Core & cOre
@ QUIET
@ MF_EXIST
#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
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.
#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
Definition Common.hpp:10
PetscErrorCode PetscOptionsGetIntArray(PetscOptions options, const char pre[], const char name[], PetscInt dvalue[], PetscInt *nmax, PetscBool *set)
CoreTmp< 0 > Core
Definition Core.hpp:1154
MoFEM::LogManager::SeverityLevel Sev
double scale
Definition plastic.cpp:124
virtual MPI_Comm & get_comm() const =0
virtual int get_comm_rank() const =0
Core (interface) class.
Definition Core.hpp:83
Deprecated interface functions.
std::function< MoFEMErrorCode(boost::ptr_deque< ForcesAndSourcesCore::UserDataOperator > &)> FieldEvaluatorHook
MoFEMErrorCode getInterface(IFACE *&iface) const
Get interface reference to pointer of interface.