From 661b05cf9a1f175a5aac3fd7d0d27ed14be3cd63 Mon Sep 17 00:00:00 2001 From: mkuemmel Date: Fri, 5 Sep 2025 10:57:35 +0200 Subject: [PATCH 1/5] Includes a nan and inf test that works with fast-math Uses it for checking PSFEx files. --- .../SEFramework/Psf/SEPP_isnan_isinf.h | 76 +++++++++++++++++++ SEFramework/SEFramework/Psf/VariablePsf.h | 2 + SEFramework/src/lib/Psf/VariablePsf.cpp | 15 ++++ 3 files changed, 93 insertions(+) create mode 100644 SEFramework/SEFramework/Psf/SEPP_isnan_isinf.h diff --git a/SEFramework/SEFramework/Psf/SEPP_isnan_isinf.h b/SEFramework/SEFramework/Psf/SEPP_isnan_isinf.h new file mode 100644 index 000000000..248f84a6d --- /dev/null +++ b/SEFramework/SEFramework/Psf/SEPP_isnan_isinf.h @@ -0,0 +1,76 @@ +#ifndef _SEIMPLEMENTATION_PSF_SEPP_ISNAN_ISINF_H +#define _SEIMPLEMENTATION_PSF_SEPP_ISNAN_ISINF_H +//#include + +/* + * From: + * https://github.com/searchivarius/BlogCode/tree/2d947d8c42ab0e10f1f28a2ad036eee10389997a/2017/6/1 + * + * IEEE 754 compliant simple functions to test for NANs and INFs. + * This functions are necessary, b/c isnan doesn't work with -Ofast -ffast-math flags + * see http://searchivarius.org/blog/gcc-disables-isnan-and-isinf-when-compiling-ffast-math-flag + * + * See also test files. The file ./regular is compiled without -ffast-math flag and it + * checks for a large number of values that the output of my functions is the same + * as the output of standard functions. For single precision numbers, i.e., for floats + * these checks are exhaustive. That is, I go over the set of all 4B+ possible float values. + * For doubles, this is not possible, so I use tests where the lower 32 bits of mantissa + * are set to zero. + * + * Copyright (c) 2017 Leonid Boytsov + * + * This code is released under the + * Apache License Version 2.0 http://www.apache.org/licenses/. + * + */ + +// A mask to extract an exponent from the single-precision floating point number +// 01111111100000000000000000000000 +const unsigned FLOAT_EXP_MASK = 0x7F800000; +// A mask to extract a mantissa/fractional part from the single-precision floating point number +// 00000000011111111111111111111111 +const unsigned FLOAT_FRAC_PART_MASK = 0x7FFFFF; + +inline bool sepp_isnan(float x) { + union { + uint32_t u; + float f; + } conv; + conv.f = x; + return ((conv.u & FLOAT_EXP_MASK) == FLOAT_EXP_MASK) && ((conv.u & FLOAT_FRAC_PART_MASK) != 0); +}; + +inline bool sepp_isinf(float x) { + union { + uint32_t u; + float f; + } conv; + conv.f = x; + return ((conv.u & FLOAT_EXP_MASK) == FLOAT_EXP_MASK) && ((conv.u & FLOAT_FRAC_PART_MASK) == 0); +}; + +// 0111 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 +const uint64_t DOUBLE_EXP_MASK = 0x7FF0000000000000ul; +// 0000 0000 0000 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 +const uint64_t DOUBLE_FRAC_PART_MASK = 0x000FFFFFFFFFFFFFul; + +inline bool sepp_isnan(double x) { + union { + uint64_t u; + double f; + } conv; + conv.f = x; + return ((conv.u & DOUBLE_EXP_MASK) == DOUBLE_EXP_MASK) && ((conv.u & DOUBLE_FRAC_PART_MASK) != 0); +}; + + +inline bool sepp_isinf(double x) { + union { + uint64_t u; + double f; + } conv; + conv.f = x; + return ((conv.u & DOUBLE_EXP_MASK) == DOUBLE_EXP_MASK) && ((conv.u & DOUBLE_FRAC_PART_MASK) == 0); +}; + +#endif diff --git a/SEFramework/SEFramework/Psf/VariablePsf.h b/SEFramework/SEFramework/Psf/VariablePsf.h index f50bda38e..57a6f4fa9 100644 --- a/SEFramework/SEFramework/Psf/VariablePsf.h +++ b/SEFramework/SEFramework/Psf/VariablePsf.h @@ -24,6 +24,8 @@ #ifndef _SEIMPLEMENTATION_PSF_VARIABLEPSF_H_ #define _SEIMPLEMENTATION_PSF_VARIABLEPSF_H_ +#include + #include "SEFramework/Image/VectorImage.h" #include "SEFramework/Psf/Psf.h" diff --git a/SEFramework/src/lib/Psf/VariablePsf.cpp b/SEFramework/src/lib/Psf/VariablePsf.cpp index c8367c350..62bfc7cd8 100644 --- a/SEFramework/src/lib/Psf/VariablePsf.cpp +++ b/SEFramework/src/lib/Psf/VariablePsf.cpp @@ -21,10 +21,13 @@ * Author: Alejandro Álvarez Ayllón */ +#include #include #include #include "SEFramework/Psf/VariablePsf.h" +#include "SEFramework/Psf/SEPP_isnan_isinf.h" +static auto stack_logger = Elements::Logging::getLogger("PSFExPsf"); namespace SourceXtractor { @@ -94,6 +97,8 @@ void VariablePsf::selfTest() { if (m_coefficients.size() == 0) { throw Elements::Exception() << "A variable PSF needs at least one set of coefficients"; } + // give some feedback + stack_logger.info() << "In VariablePsf::selfTest()"; // Pre-condition: There is a degree value per unique group std::vector n_component_per_group(m_group_degrees.size()); @@ -130,6 +135,16 @@ void VariablePsf::selfTest() { if (coeff->getWidth() != psf_width || coeff->getHeight() != psf_height) { throw Elements::Exception() << "Malformed variable PSF, coefficient matrices do not have the same dimensions"; } + for (auto x = 0; x < psf_width; ++x) { + for (auto y = 0; y < psf_height; ++y) { + if (sepp_isnan(coeff->at(x, y))) { + throw Elements::Exception() << "Malformed variable PSF, coefficient matrices contains NANs"; + } + else if (sepp_isnan(coeff->at(x, y))) { + throw Elements::Exception() << "Malformed variable PSF, coefficient matrices contains INFs"; + } + } + } } } From cf0f9c80895f7e3423cd624966ad3ba467d6a1ba Mon Sep 17 00:00:00 2001 From: mkuemmel Date: Sat, 20 Sep 2025 23:08:41 +0200 Subject: [PATCH 2/5] Pushed some feedback to debug --- SEFramework/src/lib/Psf/VariablePsf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SEFramework/src/lib/Psf/VariablePsf.cpp b/SEFramework/src/lib/Psf/VariablePsf.cpp index 62bfc7cd8..51d9c2439 100644 --- a/SEFramework/src/lib/Psf/VariablePsf.cpp +++ b/SEFramework/src/lib/Psf/VariablePsf.cpp @@ -98,7 +98,7 @@ void VariablePsf::selfTest() { throw Elements::Exception() << "A variable PSF needs at least one set of coefficients"; } // give some feedback - stack_logger.info() << "In VariablePsf::selfTest()"; + stack_logger.debug() << "In VariablePsf::selfTest()"; // Pre-condition: There is a degree value per unique group std::vector n_component_per_group(m_group_degrees.size()); From a092f362fab6482817a273109bb8cfb764a95e1d Mon Sep 17 00:00:00 2001 From: Marc Schefer Date: Fri, 26 Sep 2025 16:46:52 +0200 Subject: [PATCH 3/5] moved isnan function to SEUtils, small fixes --- SEFramework/SEFramework/Psf/VariablePsf.h | 2 -- SEFramework/src/lib/Psf/VariablePsf.cpp | 25 +++++++++---------- .../SEUtils/IsNan.h | 20 +++++++++------ 3 files changed, 24 insertions(+), 23 deletions(-) rename SEFramework/SEFramework/Psf/SEPP_isnan_isinf.h => SEUtils/SEUtils/IsNan.h (89%) diff --git a/SEFramework/SEFramework/Psf/VariablePsf.h b/SEFramework/SEFramework/Psf/VariablePsf.h index 57a6f4fa9..f50bda38e 100644 --- a/SEFramework/SEFramework/Psf/VariablePsf.h +++ b/SEFramework/SEFramework/Psf/VariablePsf.h @@ -24,8 +24,6 @@ #ifndef _SEIMPLEMENTATION_PSF_VARIABLEPSF_H_ #define _SEIMPLEMENTATION_PSF_VARIABLEPSF_H_ -#include - #include "SEFramework/Image/VectorImage.h" #include "SEFramework/Psf/Psf.h" diff --git a/SEFramework/src/lib/Psf/VariablePsf.cpp b/SEFramework/src/lib/Psf/VariablePsf.cpp index 51d9c2439..6e66e98a9 100644 --- a/SEFramework/src/lib/Psf/VariablePsf.cpp +++ b/SEFramework/src/lib/Psf/VariablePsf.cpp @@ -21,11 +21,12 @@ * Author: Alejandro Álvarez Ayllón */ +#include #include #include -#include +#include "SEUtils/IsNan.h" + #include "SEFramework/Psf/VariablePsf.h" -#include "SEFramework/Psf/SEPP_isnan_isinf.h" static auto stack_logger = Elements::Logging::getLogger("PSFExPsf"); @@ -97,8 +98,6 @@ void VariablePsf::selfTest() { if (m_coefficients.size() == 0) { throw Elements::Exception() << "A variable PSF needs at least one set of coefficients"; } - // give some feedback - stack_logger.debug() << "In VariablePsf::selfTest()"; // Pre-condition: There is a degree value per unique group std::vector n_component_per_group(m_group_degrees.size()); @@ -135,15 +134,15 @@ void VariablePsf::selfTest() { if (coeff->getWidth() != psf_width || coeff->getHeight() != psf_height) { throw Elements::Exception() << "Malformed variable PSF, coefficient matrices do not have the same dimensions"; } - for (auto x = 0; x < psf_width; ++x) { - for (auto y = 0; y < psf_height; ++y) { - if (sepp_isnan(coeff->at(x, y))) { - throw Elements::Exception() << "Malformed variable PSF, coefficient matrices contains NANs"; - } - else if (sepp_isnan(coeff->at(x, y))) { - throw Elements::Exception() << "Malformed variable PSF, coefficient matrices contains INFs"; - } - } + for (auto x = 0; x < psf_width; ++x){ + for (auto y = 0; y < psf_height; ++y) { + if (fastmath_isnan(coeff->at(x, y))) { + throw Elements::Exception() << "Malformed variable PSF, coefficient matrices contains NANs"; + } + else if (fastmath_isinf(coeff->at(x, y))){ + throw Elements::Exception() << "Malformed variable PSF, coefficient matrices contains INFs"; + } + } } } } diff --git a/SEFramework/SEFramework/Psf/SEPP_isnan_isinf.h b/SEUtils/SEUtils/IsNan.h similarity index 89% rename from SEFramework/SEFramework/Psf/SEPP_isnan_isinf.h rename to SEUtils/SEUtils/IsNan.h index 248f84a6d..c4dea6479 100644 --- a/SEFramework/SEFramework/Psf/SEPP_isnan_isinf.h +++ b/SEUtils/SEUtils/IsNan.h @@ -1,6 +1,9 @@ -#ifndef _SEIMPLEMENTATION_PSF_SEPP_ISNAN_ISINF_H -#define _SEIMPLEMENTATION_PSF_SEPP_ISNAN_ISINF_H -//#include +#ifndef _SEUTILS_ISNAN_H +#define _SEUTILS_ISNAN_H + +#include + +namespace SourceXtractor { /* * From: @@ -31,7 +34,7 @@ const unsigned FLOAT_EXP_MASK = 0x7F800000; // 00000000011111111111111111111111 const unsigned FLOAT_FRAC_PART_MASK = 0x7FFFFF; -inline bool sepp_isnan(float x) { +inline bool fastmath_isnan(float x) { union { uint32_t u; float f; @@ -40,7 +43,7 @@ inline bool sepp_isnan(float x) { return ((conv.u & FLOAT_EXP_MASK) == FLOAT_EXP_MASK) && ((conv.u & FLOAT_FRAC_PART_MASK) != 0); }; -inline bool sepp_isinf(float x) { +inline bool fastmath_isinf(float x) { union { uint32_t u; float f; @@ -54,7 +57,7 @@ const uint64_t DOUBLE_EXP_MASK = 0x7FF0000000000000ul; // 0000 0000 0000 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 const uint64_t DOUBLE_FRAC_PART_MASK = 0x000FFFFFFFFFFFFFul; -inline bool sepp_isnan(double x) { +inline bool fastmath_isnan(double x) { union { uint64_t u; double f; @@ -63,8 +66,7 @@ inline bool sepp_isnan(double x) { return ((conv.u & DOUBLE_EXP_MASK) == DOUBLE_EXP_MASK) && ((conv.u & DOUBLE_FRAC_PART_MASK) != 0); }; - -inline bool sepp_isinf(double x) { +inline bool fastmath_isinf(double x) { union { uint64_t u; double f; @@ -73,4 +75,6 @@ inline bool sepp_isinf(double x) { return ((conv.u & DOUBLE_EXP_MASK) == DOUBLE_EXP_MASK) && ((conv.u & DOUBLE_FRAC_PART_MASK) == 0); }; +} // namespace SourceXtractor + #endif From 6aea35fabb0ff6201b51baa8ca81ac6bd27e7e49 Mon Sep 17 00:00:00 2001 From: Marc Schefer Date: Fri, 26 Sep 2025 17:03:04 +0200 Subject: [PATCH 4/5] print filename of PSF that doesn't load --- SEImplementation/src/lib/Plugin/Psf/PsfPluginConfig.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/SEImplementation/src/lib/Plugin/Psf/PsfPluginConfig.cpp b/SEImplementation/src/lib/Plugin/Psf/PsfPluginConfig.cpp index 462835f93..640cfa654 100644 --- a/SEImplementation/src/lib/Plugin/Psf/PsfPluginConfig.cpp +++ b/SEImplementation/src/lib/Plugin/Psf/PsfPluginConfig.cpp @@ -124,10 +124,11 @@ static std::shared_ptr readPsfEx(std::unique_ptr &pFi } return std::make_shared(pixel_sampling, components, group_degrees, coefficients); - } catch (CCfits::FITS::NoSuchHDU&) { - throw; } catch (CCfits::FitsException &e) { throw Elements::Exception() << "Error loading PSFEx file: " << e.message(); + } catch (...) { + logger.error() << "Failed loading a psf file: " << pFits->name(); + throw; } } From 13905a58a51228cdada6406de7cf167188b66b84 Mon Sep 17 00:00:00 2001 From: Marc Schefer Date: Mon, 29 Sep 2025 14:56:35 +0200 Subject: [PATCH 5/5] fix regression --- SEImplementation/src/lib/Plugin/Psf/PsfPluginConfig.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SEImplementation/src/lib/Plugin/Psf/PsfPluginConfig.cpp b/SEImplementation/src/lib/Plugin/Psf/PsfPluginConfig.cpp index 640cfa654..e0c61a9da 100644 --- a/SEImplementation/src/lib/Plugin/Psf/PsfPluginConfig.cpp +++ b/SEImplementation/src/lib/Plugin/Psf/PsfPluginConfig.cpp @@ -124,6 +124,8 @@ static std::shared_ptr readPsfEx(std::unique_ptr &pFi } return std::make_shared(pixel_sampling, components, group_degrees, coefficients); + } catch (CCfits::FITS::NoSuchHDU&) { // Make sure we propagate this specific exception + throw; } catch (CCfits::FitsException &e) { throw Elements::Exception() << "Error loading PSFEx file: " << e.message(); } catch (...) {