#ifndef __SIMPLE_FFT__CHECK_FFT_HPP__ #define __SIMPLE_FFT__CHECK_FFT_HPP__ #include "fft_settings.h" #include "error_handling.hpp" #include "copy_array.hpp" #include #include #include using std::size_t; namespace simple_fft { namespace check_fft_private { enum CheckMode { CHECK_FFT_PARSEVAL, CHECK_FFT_ENERGY, CHECK_FFT_EQUALITY }; template void getMaxAbsoluteAndRelativeErrorNorms(const TArray1D & array1, const TComplexArray1D & array2, const size_t size, real_type & max_absolute_error_norm, real_type & max_relative_error_norm) { using std::abs; real_type current_error; // NOTE: no parallelization here, it is a completely sequential loop! for(size_t i = 0; i < size; ++i) { #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR current_error = abs(array1[i] - array2[i]); #else current_error = abs(array1(i) - array2(i)); #endif if (current_error > max_absolute_error_norm) { max_absolute_error_norm = current_error; #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR if (abs(array1[i]) > abs(array2[i])) { max_relative_error_norm = (abs(array1[i]) > 1e-20 ? max_absolute_error_norm / abs(array1[i]) : 0.0); } else { max_relative_error_norm = (abs(array2[i]) > 1e-20 ? max_absolute_error_norm / abs(array2[i]) : 0.0); } #else if (abs(array1(i)) > abs(array2(i))) { max_relative_error_norm = (abs(array1(i)) > 1e-20 ? max_absolute_error_norm / abs(array1(i)) : 0.0); } else { max_relative_error_norm = (abs(array2(i)) > 1e-20 ? max_absolute_error_norm / abs(array2(i)) : 0.0); } #endif } } } template void getMaxAbsoluteAndRelativeErrorNorms(const TArray2D & array1, const TComplexArray2D & array2, const size_t size1, const size_t size2, real_type & max_absolute_error_norm, real_type & max_relative_error_norm) { using std::abs; real_type current_error; // NOTE: no parallelization here, it is a completely sequential loop! for(int i = 0; i < static_cast(size1); ++i) { for(int j = 0; j < static_cast(size2); ++j) { #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR current_error = abs(array1[i][j] - array2[i][j]); #else current_error = abs(array1(i,j) - array2(i,j)); #endif if (current_error > max_absolute_error_norm) { max_absolute_error_norm = current_error; #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR if (abs(array1[i][j]) > abs(array2[i][j])) { max_relative_error_norm = (abs(array1[i][j]) > 1e-20 ? max_absolute_error_norm / abs(array1[i][j]) : 0.0); } else { max_relative_error_norm = (abs(array2[i][j]) > 1e-20 ? max_absolute_error_norm / abs(array2[i][j]) : 0.0); } #else if (abs(array1(i,j)) > abs(array2(i,j))) { max_relative_error_norm = (abs(array1(i,j)) > 1e-20 ? max_absolute_error_norm / abs(array1(i,j)) : 0.0); } else { max_relative_error_norm = (abs(array2(i,j)) > 1e-20 ? max_absolute_error_norm / abs(array2(i,j)) : 0.0); } #endif } } } } template void getMaxAbsoluteAndRelativeErrorNorms(const TArray3D & array1, const TComplexArray3D & array2, const size_t size1, const size_t size2, const size_t size3, real_type & max_absolute_error_norm, real_type & max_relative_error_norm) { using std::abs; real_type current_error; // NOTE: no parallelization here, it is a completely sequential loop! for(int i = 0; i < static_cast(size1); ++i) { for(int j = 0; j < static_cast(size2); ++j) { for(int k = 0; k < static_cast(size3); ++k) { #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR current_error = abs(array1[i][j][k] - array2[i][j][k]); #else current_error = abs(array1(i,j,k) - array2(i,j,k)); #endif if (current_error > max_absolute_error_norm) { max_absolute_error_norm = current_error; #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR if (abs(array1[i][j][k]) > abs(array2[i][j][k])) { max_relative_error_norm = (abs(array1[i][j][k]) > 1e-20 ? max_absolute_error_norm / abs(array1[i][j][k]) : 0.0); } else { max_relative_error_norm = (abs(array2[i][j][k]) > 1e-20 ? max_absolute_error_norm / abs(array2[i][j][k]) : 0.0); } #else if (abs(array1(i,j,k)) > abs(array2(i,j,k))) { max_relative_error_norm = (abs(array1(i,j,k)) > 1e-20 ? max_absolute_error_norm / abs(array1(i,j,k)) : 0.0); } else { max_relative_error_norm = (abs(array2(i,j,k)) > 1e-20 ? max_absolute_error_norm / abs(array2(i,j,k)) : 0.0); } #endif } } } } } template real_type squareAbsAccumulate(const TArray1D & array, const size_t size, const real_type init) { int size_signed = static_cast(size); real_type sum = init; using std::abs; #ifndef __clang__ #ifdef __USE_OPENMP #pragma omp parallel for reduction(+:sum) #endif #endif for(int i = 0; i < size_signed; ++i) { #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR sum += abs(array[i] * array[i]); #else sum += abs(array(i) * array(i)); #endif } return sum; } template real_type squareAbsAccumulate(const TArray2D & array, const size_t size1, const size_t size2, const real_type init) { int size1_signed = static_cast(size1); int size2_signed = static_cast(size2); real_type sum = init; using std::abs; #ifndef __clang__ #ifdef __USE_OPENMP #pragma omp parallel for reduction(+:sum) #endif #endif for(int i = 0; i < size1_signed; ++i) { for(int j = 0; j < size2_signed; ++j) { #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR sum += abs(array[i][j] * array[i][j]); #else sum += abs(array(i,j) * array(i,j)); #endif } } return sum; } template real_type squareAbsAccumulate(const TArray3D & array, const size_t size1, const size_t size2, const size_t size3, const real_type init) { int size1_signed = static_cast(size1); int size2_signed = static_cast(size2); int size3_signed = static_cast(size3); real_type sum = init; using std::abs; #ifndef __clang__ #ifdef __USE_OPENMP #pragma omp parallel for reduction(+:sum) #endif #endif for(int i = 0; i < size1_signed; ++i) { for(int j = 0; j < size2_signed; ++j) { for(int k = 0; k < size3_signed; ++k) { #ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR sum += abs(array[i][j][k] * array[i][j][k]); #else sum += abs(array(i,j,k) * array(i,j,k)); #endif } } } return sum; } // Generic template for CCheckFFT struct followed by its explicit specializations // for certain numbers of dimensions. TArray can be either of real or complex type. // The technique is similar to the one applied for CFFT struct. template struct CCheckFFT {}; template struct CCheckFFT { static bool check_fft(const TArray1D & data_before, const TComplexArray1D & data_after, const size_t size, const real_type relative_tolerance, real_type & discrepancy, const CheckMode check_mode, const char *& error_description) { using namespace error_handling; if(0 == size) { GetErrorDescription(EC_NUM_OF_ELEMS_IS_ZERO, error_description); return false; } if ( (CHECK_FFT_PARSEVAL != check_mode) && (CHECK_FFT_ENERGY != check_mode) && (CHECK_FFT_EQUALITY != check_mode) ) { GetErrorDescription(EC_WRONG_CHECK_FFT_MODE, error_description); return false; } if (CHECK_FFT_EQUALITY != check_mode) { real_type sum_before = squareAbsAccumulate(data_before, size, 0.0); real_type sum_after = squareAbsAccumulate(data_after, size, 0.0); if (CHECK_FFT_PARSEVAL == check_mode) { sum_after /= size; } using std::abs; discrepancy = abs(sum_before - sum_after); if (discrepancy / ((sum_before < 1e-20) ? (sum_before + 1e-20) : sum_before) > relative_tolerance) { GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description); return false; } else { return true; } } else { real_type relative_error; getMaxAbsoluteAndRelativeErrorNorms(data_before, data_after, size, discrepancy, relative_error); if (relative_error < relative_tolerance) { return true; } else { GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description); return false; } } } }; template struct CCheckFFT { static bool check_fft(const TArray2D & data_before, const TComplexArray2D & data_after, const size_t size1, const size_t size2, const real_type relative_tolerance, real_type & discrepancy, const CheckMode check_mode, const char *& error_description) { using namespace error_handling; if( (0 == size1) || (0 == size2) ) { GetErrorDescription(EC_NUM_OF_ELEMS_IS_ZERO, error_description); return false; } if ( (CHECK_FFT_PARSEVAL != check_mode) && (CHECK_FFT_ENERGY != check_mode) && (CHECK_FFT_EQUALITY != check_mode) ) { GetErrorDescription(EC_WRONG_CHECK_FFT_MODE, error_description); return false; } if (CHECK_FFT_EQUALITY != check_mode) { real_type sum_before = squareAbsAccumulate(data_before, size1, size2, 0.0); real_type sum_after = squareAbsAccumulate(data_after, size1, size2, 0.0); if (CHECK_FFT_PARSEVAL == check_mode) { sum_after /= size1 * size2; } using std::abs; discrepancy = abs(sum_before - sum_after); if (discrepancy / ((sum_before < 1e-20) ? (sum_before + 1e-20) : sum_before) > relative_tolerance) { GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description); return false; } else { return true; } } else { real_type relative_error; getMaxAbsoluteAndRelativeErrorNorms(data_before, data_after, size1, size2, discrepancy, relative_error); if (relative_error < relative_tolerance) { return true; } else { GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description); return false; } } } }; template struct CCheckFFT { static bool check_fft(const TArray3D & data_before, const TComplexArray3D & data_after, const size_t size1, const size_t size2, const size_t size3, const real_type relative_tolerance, real_type & discrepancy, const CheckMode check_mode, const char *& error_description) { using namespace error_handling; if( (0 == size1) || (0 == size2) || (0 == size3) ) { GetErrorDescription(EC_NUM_OF_ELEMS_IS_ZERO, error_description); return false; } if ( (CHECK_FFT_PARSEVAL != check_mode) && (CHECK_FFT_ENERGY != check_mode) && (CHECK_FFT_EQUALITY != check_mode) ) { GetErrorDescription(EC_WRONG_CHECK_FFT_MODE, error_description); return false; } if (CHECK_FFT_EQUALITY != check_mode) { real_type sum_before = squareAbsAccumulate(data_before, size1, size2, size3, 0.0); real_type sum_after = squareAbsAccumulate(data_after, size1, size2, size3, 0.0); if (CHECK_FFT_PARSEVAL == check_mode) { sum_after /= size1 * size2 * size3; } using std::abs; discrepancy = abs(sum_before - sum_after); if (discrepancy / ((sum_before < 1e-20) ? (sum_before + 1e-20) : sum_before) > relative_tolerance) { GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description); return false; } else { return true; } } else { real_type relative_error; getMaxAbsoluteAndRelativeErrorNorms(data_before, data_after, size1, size2, size3, discrepancy, relative_error); if (relative_error < relative_tolerance) { return true; } else { GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description); return false; } } } }; } // namespace check_fft_private namespace check_fft { template bool checkParsevalTheorem(const TArray1D & data_before_FFT, const TComplexArray1D & data_after_FFT, const size_t size, const real_type relative_tolerance, real_type & discrepancy, const char *& error_description) { return check_fft_private::CCheckFFT::check_fft(data_before_FFT, data_after_FFT, size, relative_tolerance, discrepancy, check_fft_private::CHECK_FFT_PARSEVAL, error_description); } template bool checkParsevalTheorem(const TArray2D & data_before_FFT, const TComplexArray2D & data_after_FFT, const size_t size1, const size_t size2, const real_type relative_tolerance, real_type & discrepancy, const char *& error_description) { return check_fft_private::CCheckFFT::check_fft(data_before_FFT, data_after_FFT, size1, size2, relative_tolerance, discrepancy, check_fft_private::CHECK_FFT_PARSEVAL, error_description); } template bool checkParsevalTheorem(const TArray3D & data_before_FFT, const TComplexArray3D & data_after_FFT, const size_t size1, const size_t size2, const size_t size3, const real_type relative_tolerance, real_type & discrepancy, const char *& error_description) { return check_fft_private::CCheckFFT::check_fft(data_before_FFT, data_after_FFT, size1, size2, size3, relative_tolerance, discrepancy, check_fft_private::CHECK_FFT_PARSEVAL, error_description); } template bool checkEnergyConservation(const TArray1D & data_before_FFT, const TComplexArray1D & data_after_FFT_and_IFFT, const size_t size, const real_type relative_tolerance, real_type & discrepancy, const char *& error_description) { return check_fft_private::CCheckFFT::check_fft(data_before_FFT, data_after_FFT_and_IFFT, size, relative_tolerance, discrepancy, check_fft_private::CHECK_FFT_ENERGY, error_description); } template bool checkEnergyConservation(const TArray2D & data_before_FFT, const TComplexArray2D & data_after_FFT_and_IFFT, const size_t size1, const size_t size2, const real_type relative_tolerance, real_type & discrepancy, const char *& error_description) { return check_fft_private::CCheckFFT::check_fft(data_before_FFT, data_after_FFT_and_IFFT, size1, size2, relative_tolerance, discrepancy, check_fft_private::CHECK_FFT_ENERGY, error_description); } template bool checkEnergyConservation(const TArray3D & data_before_FFT, const TComplexArray3D & data_after_FFT_and_IFFT, const size_t size1, const size_t size2, const size_t size3, const real_type relative_tolerance, real_type & discrepancy, const char *& error_description) { return check_fft_private::CCheckFFT::check_fft(data_before_FFT, data_after_FFT_and_IFFT, size1, size2, size3, relative_tolerance, discrepancy, check_fft_private::CHECK_FFT_ENERGY, error_description); } template bool checkEquality(const TArray1D & data_before_FFT, const TComplexArray1D & data_after_FFT_and_IFFT, const size_t size, const real_type relative_tolerance, real_type & discrepancy, const char *& error_description) { return check_fft_private::CCheckFFT::check_fft(data_before_FFT, data_after_FFT_and_IFFT, size, relative_tolerance, discrepancy, check_fft_private::CHECK_FFT_EQUALITY, error_description); } template bool checkEquality(const TArray2D & data_before_FFT, const TComplexArray2D & data_after_FFT_and_IFFT, const size_t size1, const size_t size2, const real_type relative_tolerance, real_type & discrepancy, const char *& error_description) { return check_fft_private::CCheckFFT::check_fft(data_before_FFT, data_after_FFT_and_IFFT, size1, size2, relative_tolerance, discrepancy, check_fft_private::CHECK_FFT_EQUALITY, error_description); } template bool checkEquality(const TArray3D & data_before_FFT, const TComplexArray3D & data_after_FFT_and_IFFT, const size_t size1, const size_t size2, const size_t size3, const real_type relative_tolerance, real_type & discrepancy, const char *& error_description) { return check_fft_private::CCheckFFT::check_fft(data_before_FFT, data_after_FFT_and_IFFT, size1, size2, size3, relative_tolerance, discrepancy, check_fft_private::CHECK_FFT_EQUALITY, error_description); } } // namespace check_fft } // namespace simple_fft #endif // __SIMPLE_FFT__CHECK_FFT_HPP__