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

@@ -31,11 +31,7 @@ extern "C" {
#include <stdbool.h> #include <stdbool.h>
#define ERROR_LABEL_LENGTH 30
typedef struct { typedef struct {
char label[ERROR_LABEL_LENGTH];
VertexBufferHandle handle;
AcReal model; AcReal model;
AcReal candidate; AcReal candidate;
long double abs_error; long double abs_error;
@@ -45,42 +41,9 @@ typedef struct {
AcReal minimum_magnitude; AcReal minimum_magnitude;
} Error; } Error;
typedef struct {
char label[ERROR_LABEL_LENGTH];
VertexBufferHandle vtxbuf;
ReductionType rtype;
AcReal candidate;
} AcScalReductionTestCase;
typedef struct {
char label[ERROR_LABEL_LENGTH];
VertexBufferHandle a;
VertexBufferHandle b;
VertexBufferHandle c;
ReductionType rtype;
AcReal candidate;
} AcVecReductionTestCase;
/** TODO comment */
Error acGetError(AcReal model, AcReal candidate);
/** TODO comment */
bool printErrorToScreen(const Error error);
/** Loads data from the config file */ /** Loads data from the config file */
AcResult acLoadConfig(const char* config_path, AcMeshInfo* config); AcResult acLoadConfig(const char* config_path, AcMeshInfo* config);
/** */
AcScalReductionTestCase acCreateScalReductionTestCase(const char* label,
const VertexBufferHandle vtxbuf,
const ReductionType rtype);
/** */
AcVecReductionTestCase acCreateVecReductionTestCase(const char* label, const VertexBufferHandle a,
const VertexBufferHandle b,
const VertexBufferHandle c,
const ReductionType rtype);
/** */ /** */
AcResult acMeshSet(const AcReal value, AcMesh* mesh); AcResult acMeshSet(const AcReal value, AcMesh* mesh);
@@ -96,23 +59,18 @@ AcResult acMeshClear(AcMesh* mesh);
/** */ /** */
AcResult acModelIntegrateStep(AcMesh mesh, const AcReal dt); AcResult acModelIntegrateStep(AcMesh mesh, const AcReal dt);
/** TODO */ /** */
AcReal acModelReduceScal(const AcMesh mesh, const ReductionType rtype, const VertexBufferHandle a); AcReal acModelReduceScal(const AcMesh mesh, const ReductionType rtype, const VertexBufferHandle a);
/** TODO */ /** */
AcReal acModelReduceVec(const AcMesh mesh, const ReductionType rtype, const VertexBufferHandle a, AcReal acModelReduceVec(const AcMesh mesh, const ReductionType rtype, const VertexBufferHandle a,
const VertexBufferHandle b, const VertexBufferHandle c); const VertexBufferHandle b, const VertexBufferHandle c);
/** */ Error acGetError(const AcReal model, const AcReal candidate);
AcResult acVerifyMesh(const AcMesh model, const AcMesh candidate);
/** */ bool acEvalError(const char* label, const Error error);
AcResult acVerifyScalReductions(const AcMesh model, const AcScalReductionTestCase* testCases,
const size_t numCases);
/** */ AcResult acVerifyMesh(const char* label, const AcMesh model, const AcMesh candidate);
AcResult acVerifyVecReductions(const AcMesh model, const AcVecReductionTestCase* testCases,
const size_t numCases);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"

View File

@@ -42,7 +42,7 @@ main(void)
acInit(info); acInit(info);
acLoad(model); acLoad(model);
acStore(&candidate); acStore(&candidate);
acVerifyMesh(model, candidate); acVerifyMesh("Load/Store", model, candidate);
// Attempt to integrate and check max and min // Attempt to integrate and check max and min
printf("Integrating... "); printf("Integrating... ");

View File

@@ -41,7 +41,7 @@ main(void)
acInit(info); acInit(info);
acLoad(model); acLoad(model);
acStore(&candidate); acStore(&candidate);
acVerifyMesh(model, candidate); acVerifyMesh("Load/Store", model, candidate);
// Attempt to integrate and check max and min // Attempt to integrate and check max and min
printf("Integrating... "); printf("Integrating... ");

View File

@@ -21,12 +21,15 @@
*/ */
#include "astaroth.h" #include "astaroth.h"
#include "astaroth_utils.h" #include "astaroth_utils.h"
#include "errchk.h"
#if AC_MPI_ENABLED #if AC_MPI_ENABLED
#include <mpi.h> #include <mpi.h>
#include <vector> #include <vector>
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(*arr))
int int
main(void) main(void)
{ {
@@ -35,6 +38,9 @@ main(void)
MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
MPI_Comm_rank(MPI_COMM_WORLD, &pid); MPI_Comm_rank(MPI_COMM_WORLD, &pid);
// Set random seed for reproducibility
srand(321654987);
// CPU alloc // CPU alloc
AcMeshInfo info; AcMeshInfo info;
acLoadConfig(AC_DEFAULT_CONFIG, &info); acLoadConfig(AC_DEFAULT_CONFIG, &info);
@@ -50,57 +56,69 @@ main(void)
// GPU alloc & compute // GPU alloc & compute
acGridInit(info); acGridInit(info);
// INTEGRATION TESTS START --------------------------------------------------------------------- // Boundconds
acGridLoadMesh(STREAM_DEFAULT, model);
acGridPeriodicBoundconds(STREAM_DEFAULT);
acGridStoreMesh(STREAM_DEFAULT, &candidate);
if (pid == 0) {
acMeshApplyPeriodicBounds(&model);
const AcResult res = acVerifyMesh("Boundconds", model, candidate);
ERRCHK_ALWAYS(res == AC_SUCCESS);
acMeshRandomize(&model);
}
// Integration
acGridLoadMesh(STREAM_DEFAULT, model); acGridLoadMesh(STREAM_DEFAULT, model);
acGridIntegrate(STREAM_DEFAULT, FLT_EPSILON); acGridIntegrate(STREAM_DEFAULT, FLT_EPSILON);
acGridPeriodicBoundconds(STREAM_DEFAULT); acGridPeriodicBoundconds(STREAM_DEFAULT);
acGridStoreMesh(STREAM_DEFAULT, &candidate); acGridStoreMesh(STREAM_DEFAULT, &candidate);
if (pid == 0) { if (pid == 0) {
acModelIntegrateStep(model, FLT_EPSILON); acModelIntegrateStep(model, FLT_EPSILON);
acMeshApplyPeriodicBounds(&model); acMeshApplyPeriodicBounds(&model);
acVerifyMesh(model, candidate); const AcResult res = acVerifyMesh("Integration", model, candidate);
ERRCHK_ALWAYS(res == AC_SUCCESS);
acMeshRandomize(&model);
} }
// INTEGRATION TESTS END -----------------------------------------------------------------------
// REDUCTION TESTS START ----------------------------------------------------------------------- // Scalar reductions
acGridLoadMesh(STREAM_DEFAULT, model); acGridLoadMesh(STREAM_DEFAULT, model);
std::vector<AcScalReductionTestCase> scalarReductionTests{
acCreateScalReductionTestCase("Scalar MAX", VTXBUF_UUX, RTYPE_MAX),
acCreateScalReductionTestCase("Scalar MIN", VTXBUF_UUX, RTYPE_MIN),
/*
acCreateScalReductionTestCase("Scalar RMS", VTXBUF_UUX, RTYPE_RMS),
acCreateScalReductionTestCase("Scalar RMS_EXP", VTXBUF_UUX, RTYPE_RMS_EXP),
acCreateScalReductionTestCase("Scalar SUM", VTXBUF_UUX, RTYPE_SUM),
*/
};
std::vector<AcVecReductionTestCase> vectorReductionTests{
acCreateVecReductionTestCase("Vector MAX", VTXBUF_UUX, VTXBUF_UUY, VTXBUF_UUZ, RTYPE_MAX),
acCreateVecReductionTestCase("Vector MIN", VTXBUF_UUX, VTXBUF_UUY, VTXBUF_UUZ, RTYPE_MIN),
/*
acCreateVecReductionTestCase("Vector RMS", VTXBUF_UUX, VTXBUF_UUY, VTXBUF_UUZ, RTYPE_RMS),
acCreateVecReductionTestCase("Vector RMS_EXP", VTXBUF_UUX, VTXBUF_UUY, VTXBUF_UUZ,
RTYPE_RMS_EXP),
acCreateVecReductionTestCase("Vector SUM", VTXBUF_UUX, VTXBUF_UUY, VTXBUF_UUZ, RTYPE_SUM),
*/
};
// False positives due to too strict error bounds, skip the tests until we can determine a
// proper error bound
fprintf(stderr, "WARNING: RTYPE_RMS, RTYPE_RMS_EXP, and RTYPE_SUM tests skipped\n");
for (auto& testCase : scalarReductionTests) {
acGridReduceScal(STREAM_DEFAULT, testCase.rtype, testCase.vtxbuf, &testCase.candidate);
}
for (auto& testCase : vectorReductionTests) {
acGridReduceVec(STREAM_DEFAULT, testCase.rtype, testCase.a, testCase.b, testCase.c,
&testCase.candidate);
}
if (pid == 0) { if (pid == 0) {
acVerifyScalReductions(model, scalarReductionTests.data(), scalarReductionTests.size()); printf("---Test: Scalar reductions---\n");
acVerifyVecReductions(model, vectorReductionTests.data(), vectorReductionTests.size()); printf("Warning: testing only RTYPE_MAX and RTYPE_MIN\n");
}
for (size_t i = 0; i < 2; ++i) { // NOTE: 2 instead of NUM_RTYPES
const VertexBufferHandle v0 = VTXBUF_UUX;
AcReal candval;
acGridReduceScal(STREAM_DEFAULT, (ReductionType)i, v0, &candval);
if (pid == 0) {
const AcReal modelval = acModelReduceScal(model, (ReductionType)i, v0);
Error error = acGetError(modelval, candval);
error.maximum_magnitude = acModelReduceScal(model, RTYPE_MAX, v0);
error.minimum_magnitude = acModelReduceScal(model, RTYPE_MIN, v0);
ERRCHK_ALWAYS(acEvalError(rtype_names[i], error));
}
}
// Vector reductions
if (pid == 0) {
printf("---Test: Vector reductions---\n");
printf("Warning: testing only RTYPE_MAX and RTYPE_MIN\n");
}
for (size_t i = 0; i < 2; ++i) { // NOTE: 2 instead of NUM_RTYPES
const VertexBufferHandle v0 = VTXBUF_UUX;
const VertexBufferHandle v1 = VTXBUF_UUY;
const VertexBufferHandle v2 = VTXBUF_UUZ;
AcReal candval;
acGridReduceVec(STREAM_DEFAULT, (ReductionType)i, v0, v1, v2, &candval);
if (pid == 0) {
const AcReal modelval = acModelReduceVec(model, (ReductionType)i, v0, v1, v2);
Error error = acGetError(modelval, candval);
error.maximum_magnitude = acModelReduceVec(model, RTYPE_MAX, v0, v1, v2);
error.minimum_magnitude = acModelReduceVec(model, RTYPE_MIN, v0, v1, v1);
ERRCHK_ALWAYS(acEvalError(rtype_names[i], error));
}
} }
// REDUCTION TESTS END -------------------------------------------------------------------------
if (pid == 0) { if (pid == 0) {
acMeshDestroy(&model); acMeshDestroy(&model);

View File

@@ -245,7 +245,7 @@ check_reductions(const AcMeshInfo& config)
acLoad(*mesh); acLoad(*mesh);
for (int rtype = 0; rtype < NUM_REDUCTION_TYPES; ++rtype) { for (int rtype = 0; rtype < NUM_RTYPES; ++rtype) {
if (rtype == RTYPE_SUM) { if (rtype == RTYPE_SUM) {
// Skip SUM test for now. The failure is either caused by floating-point // Skip SUM test for now. The failure is either caused by floating-point

View File

@@ -4,6 +4,8 @@
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
#include "errchk.h"
#define max(a, b) ((a) > (b) ? (a) : (b)) #define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b)) #define min(a, b) ((a) < (b) ? (a) : (b))
@@ -26,7 +28,7 @@ is_valid(const AcReal a)
} }
Error Error
acGetError(AcReal model, AcReal candidate) acGetError(const AcReal model, const AcReal candidate)
{ {
Error error; Error error;
error.abs_error = 0; error.abs_error = 0;
@@ -57,9 +59,48 @@ acGetError(AcReal model, AcReal candidate)
error.rel_error = fabsl(1.0l - candidate / model) / machine_epsilon; error.rel_error = fabsl(1.0l - candidate / model) / machine_epsilon;
} }
error.maximum_magnitude = error.minimum_magnitude = 0;
return error; 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 static AcReal
get_maximum_magnitude(const AcReal* field, const AcMeshInfo info) 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 // 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) // zero (f.ex. 1e-19 and 1e-22 give error of around 1e4 ulps)
static Error static Error
get_max_abs_error(const VertexBufferHandle vtxbuf_handle, const AcMesh model_mesh, get_max_abs_error(const AcReal* model, const AcReal* candidate, const AcMeshInfo info)
const AcMesh candidate_mesh)
{ {
AcReal* model_vtxbuf = model_mesh.vertex_buffer[vtxbuf_handle]; Error error = {.abs_error = -1};
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]);
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) if (curr_error.abs_error > error.abs_error)
error = curr_error; error = curr_error;
} }
error.maximum_magnitude = get_maximum_magnitude(model, info);
error.handle = vtxbuf_handle; error.minimum_magnitude = get_minimum_magnitude(model, info);
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);
return error; 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. */ /** Returns true when successful, false if errors were found. */
AcResult 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"); 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) { for (int i = 0; i < NUM_VTXBUF_HANDLES; ++i) {
Error field_error = get_max_abs_error(i, model, candidate); const Error error = get_max_abs_error(model.vertex_buffer[i], candidate.vertex_buffer[i],
errors_found |= printErrorToScreen(field_error); 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" if (errors_found > 0)
: "Success. No errors found."); printf("Failure. Found %d errors\n", errors_found);
return errors_found ? AC_FAILURE : AC_SUCCESS; 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;
}