Streamlined verification

This commit is contained in:
jpekkila
2020-08-21 20:11:25 +03:00
parent b847b67805
commit 764e4dda69
6 changed files with 122 additions and 234 deletions

View File

@@ -4,6 +4,8 @@
#include <stdbool.h>
#include <string.h>
#include "errchk.h"
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
@@ -26,7 +28,7 @@ is_valid(const AcReal a)
}
Error
acGetError(AcReal model, AcReal candidate)
acGetError(const AcReal model, const AcReal candidate)
{
Error error;
error.abs_error = 0;
@@ -57,9 +59,48 @@ acGetError(AcReal model, AcReal candidate)
error.rel_error = fabsl(1.0l - candidate / model) / machine_epsilon;
}
error.maximum_magnitude = error.minimum_magnitude = 0;
return error;
}
static inline void
print_error_to_file(const char* label, const Error error, const char* path)
{
FILE* file = fopen(path, "a");
ERRCHK_ALWAYS(file);
fprintf(file, "%s, %Lg, %Lg, %Lg, %g, %g\n", label, error.ulp_error, error.abs_error,
error.rel_error, (double)error.maximum_magnitude, (double)error.minimum_magnitude);
fclose(file);
}
/** Returns true if the error is acceptable, false otherwise. */
bool
acEvalError(const char* label, const Error error)
{
// Accept the error if the relative error is < max_ulp_error ulps.
// Also consider the error zero if it is less than the minimum value in the mesh scaled to
// machine epsilon
const long double max_ulp_error = 5;
bool acceptable;
if (error.ulp_error < max_ulp_error)
acceptable = true;
else if (error.abs_error < error.minimum_magnitude * AC_REAL_EPSILON)
acceptable = true;
else
acceptable = false;
printf("%-15s... %s ", label, acceptable ? GRN "OK! " RESET : RED "FAIL! " RESET);
printf("| %.3Lg (abs), %.3Lg (ulps), %.3Lg (rel). Range: [%.3g, %.3g]\n", //
error.abs_error, error.ulp_error, error.rel_error, //
(double)error.minimum_magnitude, (double)error.maximum_magnitude);
print_error_to_file(label, error, "verification.out");
return acceptable;
}
static AcReal
get_maximum_magnitude(const AcReal* field, const AcMeshInfo info)
{
@@ -88,168 +129,39 @@ get_minimum_magnitude(const AcReal* field, const AcMeshInfo info)
// floating-point precision range and gives huge errors with values that should be considered
// zero (f.ex. 1e-19 and 1e-22 give error of around 1e4 ulps)
static Error
get_max_abs_error(const VertexBufferHandle vtxbuf_handle, const AcMesh model_mesh,
const AcMesh candidate_mesh)
get_max_abs_error(const AcReal* model, const AcReal* candidate, const AcMeshInfo info)
{
AcReal* model_vtxbuf = model_mesh.vertex_buffer[vtxbuf_handle];
AcReal* candidate_vtxbuf = candidate_mesh.vertex_buffer[vtxbuf_handle];
Error error;
error.abs_error = -1;
for (size_t i = 0; i < acVertexBufferSize(model_mesh.info); ++i) {
Error curr_error = acGetError(model_vtxbuf[i], candidate_vtxbuf[i]);
Error error = {.abs_error = -1};
for (size_t i = 0; i < acVertexBufferSize(info); ++i) {
Error curr_error = acGetError(model[i], candidate[i]);
if (curr_error.abs_error > error.abs_error)
error = curr_error;
}
error.handle = vtxbuf_handle;
strncpy(error.label, vtxbuf_names[vtxbuf_handle], ERROR_LABEL_LENGTH - 1);
error.label[ERROR_LABEL_LENGTH - 1] = '\0';
error.maximum_magnitude = get_maximum_magnitude(model_vtxbuf, model_mesh.info);
error.minimum_magnitude = get_minimum_magnitude(model_vtxbuf, model_mesh.info);
error.maximum_magnitude = get_maximum_magnitude(model, info);
error.minimum_magnitude = get_minimum_magnitude(model, info);
return error;
}
static inline void
print_error_to_file(const char* path, const int n, const Error error)
{
FILE* file = fopen(path, "a");
fprintf(file, "%d, %Lg, %Lg, %Lg, %g, %g\n", n, error.ulp_error, error.abs_error,
error.rel_error, (double)error.maximum_magnitude, (double)error.minimum_magnitude);
fclose(file);
}
static bool
is_acceptable(const Error error)
{
// Accept the error if the relative error is < max_ulp_error ulps.
// Also consider the error zero if it is less than the minimum value in the mesh scaled to
// machine epsilon
const long double max_ulp_error = 5;
if (error.ulp_error < max_ulp_error)
return true;
else if (error.abs_error < error.minimum_magnitude * AC_REAL_EPSILON)
return true;
else
return false;
}
bool
printErrorToScreen(const Error error)
{
bool errors_found = false;
printf("\t%-15s... ", error.label);
if (is_acceptable(error)) {
printf(GRN "OK! " RESET);
}
else {
printf(RED "FAIL! " RESET);
errors_found = true;
}
fprintf(stdout, "| %.3Lg (abs), %.3Lg (ulps), %.3Lg (rel). Range: [%.3g, %.3g]\n", //
error.abs_error, error.ulp_error, error.rel_error, //
(double)error.minimum_magnitude, (double)error.maximum_magnitude);
return errors_found;
}
/** Returns true when successful, false if errors were found. */
AcResult
acVerifyMesh(const AcMesh model, const AcMesh candidate)
acVerifyMesh(const char* label, const AcMesh model, const AcMesh candidate)
{
printf("---Test: %s---\n", label);
printf("Errors at the point of the maximum absolute error:\n");
bool errors_found = false;
int errors_found = 0;
for (int i = 0; i < NUM_VTXBUF_HANDLES; ++i) {
Error field_error = get_max_abs_error(i, model, candidate);
errors_found |= printErrorToScreen(field_error);
const Error error = get_max_abs_error(model.vertex_buffer[i], candidate.vertex_buffer[i],
model.info);
const bool acceptable = acEvalError(vtxbuf_names[i], error);
if (!acceptable)
++errors_found;
}
printf("%s\n", errors_found ? "Failure. Found errors in one or more vertex buffers"
: "Success. No errors found.");
if (errors_found > 0)
printf("Failure. Found %d errors\n", errors_found);
return errors_found ? AC_FAILURE : AC_SUCCESS;
}
/** Verification function for scalar reductions*/
AcResult
acVerifyScalReductions(const AcMesh model, const AcScalReductionTestCase* testCases,
const size_t numCases)
{
printf("\nTesting scalar reductions:\n");
bool errors_found = false;
for (size_t i = 0; i < numCases; i++) {
AcReal model_reduction = acModelReduceScal(model, testCases[i].rtype, testCases[i].vtxbuf);
Error error = acGetError(model_reduction, testCases[i].candidate);
strncpy(error.label, testCases[i].label, ERROR_LABEL_LENGTH - 1);
error.label[ERROR_LABEL_LENGTH - 1] = '\0';
errors_found |= printErrorToScreen(error);
}
printf("%s\n", errors_found ? "Failure. Found errors in one or more scalar reductions"
: "Success. No errors found.");
return errors_found ? AC_FAILURE : AC_SUCCESS;
}
/** Verification function for vector reductions*/
AcResult
acVerifyVecReductions(const AcMesh model, const AcVecReductionTestCase* testCases,
const size_t numCases)
{
printf("\nTesting vector reductions:\n");
bool errors_found = false;
for (size_t i = 0; i < numCases; i++) {
AcReal model_reduction = acModelReduceVec(model, testCases[i].rtype, testCases[i].a,
testCases[i].b, testCases[i].c);
Error error = acGetError(model_reduction, testCases[i].candidate);
strncpy(error.label, testCases[i].label, ERROR_LABEL_LENGTH - 1);
error.label[ERROR_LABEL_LENGTH - 1] = '\0';
errors_found |= printErrorToScreen(error);
}
printf("%s\n", errors_found ? "Failure. Found errors in one or more vector reductions"
: "Success. No errors found.");
return errors_found ? AC_FAILURE : AC_SUCCESS;
}
/** Constructor for scalar reduction test case */
AcScalReductionTestCase
acCreateScalReductionTestCase(const char* label, const VertexBufferHandle vtxbuf, const ReductionType rtype)
{
AcScalReductionTestCase testCase;
strncpy(testCase.label,label,ERROR_LABEL_LENGTH - 1);
testCase.label[ERROR_LABEL_LENGTH - 1] = '\0';
testCase.vtxbuf = vtxbuf;
testCase.rtype = rtype;
testCase.candidate = 0;
return testCase;
}
/** Constructor for vector reduction test case */
AcVecReductionTestCase
acCreateVecReductionTestCase(const char* label, const VertexBufferHandle a,
const VertexBufferHandle b, const VertexBufferHandle c, const ReductionType rtype)
{
AcVecReductionTestCase testCase;
strncpy(testCase.label,label,ERROR_LABEL_LENGTH - 1);
testCase.label[ERROR_LABEL_LENGTH - 1] = '\0';
testCase.a = a;
testCase.b = b;
testCase.c = c;
testCase.rtype = rtype;
testCase.candidate = 0;
return testCase;
}