Skip to content

Commit 42b0bd5

Browse files
committed
Merge branch 'feature/max_files_option' of github.com:astrorama/SourceXtractorPlusPlus into feature/max_files_option
2 parents 03cd776 + 6d7ce09 commit 42b0bd5

File tree

21 files changed

+731
-97
lines changed

21 files changed

+731
-97
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ __pycache__
1111
/packages
1212
/doc/build/
1313
/build/
14+
.vscode

SEBenchmarks/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ elements_add_executable(BenchRendering src/program/BenchRendering.cpp
5656
LINK_LIBRARIES SEFramework SEImplementation ${Boost_LIBRARIES})
5757
elements_add_executable(BenchBackgroundModel src/program/BenchBackgroundModel.cpp
5858
LINK_LIBRARIES SEFramework SEImplementation ${Boost_LIBRARIES})
59+
elements_add_executable(BenchVariablePsfStack src/program/BenchVariablePsfStack.cpp
60+
LINK_LIBRARIES SEFramework ${Boost_LIBRARIES})
5961

6062
#===============================================================================
6163
# Declare the Boost tests here
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/** Copyright © 2019-2025 Université de Genève, LMU Munich - Faculty of Physics, IAP-CNRS/Sorbonne Université
2+
*
3+
* This library is free software; you can redistribute it and/or modify it under
4+
* the terms of the GNU Lesser General Public License as published by the Free
5+
* Software Foundation; either version 3.0 of the License, or (at your option)
6+
* any later version.
7+
*
8+
* This library is distributed in the hope that it will be useful, but WITHOUT
9+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10+
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11+
* details.
12+
*
13+
* You should have received a copy of the GNU Lesser General Public License
14+
* along with this library; if not, write to the Free Software Foundation, Inc.,
15+
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16+
*/
17+
18+
/**
19+
* @file src/program/BenchVariablePsfStack.cpp
20+
* @date 06/27/25
21+
* @author marc schefer
22+
*/
23+
24+
#include <map>
25+
#include <string>
26+
#include <random>
27+
28+
#include <boost/program_options.hpp>
29+
#include <boost/timer/timer.hpp>
30+
#include "ElementsKernel/ProgramHeaders.h"
31+
#include "ElementsKernel/Real.h"
32+
#include "SEFramework/Psf/VariablePsfStack.h"
33+
34+
namespace po = boost::program_options;
35+
namespace timer = boost::timer;
36+
using namespace SourceXtractor;
37+
38+
static Elements::Logging logger = Elements::Logging::getLogger("BenchVariablePsfStack");
39+
40+
class BenchVariablePsfStack : public Elements::Program {
41+
private:
42+
std::default_random_engine random_generator;
43+
std::uniform_real_distribution<double> random_dist{0.0, 1000.0};
44+
45+
public:
46+
47+
po::options_description defineSpecificProgramOptions() override {
48+
po::options_description options{};
49+
options.add_options()
50+
("iterations", po::value<int>()->default_value(100000), "Number of getPsf calls to benchmark")
51+
("measures", po::value<int>()->default_value(3), "Number of timing measurements to take")
52+
("fits-file", po::value<std::string>()->default_value(""), "FITS file containing PSF stack");
53+
return options;
54+
}
55+
56+
Elements::ExitCode mainMethod(std::map<std::string, po::variable_value> &args) override {
57+
58+
auto iterations = args["iterations"].as<int>();
59+
auto measures = args["measures"].as<int>();
60+
auto fits_file = args["fits-file"].as<std::string>();
61+
62+
logger.info() << "Benchmarking VariablePsfStack::getPsf() with " << iterations << " iterations";
63+
logger.info() << "Taking " << measures << " timing measurements";
64+
65+
// Initialize VariablePsfStack with FITS file if provided, otherwise nullptr
66+
std::shared_ptr<CCfits::FITS> fitsPtr = nullptr;
67+
if (!fits_file.empty()) {
68+
try {
69+
fitsPtr = std::make_shared<CCfits::FITS>(fits_file);
70+
logger.info() << "Using FITS file: " << fits_file;
71+
} catch (const std::exception& e) {
72+
logger.error() << "Failed to load FITS file '" << fits_file << "': " << e.what();
73+
return Elements::ExitCode::DATAERR;
74+
}
75+
} else {
76+
logger.error() << "No FITS file provided";
77+
return Elements::ExitCode::USAGE;
78+
}
79+
80+
try {
81+
VariablePsfStack psfStack(fitsPtr);
82+
83+
logger.info() << "VariablePsfStack loaded successfully with " << psfStack.getNumberOfPsfs() << " PSFs";
84+
logger.info() << "PSF size: " << psfStack.getWidth() << "x" << psfStack.getHeight();
85+
logger.info() << "Pixel sampling: " << psfStack.getPixelSampling();
86+
87+
std::cout << "Iterations,Measurement,Time_nanoseconds" << std::endl;
88+
89+
for (int m = 0; m < measures; ++m) {
90+
logger.info() << "Measurement " << (m + 1) << "/" << measures;
91+
92+
timer::cpu_timer timer;
93+
timer.stop();
94+
95+
// Prepare test values for getPsf calls
96+
std::vector<std::vector<double>> testValues;
97+
testValues.reserve(iterations);
98+
99+
for (int i = 0; i < iterations; ++i) {
100+
testValues.push_back({random_dist(random_generator), random_dist(random_generator)});
101+
}
102+
103+
// Start timing
104+
timer.start();
105+
106+
for (int i = 0; i < iterations; ++i) {
107+
try {
108+
auto psf = psfStack.getPsf(testValues[i]);
109+
// Prevent compiler optimization by using the result
110+
volatile auto width = psf->getWidth();
111+
(void)width; // Suppress unused variable warning
112+
} catch (const std::exception& e) {
113+
// Expected to fail with nullptr, but we still measure the timing
114+
// until the exception is thrown
115+
}
116+
}
117+
118+
timer.stop();
119+
120+
auto elapsed_ns = timer.elapsed().wall;
121+
std::cout << iterations << "," << (m + 1) << "," << elapsed_ns << std::endl;
122+
123+
logger.info() << "Time for " << iterations << " calls: " << (elapsed_ns / 1e9) << " seconds";
124+
logger.info() << "Average time per call: " << (elapsed_ns / iterations) << " nanoseconds";
125+
}
126+
127+
} catch (const std::exception& e) {
128+
logger.error() << "Error initializing VariablePsfStack: " << e.what();
129+
return Elements::ExitCode::DATAERR;
130+
}
131+
132+
return Elements::ExitCode::OK;
133+
}
134+
};
135+
136+
MAIN_FOR(BenchVariablePsfStack)

SEFramework/SEFramework/CoordinateSystem/CoordinateSystem.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,30 @@ struct ImageCoordinate {
4545

4646
ImageCoordinate() : m_x(0), m_y(0) {}
4747
ImageCoordinate(double x, double y) : m_x(x), m_y(y) {}
48+
49+
ImageCoordinate operator*(double scalar) const {
50+
return ImageCoordinate(m_x * scalar, m_y * scalar);
51+
}
52+
53+
ImageCoordinate operator+(const ImageCoordinate& other) const {
54+
return ImageCoordinate(m_x + other.m_x, m_y + other.m_y);
55+
}
56+
57+
ImageCoordinate& operator+=(const ImageCoordinate& other) {
58+
m_x += other.m_x;
59+
m_y += other.m_y;
60+
return *this;
61+
}
62+
63+
ImageCoordinate operator-(const ImageCoordinate& other) const {
64+
return ImageCoordinate(m_x - other.m_x, m_y - other.m_y);
65+
}
66+
67+
ImageCoordinate& operator-=(const ImageCoordinate& other) {
68+
m_x -= other.m_x;
69+
m_y -= other.m_y;
70+
return *this;
71+
}
4872
};
4973

5074
class CoordinateSystem {

SEFramework/SEFramework/Output/OutputRegistry.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ class OutputRegistry {
169169
return converter(source.getProperty<PropertyType>(i));
170170
};
171171
}
172-
Euclid::Table::Row::cell_type operator()(const SourceInterface& source) {
172+
Euclid::Table::Row::cell_type operator()(const SourceInterface& source) const {
173173
return m_convert_func(source, index);
174174
}
175175
std::size_t index = 0;

SEFramework/SEFramework/Psf/VariablePsfStack.h

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@
2525
#define _SEIMPLEMENTATION_PSF_VARIABLEPSFSTACK_H_
2626

2727
#include <CCfits/CCfits>
28+
#include <memory>
2829
#include <SEFramework/Image/VectorImage.h>
2930
#include <SEFramework/Psf/Psf.h>
31+
#include <SEUtils/KdTree.h>
3032

3133
namespace SourceXtractor {
3234

@@ -42,6 +44,18 @@ namespace SourceXtractor {
4244
*/
4345
class VariablePsfStack final : public Psf {
4446
public:
47+
/**
48+
* @brief Structure to hold PSF position data
49+
*/
50+
struct PsfPosition {
51+
SeFloat ra;
52+
SeFloat dec;
53+
SeFloat x;
54+
SeFloat y;
55+
double gridx;
56+
double gridy;
57+
};
58+
4559
/**
4660
* Constructor
4761
*/
@@ -83,6 +97,13 @@ class VariablePsfStack final : public Psf {
8397
return m_components;
8498
};
8599

100+
/**
101+
* @return The number of PSFs loaded in the stack
102+
*/
103+
long getNumberOfPsfs() const {
104+
return m_nrows;
105+
};
106+
86107
/**
87108
*
88109
*/
@@ -99,12 +120,8 @@ class VariablePsfStack final : public Psf {
99120

100121
long m_nrows;
101122

102-
std::vector<SeFloat> m_ra_values;
103-
std::vector<SeFloat> m_dec_values;
104-
std::vector<SeFloat> m_x_values;
105-
std::vector<SeFloat> m_y_values;
106-
std::vector<double> m_gridx_values;
107-
std::vector<double> m_gridy_values;
123+
std::vector<PsfPosition> m_positions;
124+
std::unique_ptr<KdTree<PsfPosition>> m_kdtree;
108125

109126
std::vector<std::string> m_components = {"X_IMAGE", "Y_IMAGE"};
110127

@@ -119,6 +136,16 @@ class VariablePsfStack final : public Psf {
119136
void selfTest();
120137
};
121138

139+
/**
140+
* @brief KdTree traits specialization for PsfPosition
141+
*/
142+
template <>
143+
struct KdTreeTraits<VariablePsfStack::PsfPosition> {
144+
static double getCoord(const VariablePsfStack::PsfPosition& pos, size_t index) {
145+
return (index == 0) ? static_cast<double>(pos.x) : static_cast<double>(pos.y);
146+
}
147+
};
148+
122149
} // namespace SourceXtractor
123150

124151
#endif //_SEIMPLEMENTATION_PSF_VARIABLEPSFSTACK_H_

SEFramework/src/lib/Output/OutputRegistry.cpp

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,21 +49,26 @@ auto OutputRegistry::getSourceToRowConverter(const std::vector<std::string>& ena
4949
return [this, out_prop_list](const SourceInterface& source) {
5050
std::vector<ColumnInfo::info_type> info_list {};
5151
std::vector<Row::cell_type> cell_values {};
52+
53+
const auto& property_to_names_map = m_property_to_names_map;
54+
const auto& name_to_col_info_map = m_name_to_col_info_map;
55+
const auto& name_to_converter_map = m_name_to_converter_map;
56+
5257
for (const auto& property : out_prop_list) {
53-
if (m_property_to_names_map.count(property) == 0) {
58+
if (property_to_names_map.count(property) == 0) {
5459
throw Elements::Exception() << "Missing column generator for " << property.name();
5560
}
56-
for (const auto& name : m_property_to_names_map.at(property)) {
57-
auto& col_info = m_name_to_col_info_map.at(name);
58-
info_list.emplace_back(name, m_name_to_converter_map.at(name).first,
59-
col_info.unit, col_info.description);
60-
cell_values.emplace_back(m_name_to_converter_map.at(name).second(source));
61+
for (const auto& name : property_to_names_map.at(property)) {
62+
auto& col_info = name_to_col_info_map.at(name);
63+
info_list.push_back(ColumnInfo::info_type {name, name_to_converter_map.at(name).first,
64+
col_info.unit, col_info.description});
65+
cell_values.push_back(name_to_converter_map.at(name).second(source));
6166
}
6267
}
6368
if (info_list.empty()) {
6469
throw Elements::Exception() << "The given configuration would not generate any output";
6570
}
66-
return Row {std::move(cell_values), std::make_shared<ColumnInfo>(move(info_list))};
71+
return Row {std::move(cell_values), std::make_shared<ColumnInfo>(std::move(info_list))};
6772
};
6873
}
6974

SEFramework/src/lib/Psf/VariablePsf.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,14 @@
2121
* Author: Alejandro Álvarez Ayllón
2222
*/
2323

24-
#include <ElementsKernel/Exception.h>
2524
#include <algorithm>
25+
#include <ElementsKernel/Logging.h>
26+
#include <ElementsKernel/Exception.h>
27+
#include "SEUtils/IsNan.h"
28+
2629
#include "SEFramework/Psf/VariablePsf.h"
2730

31+
static auto stack_logger = Elements::Logging::getLogger("PSFExPsf");
2832

2933
namespace SourceXtractor {
3034

@@ -130,6 +134,16 @@ void VariablePsf::selfTest() {
130134
if (coeff->getWidth() != psf_width || coeff->getHeight() != psf_height) {
131135
throw Elements::Exception() << "Malformed variable PSF, coefficient matrices do not have the same dimensions";
132136
}
137+
for (auto x = 0; x < psf_width; ++x){
138+
for (auto y = 0; y < psf_height; ++y) {
139+
if (fastmath_isnan(coeff->at(x, y))) {
140+
throw Elements::Exception() << "Malformed variable PSF, coefficient matrices contains NANs";
141+
}
142+
else if (fastmath_isinf(coeff->at(x, y))){
143+
throw Elements::Exception() << "Malformed variable PSF, coefficient matrices contains INFs";
144+
}
145+
}
146+
}
133147
}
134148
}
135149

0 commit comments

Comments
 (0)