v0.16.3
Loading...
Searching...
No Matches
TestingInterface.hpp
Go to the documentation of this file.
1/** \file TestingInterface.hpp
2 * \brief Collective numerical comparisons for tutorials and atom tests.
3 *
4 * Tutorials supply the measured quantities and independent references. Field
5 * evaluation, physical norms and derivative assembly remain with the existing
6 * operators, FieldEvaluatorInterface and OperatorsTester. In particular, a
7 * coefficient-vector norm is not interchangeable with a physical field norm.
8 *
9 * Numerical comparisons and solver checks are collective on the Core
10 * communicator, in the same order on every rank. A failure on any rank fails
11 * the check on all ranks. Field evaluation is local, as documented below.
12 * Tolerances are explicit; non-finite measurements or invalid tolerances fail.
13 * Results use the TEST_INTERFACE logging channel with the TestingInterface tag.
14 *
15 * addTest/runTest/runTests provide an optional registry so a tutorial's
16 * solving code need not branch on which test is requested: register what to
17 * check, then let a single option (e.g. "-test_nb N") choose what actually
18 * runs. addTestCommandLine is the preferred way to populate it: list every
19 * candidate test unconditionally, each guarded by its own addTestCommandLine
20 * call, then run whatever was actually selected with one runTests() call.
21 */
22
23#ifndef MOFEM_TESTING_INTERFACE_HPP
24#define MOFEM_TESTING_INTERFACE_HPP
25
26#include "UnknownInterface.hpp"
27#include <array>
28#include <functional>
29
30namespace MoFEM {
31
32/** Compile-time check categories; strings are used only for diagnostics. */
34
36 ~TestingInterface() override = default;
37
38 /** Read or refresh selectors after JSON options have been applied.
39 * Selection methods load each prefix lazily on first use. Refreshing changes
40 * future selection, not existing registrations or installed TS monitors. */
41 virtual MoFEMErrorCode getOptions(const std::string &prefix = "") const = 0;
42
43 /** Check convergence and the configured final time, respecting STEPOVER. */
45 const std::string &check_name) const = 0;
46
47 using TSCheck = std::function<MoFEMErrorCode(TS, PetscInt, PetscReal, Vec)>;
48
49 /**
50 * Add a numerical check to the TS monitor sequence. TS owns the callback
51 * until its monitors are cancelled or it is destroyed; existing monitors
52 * are retained. Initial step zero and interpolated output (step -1) are
53 * skipped. The callback is collective on the TS communicator, which must be
54 * identical or congruent to the Core communicator.
55 *
56 * The supplied solution is borrowed and must not be modified. Operator
57 * checks must use separate work vectors and restore any modified mesh,
58 * history or pipeline data, including on failure. Time/rate/shift data for
59 * transient derivative checks must come from the actual tested residual;
60 * the monitor does not infer them from the time-step size.
61 */
62 virtual MoFEMErrorCode addTSMonitor(TS ts, TSCheck check) = 0;
63
64 /**
65 * Register a TS monitor the same way as addTSMonitor(TS, TSCheck), but
66 * only if its number appears in the comma-separated
67 * "-<prefix>test_nb <int,int,...>" list or its name appears in the
68 * comma-separated "-<prefix>test_name <name,name,...>" list read from the
69 * command line; otherwise this call does nothing. Giving both options at
70 * once is an error.
71 *
72 * Pairs with addTestCommandLine: register a per-time-step numerical check
73 * under the same prefix/number/name selector used for post-solve tests, so
74 * a single "-test_nb"/"-test_name" choice can activate both a solve-time
75 * monitor and its matching checkResults() test.
76 * Selected monitors share the test registry identity. runTest/runTests
77 * recognise them but do not invoke their callbacks; TS invokes those.
78 */
79 virtual MoFEMErrorCode addTSMonitor(const std::string &prefix, int number,
80 const std::string &name, TS ts,
81 TSCheck check) = 0;
82
83 using FieldEvaluatorHook = std::function<MoFEMErrorCode(
84 boost::ptr_deque<ForcesAndSourcesCore::UserDataOperator> &)>;
85
86 /**
87 * Build an operator pipeline and immediately evaluate one local point.
88 * DIM selects the face (2) or volume (3) element and search dimension.
89 * Coordinates are always an (x,y,z) triplet.
90 * The hook receives the pipeline and supplies its geometry,
91 * field and checking operators; outputs can be captured shared data.
92 *
93 * The evaluator and its operators live only for this call. Coordinates and
94 * hook captures need not outlive it. Evaluation uses the current mesh field
95 * coefficients, so a TS callback must first scatter the accepted solution.
96 * Results are local and are not communicated. By default a point missing
97 * from the calling rank's problem elements returns a test failure. Supplying
98 * point_found instead reports local ownership without failing for a missing
99 * point; the caller must collectively check that some rank found it before
100 * comparing owner results. Other evaluation errors still propagate.
101 * Operators must not call collective checks on only the owning rank.
102 */
103 template <int DIM>
105 addFieldEvaluator(const std::string &problem,
106 const std::string &finite_element,
107 const std::array<double, 3> &field_eval_coords,
108 FieldEvaluatorHook hook, double distance = 1e-12,
109 bool *point_found = nullptr);
110
111 /** Evaluate a point in dimension 2 or 3. */
112 virtual MoFEMErrorCode
113 addFieldEvaluator(int dimension, const std::string &problem,
114 const std::string &finite_element,
115 const std::array<double, 3> &field_eval_coords,
116 FieldEvaluatorHook hook, double distance = 1e-12,
117 bool *point_found = nullptr) = 0;
118
119 /** Acceptance bound: absolute + relative * scale, with no implicit floor. */
120 struct Tolerance {
121 PetscReal absolute;
122 PetscReal relative;
123 };
124
125 /** Compare real scalars with scale = abs(expected). */
126 template <TestType TYPE>
127 MoFEMErrorCode checkValue(const std::string &check_name, PetscReal actual,
128 PetscReal expected, Tolerance tolerance,
129 Sev error_severity = Sev::error,
130 Sev success_severity = Sev::inform) const;
131
132 /**
133 * Compare a non-negative error norm with absolute + relative * scale.
134 *
135 * The caller defines the norm and scale. For derivative checks, a useful
136 * scale is max(norm(FD), norm(J*v)); for tensors use the intended FTensor
137 * contraction, including symmetric off-diagonal weights. Missing evaluation
138 * points must be reported as failures, not represented by zero error.
139 *
140 * Local comparisons are reduced collectively; an already-global norm is
141 * also valid. Logging severities are configurable; a failed check still
142 * returns MOFEM_ATOM_TEST_INVALID regardless of its logging severity.
143 * Logs and propagated errors include the global maximum error, failing-rank
144 * count and a representative failing rank's paired error and acceptance bound.
145 * Invalid/non-finite inputs are distinguished from tolerance violations.
146 */
147 template <TestType TYPE>
148 MoFEMErrorCode checkError(const std::string &check_name, PetscReal error,
149 PetscReal scale, Tolerance tolerance,
150 Sev error_severity = Sev::error,
151 Sev success_severity = Sev::inform) const;
152
153 /** Compare an error using the specified check category. */
154 virtual MoFEMErrorCode
155 checkError(TestType type, const std::string &check_name, PetscReal error,
156 PetscReal scale, Tolerance tolerance,
157 Sev error_severity = Sev::error,
158 Sev success_severity = Sev::inform) const = 0;
159
160 /**
161 * Check successful termination at the requested final time. A positive TS
162 * reason alone can mean a step limit or event before that time. Event-driven
163 * tests should check their expected reason and event state explicitly.
164 * Also check the last SNES/KSP solve when a nested solver has run. Unused
165 * nested solvers retain the ITERATING reason and are skipped. Earlier
166 * failures recovered by the solver do not invalidate successful completion.
167 * All applicable numerical results are reported before returning failure,
168 * including when the outer solver diverges. PETSc/MPI API errors propagate
169 * immediately. All checkSolver overloads require an identical or congruent
170 * communicator (same members and rank order), checked before Core reductions.
171 * All Core ranks must still call these checks in the same order.
172 */
173 virtual MoFEMErrorCode checkSolver(TS ts, PetscReal expected_time,
174 Tolerance tolerance,
175 const std::string &check_name) const = 0;
176
177 /** Require SNES convergence and check its KSP if it has run. */
178 virtual MoFEMErrorCode checkSolver(SNES snes,
179 const std::string &check_name) const = 0;
180
181 /** Require KSP convergence; an unsolved solver fails this direct check. */
183 const std::string &check_name) const = 0;
184
185 /** Require TAO convergence under its configured convergence test.
186 * Does not independently test the raw objective gradient or custom subsolvers.
187 */
189 const std::string &check_name) const = 0;
190
191 using TestHook = std::function<MoFEMErrorCode()>;
192
193 /**
194 * Register a named, numbered test for later, on-demand execution.
195 *
196 * This registry lets a tutorial keep its solving code (readMesh,
197 * setupProblem, ..., solveSystem) free of "-test_nb N" branching: checks are
198 * registered where it is natural to write them, then run only when asked,
199 * through runTest/runTests. A hook is never invoked at registration time,
200 * so calling addTest never disturbs already-solved state. Hooks are
201 * expected to be collective, called identically by every rank, since they
202 * typically perform collective checks (checkValue/checkError, or use
203 * addFieldEvaluator). Both number and name must be unique among currently
204 * registered tests. Registering either again replaces the conflicting
205 * tests and logs a warning for each replaced test. A monitor-only entry with
206 * the same number and name gains this hook without a replacement warning.
207 * Registry changes do not cancel callbacks already owned by TS.
208 */
209 virtual MoFEMErrorCode addTest(int number, const std::string &name,
210 TestHook hook) = 0;
211
212 /** Run one previously registered test, looked up by number. Logs a warning
213 * and skips the test if that number is not registered. */
214 virtual MoFEMErrorCode runTest(int number) const = 0;
215
216 /** Run one previously registered test, looked up by name. Logs a warning
217 * and skips the test if that name is not registered. */
218 virtual MoFEMErrorCode runTest(const std::string &name) const = 0;
219
220 /** Run every registered post-solve hook, in ascending order of test number,
221 * stopping at and propagating the first failure. Check cached selectors
222 * against the current registry and warn about each unmatched selection.
223 * Monitor-only entries are recognised but are invoked exclusively by TS. */
224 virtual MoFEMErrorCode runTests() const = 0;
225
226 /** True if a test or selected TS monitor with this number is registered,
227 * e.g. because addTestCommandLine selected it. Lets a tutorial branch on
228 * whether a test run was requested without keeping its own flag. */
229 virtual bool checkTest(int number) const = 0;
230
231 /** True if a test or selected TS monitor with this name is registered. */
232 virtual bool checkTest(const std::string &name) const = 0;
233
234 /**
235 * Run every test selected by "-<prefix>test_nb <int,int,...>" or
236 * "-<prefix>test_name <name,name,...>", comma-separated lists read from
237 * the command line. Prefix defaults to none, i.e. plain
238 * "-test_nb"/"-test_name". Does nothing if neither option is given; giving
239 * both is an error. This is the usual entry point for a tutorial's
240 * checkResults(): register tests with addTest, then let this one call
241 * pick and run everything that was asked for. Each matching post-solve hook
242 * runs once, in ascending test-number order, stopping at the first failure.
243 * Unmatched selectors warn; monitor-only entries are recognised but skipped.
244 */
245 virtual MoFEMErrorCode
246 runTestCommandLine(const std::string &prefix = "") const = 0;
247
248 /**
249 * Register a test the same way as addTest, but only if its number appears
250 * in the comma-separated "-<prefix>test_nb <int,int,...>" list or its
251 * name appears in the comma-separated "-<prefix>test_name
252 * <name,name,...>" list read from the command line; otherwise this call
253 * does nothing. Giving both options at once is an error.
254 *
255 * This is the preferred way to add tests: list every candidate test
256 * unconditionally in checkResults(), each through its own
257 * addTestCommandLine call, then finish with a single runTests() call.
258 * Selection is applied at registration time. runTests() runs all registered
259 * hooks, including those added directly with addTest. To apply refreshed
260 * selectors to existing registrations, use runTestCommandLine instead.
261 */
262 virtual MoFEMErrorCode addTestCommandLine(const std::string &prefix,
263 int number,
264 const std::string &name,
265 TestHook hook) = 0;
266};
267
268template <>
269MoFEMErrorCode TestingInterface::addFieldEvaluator<2>(
270 const std::string &problem, const std::string &finite_element,
271 const std::array<double, 3> &field_eval_coords, FieldEvaluatorHook hook,
272 double distance, bool *point_found);
273
274template <>
275MoFEMErrorCode TestingInterface::addFieldEvaluator<3>(
276 const std::string &problem, const std::string &finite_element,
277 const std::array<double, 3> &field_eval_coords, FieldEvaluatorHook hook,
278 double distance, bool *point_found);
279
280template <>
281MoFEMErrorCode TestingInterface::checkError<TestType::SCALAR>(
282 const std::string &check_name, PetscReal error, PetscReal scale,
283 Tolerance tolerance, Sev error_severity, Sev success_severity) const;
284
285template <>
286MoFEMErrorCode TestingInterface::checkError<TestType::POINTWISE>(
287 const std::string &check_name, PetscReal error, PetscReal scale,
288 Tolerance tolerance, Sev error_severity, Sev success_severity) const;
289
290template <>
291MoFEMErrorCode TestingInterface::checkError<TestType::DERIVATIVE>(
292 const std::string &check_name, PetscReal error, PetscReal scale,
293 Tolerance tolerance, Sev error_severity, Sev success_severity) const;
294
295template <>
296MoFEMErrorCode TestingInterface::checkError<TestType::SOLVER>(
297 const std::string &check_name, PetscReal error, PetscReal scale,
298 Tolerance tolerance, Sev error_severity, Sev success_severity) const;
299
300template <>
301MoFEMErrorCode TestingInterface::checkValue<TestType::SCALAR>(
302 const std::string &check_name, PetscReal actual, PetscReal expected,
303 Tolerance tolerance, Sev error_severity, Sev success_severity) const;
304
305template <>
306MoFEMErrorCode TestingInterface::checkValue<TestType::POINTWISE>(
307 const std::string &check_name, PetscReal actual, PetscReal expected,
308 Tolerance tolerance, Sev error_severity, Sev success_severity) const;
309
310template <>
311MoFEMErrorCode TestingInterface::checkValue<TestType::DERIVATIVE>(
312 const std::string &check_name, PetscReal actual, PetscReal expected,
313 Tolerance tolerance, Sev error_severity, Sev success_severity) const;
314
315template <>
316MoFEMErrorCode TestingInterface::checkValue<TestType::SOLVER>(
317 const std::string &check_name, PetscReal actual, PetscReal expected,
318 Tolerance tolerance, Sev error_severity, Sev success_severity) const;
319
320/** Create an owned interface; the referenced Core must outlive it.
321 * Core normally owns this object; use getInterface<TestingInterface>() to borrow it.
322 */
323template <>
325
326} // namespace MoFEM
327
328#endif // MOFEM_TESTING_INTERFACE_HPP
SmartPetscObj< KSP > ksp
std::string type
TestHook hook
int number
std::string name
MoFEM interface.
SeverityLevel
Severity levels.
PetscErrorCode MoFEMErrorCode
MoFEM/PETSc error code.
implementation of Data Operators for Forces and Sources
Definition Common.hpp:10
TestingInterface * createInterface< TestingInterface >(const Core &core)
double scale
Definition plastic.cpp:124
Core (interface) class.
Definition Core.hpp:83
virtual bool checkTest(int number) const =0
virtual MoFEMErrorCode addFieldEvaluator(int dimension, const std::string &problem, const std::string &finite_element, const std::array< double, 3 > &field_eval_coords, FieldEvaluatorHook hook, double distance=1e-12, bool *point_found=nullptr)=0
std::function< MoFEMErrorCode()> TestHook
virtual MoFEMErrorCode runTest(int number) const =0
~TestingInterface() override=default
virtual MoFEMErrorCode checkSolver(Tao tao, const std::string &check_name) const =0
virtual MoFEMErrorCode checkSolver(TS ts, PetscReal expected_time, Tolerance tolerance, const std::string &check_name) const =0
virtual MoFEMErrorCode addTest(int number, const std::string &name, TestHook hook)=0
virtual MoFEMErrorCode addTSMonitor(TS ts, TSCheck check)=0
virtual MoFEMErrorCode runTests() const =0
virtual MoFEMErrorCode checkError(TestType type, const std::string &check_name, PetscReal error, PetscReal scale, Tolerance tolerance, Sev error_severity=Sev::error, Sev success_severity=Sev::inform) const =0
virtual bool checkTest(const std::string &name) const =0
virtual MoFEMErrorCode checkSolver(TS ts, const std::string &check_name) const =0
MoFEMErrorCode checkValue(const std::string &check_name, PetscReal actual, PetscReal expected, Tolerance tolerance, Sev error_severity=Sev::error, Sev success_severity=Sev::inform) const
std::function< MoFEMErrorCode(TS, PetscInt, PetscReal, Vec)> TSCheck
virtual MoFEMErrorCode getOptions(const std::string &prefix="") const =0
virtual MoFEMErrorCode runTestCommandLine(const std::string &prefix="") const =0
virtual MoFEMErrorCode addTestCommandLine(const std::string &prefix, int number, const std::string &name, TestHook hook)=0
virtual MoFEMErrorCode checkSolver(SNES snes, const std::string &check_name) const =0
virtual MoFEMErrorCode runTest(const std::string &name) const =0
MoFEMErrorCode checkError(const std::string &check_name, PetscReal error, PetscReal scale, Tolerance tolerance, Sev error_severity=Sev::error, Sev success_severity=Sev::inform) const
virtual MoFEMErrorCode checkSolver(KSP ksp, const std::string &check_name) const =0
std::function< MoFEMErrorCode(boost::ptr_deque< ForcesAndSourcesCore::UserDataOperator > &)> FieldEvaluatorHook
MoFEMErrorCode addFieldEvaluator(const std::string &problem, const std::string &finite_element, const std::array< double, 3 > &field_eval_coords, FieldEvaluatorHook hook, double distance=1e-12, bool *point_found=nullptr)
virtual MoFEMErrorCode addTSMonitor(const std::string &prefix, int number, const std::string &name, TS ts, TSCheck check)=0
base class for all interface classes