From e6ca2c0d81c8bcc6ee6bcb83c0ba20bef950abdd Mon Sep 17 00:00:00 2001 From: Zamaan Raza <z.raza@fz-juelich.de> Date: Thu, 12 Oct 2023 10:40:50 +0200 Subject: [PATCH 01/22] Replace shell with d max --- gui/subframe_merge/SubframeMerge.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gui/subframe_merge/SubframeMerge.cpp b/gui/subframe_merge/SubframeMerge.cpp index d6e98525f..af54a362b 100644 --- a/gui/subframe_merge/SubframeMerge.cpp +++ b/gui/subframe_merge/SubframeMerge.cpp @@ -739,7 +739,8 @@ void SubframeMerge::refreshGraph(int column) QVector<double> sum_yvals; QVector<double> profile_yvals; for (int i = 0; i < nshells; ++i) { - xvals.push_back(double(i)); + double d_max = _sum_shell_model->item(i, 1)->data(Qt::DisplayRole).value<double>(); + xvals.push_back(d_max); double sum_val = _sum_shell_model->item(i, column)->data(Qt::DisplayRole).value<double>(); double profile_val = _profile_shell_model->item(i, column)->data(Qt::DisplayRole).value<double>(); @@ -765,7 +766,7 @@ void SubframeMerge::refreshGraph(int column) _statistics_plot->graph(1)->setName("Profile"); _statistics_plot->legend->setVisible(true); - _statistics_plot->xAxis->setLabel("shell"); + _statistics_plot->xAxis->setLabel("d max"); _statistics_plot->yAxis->setLabel(_plottable_statistics->itemText(column)); _statistics_plot->setNotAntialiasedElements(QCP::aeAll); -- GitLab From 02e8d5c5eb2a8caacf01deae13e8f8c3fecf9a07 Mon Sep 17 00:00:00 2001 From: Zamaan Raza <z.raza@fz-juelich.de> Date: Tue, 14 May 2024 08:46:37 +0200 Subject: [PATCH 02/22] Buffer all frames by default in GUI --- core/data/DataSet.cpp | 7 ++++++- core/data/DataSet.h | 6 +++++- gui/models/Session.cpp | 2 ++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/core/data/DataSet.cpp b/core/data/DataSet.cpp index ee52ffebc..58d00a753 100644 --- a/core/data/DataSet.cpp +++ b/core/data/DataSet.cpp @@ -53,7 +53,11 @@ namespace ohkl { DataSet::DataSet(const std::string& dataset_name, Diffractometer* diffractometer) - : _diffractometer{diffractometer}, _states(nullptr), _total_histogram(nullptr), _buffered(false) + : _diffractometer{diffractometer} + , _states(nullptr) + , _total_histogram(nullptr) + , _buffered(false) + , _all_frames_buffered(true) { setName(dataset_name); if (!_diffractometer) @@ -513,6 +517,7 @@ void DataSet::initBuffer(bool bufferAll) _frame_buffer.at(idx) = std::make_unique<Eigen::MatrixXi>(_reader->data(idx)); } _buffered = true; + _all_frames_buffered = bufferAll; ohklLog(Level::Debug, "DataSet::initBuffer: ", _name, " buffered"); } diff --git a/core/data/DataSet.h b/core/data/DataSet.h index 54a401aa9..d6c2201d8 100644 --- a/core/data/DataSet.h +++ b/core/data/DataSet.h @@ -174,6 +174,8 @@ class DataSet { void initBuffer(bool bufferAll = true); //! Clear the frame buffer void clearBuffer(); + //! Check whether all frames are buffered + bool allFramesBuffered() const { return _all_frames_buffered; }; virtual void setNFrames(std::size_t nframes) { std::ignore = nframes; }; @@ -204,8 +206,10 @@ class DataSet { //! Buffer for image data std::vector<std::unique_ptr<Eigen::MatrixXi>> _frame_buffer; - //! Whether or not the buffer is active + //! Buffer is active bool _buffered; + //! All frames are buffered + bool _all_frames_buffered; }; /*! @}*/ diff --git a/gui/models/Session.cpp b/gui/models/Session.cpp index beb8828be..fbf3db7a1 100644 --- a/gui/models/Session.cpp +++ b/gui/models/Session.cpp @@ -572,6 +572,8 @@ void Session::onDataChanged() if (!gSession->currentProject()->hasDataSet()) return; + _data_combo->currentData()->initBuffer(true); + double x_offset = _data_combo->currentData()->diffractometer()->source().selectedMonochromator().xOffset(); double y_offset = -- GitLab From e70260d1c306bc463b929e9158bfb84c417d7b1d Mon Sep 17 00:00:00 2001 From: Zamaan Raza <z.raza@fz-juelich.de> Date: Tue, 14 May 2024 08:49:50 +0200 Subject: [PATCH 03/22] Remove OpenMP pragmas --- core/algo/RefinementBatch.cpp | 2 -- core/algo/Refiner.cpp | 2 -- core/experiment/PeakFinder.cpp | 16 ++++++---------- core/integration/IIntegrator.cpp | 2 -- core/peak/Qs2Events.cpp | 16 ++++------------ core/shape/ShapeModel.cpp | 1 - 6 files changed, 10 insertions(+), 29 deletions(-) diff --git a/core/algo/RefinementBatch.cpp b/core/algo/RefinementBatch.cpp index 37e721354..72f3ece4f 100644 --- a/core/algo/RefinementBatch.cpp +++ b/core/algo/RefinementBatch.cpp @@ -224,7 +224,6 @@ int RefinementBatch::qSpaceResiduals(Eigen::VectorXd& fvec) const UnitCell uc = _cell->fromParameters(_u0, _uOffsets, _cellParameters); const Eigen::Matrix3d& UB = uc.reciprocalBasis(); - // #pragma omp parallel for for (unsigned int i = 0; i < _peaks.size(); ++i) { const Eigen::RowVector3d q0 = _peaks[i]->q().rowVector(); const Eigen::RowVector3d q1 = _hkls[i] * UB; @@ -249,7 +248,6 @@ int RefinementBatch::realSpaceResiduals(Eigen::VectorXd& fvec) const UnitCell uc = _cell->fromParameters(_u0, _uOffsets, _cellParameters); const Eigen::Matrix3d& UB = uc.reciprocalBasis(); - // #pragma omp parallel for for (unsigned int i = 0; i < _peaks.size(); ++i) { const Eigen::RowVector3d x0 = _peaks[i]->shape().center(); auto data = _peaks[i]->dataSet(); diff --git a/core/algo/Refiner.cpp b/core/algo/Refiner.cpp index bb8eded89..1afb5a376 100644 --- a/core/algo/Refiner.cpp +++ b/core/algo/Refiner.cpp @@ -273,10 +273,8 @@ bool Refiner::refine(sptrDataSet data, const std::vector<Peak3D*> peaks, sptrUni return false; unsigned int failed_batches = 0; - // #pragma omp parallel for for (auto&& batch : _batches) { if (!batch.refine(_params->max_iter)) { - // #pragma omp atomic ++failed_batches; } } diff --git a/core/experiment/PeakFinder.cpp b/core/experiment/PeakFinder.cpp index 7080b460f..e3034d18b 100644 --- a/core/experiment/PeakFinder.cpp +++ b/core/experiment/PeakFinder.cpp @@ -238,7 +238,6 @@ void PeakFinder::findPrimaryBlobs( // Iterate on all pixels in the image int nframes = 0; -#pragma omp for schedule(dynamic, DYNAMIC_CHUNK) for (size_t idx = begin; idx < end; ++idx) { ++nframes; @@ -318,15 +317,12 @@ void PeakFinder::findPrimaryBlobs( labels[index2D] = labels2[index2D] = label; index2D++; auto value = frame_data(row, col); -#pragma omp critical(dataupdate) - { - // Create a new blob if necessary - if (newlabel) - blobs.insert(std::make_pair(label, Blob3D(col, row, idx, value))); - else { - auto it = blobs.find(label); - it->second.addPoint(col, row, idx, value); - } + // Create a new blob if necessary + if (newlabel) + blobs.insert(std::make_pair(label, Blob3D(col, row, idx, value))); + else { + auto it = blobs.find(label); + it->second.addPoint(col, row, idx, value); } } } diff --git a/core/integration/IIntegrator.cpp b/core/integration/IIntegrator.cpp index 2b18162c3..8a74f1a0a 100644 --- a/core/integration/IIntegrator.cpp +++ b/core/integration/IIntegrator.cpp @@ -172,7 +172,6 @@ void IIntegrator::integrate( current_peak->updateMask(mask, idx); } -#pragma omp parallel for for (auto peak : peaks) { auto* current_peak = regions.at(peak).get(); // Check whether the peak intersects a mask @@ -221,7 +220,6 @@ void IIntegrator::integrate( peak->setIntegrationFlag( RejectionFlag::SaturatedPixel, _params.integrator_type); } else { -#pragma omp atomic ++nfailures; // This is a fallback. The RejectionFlag should have been set by this point. peak->setIntegrationFlag( diff --git a/core/peak/Qs2Events.cpp b/core/peak/Qs2Events.cpp index ee2ce955f..eb1064611 100644 --- a/core/peak/Qs2Events.cpp +++ b/core/peak/Qs2Events.cpp @@ -54,15 +54,11 @@ std::vector<std::pair<MillerIndex, DetectorEvent>> algo::qMap2Events( } // for each sample q, determine the rotation that makes it intersect the Ewald sphere -#pragma omp parallel for for (const auto& [hkl, sample_q] : sample_qs) { std::vector<DetectorEvent> new_events = qVector2Events(sample_q, states, detector, n_intervals); -#pragma omp critical(dataupdate) - { - for (auto event : new_events) - events.push_back({hkl, event}); - } + for (auto event : new_events) + events.push_back({hkl, event}); if (handler) handler->setProgress(++count * 100.0 / sample_qs.size()); } @@ -93,15 +89,11 @@ std::vector<DetectorEvent> algo::qVectorList2Events( } // for each sample q, determine the rotation that makes it intersect the Ewald sphere -#pragma omp parallel for for (const ReciprocalVector& sample_q : sample_qs) { std::vector<DetectorEvent> new_events = qVector2Events(sample_q, states, detector, n_intervals); -#pragma omp critical(dataupdate) - { - for (auto event : new_events) - events.emplace_back(event); - } + for (auto event : new_events) + events.emplace_back(event); if (handler) handler->setProgress(++count * 100.0 / sample_qs.size()); } diff --git a/core/shape/ShapeModel.cpp b/core/shape/ShapeModel.cpp index f591508be..942deec56 100644 --- a/core/shape/ShapeModel.cpp +++ b/core/shape/ShapeModel.cpp @@ -446,7 +446,6 @@ void ShapeModel::setPredictedShapes(PeakCollection* peaks) _handler->setProgress(0); } -#pragma omp parallel for for (auto peak : peaks->getPeakList()) { // Skip the peak if any error occur when computing its mean covariance (e.g. // too few or no neighbouring peaks found) -- GitLab From 04bee7344b8894dd804366133f72d3d931caefc4 Mon Sep 17 00:00:00 2001 From: Zamaan Raza <z.raza@fz-juelich.de> Date: Tue, 14 May 2024 10:58:01 +0200 Subject: [PATCH 04/22] Correct reference values for Profile3D integration test --- .../functional/TestProfile3DIntegration.cpp | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/test/cpp/functional/TestProfile3DIntegration.cpp b/test/cpp/functional/TestProfile3DIntegration.cpp index 6569dcd5d..c0d9c3321 100644 --- a/test/cpp/functional/TestProfile3DIntegration.cpp +++ b/test/cpp/functional/TestProfile3DIntegration.cpp @@ -38,16 +38,19 @@ TEST_CASE("test/data/TestProfile3DIntegration.cpp", "") { // reference values const int ref_n_peaks = 59209; - const int ref_n_integrated_peaks = 56160; - const double ref_rpim_overall = 0.0798; - const double ref_ccstar_overall = 0.9972; + const int ref_n_integrated_peaks = 55986; + const double ref_rpim_overall = 0.0763; + const double ref_ccstar_overall = 0.9973; + const double ref_cchalf_overall = 0.9892; const std::vector<double> ref_rpim = - {0.0267, 0.0605, 0.1028, 0.1362, 0.1766, 0.2219, 0.2649, 0.3084, 0.3632, 0.4278}; + {0.0267, 0.0605, 0.1019, 0.1325, 0.1729, 0.2113, 0.2474, 0.2790, 0.3272, 0.3974}; const std::vector<double> ref_ccstar = - {0.9988, 0.9957, 0.9871, 0.9778, 0.9670, 0.9406, 0.9223, 0.9002, 0.8395, 0.7645}; + {0.9988, 0.9956, 0.9873, 0.9786, 0.9685, 0.9464, 0.9330, 0.9283, 0.8837, 0.8071}; + const std::vector<double> ref_cchalf = + {0.9953, 0.9826, 0.9507, 0.9187, 0.8833, 0.8114, 0.7707, 0.7570, 0.6406, 0.4831}; const int eps_peaks = 100; - const double eps_stats = 0.02; + const double eps_stats = 0.01; const std::string filename = "Trypsin-pxsum.ohkl"; ohkl::Experiment experiment("Trypsin", "BioDiff"); @@ -66,7 +69,7 @@ TEST_CASE("test/data/TestProfile3DIntegration.cpp", "") shape_params.sigma_d = found->sigmaD(); shape_params.sigma_m = found->sigmaM(); shape_params.strength_min = 1.0; - shape_params.neighbour_range_pixels = 200; + shape_params.neighbour_range_pixels = 50; shape_params.neighbour_range_frames = 10; found->buildShapeModel(data, shape_params); ohkl::ShapeModel* shapes = found->shapeModel(); @@ -80,9 +83,9 @@ TEST_CASE("test/data/TestProfile3DIntegration.cpp", "") params->discard_saturated = true; params->skip_masked = true; params->use_max_strength = true; - params->max_strength = 5.0; + params->max_strength = 1.0; params->use_max_d = true; - params->max_d = 2.56; + params->max_d = 2.5; integrator->integratePeaks(data, peaks, params, shapes); @@ -111,19 +114,28 @@ TEST_CASE("test/data/TestProfile3DIntegration.cpp", "") auto* shell_quality = merger->profileShellQuality(); const double rpim_overall = overall_quality->shells[0].Rpim; const double ccstar_overall = overall_quality->shells[0].CCstar; + const double cchalf_overall = overall_quality->shells[0].CChalf; std::vector<double> rpim; std::vector<double> ccstar; + std::vector<double> cchalf; for (int i = 0; i < merge_params->n_shells; ++i) { rpim.push_back(shell_quality->shells[i].Rpim); ccstar.push_back(shell_quality->shells[i].CCstar); + cchalf.push_back(shell_quality->shells[i].CChalf); } CHECK(rpim_overall < ref_rpim_overall + eps_stats); - CHECK(ccstar_overall < ref_ccstar_overall + eps_stats); + CHECK(ccstar_overall > ref_ccstar_overall - eps_stats); + CHECK(cchalf_overall > ref_cchalf_overall - eps_stats); + std::cout << "rpim_overall = " << rpim_overall << std::endl; + std::cout << "ccstar_overall = " << ccstar_overall << std::endl; + std::cout << "cchalf_overall = " << cchalf_overall << std::endl; for (int i = 0; i < rpim.size(); ++i) { CHECK(rpim[i] < ref_rpim[i] + eps_stats); - CHECK(ccstar[i] < ref_ccstar[i] + eps_stats); + CHECK(ccstar[i] > ref_ccstar[i] - eps_stats); + CHECK(cchalf[i] > ref_cchalf[i] - eps_stats); std::cout << rpim[i] << " " << ref_rpim[i] << std::endl; std::cout << ccstar[i] << " " << ref_ccstar[i] << std::endl; + std::cout << cchalf[i] << " " << ref_cchalf[i] << std::endl << std::endl; } } -- GitLab From 418647e2b12e076a4fbfa356db0bb64aae2974d2 Mon Sep 17 00:00:00 2001 From: Zamaan Raza <z.raza@fz-juelich.de> Date: Tue, 14 May 2024 12:55:18 +0200 Subject: [PATCH 05/22] Generate Integrators as-needed using IntegratorFactory --- core/experiment/Integrator.cpp | 32 +++-------- core/experiment/Integrator.h | 9 +-- core/integration/GaussianIntegrator.cpp | 6 +- core/integration/GaussianIntegrator.h | 2 +- core/integration/ISigmaIntegrator.cpp | 2 +- core/integration/IntegratorFactory.cpp | 57 +++++++++++++++++++ core/integration/IntegratorFactory.h | 37 ++++++++++++ core/integration/PixelSumIntegrator.cpp | 6 +- core/integration/PixelSumIntegrator.h | 2 +- core/integration/ShapeIntegrator.cpp | 2 +- .../unit/integrate/TestPeakIntegration.cpp | 4 +- 11 files changed, 121 insertions(+), 38 deletions(-) create mode 100644 core/integration/IntegratorFactory.cpp create mode 100644 core/integration/IntegratorFactory.h diff --git a/core/experiment/Integrator.cpp b/core/experiment/Integrator.cpp index 4b2f8ef76..74bc47bb0 100644 --- a/core/experiment/Integrator.cpp +++ b/core/experiment/Integrator.cpp @@ -17,11 +17,7 @@ #include "base/utils/Logger.h" #include "core/experiment/DataHandler.h" #include "core/experiment/PeakFinder.h" -#include "core/integration/GaussianIntegrator.h" -#include "core/integration/ISigmaIntegrator.h" -#include "core/integration/PixelSumIntegrator.h" -#include "core/integration/Profile1DIntegrator.h" -#include "core/integration/Profile3DIntegrator.h" +#include "core/integration/IIntegrator.h" #include "core/integration/ShapeIntegrator.h" #include "core/peak/Peak3D.h" #include "core/shape/PeakCollection.h" @@ -30,19 +26,8 @@ namespace ohkl { Integrator::Integrator(std::shared_ptr<DataHandler> data_handler) - : _handler(nullptr), _data_handler(data_handler) + : _handler(nullptr), _data_handler(data_handler), _factory() { - _integrator_map.clear(); - _integrator_map.insert( - std::make_pair(IntegratorType::PixelSum, std::make_unique<PixelSumIntegrator>(true, true))); - _integrator_map.insert( - std::make_pair(IntegratorType::Gaussian, std::make_unique<GaussianIntegrator>(true, true))); - _integrator_map.insert( - std::make_pair(IntegratorType::ISigma, std::make_unique<ISigmaIntegrator>())); - _integrator_map.insert( - std::make_pair(IntegratorType::Profile1D, std::make_unique<Profile1DIntegrator>())); - _integrator_map.insert( - std::make_pair(IntegratorType::Profile3D, std::make_unique<Profile3DIntegrator>())); _params = std::make_unique<IntegrationParameters>(); } @@ -53,12 +38,7 @@ DataHandler* Integrator::getDataHandler() IIntegrator* Integrator::getIntegrator(const IntegratorType integrator_type) const { - std::map<IntegratorType, std::unique_ptr<IIntegrator>>::const_iterator it; - for (it = _integrator_map.begin(); it != _integrator_map.end(); ++it) { - if (it->first == integrator_type) - return it->second.get(); - } - return nullptr; + return _factory.create(integrator_type); } void Integrator::integratePeaks( @@ -82,6 +62,8 @@ void Integrator::integratePeaks( if (peak->enabled()) ++_n_valid; } + + delete integrator; } void Integrator::integratePeaks( @@ -105,6 +87,8 @@ void Integrator::integratePeaks( if (peak->enabled()) ++_n_valid; } + + delete integrator; } void Integrator::integrateFoundPeaks(PeakFinder* peak_finder) @@ -125,6 +109,8 @@ void Integrator::integrateFoundPeaks(PeakFinder* peak_finder) peak_finder->setIntegrated(true); if (_params->use_gradient) peak_finder->setBkgGradient(true); + + delete integrator; } void Integrator::integrateShapeModel( diff --git a/core/experiment/Integrator.h b/core/experiment/Integrator.h index c09b2dcb0..270450b85 100644 --- a/core/experiment/Integrator.h +++ b/core/experiment/Integrator.h @@ -15,19 +15,19 @@ #define OPENHKL_CORE_EXPERIMENT_INTEGRATOR_H #include "core/data/DataTypes.h" -#include "core/integration/IIntegrator.h" +#include "core/integration/IntegratorFactory.h" #include <map> #include <string> namespace ohkl { -using IntegratorMap = std::map<IntegratorType, std::unique_ptr<ohkl::IIntegrator>>; - class PeakCollection; class PeakFinder; class DataHandler; +class IIntegrator; struct ShapeModelParameters; +struct IntegrationParameters; /*! \addtogroup python_api * @{*/ @@ -75,12 +75,13 @@ class Integrator { private: sptrProgressHandler _handler; - IntegratorMap _integrator_map; std::shared_ptr<DataHandler> _data_handler; std::shared_ptr<IntegrationParameters> _params; unsigned int _n_peaks; unsigned int _n_valid; + + IntegratorFactory _factory; }; /*! @}*/ diff --git a/core/integration/GaussianIntegrator.cpp b/core/integration/GaussianIntegrator.cpp index d19bf5420..4571fbcf4 100644 --- a/core/integration/GaussianIntegrator.cpp +++ b/core/integration/GaussianIntegrator.cpp @@ -25,10 +25,10 @@ namespace ohkl { -GaussianIntegrator::GaussianIntegrator(bool fit_center, bool fit_cov) +GaussianIntegrator::GaussianIntegrator() : IIntegrator() { - _params.fit_center = fit_center; - _params.fit_cov = fit_cov; + _params.fit_center = true; + _params.fit_cov = true; } static Eigen::Matrix3d from_cholesky(const Eigen::VectorXd a) diff --git a/core/integration/GaussianIntegrator.h b/core/integration/GaussianIntegrator.h index ed531abec..2ef5e3d66 100644 --- a/core/integration/GaussianIntegrator.h +++ b/core/integration/GaussianIntegrator.h @@ -25,7 +25,7 @@ namespace ohkl { /*! \brief Compute integrated intensity by fitting to an analytic 3D Gaussian.*/ class GaussianIntegrator : public IIntegrator { public: - GaussianIntegrator(bool fit_center, bool fit_cov); + GaussianIntegrator(); protected: //! Integrate a peak diff --git a/core/integration/ISigmaIntegrator.cpp b/core/integration/ISigmaIntegrator.cpp index 36a7bdbb7..11322aa82 100644 --- a/core/integration/ISigmaIntegrator.cpp +++ b/core/integration/ISigmaIntegrator.cpp @@ -23,7 +23,7 @@ namespace ohkl { -ISigmaIntegrator::ISigmaIntegrator() : PixelSumIntegrator(false, false) { } +ISigmaIntegrator::ISigmaIntegrator() : PixelSumIntegrator() { } bool ISigmaIntegrator::compute( Peak3D* peak, ShapeModel* shape_model, const IntegrationRegion& region) diff --git a/core/integration/IntegratorFactory.cpp b/core/integration/IntegratorFactory.cpp new file mode 100644 index 000000000..cf8e4440e --- /dev/null +++ b/core/integration/IntegratorFactory.cpp @@ -0,0 +1,57 @@ +// *********************************************************************************************** +// +// OpenHKL: data reduction for single crystal diffraction +// +//! @file core/convolve/IntegratorFactory.cpp +//! @brief Implements class IntegratorFactory +//! +//! @homepage https://openhkl.org +//! @license GNU General Public License v3 or higher (see COPYING) +//! @copyright Institut Laue-Langevin and Forschungszentrum Jülich GmbH 2016- +//! @authors see CITATION, MAINTAINER +// +// *********************************************************************************************** + +#include "core/integration/IntegratorFactory.h" + +#include "core/integration/GaussianIntegrator.h" +#include "core/integration/IIntegrator.h" +#include "core/integration/ISigmaIntegrator.h" +#include "core/integration/PixelSumIntegrator.h" +#include "core/integration/Profile1DIntegrator.h" +#include "core/integration/Profile3DIntegrator.h" +#include "core/peak/Peak3D.h" + + +namespace ohkl { + +template <typename T> T* create_integrator() +{ + return new T(); +} + +IntegratorFactory::IntegratorFactory() : _callbacks() +{ + _callbacks[IntegratorType::PixelSum] = &create_integrator<PixelSumIntegrator>; + _callbacks[IntegratorType::Profile3D] = &create_integrator<Profile3DIntegrator>; + _callbacks[IntegratorType::Profile1D] = &create_integrator<Profile1DIntegrator>; + _callbacks[IntegratorType::ISigma] = &create_integrator<ISigmaIntegrator>; + _callbacks[IntegratorType::Gaussian] = &create_integrator<GaussianIntegrator>; +} + +IIntegrator* IntegratorFactory::create(const IntegratorType& integrator_type) const +{ + const auto it = _callbacks.find(integrator_type); + + if (it == _callbacks.end()) + throw std::runtime_error("IntegratorFactory::create: Invalid integrator type"); + + return it->second(); +} + +const std::map<IntegratorType, IntegratorFactory::callback>& IntegratorFactory::callbacks() const +{ + return _callbacks; +} + +} // namespace ohkl diff --git a/core/integration/IntegratorFactory.h b/core/integration/IntegratorFactory.h new file mode 100644 index 000000000..80695a056 --- /dev/null +++ b/core/integration/IntegratorFactory.h @@ -0,0 +1,37 @@ +// *********************************************************************************************** +// +// OpenHKL: data reduction for single crystal diffraction +// +//! @file core/integration/IntegratorFactory.h +//! @brief Defines class IntegratorFactory +//! +//! @homepage https://openhkl.org +//! @license GNU General Public License v3 or higher (see COPYING) +//! @copyright Institut Laue-Langevin and Forschungszentrum Jülich GmbH 2016- +//! @authors see CITATION, MAINTAINER +// +// *********************************************************************************************** + +#ifndef OHKL_CORE_INTEGRATE_INTEGRATORFACTORY_H +#define OHKL_CORE_INTEGRATE_INTEGRATORFACTORY_H + +#include "core/integration/IIntegrator.h" + +namespace ohkl { + +class IntegratorFactory { + public: + using callback = std::function<IIntegrator*()>; + + IntegratorFactory(); + IIntegrator* create(const IntegratorType& integrator_type) const; + + const std::map<IntegratorType, callback>& callbacks() const; + + private: + std::map<IntegratorType, callback> _callbacks; +}; + +} // namespace ohkl + +#endif // OHKL_CORE_INTEGRATE_INTEGRATORFACTORY_H diff --git a/core/integration/PixelSumIntegrator.cpp b/core/integration/PixelSumIntegrator.cpp index 592f7aa8b..ed5aae273 100644 --- a/core/integration/PixelSumIntegrator.cpp +++ b/core/integration/PixelSumIntegrator.cpp @@ -85,10 +85,10 @@ std::pair<Intensity, Intensity> compute_background( } // namespace -PixelSumIntegrator::PixelSumIntegrator(bool fit_center, bool fit_covariance) : IIntegrator() +PixelSumIntegrator::PixelSumIntegrator() : IIntegrator() { - _params.fit_center = fit_center; - _params.fit_cov = fit_covariance; + _params.fit_center = true; + _params.fit_cov = true; } bool PixelSumIntegrator::compute(Peak3D* peak, ShapeModel*, const IntegrationRegion& region) diff --git a/core/integration/PixelSumIntegrator.h b/core/integration/PixelSumIntegrator.h index e19d05704..60139d9a9 100644 --- a/core/integration/PixelSumIntegrator.h +++ b/core/integration/PixelSumIntegrator.h @@ -29,7 +29,7 @@ class PixelSumIntegrator : public IIntegrator { //! Construct the pixel sum integrator //! @param fit_center update the peak center as part of integration //! @param fit_covariance update the peak shape covariance matrix as part of integration - PixelSumIntegrator(bool fit_center, bool fit_covariance); + PixelSumIntegrator(); protected: //! Integrate a peak diff --git a/core/integration/ShapeIntegrator.cpp b/core/integration/ShapeIntegrator.cpp index 07b547f2e..e3bc6a1da 100644 --- a/core/integration/ShapeIntegrator.cpp +++ b/core/integration/ShapeIntegrator.cpp @@ -26,7 +26,7 @@ namespace ohkl { ShapeIntegrator::ShapeIntegrator( ShapeModel* lib, const AABB& aabb, int nx, int ny, int nz, int subdiv) - : PixelSumIntegrator(false, false) + : PixelSumIntegrator() , _shape_model(lib) , _aabb(aabb) , _nx(nx) diff --git a/test/cpp/unit/integrate/TestPeakIntegration.cpp b/test/cpp/unit/integrate/TestPeakIntegration.cpp index 73f7e71d5..4a1564819 100644 --- a/test/cpp/unit/integrate/TestPeakIntegration.cpp +++ b/test/cpp/unit/integrate/TestPeakIntegration.cpp @@ -78,8 +78,10 @@ TEST_CASE("test/integrate/Test_6_12_38.cpp", "") std::vector<ohkl::Peak3D*> peaks; peaks.push_back(&peak); - ohkl::PixelSumIntegrator integrator(false, false); + ohkl::PixelSumIntegrator integrator; ohkl::IntegrationParameters params{}; + params.fit_center = false; + params.fit_cov = false; params.peak_end = 2.7; params.bkg_begin = 3.0; params.bkg_end = 4.0; -- GitLab From 59f73632bf946bc99c145ffbe308a9d321e8a791 Mon Sep 17 00:00:00 2001 From: Zamaan Raza <z.raza@fz-juelich.de> Date: Tue, 14 May 2024 13:04:16 +0200 Subject: [PATCH 06/22] Add ParallelFor --- base/utils/ParallelFor.h | 79 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 base/utils/ParallelFor.h diff --git a/base/utils/ParallelFor.h b/base/utils/ParallelFor.h new file mode 100644 index 000000000..3245fd170 --- /dev/null +++ b/base/utils/ParallelFor.h @@ -0,0 +1,79 @@ +// *********************************************************************************************** +// +// OpenHKL: data reduction for single crystal diffraction +// +//! @file base/utils/ParallelFor.h +//! @brief Defines class ParallelFor +//! +//! @homepage https://openhkl.org +//! @license GNU General Public License v3 or higher (see COPYING) +//! @copyright Institut Laue-Langevin and Forschungszentrum Jülich GmbH 2016- +//! @authors see CITATION, MAINTAINER +// +// *********************************************************************************************** + +#ifndef OHKL_BASE_UTILS_PARALLELFOR_H +#define OHKL_BASE_UTILS_PARALLELFOR_H + +#include <algorithm> +#include <functional> +#include <thread> +#include <vector> + +#include <iostream> + +namespace ohkl { + +/// @param[in] nb_elements : size of your for loop +/// @param[in] functor(start, end) : +/// your function processing a sub chunk of the for loop. +/// "start" is the first index to process (included) until the index "end" +/// (excluded) +/// @code +/// for(int i = start; i < end; ++i) +/// computation(i); +/// @endcode +/// @param use_threads : enable / disable threads. +/// +/// +static void parallel_for( + unsigned int nb_elements, std::function<void (int start, int end)> functor, + bool use_threads = true, unsigned int max_threads = 0) +{ + unsigned int nb_threads_hint = std::thread::hardware_concurrency(); + unsigned int nb_threads = nb_threads_hint == 0 ? 8 : nb_threads_hint; + if (max_threads > 0) // Do not exceed the maximum number of threads (prevents OOM) + nb_threads = nb_threads > max_threads ? max_threads : nb_threads; + + unsigned int batch_size = nb_elements / nb_threads; + unsigned int batch_remainder = nb_elements % nb_threads; + + std::vector<std::thread> my_threads(nb_threads); + + if (use_threads) { + // Multithread execution + for(unsigned int i = 0; i < nb_threads; ++i) { + int start = i * batch_size; + my_threads[i] = std::thread(functor, start, start+batch_size); + } + } + else { + // Single thread execution (for easy debugging) + for(unsigned int i = 0; i < nb_threads; ++i){ + int start = i * batch_size; + functor( start, start+batch_size ); + } + } + + // Deform the elements left + int start = nb_threads * batch_size; + functor( start, start+batch_remainder); + + // Wait for the other thread to finish their task + if (use_threads) + std::for_each(my_threads.begin(), my_threads.end(), std::mem_fn(&std::thread::join)); +} + +} // namespace ohkl + +#endif // OHKL_BASE_UTILS_PARALLELFOR_H -- GitLab From 8808afca53471c4389f7d88e70aa7e928b728906 Mon Sep 17 00:00:00 2001 From: Zamaan Raza <z.raza@fz-juelich.de> Date: Tue, 14 May 2024 15:47:35 +0200 Subject: [PATCH 07/22] Make serial integration thread-safe add missing definition --- core/integration/GaussianIntegrator.cpp | 78 ++++++----- core/integration/GaussianIntegrator.h | 3 +- core/integration/IIntegrator.cpp | 161 ++++++++++------------- core/integration/IIntegrator.h | 77 +++++++---- core/integration/ISigmaIntegrator.cpp | 46 +++---- core/integration/ISigmaIntegrator.h | 3 +- core/integration/PixelSumIntegrator.cpp | 75 ++++++----- core/integration/PixelSumIntegrator.h | 2 +- core/integration/Profile1DIntegrator.cpp | 60 +++++---- core/integration/Profile1DIntegrator.h | 3 +- core/integration/Profile3DIntegrator.cpp | 68 +++++----- core/integration/Profile3DIntegrator.h | 3 +- core/integration/ShapeIntegrator.cpp | 27 ++-- core/integration/ShapeIntegrator.h | 3 +- core/peak/IntegrationRegion.cpp | 94 +++++++------ core/peak/IntegrationRegion.h | 10 +- core/peak/Peak3D.cpp | 116 ++++++++-------- core/peak/Peak3D.h | 8 +- core/peak/PeakData.cpp | 17 ++- core/peak/PeakData.h | 6 + 20 files changed, 464 insertions(+), 396 deletions(-) diff --git a/core/integration/GaussianIntegrator.cpp b/core/integration/GaussianIntegrator.cpp index 4571fbcf4..5821f463f 100644 --- a/core/integration/GaussianIntegrator.cpp +++ b/core/integration/GaussianIntegrator.cpp @@ -13,6 +13,7 @@ // *********************************************************************************************** #include "core/integration/GaussianIntegrator.h" + #include "base/fit/FitParameters.h" #include "base/fit/Minimizer.h" #include "base/geometry/Ellipsoid.h" @@ -23,15 +24,9 @@ #include <Eigen/Cholesky> -namespace ohkl { - -GaussianIntegrator::GaussianIntegrator() : IIntegrator() -{ - _params.fit_center = true; - _params.fit_cov = true; -} +namespace { -static Eigen::Matrix3d from_cholesky(const Eigen::VectorXd a) +Eigen::Matrix3d from_cholesky(const Eigen::VectorXd a) { // Reconstruct Cholesky L factor Eigen::Matrix3d L; @@ -46,7 +41,7 @@ static Eigen::Matrix3d from_cholesky(const Eigen::VectorXd a) return L * L.transpose(); } -static void residuals( +void residuals( Eigen::VectorXd& res, double B, double I, const Eigen::Vector3d x0, const Eigen::VectorXd& a, const std::vector<Eigen::Vector3d>& x, const std::vector<double>& M, double* pearson) { @@ -59,7 +54,7 @@ static void residuals( double u = 0, v = 0, uu = 0, vv = 0, uv = 0; for (size_t i = 0; i < n; ++i) { - Eigen::Vector3d dx = x[i] - x0; + const Eigen::Vector3d dx = x[i] - x0; const double xAx = dx.dot(A * dx); const double M_pred = B + I * std::exp(-0.5 * xAx); const double M_obs = M[i]; @@ -85,11 +80,21 @@ static void residuals( } } -bool GaussianIntegrator::compute( +} // namespace + +namespace ohkl { + +GaussianIntegrator::GaussianIntegrator() : IIntegrator() +{ + _params.fit_center = true; + _params.fit_cov = true; +} + +ComputeResult GaussianIntegrator::compute( Peak3D* peak, ShapeModel* /*shape_model*/, const IntegrationRegion& region) { - if (!peak) - return false; + ComputeResult result; + result.integrator_type = IntegratorType::Gaussian; const size_t N = region.peakData().events().size(); @@ -149,39 +154,41 @@ bool GaussianIntegrator::compute( min.setWeights(wts); try { - bool success = min.fit(100); - if (!success) - return false; + const bool success = min.fit(100); + if (!success) { + result.integration_flag = RejectionFlag::BadGaussianFit; + return result; + } } catch (std::exception& e) { - ohklLog(Level::Warning, __FUNCTION__, ": Gaussian fit failed: ", e.what()); - return false; + result.integration_flag = RejectionFlag::BadGaussianFit; + return result; } // consistency check: center should still be in dataset! if (x0(0) < 0 || x0(0) >= peak->dataSet()->nCols()) { - peak->setIntegrationFlag(RejectionFlag::CentreOutOfBounds, IntegratorType::Gaussian); - return false; + result.integration_flag = RejectionFlag::CentreOutOfBounds; + return result; } if (x0(1) < 0 || x0(1) >= peak->dataSet()->nRows()) { - peak->setIntegrationFlag(RejectionFlag::CentreOutOfBounds, IntegratorType::Gaussian); - return false; + result.integration_flag = RejectionFlag::CentreOutOfBounds; + return result; } if (x0(2) < 0 || x0(2) >= peak->dataSet()->nFrames()) { - peak->setIntegrationFlag(RejectionFlag::CentreOutOfBounds, IntegratorType::Gaussian); - return false; + result.integration_flag = RejectionFlag::CentreOutOfBounds; + return result; } // consistency check: covariance matrix should be positive definite - Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> solver(from_cholesky(a)); + const Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> solver(from_cholesky(a)); if (solver.eigenvalues().minCoeff() <= 0) { - peak->setIntegrationFlag(RejectionFlag::InvalidCovariance, IntegratorType::Gaussian); - return false; + result.integration_flag = RejectionFlag::InvalidCovariance; + return result; } const auto& covar = min.covariance(); - _profileBackground = {B, covar(0, 0)}; - _profileIntensity = {I, covar(1, 1)}; + result.profile_background = {B, covar(0, 0)}; + result.profile_intensity = {I, covar(1, 1)}; // Gets pearson coefficient of fit double pearson; @@ -189,14 +196,15 @@ bool GaussianIntegrator::compute( residuals(r, B, I, x0, a, x, counts, &pearson); if (pearson <= 0.75) { - peak->setIntegrationFlag(RejectionFlag::BadIntegrationFit, IntegratorType::Gaussian); - return false; + result.integration_flag = RejectionFlag::BadIntegrationFit; + return result; } - _sumIntensity = {}; - _sumBackground = {}; + result.sum_background = {}; + result.sum_intensity = {}; - peak->setShape({x0, from_cholesky(a)}); - return true; + result.shape = {x0, from_cholesky(a)}; + + return result; } std::vector<double> GaussianIntegrator::profile(Peak3D* peak, const IntegrationRegion& region) diff --git a/core/integration/GaussianIntegrator.h b/core/integration/GaussianIntegrator.h index 2ef5e3d66..32764a266 100644 --- a/core/integration/GaussianIntegrator.h +++ b/core/integration/GaussianIntegrator.h @@ -29,7 +29,8 @@ class GaussianIntegrator : public IIntegrator { protected: //! Integrate a peak - bool compute(Peak3D* peak, ShapeModel* shape_model, const IntegrationRegion& region) override; + ComputeResult compute( + Peak3D* peak, ShapeModel* shape_model, const IntegrationRegion& region) override; //! Returns the analytic profile computed over the given integration region std::vector<double> profile(Peak3D* peak, const IntegrationRegion& region); }; diff --git a/core/integration/IIntegrator.cpp b/core/integration/IIntegrator.cpp index 8a74f1a0a..9ba640452 100644 --- a/core/integration/IIntegrator.cpp +++ b/core/integration/IIntegrator.cpp @@ -54,23 +54,25 @@ void IntegrationParameters::log(const Level& level) const ohklLog(level, "max_width = ", max_width); } -IIntegrator::IIntegrator() - : _sumBackground() - , _profileBackground() - , _meanBkgGradient() - , _sumIntensity() - , _profileIntensity() - , _rockingCurve() - , _handler(nullptr) - , _params{} +ComputeResult::ComputeResult() + : integration_flag(RejectionFlag::NotRejected) + , sum_intensity() + , profile_intensity() + , sum_background() + , profile_background() + , bkg_gradient() + , rocking_curve() + , integrator_type(IntegratorType::PixelSum) + , shape() { } -IIntegrator::~IIntegrator() = default; - -const std::vector<Intensity>& IIntegrator::rockingCurve() const +IIntegrator::IIntegrator() + : _handler(nullptr) + , _params{} + , _thread_parallel(true) + , _max_threads(8) { - return _rockingCurve; } void IIntegrator::integrate( @@ -93,38 +95,40 @@ void IIntegrator::integrate( _handler->setProgress(0); } - bool profile_integration = false; + _profile_integration = false; if (_params.integrator_type == IntegratorType::Profile1D || _params.integrator_type == IntegratorType::Profile3D - || _params.integrator_type == IntegratorType::Gaussian) - profile_integration = true; + || _params.integrator_type == IntegratorType::Gaussian + || _params.integrator_type == IntegratorType::ISigma) + _profile_integration = true; size_t idx = 0; - int num_frames_done = 0; + _n_frames_done = 0; std::map<Peak3D*, std::unique_ptr<IntegrationRegion>> regions; std::map<Peak3D*, bool> integrated; - double peak_end, bkg_begin, bkg_end; if (_params.region_type == ohkl::RegionType::VariableEllipsoid) { - peak_end = _params.peak_end; - bkg_begin = _params.bkg_begin; - bkg_end = _params.bkg_end; + _peak_end = _params.peak_end; + _bkg_begin = _params.bkg_begin; + _bkg_end = _params.bkg_end; } else { - peak_end = _params.fixed_peak_end; - bkg_begin = _params.fixed_bkg_begin; - bkg_end = _params.fixed_bkg_end; + _peak_end = _params.fixed_peak_end; + _bkg_begin = _params.fixed_bkg_begin; + _bkg_end = _params.fixed_bkg_end; } for (auto peak : peaks) { - if (!peak->enabled() && peak->isRejectedFor(RejectionFlag::Extinct)) - continue; + if (peak->isRejectedFor(RejectionFlag::Extinct)) { + if (!peak->enabled()) + continue; + } regions.emplace(std::make_pair( peak, std::make_unique<IntegrationRegion>( - peak, peak_end, bkg_begin, bkg_end, _params.region_type))); + peak, _peak_end, _bkg_begin, _bkg_end, _params.region_type))); integrated.emplace(std::make_pair(peak, false)); // ignore partials @@ -154,7 +158,7 @@ void IIntegrator::integrate( removeOverlaps(regions); ohklLog(Level::Debug, "IIntegrator::integrate: frames loop"); - int nfailures = 0; + _n_failures = 0; for (idx = 0; idx < data->nFrames(); ++idx) { Eigen::MatrixXd current_frame, gradient; Eigen::MatrixXi mask; @@ -167,42 +171,43 @@ void IIntegrator::integrate( for (auto peak : peaks) { assert(peak != nullptr); - auto* current_peak = regions.at(peak).get(); - // assert(current_peak != regions.end()); - current_peak->updateMask(mask, idx); + auto* current_region = regions.at(peak).get(); + current_region->updateMask(mask, idx); } for (auto peak : peaks) { - auto* current_peak = regions.at(peak).get(); + auto* current_region = regions.at(peak).get(); // Check whether the peak intersects a mask if (_params.skip_masked) { Ellipsoid shape = peak->shape(); - shape.scale(bkg_end); + shape.scale(_bkg_end); for (const auto* mask : data->masks()) { if (mask->collide(shape)) { - peak->setRejectionFlag(RejectionFlag::Masked); - ++nfailures; + peak->setMasked(); continue; } } } // Check for saturated pixels - const auto& counts = current_peak->peakData().counts(); + const auto& counts = current_region->peakData().counts(); double max = 0; if (!counts.empty()) // std::max on empty vector segfaults under MacOS max = *std::max_element(counts.begin(), counts.end()); - bool saturated = _params.discard_saturated && (max > _params.max_counts); + const bool saturated = _params.discard_saturated && (max > _params.max_counts); - bool result; + bool result = false; if (_params.use_gradient) - result = current_peak->advanceFrame(current_frame, mask, idx, gradient); + result = current_region->advanceFrame(current_frame, mask, idx, &gradient); else - result = current_peak->advanceFrame(current_frame, mask, idx); - - // Skip strong and low resolution peaks during profile integration - if (profile_integration && !reintegrate(peak)) { - result = false; - integrated[peak] = true; + result = current_region->advanceFrame(current_frame, mask, idx); + + bool reintegrate = true; + if (_profile_integration) { + if (_params.use_max_strength && peak->sumIntensity().strength() > + _params.max_strength) + reintegrate = false; + if (_params.use_max_d && peak->d() > _params.max_d) + reintegrate = false; } // this allows for partials at end of data @@ -210,34 +215,38 @@ void IIntegrator::integrate( // done reading peak data if (result && !integrated[peak]) { - current_peak->peakData().standardizeCoords(); - if (compute(peak, shape_model, *current_peak)) { - peak->updateIntegration( - _rockingCurve, _sumBackground, _profileBackground, _meanBkgGradient, - _sumIntensity, _profileIntensity, _params.peak_end, _params.bkg_begin, - _params.bkg_end, _params.region_type); - if (saturated) - peak->setIntegrationFlag( - RejectionFlag::SaturatedPixel, _params.integrator_type); - } else { - ++nfailures; - // This is a fallback. The RejectionFlag should have been set by this point. - peak->setIntegrationFlag( - RejectionFlag::IntegrationFailure, _params.integrator_type); + ComputeResult compute_result; + if (!reintegrate) { // do not reintegrate using profile fitting + compute_result.integrator_type = _params.integrator_type; + compute_result.rocking_curve = peak->rockingCurve(); + compute_result.profile_intensity = peak->sumIntensity(); + compute_result.profile_background = peak->sumBackground(); + compute_result.integration_flag = peak->sumRejectionFlag(); + integrated[peak] = true; + } else { // do the integration + current_region->peakData().standardizeCoords(); + compute_result = compute(peak, shape_model, *current_region); } - // free memory (important!!) - current_peak->reset(); + + if (saturated) + compute_result.integration_flag = RejectionFlag::SaturatedPixel; + if (compute_result.integration_flag != RejectionFlag::NotRejected) + ++_n_failures; integrated[peak] = true; + peak->updateIntegration( + compute_result, _params.peak_end, _params.bkg_begin, _params.bkg_end, + _params.region_type); + current_region->reset(); } } if (_handler) { - ++num_frames_done; - double progress = num_frames_done * 100.0 / data->nFrames(); + ++_n_frames_done; + double progress = _n_frames_done * 100.0 / data->nFrames(); _handler->setProgress(progress); } } - ohklLog(Level::Info, "IIntegrator::integrate: end; ", nfailures, " failures"); + ohklLog(Level::Info, "IIntegrator::integrate: end; ", _n_failures, " failures"); } void IIntegrator::setHandler(sptrProgressHandler handler) @@ -295,28 +304,4 @@ void IIntegrator::removeOverlaps( ohklLog(Level::Info, "IIntegrator::removeOverlaps: ", nrejected, " overlapping peaks rejected"); } -bool IIntegrator::reintegrate(Peak3D* peak) -{ - bool reintegrate = true; - if (_params.use_max_strength) // Skip strong peaks - if (peak->sumIntensity().strength() > _params.max_strength) - reintegrate = false; - if (_params.use_max_d) // Skip low resolution peaks - if (peak->d() > _params.max_d) - reintegrate = false; - - if (!reintegrate) { - if (peak->sumRejectionFlag() == RejectionFlag::NotRejected) { - peak->updateIntegration( - _rockingCurve, peak->sumBackground(), peak->sumBackground(), - peak->meanBkgGradient(), peak->sumIntensity(), peak->sumIntensity(), - _params.peak_end, _params.bkg_begin, _params.bkg_end, _params.region_type); - } else { - peak->setIntegrationFlag(peak->sumRejectionFlag(), IntegratorType::Profile3D); - } - } - - return reintegrate; -} - } // namespace ohkl diff --git a/core/integration/IIntegrator.h b/core/integration/IIntegrator.h index 242ad0dd4..c5ad87964 100644 --- a/core/integration/IIntegrator.h +++ b/core/integration/IIntegrator.h @@ -20,15 +20,47 @@ #include "core/peak/IntegrationRegion.h" #include "core/peak/Intensity.h" +#include <optional> + namespace ohkl { enum class Level; +enum class RejectionFlag; -enum class IntegratorType { PixelSum = 0, Gaussian, ISigma, Profile1D, Profile3D, Count }; +enum class IntegratorType { PixelSum = 0, Gaussian, ISigma, Profile1D, Profile3D, Shape, Count }; /*! \addtogroup python_api * @{*/ + //! Return value of all integrator compute methods. Contains any mutable data from integration. +struct ComputeResult { + //! Construct an invalid result by default + ComputeResult(); + ComputeResult(const ComputeResult& other) = default; + ComputeResult& operator=(const ComputeResult& other) = default; + + //! Rejection flag for integration + RejectionFlag integration_flag; + //! Pixel sum integrated intensity after background subtraction + Intensity sum_intensity; + //! Profile integrated intensity after background correction + Intensity profile_intensity; + //! Mean local pixel sum background of peak. The uncertainty is the uncertainty of the + //! _estimate_ of the background. + Intensity sum_background; + //! Profile integrated background of peak. The uncertainty is the uncertainty of the + //! _estimate_ of the background. + Intensity profile_background; + //! Mean gradient of background (Gaussian statistics) + Intensity bkg_gradient; + //! The rocking curve of the peak. + std::vector<Intensity> rocking_curve; + //! Type of integrator used + IntegratorType integrator_type; + //! Shape of peak when centre/covariance are changed during integration + std::optional<Ellipsoid> shape; +}; + //! Structure containing parameters for all integrators struct IntegrationParameters { //! End of peak region for RegionType::VariableEllipsoid (sigmas) @@ -90,46 +122,37 @@ class ShapeModel; class IIntegrator { public: IIntegrator(); - virtual ~IIntegrator(); - //! Integrate all peaks in the list which are contained in the specified data set. + virtual ~IIntegrator() = default; + //! Integrate a vector of peaks void integrate(std::vector<Peak3D*> peaks, ShapeModel* shape_model, sptrDataSet data); - const std::vector<Intensity>& rockingCurve() const; //! Set the progress handler. void setHandler(sptrProgressHandler handler); + //! Assign a parameter set to the integrator + void setParameters(const IntegrationParameters& params); + //! Remove overlapping peak intensity regions + void removeOverlaps(const std::map<Peak3D*, std::unique_ptr<IntegrationRegion>>& regions); protected: - //! If IntegratorType != PixelSum, check whether we should profile integrate this peak - bool reintegrate(Peak3D* peak); - - //! Mean local pixel sum background of peak. The uncertainty is the uncertainty of the - //! _estimate_ of the background. - Intensity _sumBackground; - //! Profile integrated background of peak. The uncertainty is the uncertainty of the - //! _estimate_ of the background. - Intensity _profileBackground; - //! Mean gradient of background (Gaussian statistics) - Intensity _meanBkgGradient; - //! Pixel sum integrated intensity, after background correction. - Intensity _sumIntensity; - //! Profile integrated intensity, after background correction. - Intensity _profileIntensity; - //! The rocking curve of the peak. - std::vector<Intensity> _rockingCurve; //! Optional pointer to progress handler. sptrProgressHandler _handler; //! Container for user-defined integration parameters IntegrationParameters _params; - public: - //! Assign a parameter set to the integrator - void setParameters(const IntegrationParameters& params); + sptrDataSet _data; + std::atomic_int _n_failures; + std::atomic_int _n_frames_done; + std::atomic_int _n_peaks_done; + bool _profile_integration; + double _peak_end; + double _bkg_begin; + double _bkg_end; - //! Remove overlapping peak intensity regions - void removeOverlaps(const std::map<Peak3D*, std::unique_ptr<IntegrationRegion>>& regions); + bool _thread_parallel; + unsigned int _max_threads; private: //! Compute the integrated intensity of the peak given the integration region. - virtual bool compute( + virtual ComputeResult compute( Peak3D* peak, ShapeModel* shape_model, const IntegrationRegion& region) = 0; }; diff --git a/core/integration/ISigmaIntegrator.cpp b/core/integration/ISigmaIntegrator.cpp index 11322aa82..e75ac4950 100644 --- a/core/integration/ISigmaIntegrator.cpp +++ b/core/integration/ISigmaIntegrator.cpp @@ -25,33 +25,33 @@ namespace ohkl { ISigmaIntegrator::ISigmaIntegrator() : PixelSumIntegrator() { } -bool ISigmaIntegrator::compute( +ComputeResult ISigmaIntegrator::compute( Peak3D* peak, ShapeModel* shape_model, const IntegrationRegion& region) { + ComputeResult result; + result.integrator_type = IntegratorType::ISigma; + if (!shape_model) { - peak->setIntegrationFlag(RejectionFlag::NoShapeModel, IntegratorType::ISigma); - return false; + result.integration_flag = RejectionFlag::NoShapeModel; + return result; } - if (!peak) - return false; - // first get mean background - PixelSumIntegrator::compute(peak, shape_model, region); - const double mean_bkg = _sumBackground.value(); - const double var_bkg = _sumBackground.variance(); + const ComputeResult pxsum_result = PixelSumIntegrator::compute(peak, shape_model, region); + const double mean_bkg = pxsum_result.sum_background.value(); + const double var_bkg = pxsum_result.sum_background.variance(); const auto& events = region.peakData().events(); const auto& counts = region.peakData().counts(); // TODO: should this be hard-coded?? if (events.size() < 29) { - peak->setIntegrationFlag(RejectionFlag::TooFewPoints, IntegratorType::ISigma); - return false; + result.integration_flag = RejectionFlag::TooFewPoints; + return result; } - Eigen::Vector3d c = peak->shape().center(); - Eigen::Matrix3d A = peak->shape().metric(); + const Eigen::Vector3d c = peak->shape().center(); + const Eigen::Matrix3d A = peak->shape().metric(); Profile1D profile; std::vector<Intensity> mean_profile = shape_model->meanProfile1D(DetectorEvent(c)); @@ -87,26 +87,26 @@ bool ISigmaIntegrator::compute( // something went wrong (nans?) if (best_idx < 0) { - peak->setIntegrationFlag(RejectionFlag::NoISigmaMinimum, IntegratorType::ISigma); - return false; + result.integration_flag = RejectionFlag::NoISigmaMinimum; + return result; } const double M = profile.counts()[best_idx]; const int n = profile.npoints()[best_idx]; - _profileIntensity = Intensity(M - n * mean_bkg, M + n * n * var_bkg); - _profileIntensity = _profileIntensity / mean_profile[best_idx]; + result.profile_intensity = Intensity(M - n * mean_bkg, M + n * n * var_bkg); + result.profile_intensity = result.profile_intensity / mean_profile[best_idx]; - double sigma = _profileIntensity.sigma(); + const double sigma = result.profile_intensity.sigma(); if (std::isnan(sigma) && sigma > 0) { - peak->setIntegrationFlag(RejectionFlag::InvalidSigma, IntegratorType::ISigma); - return false; + result.integration_flag = RejectionFlag::InvalidSigma; + return result; } - _sumIntensity = {}; - _sumBackground = {}; + result.sum_intensity = {}; + result.sum_background = {}; - return true; + return result; } } // namespace ohkl diff --git a/core/integration/ISigmaIntegrator.h b/core/integration/ISigmaIntegrator.h index 799133986..397fdc319 100644 --- a/core/integration/ISigmaIntegrator.h +++ b/core/integration/ISigmaIntegrator.h @@ -42,7 +42,8 @@ class ISigmaIntegrator : public PixelSumIntegrator { ISigmaIntegrator(); protected: - bool compute(Peak3D* peak, ShapeModel* shape_model, const IntegrationRegion& region) override; + ComputeResult compute( + Peak3D* peak, ShapeModel* shape_model, const IntegrationRegion& region) override; }; /*! @}*/ diff --git a/core/integration/PixelSumIntegrator.cpp b/core/integration/PixelSumIntegrator.cpp index ed5aae273..3cffcfaf1 100644 --- a/core/integration/PixelSumIntegrator.cpp +++ b/core/integration/PixelSumIntegrator.cpp @@ -13,6 +13,7 @@ // *********************************************************************************************** #include "core/integration/PixelSumIntegrator.h" + #include "base/geometry/Ellipsoid.h" #include "base/utils/Logger.h" #include "core/data/DataSet.h" @@ -91,31 +92,34 @@ PixelSumIntegrator::PixelSumIntegrator() : IIntegrator() _params.fit_cov = true; } -bool PixelSumIntegrator::compute(Peak3D* peak, ShapeModel*, const IntegrationRegion& region) +ComputeResult PixelSumIntegrator::compute(Peak3D* peak, ShapeModel*, const IntegrationRegion& region) { + ComputeResult result; + result.integrator_type = IntegratorType::PixelSum; + const auto& events = region.peakData().events(); const auto& counts = region.peakData().counts(); if (events.empty()) { - peak->setIntegrationFlag(RejectionFlag::TooFewPoints, IntegratorType::PixelSum); - return false; + result.integration_flag = RejectionFlag::TooFewPoints; + return result; } auto [meanBackground, bkgGradient] = compute_background(region, _params.use_gradient); if (!meanBackground.isValid()) { - peak->setIntegrationFlag(RejectionFlag::TooFewPoints, IntegratorType::PixelSum); - return false; + result.integration_flag = RejectionFlag::TooFewPoints; + return result; } - _sumBackground = meanBackground; - _meanBkgGradient = bkgGradient; + result.sum_background = meanBackground; + result.bkg_gradient = bkgGradient; - PeakCoordinateSystem frame(peak); + const PeakCoordinateSystem frame(peak); double sum_peak = 0.0; - const double mean_bkg = _sumBackground.value(); + const double mean_bkg = result.sum_background.value(); // note that this is the std of the _estimate_ of the background // should be approximately mean_bkg / num_bkg for Poisson statistics - const double std_bkg = _sumBackground.sigma(); + const double std_bkg = result.sum_background.sigma(); size_t npeak = 0.0; Blob3D blob; @@ -126,9 +130,8 @@ bool PixelSumIntegrator::compute(Peak3D* peak, ShapeModel*, const IntegrationReg const auto& ev = events[i]; auto ev_type = region.classify(ev); - if (ev_type == IntegrationRegion::EventType::BACKGROUND) { + if (ev_type == IntegrationRegion::EventType::BACKGROUND) continue; - } if (ev_type == IntegrationRegion::EventType::PEAK) { sum_peak += counts[i]; @@ -140,11 +143,11 @@ bool PixelSumIntegrator::compute(Peak3D* peak, ShapeModel*, const IntegrationReg } } - sum_peak -= npeak * _sumBackground.value(); + sum_peak -= npeak * result.sum_background.value(); // TODO: ERROR ESTIMATE!! // This INCORRECTLY assumes Poisson statistics (no gain or baseline) - _sumIntensity = + result.sum_intensity = Intensity(sum_peak, sum_peak + npeak * mean_bkg + npeak * npeak * std_bkg * std_bkg); // TODO: compute rocking curve @@ -164,8 +167,8 @@ bool PixelSumIntegrator::compute(Peak3D* peak, ShapeModel*, const IntegrationReg if (blob.isValid()) { center = blob.center(); } else { - peak->setIntegrationFlag(RejectionFlag::InvalidCentroid, IntegratorType::PixelSum); - return false; + result.integration_flag = RejectionFlag::InvalidCentroid; + return result; } } else { center = peak->shape().center(); @@ -175,8 +178,8 @@ bool PixelSumIntegrator::compute(Peak3D* peak, ShapeModel*, const IntegrationReg if (blob.isValid()) { cov = blob.covariance(); } else { - peak->setIntegrationFlag(RejectionFlag::InvalidCentroid, IntegratorType::PixelSum); - return false; + result.integration_flag = RejectionFlag::InvalidCentroid; + return result; } } else { cov = peak->shape().inverseMetric(); @@ -184,37 +187,37 @@ bool PixelSumIntegrator::compute(Peak3D* peak, ShapeModel*, const IntegrationReg // center of mass is consistent if (std::isnan(center.norm())) { - peak->setIntegrationFlag(RejectionFlag::InvalidCentroid, IntegratorType::PixelSum); - return false; + result.integration_flag = RejectionFlag::InvalidCentroid; + return result; } if (!peak->shape().isInside(center)) { - peak->setIntegrationFlag(RejectionFlag::InvalidCentroid, IntegratorType::PixelSum); - return false; + result.integration_flag = RejectionFlag::InvalidCentroid; + return result; } - Eigen::Matrix3d A0 = peak->shape().metric(); - Eigen::Matrix3d A1 = cov.inverse(); + const Eigen::Matrix3d A0 = peak->shape().metric(); + const Eigen::Matrix3d A1 = cov.inverse(); const double dA = (A1 - A0).norm() / A0.norm(); // check that the covariance is consistent if (!(dA < 2.0)) { - peak->setIntegrationFlag(RejectionFlag::InvalidCovariance, IntegratorType::PixelSum); - return false; + result.integration_flag = RejectionFlag::InvalidCovariance; + return result; } // shape is not too small or too large - Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> solver(cov); + const Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> solver(cov); const auto& w = solver.eigenvalues(); if (w.minCoeff() < 0.1 || w.maxCoeff() > 100) { - peak->setIntegrationFlag(RejectionFlag::InvalidShape, IntegratorType::PixelSum); - return false; + result.integration_flag = RejectionFlag::InvalidShape; + return result; } - peak->setShape(Ellipsoid(center, A1)); + result.shape = Ellipsoid(center, A1); - size_t nframes = size_t(f_max - f_min) + 1; - _rockingCurve.resize(nframes); + const size_t nframes = size_t(f_max - f_min) + 1; + result.rocking_curve.resize(nframes); std::vector<double> intensity_per_frame(nframes, 0.0); std::vector<double> n_peak_points_per_frame(nframes, 0.0); @@ -236,12 +239,12 @@ bool PixelSumIntegrator::compute(Peak3D* peak, ShapeModel*, const IntegrationReg for (int i = 0; i < nframes; ++i) { const double corrected_intensity = intensity_per_frame[i] - n_peak_points_per_frame[i] * mean_bkg; - _rockingCurve[i] = Intensity(corrected_intensity, sqrt(corrected_intensity)); + result.rocking_curve[i] = Intensity(corrected_intensity, sqrt(corrected_intensity)); } - _profileIntensity = {}; - _profileBackground = {}; + result.profile_intensity = {}; + result.profile_background = {}; - return true; + return result; } } // namespace ohkl diff --git a/core/integration/PixelSumIntegrator.h b/core/integration/PixelSumIntegrator.h index 60139d9a9..d42e659d3 100644 --- a/core/integration/PixelSumIntegrator.h +++ b/core/integration/PixelSumIntegrator.h @@ -33,7 +33,7 @@ class PixelSumIntegrator : public IIntegrator { protected: //! Integrate a peak - bool compute(Peak3D* peak, ShapeModel*, const IntegrationRegion& region) override; + ComputeResult compute(Peak3D* peak, ShapeModel*, const IntegrationRegion& region) override; }; /*! @}*/ diff --git a/core/integration/Profile1DIntegrator.cpp b/core/integration/Profile1DIntegrator.cpp index feb17e6eb..26fee4cc5 100644 --- a/core/integration/Profile1DIntegrator.cpp +++ b/core/integration/Profile1DIntegrator.cpp @@ -21,13 +21,11 @@ #include "core/peak/PeakCoordinateSystem.h" #include "core/shape/ShapeModel.h" -namespace ohkl { - -Profile1DIntegrator::Profile1DIntegrator() : IIntegrator() { } - -static void updateFit( - Intensity& I, Intensity& B, const std::vector<double>& dp, const std::vector<double>& dM, - const std::vector<int>& dn) +namespace { + +void updateFit( + ohkl::Intensity& I, ohkl::Intensity& B, const std::vector<double>& dp, + const std::vector<double>& dM, const std::vector<int>& dn) { Eigen::Matrix2d A; A.setZero(); @@ -72,36 +70,42 @@ static void updateFit( // Note: this error estimate assumes the variances are correct (i.e., gain and // baseline accounted for) - B = Intensity(new_B, cov(0, 0)); - I = Intensity(new_I, cov(1, 1)); + B = ohkl::Intensity(new_B, cov(0, 0)); + I = ohkl::Intensity(new_I, cov(1, 1)); } -bool Profile1DIntegrator::compute( +} // namespace + +namespace ohkl { + +Profile1DIntegrator::Profile1DIntegrator() : IIntegrator() { } + +ComputeResult Profile1DIntegrator::compute( Peak3D* peak, ShapeModel* shape_model, const IntegrationRegion& region) { + ComputeResult result; + result.integrator_type = IntegratorType::Profile1D; + if (!shape_model) { - peak->setIntegrationFlag(RejectionFlag::NoShapeModel, IntegratorType::Profile1D); - return false; + result.integration_flag = RejectionFlag::NoShapeModel; + return result; } - if (!peak) - return false; - const auto& events = region.peakData().events(); const auto& counts = region.peakData().counts(); // TODO: should this be hard-coded?? if (events.size() < 29) { - peak->setIntegrationFlag(RejectionFlag::TooFewPoints, IntegratorType::Profile1D); - return false; + result.integration_flag = RejectionFlag::TooFewPoints; + return result; } - Eigen::Vector3d c = peak->shape().center(); - Eigen::Matrix3d A = peak->shape().metric(); + const Eigen::Vector3d c = peak->shape().center(); + const Eigen::Matrix3d A = peak->shape().metric(); Profile1D profile(0.0, region.peakEnd()); - std::vector<Intensity> mean_profile = shape_model->meanProfile1D(DetectorEvent(c)); + const std::vector<Intensity> mean_profile = shape_model->meanProfile1D(DetectorEvent(c)); // construct the observed profile for (size_t i = 0; i < events.size(); ++i) { @@ -134,22 +138,22 @@ bool Profile1DIntegrator::compute( for (auto i = 0; i < 10 && I.value() > 0; ++i) updateFit(I, B, dp, dm, dn); - double sigma = I.sigma(); + const double sigma = I.sigma(); if (std::isnan(sigma) || sigma <= 0.0) { - peak->setIntegrationFlag(RejectionFlag::InvalidSigma, IntegratorType::Profile1D); - return false; + result.integration_flag = RejectionFlag::InvalidSigma; + return result; } - _profileIntensity = I; - _profileBackground = B; + result.profile_intensity = I; + result.profile_background = B; - _sumIntensity = {}; - _sumBackground = {}; + result.sum_intensity = {}; + result.sum_background = {}; // TODO: rocking curve! - return true; + return result; } } // namespace ohkl diff --git a/core/integration/Profile1DIntegrator.h b/core/integration/Profile1DIntegrator.h index 306e4b8bc..2a503a2cb 100644 --- a/core/integration/Profile1DIntegrator.h +++ b/core/integration/Profile1DIntegrator.h @@ -41,7 +41,8 @@ class Profile1DIntegrator : public IIntegrator { protected: //! Integrate a peak - bool compute(Peak3D* peak, ShapeModel* shape_model, const IntegrationRegion& region) override; + ComputeResult compute( + Peak3D* peak, ShapeModel* shape_model, const IntegrationRegion& region) override; }; /*! @}*/ diff --git a/core/integration/Profile3DIntegrator.cpp b/core/integration/Profile3DIntegrator.cpp index 4a5c57ef2..ff2c616f1 100644 --- a/core/integration/Profile3DIntegrator.cpp +++ b/core/integration/Profile3DIntegrator.cpp @@ -18,10 +18,10 @@ #include "core/peak/Peak3D.h" #include "core/shape/ShapeModel.h" -namespace ohkl { +namespace { -static void updateFit( - Intensity& I, Intensity& B, const std::vector<double>& profile, +void updateFit( + ohkl::Intensity& I, ohkl::Intensity& B, const std::vector<double>& profile, const std::vector<double>& counts) { Eigen::Matrix2d A; @@ -43,7 +43,7 @@ static void updateFit( b(1) += M * p / var; } - Eigen::Matrix2d AI = A.inverse(); + const Eigen::Matrix2d AI = A.inverse(); const Eigen::Vector2d& x = AI * b; const double new_B = x(0); @@ -54,33 +54,37 @@ static void updateFit( // Note: this error estimate assumes the variances are correct (i.e., gain and // baseline accounted for) - B = Intensity(new_B, cov(0, 0)); - I = Intensity(new_I, cov(1, 1)); + B = ohkl::Intensity(new_B, cov(0, 0)); + I = ohkl::Intensity(new_I, cov(1, 1)); +} + } -bool Profile3DIntegrator::compute( +namespace ohkl { + +ComputeResult Profile3DIntegrator::compute( Peak3D* peak, ShapeModel* shape_model, const IntegrationRegion& region) { + ComputeResult result; + result.integrator_type = IntegratorType::Profile3D; + if (!shape_model) { - peak->setIntegrationFlag(RejectionFlag::NoShapeModel, IntegratorType::Profile3D); - return false; + result.integration_flag = RejectionFlag::NoShapeModel; + return result; } - if (!peak) - return false; - const auto& events = region.peakData().events(); const auto& counts = region.peakData().counts(); // TODO: should this be hard-coded?? if (events.size() < 29) { - peak->setIntegrationFlag(RejectionFlag::TooFewPoints, IntegratorType::Profile3D); - return false; + result.integration_flag = RejectionFlag::TooFewPoints; + return result; } // dummy value for initial guess - _profileBackground = Intensity(1.0, 1.0); - _profileIntensity = Intensity(0.0, 0.0); + result.profile_background = Intensity(1.0, 1.0); + result.profile_intensity = Intensity(0.0, 0.0); std::vector<double> profile; std::vector<double> obs_counts; @@ -90,11 +94,9 @@ bool Profile3DIntegrator::compute( const double tolerance = 1e-5; - DetectorEvent event(peak->shape().center()); - - Profile3D model_profile = shape_model->meanProfile(event); - - PeakCoordinateSystem coord(peak); + const DetectorEvent event(peak->shape().center()); + const Profile3D model_profile = shape_model->meanProfile(event); + const PeakCoordinateSystem coord(peak); // evaluate the model profile at the given events for (int i = 0; i < events.size(); ++i) { @@ -116,13 +118,13 @@ bool Profile3DIntegrator::compute( // todo: stopping criterion for (auto i = 0; i < 20; ++i) { - Intensity old_intensity = _profileIntensity; - const double I0 = _profileIntensity.value(); - updateFit(_profileIntensity, _profileBackground, profile, obs_counts); - const double I1 = _profileIntensity.value(); + const Intensity old_intensity = result.profile_intensity; + const double I0 = result.profile_intensity.value(); + updateFit(result.profile_intensity, result.profile_background, profile, obs_counts); + const double I1 = result.profile_intensity.value(); - if (std::isnan(I1) || std::isnan(_profileBackground.value())) { - _profileIntensity = old_intensity; + if (std::isnan(I1) || std::isnan(result.profile_background.value())) { + result.profile_intensity = old_intensity; break; } @@ -130,16 +132,16 @@ bool Profile3DIntegrator::compute( break; } - double sigma = _profileIntensity.sigma(); + const double sigma = result.profile_intensity.sigma(); if (std::isnan(sigma) && sigma > 0) { - peak->setIntegrationFlag(RejectionFlag::InvalidSigma, IntegratorType::Profile3D); - return false; + result.integration_flag = RejectionFlag::InvalidSigma; + return result; } - _sumIntensity = {}; - _sumBackground = {}; + result.sum_intensity = {}; + result.sum_background = {}; - return true; + return result; } } // namespace ohkl diff --git a/core/integration/Profile3DIntegrator.h b/core/integration/Profile3DIntegrator.h index 04a9eede9..bae5085f6 100644 --- a/core/integration/Profile3DIntegrator.h +++ b/core/integration/Profile3DIntegrator.h @@ -54,7 +54,8 @@ class Profile3DIntegrator : public IIntegrator { protected: //! Do the integration - bool compute(Peak3D* peak, ShapeModel* shape_model, const IntegrationRegion& region) override; + ComputeResult compute( + Peak3D* peak, ShapeModel* shape_model, const IntegrationRegion& region) override; }; /*! @}*/ diff --git a/core/integration/ShapeIntegrator.cpp b/core/integration/ShapeIntegrator.cpp index e3bc6a1da..daeb331ba 100644 --- a/core/integration/ShapeIntegrator.cpp +++ b/core/integration/ShapeIntegrator.cpp @@ -15,6 +15,7 @@ #include "core/integration/ShapeIntegrator.h" #include "base/geometry/Ellipsoid.h" #include "core/data/DataSet.h" +#include "core/integration/IIntegrator.h" #include "core/peak/Intensity.h" #include "core/peak/Peak3D.h" #include "core/peak/PeakCoordinateSystem.h" @@ -36,34 +37,32 @@ ShapeIntegrator::ShapeIntegrator( { } -bool ShapeIntegrator::compute( +ComputeResult ShapeIntegrator::compute( Peak3D* peak, ShapeModel* shape_model, const IntegrationRegion& region) { + ComputeResult result; + result.integrator_type = IntegratorType::Shape; + const UnitCell* uc = peak->unitCell(); auto data = peak->dataSet(); if (!uc) { - peak->setIntegrationFlag(RejectionFlag::NoUnitCell, IntegratorType::PixelSum); - return false; - } - - if (!data) { - peak->setIntegrationFlag(RejectionFlag::NoDataSet, IntegratorType::PixelSum); - return false; + result.integration_flag = RejectionFlag::NoUnitCell; + return result; } - PixelSumIntegrator::compute(peak, shape_model, region); + const ComputeResult pxsum_result = PixelSumIntegrator::compute(peak, shape_model, region); - const double mean_bkg = _sumBackground.value(); + const double mean_bkg = pxsum_result.sum_background.value(); const auto& events = region.peakData().events(); const auto& counts = region.peakData().counts(); Profile3D profile(_aabb, _nx, _ny, _nz); // todo: don't use default constructor! - Profile1D integrated_profile(_sumBackground, region.peakEnd()); - PeakCoordinateSystem frame(peak); + Profile1D integrated_profile(pxsum_result.sum_background, region.peakEnd()); + const PeakCoordinateSystem frame(peak); - Ellipsoid e = peak->shape(); + const Ellipsoid e = peak->shape(); for (size_t i = 0; i < events.size(); ++i) { const auto& ev = events[i]; @@ -87,7 +86,7 @@ bool ShapeIntegrator::compute( } if (profile.normalize()) _shape_model->addPeak(peak, std::move(profile), std::move(integrated_profile)); - return true; + return result; } const ShapeModel* ShapeIntegrator::shapeModel() const diff --git a/core/integration/ShapeIntegrator.h b/core/integration/ShapeIntegrator.h index f1e79d846..ea7b07d73 100644 --- a/core/integration/ShapeIntegrator.h +++ b/core/integration/ShapeIntegrator.h @@ -32,7 +32,8 @@ class ShapeIntegrator : public PixelSumIntegrator { protected: //! Integrate a peak - bool compute(Peak3D* peak, ShapeModel* shape_model, const IntegrationRegion& region) override; + ComputeResult compute( + Peak3D* peak, ShapeModel* shape_model, const IntegrationRegion& region) override; //! Returns the collection of cached peak shapes const ShapeModel* shapeModel() const; diff --git a/core/peak/IntegrationRegion.cpp b/core/peak/IntegrationRegion.cpp index efd986e47..83d0ca5a8 100644 --- a/core/peak/IntegrationRegion.cpp +++ b/core/peak/IntegrationRegion.cpp @@ -19,6 +19,7 @@ #include "core/instrument/Diffractometer.h" #include "core/integration/IIntegrator.h" #include "core/peak/Peak3D.h" +#include "core/peak/PeakData.h" #include "tables/crystal/UnitCell.h" namespace ohkl { @@ -26,14 +27,14 @@ namespace ohkl { IntegrationRegion::IntegrationRegion( Peak3D* peak, double peak_end, double bkg_begin, double bkg_end, RegionType region_type /* = RegionType::VariableEllipsoid */) - : _bkgBegin(bkg_begin) + : _shape(peak->shape()) + , _bkgBegin(bkg_begin) , _bkgEnd(bkg_end) , _data(peak) , _regionType(region_type) , _peak(peak) , _valid(true) { - _shape = peak->shape(); if (_shape.aabb().lower().hasNaN() || _shape.aabb().upper().hasNaN()) { peak->setIntegrationFlag(RejectionFlag::InvalidShape, IntegratorType::PixelSum); peak->setIntegrationFlag(RejectionFlag::InvalidShape, IntegratorType::Profile3D); @@ -132,7 +133,7 @@ void IntegrationRegion::updateMask(Eigen::MatrixXi& mask, double z) const if (val == EventType::FORBIDDEN) continue; - DetectorEvent ev(x, y, z); + const DetectorEvent ev(x, y, z); auto ev_type = classify(ev); switch (ev_type) { @@ -168,8 +169,8 @@ RegionData* IntegrationRegion::getRegion() xmax = std::min(xmax, long(data->nCols())); ymax = std::min(ymax, long(data->nRows())); - int zmin = std::ceil(lower[2]); - int zmax = std::floor(upper[2]); + const int zmin = std::ceil(lower[2]); + const int zmax = std::floor(upper[2]); _region_data = RegionData(this, xmin, xmax, ymin, ymax, zmin, zmax); @@ -185,7 +186,7 @@ RegionData* IntegrationRegion::getRegion() for (auto y = ymin; y < ymax; ++y) { EventType val = EventType::EXCLUDED; - DetectorEvent ev(x, y, z); + const DetectorEvent ev(x, y, z); auto ev_type = classify(ev); switch (ev_type) { @@ -237,7 +238,8 @@ IntegrationRegion::EventType IntegrationRegion::classify(const DetectorEvent& ev } bool IntegrationRegion::advanceFrame( - const Eigen::MatrixXd& image, const Eigen::MatrixXi& mask, double frame) + const Eigen::MatrixXd& image, const Eigen::MatrixXi& mask, double frame, + const Eigen::MatrixXd* gradient /* = nullptr */) { const auto aabb = _hull.aabb(); auto lower = aabb.lower(); @@ -249,28 +251,30 @@ bool IntegrationRegion::advanceFrame( if (frame > upper[2]) return true; - long xmin = std::max(0L, std::lround(lower[0])); - long ymin = std::max(0L, std::lround(lower[1])); + const long xmin = std::max(0L, std::lround(lower[0])); + const long ymin = std::max(0L, std::lround(lower[1])); - long xmax = std::min(long(image.cols()), std::lround(upper[0])); - long ymax = std::min(long(image.rows()), std::lround(upper[1])); + const long xmax = std::min(long(image.cols()), std::lround(upper[0])); + const long ymax = std::min(long(image.rows()), std::lround(upper[1])); for (auto x = xmin; x < xmax; ++x) { for (auto y = ymin; y < ymax; ++y) { - EventType mask_type = EventType(mask(y, x)); + const EventType mask_type = EventType(mask(y, x)); if (mask_type == EventType::FORBIDDEN) continue; - DetectorEvent ev(x, y, frame); - Eigen::Vector3d p(x, y, frame); + const DetectorEvent ev(x, y, frame); + const Eigen::Vector3d p(x, y, frame); auto event_type = classify(ev); - if (event_type == EventType::PEAK) { - _data.addEvent(ev, image(y, x)); - } + double grad = 0; + if (gradient) + grad = (*gradient)(y, x); - if (event_type == EventType::BACKGROUND && mask_type == EventType::BACKGROUND) { - _data.addEvent(ev, image(y, x)); - } + if (event_type == EventType::PEAK) + _data.addEvent(ev, image(y, x), grad); + + if (event_type == EventType::BACKGROUND && mask_type == EventType::BACKGROUND) + _data.addEvent(ev, image(y, x), grad); // check if point is in Brillouin zone (or AABB if no UC available) // if (_hull.contains(p)) { @@ -281,53 +285,51 @@ bool IntegrationRegion::advanceFrame( return false; } -bool IntegrationRegion::advanceFrame( +PeakData IntegrationRegion::threadSafeAdvanceFrame( const Eigen::MatrixXd& image, const Eigen::MatrixXi& mask, double frame, - const Eigen::MatrixXd& gradient) + const Eigen::MatrixXd* gradient /* = nullptr */) { + PeakData peak_data(_peak); + const auto aabb = _hull.aabb(); auto lower = aabb.lower(); auto upper = aabb.upper(); if (frame < lower[2]) - return false; + return peak_data; if (frame > upper[2]) - return true; + return peak_data; - long xmin = std::max(0L, std::lround(lower[0])); - long ymin = std::max(0L, std::lround(lower[1])); + const long xmin = std::max(0L, std::lround(lower[0])); + const long ymin = std::max(0L, std::lround(lower[1])); - long xmax = std::min(long(image.cols()), std::lround(upper[0])); - long ymax = std::min(long(image.rows()), std::lround(upper[1])); + const long xmax = std::min(long(image.cols()), std::lround(upper[0])); + const long ymax = std::min(long(image.rows()), std::lround(upper[1])); for (auto x = xmin; x < xmax; ++x) { for (auto y = ymin; y < ymax; ++y) { - EventType mask_type = EventType(mask(y, x)); + const EventType mask_type = EventType(mask(y, x)); if (mask_type == EventType::FORBIDDEN) continue; - DetectorEvent ev(x, y, frame); - Eigen::Vector3d p(x, y, frame); + const DetectorEvent ev(x, y, frame); + const Eigen::Vector3d p(x, y, frame); auto event_type = classify(ev); - if (event_type == EventType::PEAK) { - _data.addEvent(ev, image(y, x), gradient(y, x)); - } + double grad = 0; + if (gradient) + grad = (*gradient)(y, x); - if (event_type == EventType::BACKGROUND && mask_type == EventType::BACKGROUND) { - _data.addEvent(ev, image(y, x), gradient(y, x)); - } + if (event_type == EventType::PEAK) + peak_data.addEvent(ev, image(y, x), grad); - // check if point is in Brillouin zone (or AABB if no UC available) - // if (_hull.contains(p)) { - // _data.addEvent(ev, image(y,x)); - //} + if (event_type == EventType::BACKGROUND && mask_type == EventType::BACKGROUND) + peak_data.addEvent(ev, image(y, x), grad); } } - return false; + return peak_data; } - void IntegrationRegion::reset() { _data.reset(); @@ -343,6 +345,12 @@ PeakData& IntegrationRegion::peakData() return _data; } +void IntegrationRegion::appendPeakData(const PeakData& peak_data) +{ + if (!peak_data.empty()) + _data.append(peak_data); +} + const Ellipsoid& IntegrationRegion::shape() const { return _shape; diff --git a/core/peak/IntegrationRegion.h b/core/peak/IntegrationRegion.h index 0c4e5858b..09ba78152 100644 --- a/core/peak/IntegrationRegion.h +++ b/core/peak/IntegrationRegion.h @@ -57,17 +57,21 @@ class IntegrationRegion { //! Classify a detector event (peak, background, forbidden, etc.) EventType classify(const DetectorEvent& ev) const; //! Update the region with the next frame - bool advanceFrame(const Eigen::MatrixXd& image, const Eigen::MatrixXi& mask, double frame); - //! Update the region with the next frame bool advanceFrame( const Eigen::MatrixXd& image, const Eigen::MatrixXi& mask, double frame, - const Eigen::MatrixXd& gradient); + const Eigen::MatrixXd* gradient = nullptr); + //! Update the region with the next frame (thread safe) + PeakData threadSafeAdvanceFrame( + const Eigen::MatrixXd& image, const Eigen::MatrixXi& mask, double frame, + const Eigen::MatrixXd* gradient = nullptr); //! Reset the integration region (i.e. free memory) void reset(); //! Returns the underlying data stored by the region const PeakData& peakData() const; //! Returns the data stored by the region PeakData& peakData(); + //! Append PeakData to this instance + void appendPeakData(const PeakData& peak_data); //! Returns the peak shape used by the region const Ellipsoid& shape() const; //! Returns the convex hull of the region (e.g. BrillouinZone) diff --git a/core/peak/Peak3D.cpp b/core/peak/Peak3D.cpp index 9aeb062b8..3da3d2519 100644 --- a/core/peak/Peak3D.cpp +++ b/core/peak/Peak3D.cpp @@ -61,6 +61,7 @@ const std::map<RejectionFlag, std::string> Peak3D::_rejection_map{ {RejectionFlag::NoShapeModel, "No shape model found"}, {RejectionFlag::NoISigmaMinimum, "Failed to find minimum of I/Sigma"}, {RejectionFlag::TooWide, "Bounding box spans too many images"}, + {RejectionFlag::BadGaussianFit, "Unable to fit Gaussian to peak"}, {RejectionFlag::PredictionUpdateFailure, "Failure updating prediction post-refinement"}, {RejectionFlag::ManuallyRejected, "Manually unselected by user"}, {RejectionFlag::OutsideIndexingTol, "Outside indexing tolerance"}, @@ -110,26 +111,26 @@ Peak3D::Peak3D(sptrDataSet data, const MillerIndex& hkl) } Peak3D::Peak3D(std::shared_ptr<ohkl::Peak3D> peak) + : _sumIntensity(peak->sumIntensity()) + , _profileIntensity(peak->profileIntensity()) + , _sumBackground(peak->sumBackground()) + , _profileBackground(peak->profileBackground()) + , _meanBkgGradient(peak->meanBkgGradient()) + , _peakEnd(peak->peakEnd()) + , _bkgBegin(peak->bkgBegin()) + , _bkgEnd(peak->bkgEnd()) + , _regionType(peak->regionType()) + , _hkl(peak->hkl()) + , _unitCell(peak->_unitCell) + , _scale(peak->scale()) + , _caught_by_filter(false) + , _rejected_by_filter(false) + , _transmission(peak->transmission()) + , _data(peak->dataSet()) + , _rockingCurve(peak->rockingCurve()) + { setShape(peak->shape()); - _sumIntensity = peak->sumIntensity(); - _profileIntensity = peak->profileIntensity(); - _sumBackground = peak->sumBackground(); - _profileBackground = peak->profileBackground(); - _meanBkgGradient = peak->meanBkgGradient(); - _peakEnd = peak->peakEnd(); - _bkgBegin = peak->bkgBegin(); - _bkgEnd = peak->bkgEnd(); - _regionType = peak->regionType(); - _unitCell = peak->_unitCell; - _scale = peak->scale(); - _transmission = peak->transmission(); - _data = peak->dataSet(); - _rockingCurve = peak->rockingCurve(); - _hkl = peak->hkl(); - - _caught_by_filter = false; - _rejected_by_filter = false; } void Peak3D::setShape(const Ellipsoid& shape) @@ -164,8 +165,7 @@ const UnitCell* Peak3D::unitCell() const { if (auto uc = _unitCell.lock()) return uc.get(); - else - return nullptr; + return nullptr; } Intensity Peak3D::correctedIntensity(const Intensity& intensity) const @@ -173,16 +173,16 @@ Intensity Peak3D::correctedIntensity(const Intensity& intensity) const auto c = _shape.center(); auto state = InterpolatedState::interpolate(_data->instrumentStates(), c[2]); if (!state.isValid()) // Interpolation error - return Intensity(); + return {}; auto diff = state.diffractometer(); if (diff == nullptr) { - return Intensity(); + return {}; } auto detector = diff->detector(); if (detector == nullptr) { - return Intensity(); + return {}; } const double lorentz = state.lorentzFactor(c[0], c[1]); @@ -235,35 +235,40 @@ void Peak3D::reject(RejectionFlag flag) } void Peak3D::updateIntegration( - const std::vector<Intensity>& rockingCurve, const Intensity& sumBkg, const Intensity& profBkg, - const Intensity& meanBkgGradient, const Intensity& sumInt, const Intensity& profInt, - double peakEnd, double bkgBegin, double bkgEnd, const RegionType& regionType) -{ - _rockingCurve = rockingCurve; - _meanBkgGradient = meanBkgGradient; - if (sumBkg.isValid()) - _sumBackground = sumBkg; - if (sumInt.isValid()) { // Default intensity constructor is zero, _valid = false - if (sumInt.sigma() < _sigma2_eps) // NaN sigma handled by Intensity constructor - setIntegrationFlag(RejectionFlag::InvalidSigma, IntegratorType::PixelSum); - else - _sumIntensity = sumInt; - } - if (profBkg.isValid()) - _profileBackground = profBkg; - if (profInt.isValid()) { - if (profInt.sigma() < _sigma2_eps) // NaN sigma handled by Intensity constructor - setIntegrationFlag(RejectionFlag::InvalidSigma, IntegratorType::Profile3D); - else - _profileIntensity = profInt; - } + const ComputeResult& result, double peakEnd, double bkgBegin, double bkgEnd, + const RegionType& regionType) +{ + if (result.integrator_type == IntegratorType::Shape) + return; - //_sumIntensity = integrator.peakIntensity(); // TODO: test, reactivate ??? - //_shape = integrator.fitShape(); // TODO: test, reactivate ??? _peakEnd = peakEnd; _bkgBegin = bkgBegin; _bkgEnd = bkgEnd; _regionType = regionType; + _rockingCurve = result.rocking_curve; + + _rockingCurve = result.rocking_curve; + if (result.integrator_type == IntegratorType::PixelSum) + _meanBkgGradient = result.bkg_gradient; + + if (result.shape) + setShape(result.shape.value()); + + setIntegrationFlag(result.integration_flag, result.integrator_type, true); + + if (result.sum_background.isValid()) + _sumBackground = result.sum_background; + + if (result.sum_intensity.isValid()) + _sumIntensity = result.sum_intensity; + + if (result.profile_background.isValid()) + _profileBackground = result.profile_background; + + if (result.profile_intensity.isValid()) + _profileIntensity = result.profile_intensity; + + //_shape = integrator.fitShape(); // TODO: test, reactivate ??? } ReciprocalVector Peak3D::q() const @@ -276,7 +281,7 @@ ReciprocalVector Peak3D::q() const double Peak3D::d() const { - double modq = q().rowVector().norm(); + const double modq = q().rowVector().norm(); if (modq < _sigma2_eps) return 0.0; return 1.0 / modq; @@ -311,15 +316,15 @@ Ellipsoid Peak3D::qShape() const throw std::range_error("Interpolation error"); } - Eigen::Vector3d q0 = q().rowVector(); + const Eigen::Vector3d q0 = q().rowVector(); // Jacobian of map from detector coords to sample q space - Eigen::Matrix3d J = state.jacobianQ(p[0], p[1]); + const Eigen::Matrix3d J = state.jacobianQ(p[0], p[1]); const Eigen::Matrix3d JI = J.inverse(); // inverse covariance matrix in sample q space const Eigen::Matrix3d q_inv_cov = JI.transpose() * _shape.metric() * JI; - return Ellipsoid(q0, q_inv_cov); + return {q0, q_inv_cov}; } bool Peak3D::caughtByFilter() const @@ -389,7 +394,7 @@ const MillerIndex& Peak3D::hkl() const void Peak3D::setMillerIndices() { if (unitCell()) { - ReciprocalVector rv = q(); + const ReciprocalVector rv = q(); if (rv.isValid()) { _hkl = MillerIndex(q(), *unitCell()); } else { @@ -429,6 +434,13 @@ void Peak3D::setIntegrationFlag( } } +void Peak3D::setMasked() +{ + _rejection_flag = RejectionFlag::Masked; + _sum_integration_flag = RejectionFlag::Masked; + _profile_integration_flag = RejectionFlag::Masked; +} + RejectionFlag Peak3D::rejectionFlag() const { if (_profile_integration_flag != RejectionFlag::NotRejected) diff --git a/core/peak/Peak3D.h b/core/peak/Peak3D.h index bbf136785..3eec28dc7 100644 --- a/core/peak/Peak3D.h +++ b/core/peak/Peak3D.h @@ -29,6 +29,7 @@ enum class RegionType; enum class IntegratorType; class ReciprocalVector; class UnitCell; +struct ComputeResult; /*! \addtogroup python_api * @{*/ @@ -61,6 +62,7 @@ enum class RejectionFlag { NoShapeModel, NoISigmaMinimum, TooWide, + BadGaussianFit, PredictionUpdateFailure, // from refiner ManuallyRejected, OutsideIndexingTol, @@ -175,9 +177,7 @@ class Peak3D { //! Update the integration parameters for this peak void updateIntegration( - const std::vector<Intensity>& rockingCurve, const Intensity& sumBkg, - const Intensity& profBkg, const Intensity& meanBkgGradient, const Intensity& sumInt, - const Intensity& profInt, double peakEnd, double bkgBegin, double bkgEnd, + const ComputeResult& result, double peakEnd, double bkgBegin, double bkgEnd, const RegionType& regionType); //! Return the q vector of the peak, transformed into sample coordinates. ReciprocalVector q() const; @@ -203,6 +203,8 @@ class Peak3D { //! Set the reason for rejection during integration void setIntegrationFlag( const RejectionFlag& flag, const IntegratorType& integrator, bool overwrite = false); + //! Label the peak as masked + void setMasked(); //! Return the rejection flag only RejectionFlag getRejectionFlag() const { return _rejection_flag; }; //! Return the sum integration flag only diff --git a/core/peak/PeakData.cpp b/core/peak/PeakData.cpp index d52a7bc80..be9d11d9f 100644 --- a/core/peak/PeakData.cpp +++ b/core/peak/PeakData.cpp @@ -46,11 +46,6 @@ const std::deque<double>& PeakData::gradients() const void PeakData::standardizeCoords() { - if (_peak == nullptr) { - throw std::runtime_error( - "PeakData::standardizeCoords() cannot be called if _peak is nullptr"); - } - _coords.resize(_events.size()); for (size_t i = 0; i < _events.size(); ++i) @@ -64,6 +59,13 @@ void PeakData::addEvent(const DetectorEvent& ev, double count, double gradient / _gradients.push_back(gradient); } +void PeakData::append(const PeakData& other) +{ + _events.insert(_events.end(), other._events.begin(), other._events.end()); + _counts.insert(_counts.end(), other._counts.begin(), other._counts.end()); + _gradients.insert(_gradients.end(), other._gradients.begin(), other._gradients.end()); +} + void PeakData::reset() { std::deque<DetectorEvent> e; @@ -77,4 +79,9 @@ void PeakData::reset() std::swap(_coords, crds); } +bool PeakData::empty() const +{ + return _events.empty(); +} + } // namespace ohkl diff --git a/core/peak/PeakData.h b/core/peak/PeakData.h index 1817a3379..335f18985 100644 --- a/core/peak/PeakData.h +++ b/core/peak/PeakData.h @@ -37,8 +37,14 @@ class PeakData { void standardizeCoords(); //! Add an event to the list of events. void addEvent(const DetectorEvent& ev, double count, double gradient = 0); + //! Append a PeakData object to this one + void append(const PeakData& other); //! Clear the events void reset(); + //! Is this PeakData empty?: + bool empty() const; + //! Get the peak pointer + Peak3D* peak() const { return _peak; }; private: Peak3D* _peak; -- GitLab From 04b3ebae1e130755771bd931d1b94520ada23a88 Mon Sep 17 00:00:00 2001 From: Zamaan Raza <z.raza@fz-juelich.de> Date: Wed, 22 May 2024 14:56:16 +0200 Subject: [PATCH 08/22] Parallelize integration --- core/experiment/Integrator.cpp | 18 +- core/experiment/Integrator.h | 7 +- core/integration/IIntegrator.cpp | 219 ++++++++++++++++++ core/integration/IIntegrator.h | 4 + core/peak/Peak3D.h | 2 +- .../functional/TestProfile3DIntegration.cpp | 6 +- test/cpp/functional/TestPxsumIntegration.cpp | 2 + test/cpp/functional/TestShapeModel.cpp | 1 + 8 files changed, 247 insertions(+), 12 deletions(-) diff --git a/core/experiment/Integrator.cpp b/core/experiment/Integrator.cpp index 74bc47bb0..41307e94b 100644 --- a/core/experiment/Integrator.cpp +++ b/core/experiment/Integrator.cpp @@ -50,7 +50,7 @@ void Integrator::integratePeaks( peaks->resetIntegrationFlags(integrator_type); IIntegrator* integrator = getIntegrator(integrator_type); integrator->setParameters(*_params); - integrator->integrate(peaks->getPeakList(), peaks->shapeModel(), data); + integrator->parallelIntegrate(peaks->getPeakList(), peaks->shapeModel(), data); peaks->setIntegrated(true); if (_params->use_gradient) peaks->setBkgGradient(true); @@ -67,15 +67,17 @@ void Integrator::integratePeaks( } void Integrator::integratePeaks( - sptrDataSet data, PeakCollection* peaks, IntegrationParameters* params, ShapeModel* shapes) + sptrDataSet data, PeakCollection* peaks, IntegrationParameters* params, ShapeModel* shapes, + bool parallel) { ohklLog( Level::Info, "Integrator::integratePeaks: integrating PeakCollection '" + peaks->name() + "'"); peaks->resetIntegrationFlags(_params->integrator_type); IIntegrator* integrator = getIntegrator(_params->integrator_type); + integrator->setParallel(parallel); integrator->setParameters(*params); - integrator->integrate(peaks->getPeakList(), shapes, data); + integrator->parallelIntegrate(peaks->getPeakList(), shapes, data); peaks->setIntegrated(true); if (params->use_gradient) peaks->setBkgGradient(true); @@ -91,13 +93,14 @@ void Integrator::integratePeaks( delete integrator; } -void Integrator::integrateFoundPeaks(PeakFinder* peak_finder) + void Integrator::integrateFoundPeaks(PeakFinder* peak_finder, bool parallel) { ohklLog(Level::Info, "Integrator::integrateFoundPeaks"); IIntegrator* integrator = getIntegrator(IntegratorType::PixelSum); + integrator->setParallel(parallel); integrator->setParameters(*_params); - integrator->integrate(peak_finder->currentPeaks(), nullptr, peak_finder->currentData()); + integrator->parallelIntegrate(peak_finder->currentPeaks(), nullptr, peak_finder->currentData()); _n_peaks = 0; _n_valid = 0; @@ -115,14 +118,15 @@ void Integrator::integrateFoundPeaks(PeakFinder* peak_finder) void Integrator::integrateShapeModel( std::vector<Peak3D*> fit_peaks, sptrDataSet data, ShapeModel* shape_model, const AABB& aabb, - const ShapeModelParameters& params) + const ShapeModelParameters& params, bool parallel) { ohklLog(Level::Info, "Integrator::integrateShapeModel"); ShapeIntegrator integrator{shape_model, aabb, params.nbins_x, params.nbins_y, params.nbins_z}; + integrator.setParallel(parallel); if (_handler) integrator.setHandler(_handler); integrator.setParameters(params); - integrator.integrate(fit_peaks, shape_model, data); + integrator.parallelIntegrate(fit_peaks, shape_model, data); } void Integrator::setParameters(std::shared_ptr<IntegrationParameters> params) diff --git a/core/experiment/Integrator.h b/core/experiment/Integrator.h index 270450b85..897d9c560 100644 --- a/core/experiment/Integrator.h +++ b/core/experiment/Integrator.h @@ -54,13 +54,14 @@ class Integrator { void integratePeaks(IntegratorType integrator_type, sptrDataSet data, PeakCollection* peaks); //! Integrate a peak collection void integratePeaks( - sptrDataSet data, PeakCollection* peaks, IntegrationParameters* params, ShapeModel* shapes); + sptrDataSet data, PeakCollection* peaks, IntegrationParameters* params, ShapeModel* shapes, + bool parallel = true); //! Integrate peaks found by _peak_finder - void integrateFoundPeaks(PeakFinder* peak_finder); + void integrateFoundPeaks(PeakFinder* peak_finder, bool parallel = true); //! Integrate the shape collection void integrateShapeModel( std::vector<Peak3D*> peaks, sptrDataSet data, ShapeModel* shape_model, const AABB& aabb, - const ShapeModelParameters& params); + const ShapeModelParameters& params, bool parallel = true); //! Set the parameters void setParameters(std::shared_ptr<IntegrationParameters> params); //! Get the parameters diff --git a/core/integration/IIntegrator.cpp b/core/integration/IIntegrator.cpp index 9ba640452..5061ae568 100644 --- a/core/integration/IIntegrator.cpp +++ b/core/integration/IIntegrator.cpp @@ -17,6 +17,7 @@ #include "base/geometry/Ellipsoid.h" #include "base/mask/IMask.h" #include "base/utils/Logger.h" +#include "base/utils/ParallelFor.h" #include "base/utils/ProgressHandler.h" #include "core/data/DataSet.h" #include "core/peak/Peak3D.h" @@ -25,6 +26,7 @@ #include "tables/crystal/UnitCell.h" #include <algorithm> +#include <mutex> namespace ohkl { @@ -249,6 +251,223 @@ void IIntegrator::integrate( ohklLog(Level::Info, "IIntegrator::integrate: end; ", _n_failures, " failures"); } +void IIntegrator::parallelIntegrate( + std::vector<Peak3D*> peaks, ShapeModel* shape_model, sptrDataSet data) +{ + ohklLog(Level::Info, "IIntegrator::parallelIntegrate: integrating ", peaks.size(), " peaks"); + _params.log(Level::Info); + if (shape_model) + shape_model->parameters()->log(Level::Info); + + // integrate only those peaks that belong to the specified dataset + auto it = std::remove_if(peaks.begin(), peaks.end(), [&](const Peak3D* peak) { + return peak->dataSet() != data; + }); + peaks.erase(it, peaks.end()); + std::ostringstream oss; + oss << "Integrating " << peaks.size() << " peaks"; + ohklLog(Level::Info, "IIntegrator::parallelIntegrate: integrating ", peaks.size(), " peaks"); + if (_handler) { + _handler->setStatus(oss.str().c_str()); + _handler->setProgress(0); + } + + _profile_integration = false; + if (_params.integrator_type == IntegratorType::Profile1D + || _params.integrator_type == IntegratorType::Profile3D + || _params.integrator_type == IntegratorType::Gaussian + || _params.integrator_type == IntegratorType::ISigma) + _profile_integration = true; + + + size_t idx = 0; + _n_frames_done = 0; + + std::map<Peak3D*, std::unique_ptr<IntegrationRegion>> regions; + std::map<Peak3D*, bool> integrated; + + if (_params.region_type == ohkl::RegionType::VariableEllipsoid) { + _peak_end = _params.peak_end; + _bkg_begin = _params.bkg_begin; + _bkg_end = _params.bkg_end; + } else { + _peak_end = _params.fixed_peak_end; + _bkg_begin = _params.fixed_bkg_begin; + _bkg_end = _params.fixed_bkg_end; + } + + for (auto peak : peaks) { + if (peak->isRejectedFor(RejectionFlag::Extinct)) { + if (!peak->enabled()) + continue; + } + + regions.emplace(std::make_pair( + peak, + std::make_unique<IntegrationRegion>( + peak, _peak_end, _bkg_begin, _bkg_end, _params.region_type))); + integrated.emplace(std::make_pair(peak, false)); + + // ignore partials + auto bb = regions.at(peak)->peakBB(); + auto data = peak->dataSet(); + auto lo = bb.lower(); + auto hi = bb.upper(); + + double width = hi[2] - lo[2]; + if (_params.use_max_width && width > _params.max_width) + peak->setIntegrationFlag(RejectionFlag::TooWide, _params.integrator_type); + + if (lo[0] < 0 || lo[1] < 0 || lo[2] < 0 || hi[0] >= data->nCols() || hi[1] >= data->nRows() + || hi[2] >= data->nFrames()) + peak->setIntegrationFlag(RejectionFlag::InvalidRegion, _params.integrator_type); + } + + // only integrate the peaks with valid integration regions + ohklLog(Level::Debug, "IIntegrator::integrate: remove invalid regions"); + it = std::remove_if(peaks.begin(), peaks.end(), [&](Peak3D*& p) { + return regions.find(p) == regions.end(); + }); + peaks.erase(it, peaks.end()); + + // check for overlaps if requested + if (_params.remove_overlaps) + removeOverlaps(regions); + + oss << "Building " << peaks.size() << " integration regions"; + if (_handler) { + _handler->setStatus(oss.str().c_str()); + _handler->setProgress(0); + } + ohklLog(Level::Debug, "IIntegrator::parallelIntegrate: integration region loop"); + std::mutex mut1; + + parallel_for(data->nFrames(), [&](int start, int end) { + for (int idx = start; idx < end; ++idx) { + + Eigen::MatrixXd current_frame, gradient; + Eigen::MatrixXi mask; + current_frame = data->transformedFrame(idx); + if (_params.use_gradient) + gradient = + data->gradientFrame(idx, _params.gradient_type, !_params.fft_gradient); + + mask.resize(data->nRows(), data->nCols()); + mask.setConstant(int(IntegrationRegion::EventType::EXCLUDED)); + + for (auto peak : peaks) + regions.at(peak)->updateMask(mask, idx); + + std::vector<std::unique_ptr<PeakData>> peak_data_vector; + for (auto peak : peaks) { + auto* current_region = regions.at(peak).get(); + PeakData peak_data(peak); + if (_params.use_gradient) + peak_data = + current_region->threadSafeAdvanceFrame(current_frame, mask, idx, &gradient); + else + peak_data = + current_region->threadSafeAdvanceFrame(current_frame, mask, idx); + if (!peak_data.empty()) + peak_data_vector.push_back(std::make_unique<PeakData>(peak_data)); + } + + { + const std::lock_guard<std::mutex> lock(mut1); + for (auto& peak_data : peak_data_vector) + regions[peak_data->peak()]->appendPeakData(*peak_data); + } + + if (_handler) { + const double progress = _n_frames_done++ * 100.0 / data->nFrames(); + _handler->setProgress(progress); + } + } + }, _thread_parallel, _max_threads); + + if (_handler) + _handler->setProgress(100); + + _n_peaks_done = 0; + if (_handler) + _handler->setProgress(0); + + ohklLog(Level::Debug, "IIntegrator::parallelIntegrate: compute loop"); + _n_failures = 0; + std::mutex mut2; + parallel_for(peaks.size(), [&](int start, int end) { + for (int idx = start; idx < end; ++idx) { + auto* peak = peaks.at(idx); + auto* current_region = regions.at(peak).get(); + const auto& counts = current_region->peakData().counts(); + + RejectionFlag reject(RejectionFlag::NotRejected); + + if (_params.discard_saturated) { + double max = 0; + if (!counts.empty()) // std::max on empty vector segfaults under MacOS + max = *std::max_element(counts.begin(), counts.end()); + if (max > _params.max_counts) + reject = RejectionFlag::SaturatedPixel; + } + + if (_params.use_max_width) { + auto aabb = current_region->peakBB(); + const auto lower = aabb.lower(); + const auto upper = aabb.upper(); + const double width = upper[2] - lower[2]; + if (width > static_cast<double>(_params.max_width)) + reject = RejectionFlag::TooWide; + } + + bool reintegrate = true; + if (_profile_integration) { + if (_params.use_max_strength + && peak->sumIntensity().strength() > _params.max_strength) + reintegrate = false; + if (_params.use_max_d && peak->d() > _params.max_d) + reintegrate = false; + } + + ComputeResult compute_result; + if (!reintegrate) { + compute_result.integrator_type = _params.integrator_type; + compute_result.rocking_curve = peak->rockingCurve(); + compute_result.profile_intensity = peak->sumIntensity(); + compute_result.profile_background = peak->sumBackground(); + compute_result.integration_flag = peak->sumRejectionFlag(); + } else { + if (reject == RejectionFlag::NotRejected) { + current_region->peakData().standardizeCoords(); + compute_result = compute(peak, shape_model, *current_region); + } else + compute_result.integration_flag = reject; + + if (compute_result.integration_flag != RejectionFlag::NotRejected) + ++_n_failures; + } + + { + const std::lock_guard<std::mutex> lock(mut2); + peak->updateIntegration( + compute_result, _params.peak_end, _params.bkg_begin, _params.bkg_end, + _params.region_type); + current_region->reset(); + } + + if (_handler) { + const double progress = _n_peaks_done++ * 100.0 / peaks.size(); + _handler->setProgress(progress); + } + } + }, _thread_parallel, _max_threads); + + if (_handler) + _handler->setProgress(100); + + ohklLog(Level::Info, "IIntegrator::parallelIntegrate: end; ", _n_failures, " failures"); +} + void IIntegrator::setHandler(sptrProgressHandler handler) { _handler = handler; diff --git a/core/integration/IIntegrator.h b/core/integration/IIntegrator.h index c5ad87964..fdc988718 100644 --- a/core/integration/IIntegrator.h +++ b/core/integration/IIntegrator.h @@ -125,10 +125,14 @@ class IIntegrator { virtual ~IIntegrator() = default; //! Integrate a vector of peaks void integrate(std::vector<Peak3D*> peaks, ShapeModel* shape_model, sptrDataSet data); + //! Thread-parallel integrate + void parallelIntegrate(std::vector<Peak3D*> peaks, ShapeModel* shape_model, sptrDataSet data); //! Set the progress handler. void setHandler(sptrProgressHandler handler); //! Assign a parameter set to the integrator void setParameters(const IntegrationParameters& params); + //! Toggle parallel integration + void setParallel(bool parallel) { _thread_parallel = parallel; }; //! Remove overlapping peak intensity regions void removeOverlaps(const std::map<Peak3D*, std::unique_ptr<IntegrationRegion>>& regions); diff --git a/core/peak/Peak3D.h b/core/peak/Peak3D.h index 3eec28dc7..c3675ec74 100644 --- a/core/peak/Peak3D.h +++ b/core/peak/Peak3D.h @@ -36,7 +36,7 @@ struct ComputeResult; //! Specifies reason why a peak was rejected enum class RejectionFlag { - NotRejected, + NotRejected = 0, Masked, OutsideThreshold, OutsideFrames, diff --git a/test/cpp/functional/TestProfile3DIntegration.cpp b/test/cpp/functional/TestProfile3DIntegration.cpp index c0d9c3321..ca8bdfdca 100644 --- a/test/cpp/functional/TestProfile3DIntegration.cpp +++ b/test/cpp/functional/TestProfile3DIntegration.cpp @@ -57,7 +57,11 @@ TEST_CASE("test/data/TestProfile3DIntegration.cpp", "") experiment.loadFromFile(filename); const ohkl::sptrUnitCell cell = experiment.getSptrUnitCell("indexed"); - const ohkl::sptrDataSet data = experiment.getData("Scan I"); ohkl::PeakCollection* peaks = experiment.getPeakCollection("predicted"); + const ohkl::sptrDataSet data = experiment.getData("Scan I"); + ohkl::PeakCollection* peaks = experiment.getPeakCollection("predicted"); + + data->initBuffer(true); + auto* integrator = experiment.integrator(); auto* params = integrator->parameters(); diff --git a/test/cpp/functional/TestPxsumIntegration.cpp b/test/cpp/functional/TestPxsumIntegration.cpp index e3dbe993f..72e3d2e60 100644 --- a/test/cpp/functional/TestPxsumIntegration.cpp +++ b/test/cpp/functional/TestPxsumIntegration.cpp @@ -57,6 +57,8 @@ TEST_CASE("test/data/TestPxsumIntegration.cpp", "") const ohkl::sptrDataSet data = experiment.getData("Scan I"); ohkl::PeakCollection* peaks = experiment.getPeakCollection("predicted"); + data->initBuffer(true); + auto* integrator = experiment.integrator(); auto* params = integrator->parameters(); diff --git a/test/cpp/functional/TestShapeModel.cpp b/test/cpp/functional/TestShapeModel.cpp index 3c79be9a8..909b017d6 100644 --- a/test/cpp/functional/TestShapeModel.cpp +++ b/test/cpp/functional/TestShapeModel.cpp @@ -40,6 +40,7 @@ TEST_CASE("test/data/TestShapeModel.cpp", "") ohkl::sptrUnitCell cell = experiment.getSptrUnitCell("accepted"); ohkl::sptrDataSet data = experiment.getData("testdata"); + data->initBuffer(true); auto* found_peaks = experiment.getPeakCollection("found"); found_peaks->computeSigmas(); -- GitLab From 05b5ea81cd2f42f3e11068ab0e0931f86e1e3694 Mon Sep 17 00:00:00 2001 From: Zamaan Raza <z.raza@fz-juelich.de> Date: Wed, 22 May 2024 14:58:27 +0200 Subject: [PATCH 09/22] Add missing integration YAML nodes --- core/experiment/ExperimentYAML.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/core/experiment/ExperimentYAML.cpp b/core/experiment/ExperimentYAML.cpp index 7843935bc..4ede768af 100644 --- a/core/experiment/ExperimentYAML.cpp +++ b/core/experiment/ExperimentYAML.cpp @@ -135,6 +135,11 @@ void ExperimentYAML::grabIntegrationParameters(IntegrationParameters* params) params->skip_masked = getNode<bool>(branch, "skip_masked"); params->remove_overlaps = getNode<bool>(branch, "remove_overlaps"); params->use_max_strength = getNode<bool>(branch, "use_max_strength"); + params->max_strength = getNode<double>(branch, "max_strength"); + params->use_max_d = getNode<bool>(branch, "use_max_d"); + params->max_d = getNode<double>(branch, "max_d"); + params->discard_saturated = getNode<bool>(branch, "discard_saturated"); + params->max_counts = getNode<double>(branch, "max_counts"); } void ExperimentYAML::setIntegrationParameters(IntegrationParameters* params) @@ -162,6 +167,11 @@ void ExperimentYAML::setIntegrationParameters(IntegrationParameters* params) int_node["skip_masked"] = params->skip_masked; int_node["remove_overlaps"] = params->remove_overlaps; int_node["use_max_strength"] = params->use_max_strength; + int_node["max_strength"] = params->max_strength; + int_node["use_max_d"] = params->use_max_d; + int_node["max_d"] = params->max_d; + int_node["discard_saturated"] = params->discard_saturated; + int_node["max_counts"] = params->max_counts; } void ExperimentYAML::grabPeakFinderParameters(PeakFinderParameters* params) -- GitLab From 1abd94d7c32b9b74ec61f1394faf464e79529329 Mon Sep 17 00:00:00 2001 From: Zamaan Raza <z.raza@fz-juelich.de> Date: Wed, 22 May 2024 15:26:27 +0200 Subject: [PATCH 10/22] Fix masking loop in parallel --- core/integration/IIntegrator.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/core/integration/IIntegrator.cpp b/core/integration/IIntegrator.cpp index 5061ae568..0b57a6ac2 100644 --- a/core/integration/IIntegrator.cpp +++ b/core/integration/IIntegrator.cpp @@ -280,7 +280,6 @@ void IIntegrator::parallelIntegrate( _profile_integration = true; - size_t idx = 0; _n_frames_done = 0; std::map<Peak3D*, std::unique_ptr<IntegrationRegion>> regions; @@ -297,6 +296,7 @@ void IIntegrator::parallelIntegrate( } for (auto peak : peaks) { + peak->setIntegrationFlag(RejectionFlag::NotRejected, _params.integrator_type, true); if (peak->isRejectedFor(RejectionFlag::Extinct)) { if (!peak->enabled()) continue; @@ -392,6 +392,19 @@ void IIntegrator::parallelIntegrate( if (_handler) _handler->setProgress(0); + for (auto peak : peaks) { + if (_params.skip_masked) { + Ellipsoid shape = peak->shape(); + shape.scale(_bkg_end); + for (const auto* mask : data->masks()) { + if (mask->collide(shape)) { + peak->setMasked(); + continue; + } + } + } + } + ohklLog(Level::Debug, "IIntegrator::parallelIntegrate: compute loop"); _n_failures = 0; std::mutex mut2; -- GitLab From db53f8720695b0710b3157a32c110bd910d51427 Mon Sep 17 00:00:00 2001 From: Zamaan Raza <z.raza@fz-juelich.de> Date: Wed, 22 May 2024 15:47:11 +0200 Subject: [PATCH 11/22] Parallelise shape model integration --- core/shape/ShapeModel.cpp | 20 +++++++++++++------ core/shape/ShapeModel.h | 7 +++++-- .../functional/TestProfile3DIntegration.cpp | 2 -- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/core/shape/ShapeModel.cpp b/core/shape/ShapeModel.cpp index 942deec56..9986cec3a 100644 --- a/core/shape/ShapeModel.cpp +++ b/core/shape/ShapeModel.cpp @@ -121,8 +121,14 @@ struct FitData { } }; -ShapeModel::ShapeModel() - : _id(0), _profiles(), _choleskyD(), _choleskyM(), _choleskyS(), _handler(nullptr) +ShapeModel::ShapeModel(bool thread_parallel) + : _id(0) + , _profiles() + , _choleskyD() + , _choleskyM() + , _choleskyS() + , _handler(nullptr) + , _thread_parallel(thread_parallel) { _choleskyD.fill(1e-6); _choleskyM.fill(1e-6); @@ -135,7 +141,7 @@ ShapeModel::ShapeModel(const std::string& name) : ShapeModel() _name = name; } -ShapeModel::ShapeModel(std::shared_ptr<ShapeModelParameters> params) +ShapeModel::ShapeModel(std::shared_ptr<ShapeModelParameters> params, bool thread_parallel) : _id(0) , _profiles() , _choleskyD() @@ -143,6 +149,7 @@ ShapeModel::ShapeModel(std::shared_ptr<ShapeModelParameters> params) , _choleskyS() , _params(params) , _handler(nullptr) + , _thread_parallel(thread_parallel) { _choleskyD.fill(1e-6); @@ -522,9 +529,9 @@ void ShapeModel::integrate( int_params.fixed_bkg_begin = _params->fixed_bkg_begin; int_params.fixed_bkg_end = _params->fixed_bkg_end; integrator.setHandler(handler); + integrator.setParallel(_thread_parallel); integrator.setParameters(int_params); - - integrator.integrate(peaks, this, data); + integrator.parallelIntegrate(peaks, this, data); ohklLog(Level::Info, "ShapeModel::integrate: finished integrating shapes"); } @@ -564,7 +571,8 @@ void ShapeModel::build(PeakCollection* peaks, sptrDataSet data) int_params.fixed_bkg_end = _params->fixed_bkg_end; int_params.region_type = _params->region_type; integrator.setParameters(int_params); - integrator.integrate(fit_peaks, this, data); + integrator.setParallel(_thread_parallel); + integrator.parallelIntegrate(fit_peaks, this, data); ohklLog(Level::Info, "ShapeModel::build: finished integrating shapes"); // ohklLog(Level::Info, "ShapeModel::build: updating fit"); // updateFit(1000); diff --git a/core/shape/ShapeModel.h b/core/shape/ShapeModel.h index ca1b7d259..12c35fb47 100644 --- a/core/shape/ShapeModel.h +++ b/core/shape/ShapeModel.h @@ -77,9 +77,9 @@ class ShapeModel { //! Construct an empty collection. //! @param detector_coords if true, store profiles in detector coordinates; //! otherwise store in Kabsch coordinates - ShapeModel(); + ShapeModel(bool thread_parallel = true); ShapeModel(const std::string& name); - ShapeModel(std::shared_ptr<ShapeModelParameters> params); + ShapeModel(std::shared_ptr<ShapeModelParameters> params, bool thread_parallel = true); //! Get the integer id unsigned int id() const { return _id; }; @@ -185,6 +185,9 @@ class ShapeModel { //! Progress handler sptrProgressHandler _handler; + + //! Parallelise integration + bool _thread_parallel; }; /*! @}*/ diff --git a/test/cpp/functional/TestProfile3DIntegration.cpp b/test/cpp/functional/TestProfile3DIntegration.cpp index ca8bdfdca..fba52cbdf 100644 --- a/test/cpp/functional/TestProfile3DIntegration.cpp +++ b/test/cpp/functional/TestProfile3DIntegration.cpp @@ -60,8 +60,6 @@ TEST_CASE("test/data/TestProfile3DIntegration.cpp", "") const ohkl::sptrDataSet data = experiment.getData("Scan I"); ohkl::PeakCollection* peaks = experiment.getPeakCollection("predicted"); - data->initBuffer(true); - auto* integrator = experiment.integrator(); auto* params = integrator->parameters(); -- GitLab From 0064f3aea4b0aaf792d65b809bf25d6bad71af47 Mon Sep 17 00:00:00 2001 From: Zamaan Raza <z.raza@fz-juelich.de> Date: Thu, 23 May 2024 08:39:12 +0200 Subject: [PATCH 12/22] Parallelise prediction - Fix GUI enum class items --- core/loader/XFileHandler.cpp | 4 +-- core/peak/Qs2Events.cpp | 32 +++++++++++++------- core/peak/Qs2Events.h | 2 +- gui/subframe_integrate/SubframeIntegrate.cpp | 3 ++ gui/subframe_integrate/SubframeIntegrate.h | 3 +- 5 files changed, 29 insertions(+), 15 deletions(-) diff --git a/core/loader/XFileHandler.cpp b/core/loader/XFileHandler.cpp index 369f136c1..671522665 100644 --- a/core/loader/XFileHandler.cpp +++ b/core/loader/XFileHandler.cpp @@ -14,6 +14,7 @@ #include "core/loader/XFileHandler.h" +#include <string> #include <fstream> #include <iterator> #include <sstream> @@ -88,9 +89,8 @@ void XFileHandler::readXFile(int frame) if (stop) { _mask = Eigen::MatrixXi(refl.maskx, refl.masky); break; - } else { - _reflections.emplace_back(refl); } + _reflections.emplace_back(refl); } // The integration mask diff --git a/core/peak/Qs2Events.cpp b/core/peak/Qs2Events.cpp index eb1064611..bda9b2666 100644 --- a/core/peak/Qs2Events.cpp +++ b/core/peak/Qs2Events.cpp @@ -16,12 +16,15 @@ #include "base/geometry/DirectVector.h" #include "base/utils/Logger.h" +#include "base/utils/ParallelFor.h" #include "base/utils/ProgressHandler.h" #include "core/data/DataSet.h" // TODO mv interpolatedState #include "core/detector/Detector.h" #include "core/instrument/InterpolatedState.h" #include "tables/crystal/MillerIndex.h" +#include <mutex> + namespace ohkl { // Tolerance for bisection search @@ -37,7 +40,7 @@ auto compute_sign = [](const Eigen::RowVector3d& q, const InterpolatedState& sta std::vector<std::pair<MillerIndex, DetectorEvent>> algo::qMap2Events( const std::vector<std::pair<MillerIndex, ReciprocalVector>>& sample_qs, const InstrumentStateList& states, const Detector& detector, const int n_intervals, - sptrProgressHandler handler /* = nullptr */) + sptrProgressHandler handler /* = nullptr */, bool thread_parallel /* = true */) { ohklLog( Level::Debug, "algo::Qs2Events::qVectorList2Events: processing ", sample_qs.size(), @@ -45,7 +48,6 @@ std::vector<std::pair<MillerIndex, DetectorEvent>> algo::qMap2Events( std::vector<std::pair<MillerIndex, DetectorEvent>> events; - int count = 1; if (handler) { std::ostringstream oss; oss << "Transforming " << sample_qs.size() << " q-vectors to detector events"; @@ -53,15 +55,23 @@ std::vector<std::pair<MillerIndex, DetectorEvent>> algo::qMap2Events( handler->setProgress(0); } -// for each sample q, determine the rotation that makes it intersect the Ewald sphere - for (const auto& [hkl, sample_q] : sample_qs) { - std::vector<DetectorEvent> new_events = - qVector2Events(sample_q, states, detector, n_intervals); - for (auto event : new_events) - events.push_back({hkl, event}); - if (handler) - handler->setProgress(++count * 100.0 / sample_qs.size()); - } + // for each sample q, determine the rotation that makes it intersect the Ewald sphere + std::mutex mutex; + std::atomic_int count = 1; + parallel_for(sample_qs.size(), [&](int start, int end) { + for (int idx = start; idx < end; ++idx) { + std::vector<DetectorEvent> new_events = + qVector2Events(sample_qs.at(idx).second, states, detector, n_intervals); + { + std::lock_guard<std::mutex> lock(mutex); + for (auto event : new_events) + events.push_back({sample_qs.at(idx).first, event}); + } + if (handler) + handler->setProgress(++count * 100.0 / sample_qs.size()); + } + }, thread_parallel); + if (handler) handler->setProgress(100); ohklLog( diff --git a/core/peak/Qs2Events.h b/core/peak/Qs2Events.h index aa41d7921..164e8179a 100644 --- a/core/peak/Qs2Events.h +++ b/core/peak/Qs2Events.h @@ -48,7 +48,7 @@ namespace algo { std::vector<std::pair<MillerIndex, DetectorEvent>> qMap2Events( const std::vector<std::pair<MillerIndex, ReciprocalVector>>& sample_qs, const InstrumentStateList& states, const Detector& detector, const int n_intervals, - sptrProgressHandler handler = nullptr); + sptrProgressHandler handler = nullptr, bool thread_parallel = true); //! Returns detector events corresponding to the list of q values. std::vector<DetectorEvent> qVectorList2Events( const std::vector<ReciprocalVector>& sample_qs, const InstrumentStateList& states, diff --git a/gui/subframe_integrate/SubframeIntegrate.cpp b/gui/subframe_integrate/SubframeIntegrate.cpp index 981383edd..2220098dc 100644 --- a/gui/subframe_integrate/SubframeIntegrate.cpp +++ b/gui/subframe_integrate/SubframeIntegrate.cpp @@ -17,6 +17,7 @@ #include "core/experiment/Experiment.h" #include "core/experiment/Integrator.h" +#include "core/integration/IIntegrator.h" #include "core/shape/PeakCollection.h" #include "core/shape/ShapeModel.h" #include "gui/MainWin.h" // gGui @@ -441,6 +442,8 @@ void SubframeIntegrate::setIntegrateUp() for (std::size_t idx = 0; idx < static_cast<int>(ohkl::IntegratorType::Count); ++idx) { const std::string integrator_type = _integrator_strings.at(static_cast<ohkl::IntegratorType>(idx)); + if (static_cast<ohkl::IntegratorType>(idx) == ohkl::IntegratorType::Shape) + continue; _integrator_combo->addItem(QString::fromStdString(integrator_type)); } diff --git a/gui/subframe_integrate/SubframeIntegrate.h b/gui/subframe_integrate/SubframeIntegrate.h index a019fdc18..954b9d893 100644 --- a/gui/subframe_integrate/SubframeIntegrate.h +++ b/gui/subframe_integrate/SubframeIntegrate.h @@ -165,7 +165,8 @@ class SubframeIntegrate : public QWidget { {ohkl::IntegratorType::Gaussian, "Gaussian integrator"}, {ohkl::IntegratorType::ISigma, "I/Sigma integrator"}, {ohkl::IntegratorType::Profile1D, "1D Profile integrator"}, - {ohkl::IntegratorType::Profile3D, "3D Profile integrator"}}; + {ohkl::IntegratorType::Profile3D, "3D Profile integrator"}, + {ohkl::IntegratorType::Shape, "Shape model integrator"}}; const std::map<ohkl::GradientKernel, QString> _kernel_description{ {ohkl::GradientKernel::CentralDifference, "Central difference"}, -- GitLab From a2d04481a5ff5026fb7a2ceb5dae3544c0c6122c Mon Sep 17 00:00:00 2001 From: Zamaan Raza <z.raza@fz-juelich.de> Date: Mon, 27 May 2024 11:00:45 +0200 Subject: [PATCH 13/22] Fix incorrect masked peak count --- core/integration/IIntegrator.cpp | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/core/integration/IIntegrator.cpp b/core/integration/IIntegrator.cpp index 0b57a6ac2..8c3fb7bd7 100644 --- a/core/integration/IIntegrator.cpp +++ b/core/integration/IIntegrator.cpp @@ -296,7 +296,6 @@ void IIntegrator::parallelIntegrate( } for (auto peak : peaks) { - peak->setIntegrationFlag(RejectionFlag::NotRejected, _params.integrator_type, true); if (peak->isRejectedFor(RejectionFlag::Extinct)) { if (!peak->enabled()) continue; @@ -392,19 +391,6 @@ void IIntegrator::parallelIntegrate( if (_handler) _handler->setProgress(0); - for (auto peak : peaks) { - if (_params.skip_masked) { - Ellipsoid shape = peak->shape(); - shape.scale(_bkg_end); - for (const auto* mask : data->masks()) { - if (mask->collide(shape)) { - peak->setMasked(); - continue; - } - } - } - } - ohklLog(Level::Debug, "IIntegrator::parallelIntegrate: compute loop"); _n_failures = 0; std::mutex mut2; @@ -433,6 +419,17 @@ void IIntegrator::parallelIntegrate( reject = RejectionFlag::TooWide; } + if (_params.skip_masked) { + Ellipsoid shape = peak->shape(); + shape.scale(_bkg_end); + for (const auto* mask : data->masks()) { + if (mask->collide(shape)) { + reject = RejectionFlag::Masked; + break; + } + } + } + bool reintegrate = true; if (_profile_integration) { if (_params.use_max_strength @@ -465,6 +462,8 @@ void IIntegrator::parallelIntegrate( peak->updateIntegration( compute_result, _params.peak_end, _params.bkg_begin, _params.bkg_end, _params.region_type); + if (reject == RejectionFlag::Masked) + peak->setMasked(); current_region->reset(); } -- GitLab From b8403d411a126c6a73bc1d26d6fb53f550896509 Mon Sep 17 00:00:00 2001 From: Zamaan Raza <z.raza@fz-juelich.de> Date: Mon, 27 May 2024 15:40:37 +0200 Subject: [PATCH 14/22] Parallelize shape assignment --- core/integration/IIntegrator.cpp | 14 +++++++------- core/shape/ShapeModel.cpp | 26 +++++++++++++++----------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/core/integration/IIntegrator.cpp b/core/integration/IIntegrator.cpp index 8c3fb7bd7..3396fd737 100644 --- a/core/integration/IIntegrator.cpp +++ b/core/integration/IIntegrator.cpp @@ -265,12 +265,7 @@ void IIntegrator::parallelIntegrate( }); peaks.erase(it, peaks.end()); std::ostringstream oss; - oss << "Integrating " << peaks.size() << " peaks"; ohklLog(Level::Info, "IIntegrator::parallelIntegrate: integrating ", peaks.size(), " peaks"); - if (_handler) { - _handler->setStatus(oss.str().c_str()); - _handler->setProgress(0); - } _profile_integration = false; if (_params.integrator_type == IntegratorType::Profile1D @@ -387,9 +382,14 @@ void IIntegrator::parallelIntegrate( if (_handler) _handler->setProgress(100); - _n_peaks_done = 0; - if (_handler) + oss.str(std::string()); // clear the stream + oss << "Integrating " << peaks.size() << " integration regions"; + if (_handler) { + _handler->setStatus(oss.str().c_str()); _handler->setProgress(0); + } + + _n_peaks_done = 0; ohklLog(Level::Debug, "IIntegrator::parallelIntegrate: compute loop"); _n_failures = 0; diff --git a/core/shape/ShapeModel.cpp b/core/shape/ShapeModel.cpp index 9986cec3a..86726429f 100644 --- a/core/shape/ShapeModel.cpp +++ b/core/shape/ShapeModel.cpp @@ -19,6 +19,7 @@ #include "base/geometry/Ellipsoid.h" #include "base/geometry/ReciprocalVector.h" #include "base/utils/Logger.h" +#include "base/utils/ParallelFor.h" #include "base/utils/ProgressHandler.h" #include "core/data/DataSet.h" #include "core/data/DataTypes.h" @@ -453,18 +454,21 @@ void ShapeModel::setPredictedShapes(PeakCollection* peaks) _handler->setProgress(0); } - for (auto peak : peaks->getPeakList()) { - // Skip the peak if any error occur when computing its mean covariance (e.g. - // too few or no neighbouring peaks found) - auto cov = meanCovariance(peak); - Eigen::Vector3d center = peak->shape().center(); - peak->setShape(Ellipsoid(center, cov.inverse())); - - if (_handler) { - double progress = ++count * 100.0 / npeaks; - _handler->setProgress(progress); + auto peaklist = peaks->getPeakList(); + parallel_for(peaklist.size(), [&](int start, int end) { + for (int idx = start; idx < end; ++idx) { + Peak3D* peak = peaklist.at(idx); + const auto cov = meanCovariance(peak); + const Eigen::Vector3d center = peak->shape().center(); + peak->setShape(Ellipsoid(center, cov.inverse())); + + if (_handler) { + const double progress = ++count * 100.0 / npeaks; + _handler->setProgress(progress); + } } - } + }, _thread_parallel); + ohklLog(Level::Info, "ShapeModel: finished computing shapes"); } -- GitLab From 5f9421ff51961d9006da09370743248ef2fc0c98 Mon Sep 17 00:00:00 2001 From: Zamaan Raza <z.raza@fz-juelich.de> Date: Mon, 3 Jun 2024 10:22:01 +0200 Subject: [PATCH 15/22] Parallelise peak processing for GUI --- gui/graphics/PeakCollectionGraphics.cpp | 49 +++++++++++++++---------- gui/graphics/PeakCollectionGraphics.h | 3 +- gui/items/PeakCollectionItem.cpp | 27 +++++++++++--- gui/items/PeakCollectionItem.h | 2 +- 4 files changed, 54 insertions(+), 27 deletions(-) diff --git a/gui/graphics/PeakCollectionGraphics.cpp b/gui/graphics/PeakCollectionGraphics.cpp index 85430bac6..0f0d5cfd7 100644 --- a/gui/graphics/PeakCollectionGraphics.cpp +++ b/gui/graphics/PeakCollectionGraphics.cpp @@ -14,6 +14,7 @@ #include "gui/graphics/PeakCollectionGraphics.h" +#include "base/utils/ParallelFor.h" #include "core/data/DataSet.h" #include "core/loader/XFileHandler.h" #include "core/peak/IntegrationRegion.h" @@ -150,32 +151,42 @@ QVector<PeakCenterGraphic*> PeakCollectionGraphics::detectorSpots(std::size_t fr return graphics; } -void PeakCollectionGraphics::getIntegrationMask(Eigen::MatrixXi& mask, std::size_t frame_idx) +void PeakCollectionGraphics::getIntegrationMask( + Eigen::MatrixXi& mask, std::size_t frame_idx, bool thread_parallel /* = true */) { std::vector<PeakItem*> peak_items = _peak_model->root()->peakItems(); std::mutex mutex; - for (auto* peak_item : peak_items) { - ohkl::Peak3D* peak = peak_item->peak(); - double peak_end, bkg_begin, bkg_end; - if (_preview_int_regions) { - if (_int_params.region_type == ohkl::RegionType::VariableEllipsoid) { - peak_end = _int_params.peak_end; - bkg_begin = _int_params.bkg_begin; - bkg_end = _int_params.bkg_end; + + ohkl::parallel_for(peak_items.size(), [&](int start, int end) { + for (int idx = start; idx < end; ++idx) { + PeakItem* peak_item = peak_items.at(idx); + ohkl::Peak3D* peak = peak_item->peak(); + + double peak_end, bkg_begin, bkg_end; + if (_preview_int_regions) { + if (_int_params.region_type == ohkl::RegionType::VariableEllipsoid) { + peak_end = _int_params.peak_end; + bkg_begin = _int_params.bkg_begin; + bkg_end = _int_params.bkg_end; + } else { + peak_end = _int_params.fixed_peak_end; + bkg_begin = _int_params.fixed_bkg_begin; + bkg_end = _int_params.fixed_bkg_end; + } } else { - peak_end = _int_params.fixed_peak_end; - bkg_begin = _int_params.fixed_bkg_begin; - bkg_end = _int_params.fixed_bkg_end; + peak_end = peak_item->peak()->peakEnd(); + bkg_begin = peak_item->peak()->bkgBegin(); + bkg_end = peak_item->peak()->bkgEnd(); + } + ohkl::IntegrationRegion region( + peak, peak_end, bkg_begin, bkg_end, _int_params.region_type); + { + std::lock_guard<std::mutex> lock(mutex); + region.updateMask(mask, frame_idx); } - } else { - peak_end = peak_item->peak()->peakEnd(); - bkg_begin = peak_item->peak()->bkgBegin(); - bkg_end = peak_item->peak()->bkgEnd(); } - ohkl::IntegrationRegion region(peak, peak_end, bkg_begin, bkg_end, _int_params.region_type); - region.updateMask(mask, frame_idx); - } + }, thread_parallel); } void PeakCollectionGraphics::getSinglePeakIntegrationMask( diff --git a/gui/graphics/PeakCollectionGraphics.h b/gui/graphics/PeakCollectionGraphics.h index eeee416ec..615577007 100644 --- a/gui/graphics/PeakCollectionGraphics.h +++ b/gui/graphics/PeakCollectionGraphics.h @@ -87,7 +87,8 @@ class PeakCollectionGraphics { private: //! Generate a mask of integration regions (a matrix of integers classifying pixels) - void getIntegrationMask(Eigen::MatrixXi& mask, std::size_t frame_idx); + void getIntegrationMask( + Eigen::MatrixXi& mask, std::size_t frame_idx, bool thread_parallel = true); //! Generate a mask for a single peak only void getSinglePeakIntegrationMask( ohkl::Peak3D* peak, Eigen::MatrixXi& mask, std::size_t frame_idx); diff --git a/gui/items/PeakCollectionItem.cpp b/gui/items/PeakCollectionItem.cpp index 0368caae5..b1bd8c0f2 100644 --- a/gui/items/PeakCollectionItem.cpp +++ b/gui/items/PeakCollectionItem.cpp @@ -15,10 +15,12 @@ #include "gui/items/PeakCollectionItem.h" #include "base/geometry/ReciprocalVector.h" +#include "base/utils/ParallelFor.h" #include "core/data/DataSet.h" #include "core/detector/Detector.h" #include "core/instrument/Diffractometer.h" #include "core/instrument/InstrumentState.h" +#include "core/peak/Peak3D.h" #include "core/raw/DataKeys.h" #include "core/raw/MetaData.h" #include "core/shape/PeakCollection.h" @@ -28,6 +30,10 @@ #include "tables/crystal/MillerIndex.h" #include "tables/crystal/UnitCell.h" +#include <Eigen/Dense> + +#include <mutex> + PeakCollectionItem::PeakCollectionItem() { _peak_collection = nullptr; @@ -46,7 +52,8 @@ PeakCollectionItem::PeakCollectionItem(const ohkl::PeakCollection* peak_collecti } } -void PeakCollectionItem::setPeakCollection(const ohkl::PeakCollection* peak_collection) +void PeakCollectionItem::setPeakCollection( + const ohkl::PeakCollection* peak_collection, bool thread_parallel /* = true */) { if (!peak_collection) { throw std::runtime_error( @@ -55,11 +62,19 @@ void PeakCollectionItem::setPeakCollection(const ohkl::PeakCollection* peak_coll _peak_collection = peak_collection; std::vector<ohkl::Peak3D*> peak_list = _peak_collection->getPeakList(); - _peak_items.clear(); - for (auto* peak : peak_list) { - auto item = std::make_unique<PeakItem>(peak); - _peak_items.push_back(std::move(item)); - } + std::mutex mutex; + + ohkl::parallel_for(peak_list.size(), [&](int start, int end) { + for (int idx = start; idx < end; ++idx) { + ohkl::Peak3D* peak = peak_list.at(idx); + auto item = std::make_unique<PeakItem>(peak); + + { + const std::lock_guard<std::mutex> lock(mutex); + _peak_items.push_back(std::move(item)); + } + } + }, thread_parallel); } std::string PeakCollectionItem::name() const diff --git a/gui/items/PeakCollectionItem.h b/gui/items/PeakCollectionItem.h index 90f27e13f..8cd9911dc 100644 --- a/gui/items/PeakCollectionItem.h +++ b/gui/items/PeakCollectionItem.h @@ -34,7 +34,7 @@ class PeakCollectionItem { public: //! Set the peak collection - void setPeakCollection(const ohkl::PeakCollection* peak_collection); + void setPeakCollection(const ohkl::PeakCollection* peak_collection, bool thread_parallel = true); //! Retrieve the name of the collection if present std::string name() const; //! Retrieve the number of children of this item -- GitLab From 4eb73f82b3cfb2e3972e582c96267156b1cc7ea9 Mon Sep 17 00:00:00 2001 From: Zamaan Raza <z.raza@fz-juelich.de> Date: Mon, 3 Jun 2024 10:23:42 +0200 Subject: [PATCH 16/22] Dump list of peaks sorted by symmetry relations --- core/integration/IIntegrator.h | 2 +- core/peak/Peak3D.cpp | 17 ++++---- core/statistics/MergedPeak.cpp | 11 +++++ core/statistics/MergedPeak.h | 3 ++ core/statistics/MergedPeakCollection.cpp | 11 +++++ core/statistics/MergedPeakCollection.h | 3 ++ core/statistics/PeakMerger.cpp | 17 ++++++++ core/statistics/PeakMerger.h | 3 ++ scripts/merge.py | 54 ++++++++++++++++++++++++ 9 files changed, 112 insertions(+), 9 deletions(-) create mode 100755 scripts/merge.py diff --git a/core/integration/IIntegrator.h b/core/integration/IIntegrator.h index fdc988718..ed30c9b71 100644 --- a/core/integration/IIntegrator.h +++ b/core/integration/IIntegrator.h @@ -32,7 +32,7 @@ enum class IntegratorType { PixelSum = 0, Gaussian, ISigma, Profile1D, Profile3D /*! \addtogroup python_api * @{*/ - //! Return value of all integrator compute methods. Contains any mutable data from integration. +//! Return value of all integrator compute methods. Contains any mutable data from integration. struct ComputeResult { //! Construct an invalid result by default ComputeResult(); diff --git a/core/peak/Peak3D.cpp b/core/peak/Peak3D.cpp index 3da3d2519..000019610 100644 --- a/core/peak/Peak3D.cpp +++ b/core/peak/Peak3D.cpp @@ -483,14 +483,15 @@ std::string Peak3D::rejectionString() const std::string Peak3D::toString() const { std::ostringstream oss; - // h, k, l, x, y, frame, intensity, sigma - oss << std::fixed << std::setw(5) << _hkl.h() << std::fixed << std::setw(5) << _hkl.k() - << std::fixed << std::setw(5) << _hkl.l() << std::fixed << std::setw(10) - << std::setprecision(2) << shape().center()[0] << std::fixed << std::setw(10) - << std::setprecision(2) << shape().center()[1] << std::fixed << std::setw(10) - << std::setprecision(2) << shape().center()[2] << std::fixed << std::setw(10) - << std::setprecision(2) << correctedSumIntensity().value() << std::fixed << std::setw(10) - << std::setprecision(2) << correctedSumIntensity().sigma(); + // d, h, k, l, x, y, frame, intensity, sigma + oss << std::fixed << std::setw(8) << std::setprecision(3) << d() + << std::fixed << std::setw(5) << _hkl.h() + << std::fixed << std::setw(5) << _hkl.k() << std::fixed << std::setw(5) << _hkl.l() + << std::fixed << std::setw(10) << std::setprecision(2) << shape().center()[0] + << std::fixed << std::setw(10) << std::setprecision(2) << shape().center()[1] + << std::fixed << std::setw(10) << std::setprecision(2) << shape().center()[2] + << std::fixed << std::setw(14) << std::setprecision(2) << correctedSumIntensity().value() + << std::fixed << std::setw(10) << std::setprecision(2) << correctedSumIntensity().sigma(); return oss.str(); } diff --git a/core/statistics/MergedPeak.cpp b/core/statistics/MergedPeak.cpp index 716e55ee6..c1ee044e3 100644 --- a/core/statistics/MergedPeak.cpp +++ b/core/statistics/MergedPeak.cpp @@ -208,4 +208,15 @@ double MergedPeak::pValue() const return gsl_cdf_chisq_P(x, k); } +std::string MergedPeak::toString() const +{ + std::string peaklist = ""; + for (auto* peak : _peaks) { + peaklist += peak->toString(); + peaklist += "\n"; + } + + return peaklist; +} + } // namespace ohkl diff --git a/core/statistics/MergedPeak.h b/core/statistics/MergedPeak.h index a29ff0127..b74b5cf87 100644 --- a/core/statistics/MergedPeak.h +++ b/core/statistics/MergedPeak.h @@ -61,6 +61,9 @@ class MergedPeak { //! split the merged peak randomly into two, for calculation of CC std::pair<MergedPeak, MergedPeak> split(bool sum_intensity) const; + //! Return list of symmetry-related peaks as a string + std::string toString() const; + private: //! Update the hkl that represents the set of equivalences. void determineRepresentativeHKL(); diff --git a/core/statistics/MergedPeakCollection.cpp b/core/statistics/MergedPeakCollection.cpp index 4f34bc132..261135552 100644 --- a/core/statistics/MergedPeakCollection.cpp +++ b/core/statistics/MergedPeakCollection.cpp @@ -208,4 +208,15 @@ double MergedPeakCollection::dMax() const return _d_max; } +std::string MergedPeakCollection::toStringUnmerged() const +{ + std::string peaklist = ""; + for (auto merged_peak : _merged_peak_set) { + peaklist += merged_peak.toString(); + peaklist += "\n"; + } + + return peaklist; +} + } // namespace ohkl diff --git a/core/statistics/MergedPeakCollection.h b/core/statistics/MergedPeakCollection.h index 6a0136eda..717099d9c 100644 --- a/core/statistics/MergedPeakCollection.h +++ b/core/statistics/MergedPeakCollection.h @@ -71,6 +71,9 @@ class MergedPeakCollection { //! Get maximum d double dMax() const; + //! Output a list of unmerged peaks as a string, sorted by symmetry relations + std::string toStringUnmerged() const; + SpaceGroup spaceGroup() const { return _group; }; private: diff --git a/core/statistics/PeakMerger.cpp b/core/statistics/PeakMerger.cpp index 6cfc291c0..2cde0d407 100644 --- a/core/statistics/PeakMerger.cpp +++ b/core/statistics/PeakMerger.cpp @@ -328,6 +328,23 @@ bool PeakMerger::saveStatistics(std::string filename) return true; } +bool PeakMerger::savePeaks(std::string filename, bool sum_intensities) const +{ + std::string data; + if (sum_intensities) + data = _sum_merged_data->toStringUnmerged(); + else + data = _profile_merged_data->toStringUnmerged(); + + std::fstream file(filename, std::ios::out); + if (!file.is_open()) + return false; + + file << data; + file.close(); + return true; +} + std::vector<double> PeakMerger::getFigureOfMerit(FigureOfMerit fom, IntegratorType integrator) { DataResolution* resolution; diff --git a/core/statistics/PeakMerger.h b/core/statistics/PeakMerger.h index 378f05d70..054444838 100644 --- a/core/statistics/PeakMerger.h +++ b/core/statistics/PeakMerger.h @@ -97,6 +97,9 @@ class PeakMerger { //! Saves the shell information to file. bool saveStatistics(std::string filename); + //! Save peaks grouped by symmetry relations to file + bool savePeaks(std::string filename, bool sum_intensities) const; + //! Return a vector containing the resolution-dependent figure of merit std::vector<double> getFigureOfMerit(FigureOfMerit fom, IntegratorType integrator); diff --git a/scripts/merge.py b/scripts/merge.py new file mode 100755 index 000000000..dcef23667 --- /dev/null +++ b/scripts/merge.py @@ -0,0 +1,54 @@ +#!/usr/bin/python +## *********************************************************************************************** +## +## OpenHKL: data reduction for single-crystal diffraction +## +##! @file test/python/TestWorkFlow.py +##! @brief Test ... +##! +##! @homepage https://openhkl.org +##! @license GNU General Public License v3 or higher (see COPYING) +##! @copyright Institut Laue-Langevin and Forschungszentrum Jülich GmbH 2016- +##! @authors see CITATION, MAINTAINER +## +## *********************************************************************************************** + +import sys +import numpy as np +from pathlib import Path + +lib_dir = "@SWIG_INSTALL_PATH@" # Path to pyohkl.py + +sys.path.append(lib_dir) +import pyohkl as ohkl + +file = Path('@CMAKE_BINARY_DIR@/test/data/Trypsin-pxsum.ohkl') +experiment = 'Trypsin' +diffractometer = 'BioDiff' +data_name = 'Scan I' +space_group = "P 21 21 21" +found_peaks_name = 'found' +predicted_peaks_name = 'predicted' + +expt = ohkl.Experiment(experiment, diffractometer) +expt.loadFromFile(str(file)) +data = expt.getData(data_name) +found_peaks = expt.getPeakCollection(found_peaks_name) +predicted_peaks = expt.getPeakCollection(predicted_peaks_name) + + +# Merge parameters +merger = expt.peakMerger() +params = merger.parameters() +merger.reset() +params.d_min = 1.5 +params.d_max = 50.0 +params.n_shells = 10 +params.friedel = True +merger.addPeakCollection(predicted_peaks) +merger.setSpaceGroup(ohkl.SpaceGroup(space_group)) +merger.mergePeaks() +merger.computeQuality() +print(merger.summary()) + +merger.savePeaks("unmerged_sum.txt", True) -- GitLab From 602f918184f12c76a0fdf87b14b8f273fcba4ef9 Mon Sep 17 00:00:00 2001 From: Zamaan Raza <z.raza@fz-juelich.de> Date: Tue, 4 Jun 2024 14:51:16 +0200 Subject: [PATCH 17/22] Fix GUI hang on integration --- base/utils/ParallelFor.h | 2 -- gui/items/PeakCollectionItem.cpp | 16 +++++----------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/base/utils/ParallelFor.h b/base/utils/ParallelFor.h index 3245fd170..8a3a5523c 100644 --- a/base/utils/ParallelFor.h +++ b/base/utils/ParallelFor.h @@ -20,8 +20,6 @@ #include <thread> #include <vector> -#include <iostream> - namespace ohkl { /// @param[in] nb_elements : size of your for loop diff --git a/gui/items/PeakCollectionItem.cpp b/gui/items/PeakCollectionItem.cpp index b1bd8c0f2..d423c19fb 100644 --- a/gui/items/PeakCollectionItem.cpp +++ b/gui/items/PeakCollectionItem.cpp @@ -62,19 +62,13 @@ void PeakCollectionItem::setPeakCollection( _peak_collection = peak_collection; std::vector<ohkl::Peak3D*> peak_list = _peak_collection->getPeakList(); - std::mutex mutex; - ohkl::parallel_for(peak_list.size(), [&](int start, int end) { - for (int idx = start; idx < end; ++idx) { - ohkl::Peak3D* peak = peak_list.at(idx); - auto item = std::make_unique<PeakItem>(peak); + _peak_items.clear(); - { - const std::lock_guard<std::mutex> lock(mutex); - _peak_items.push_back(std::move(item)); - } - } - }, thread_parallel); + for (auto* peak : peak_list) { + auto item = std::make_unique<PeakItem>(peak); + _peak_items.push_back(std::move(item)); + } } std::string PeakCollectionItem::name() const -- GitLab From 065f3d4c3869dd0efc3519ec4c9033002b625f54 Mon Sep 17 00:00:00 2001 From: Zamaan Raza <z.raza@fz-juelich.de> Date: Tue, 4 Jun 2024 14:52:22 +0200 Subject: [PATCH 18/22] Always save DataReaderParameters section of YAML file --- core/experiment/Experiment.cpp | 4 ++ core/experiment/Experiment.h | 7 +++ gui/models/Session.cpp | 78 +++++++++++++++++----------------- 3 files changed, 50 insertions(+), 39 deletions(-) diff --git a/core/experiment/Experiment.cpp b/core/experiment/Experiment.cpp index d664dc7e7..80ad738c2 100644 --- a/core/experiment/Experiment.cpp +++ b/core/experiment/Experiment.cpp @@ -34,6 +34,7 @@ #include "core/instrument/InstrumentStateSet.h" #include "core/instrument/Monochromator.h" #include "core/raw/DataKeys.h" +#include "core/loader/IDataReader.h" #include "core/raw/MetaData.h" #include "core/shape/PeakCollection.h" #include "core/shape/PeakFilter.h" @@ -72,6 +73,7 @@ Experiment::Experiment() : _diffractometer(nullptr), _strategy(false) _peak_merger = std::make_unique<PeakMerger>(); _shape_params = std::make_shared<ShapeModelParameters>(); + _data_reader_params = std::make_unique<DataReaderParameters>(); } Experiment::~Experiment() = default; @@ -106,6 +108,7 @@ void Experiment::readFromYaml(const std::string& filename) { ohklLog(Level::Info, "Experiment::readFromYaml: ", filename); ExperimentYAML yaml(filename); + yaml.grabDataReaderParameters(_data_reader_params.get()); yaml.grabPeakFinderParameters(_peak_finder->parameters()); yaml.grabAutoindexerParameters(_auto_indexer->parameters()); yaml.grabShapeParameters(_shape_params.get()); @@ -118,6 +121,7 @@ void Experiment::saveToYaml(const std::string& filename) { ohklLog(Level::Info, "Experiment::saveToYaml: ", filename); ExperimentYAML yaml(filename); + yaml.setDataReaderParameters(_data_reader_params.get()); yaml.setPeakFinderParameters(_peak_finder->parameters()); yaml.setAutoindexerParameters(_auto_indexer->parameters()); yaml.setShapeParameters(_shape_params.get()); diff --git a/core/experiment/Experiment.h b/core/experiment/Experiment.h index bb1981b40..bc3d44d51 100644 --- a/core/experiment/Experiment.h +++ b/core/experiment/Experiment.h @@ -43,6 +43,7 @@ class Refiner; class ShapeHandler; class ShapeModel; class UnitCellHandler; +struct DataReaderParameters; struct ShapeModelParameters; using DataMap = std::map<std::string, sptrDataSet>; @@ -91,6 +92,9 @@ class Experiment { //! get pointer to ShapeModel parameters std::shared_ptr<ShapeModelParameters> shapeParameters() const { return _shape_params; }; + //! Get a pointer to the data reader parameters + DataReaderParameters* dataReaderParameters() const {return _data_reader_params.get(); }; + // Data handler //! Get the map of data sets from the handler const DataMap* getDataMap() const; @@ -296,6 +300,9 @@ class Experiment { // ShapeModel parameters, since there is no experiment-level container std::shared_ptr<ShapeModelParameters> _shape_params; + // Data reader parameters, since there is nowhere else to store this + std::unique_ptr<DataReaderParameters> _data_reader_params; + bool _strategy; }; diff --git a/gui/models/Session.cpp b/gui/models/Session.cpp index fbf3db7a1..013d83cb2 100644 --- a/gui/models/Session.cpp +++ b/gui/models/Session.cpp @@ -366,54 +366,54 @@ bool Session::loadRawData(bool single_file /* = false */) // Get metadata from readme file, then edit them in dialog. const QStringList& extant_dataset_names = currentProject()->getDataNames(); - ohkl::DataReaderParameters parameters; - QString yml_file = QString::fromStdString(currentProject()->experiment()->name() + ".yml"); + currentProject()->setDirectory(path); + ohkl::Experiment* exp = currentProject()->experiment(); + + ohkl::DataReaderParameters* parameters = exp->dataReaderParameters(); + QString yml_file = QString::fromStdString(exp->name() + ".yml"); QString yml_path = QDir(path).filePath(yml_file); QFileInfo info(yml_path); if (info.exists() && info.isFile()) - parameters.loadFromYAML(yml_path.toStdString()); - ImageReaderDialog dialog(filenames, ¶meters, ohkl::DataFormat::RAW); + parameters->loadFromYAML(yml_path.toStdString()); + ImageReaderDialog dialog(filenames, parameters, ohkl::DataFormat::RAW); dialog.setWindowTitle("Raw data parameters"); if (single_file) dialog.setSingleImageMode(); if (!dialog.exec()) return false; - - currentProject()->setDirectory(path); - ohkl::Experiment* exp = currentProject()->experiment(); - parameters = dialog.dataReaderParameters(); + *parameters = dialog.dataReaderParameters(); // Transfer metadata to diffractometer. ohkl::Detector* detector = exp->getDiffractometer()->detector(); - detector->setBaseline(parameters.baseline); - detector->setGain(parameters.gain); + detector->setBaseline(parameters->baseline); + detector->setGain(parameters->gain); // Transfer metadata to dataset, and load the raw data. if (single_file) { const std::shared_ptr<ohkl::DataSet> dataset{std::make_shared<ohkl::SingleFrame>( - parameters.dataset_name, exp->getDiffractometer())}; - dataset->setImageReaderParameters(parameters); + parameters->dataset_name, exp->getDiffractometer())}; + dataset->setImageReaderParameters(*parameters); dataset->addRawFrame(filenames[0].toStdString()); dataset->finishRead(); - parameters.cols = dataset->nCols(); - parameters.rows = dataset->nRows(); + parameters->cols = dataset->nCols(); + parameters->rows = dataset->nRows(); exp->addData(dataset); } else { const std::shared_ptr<ohkl::DataSet> dataset{ - std::make_shared<ohkl::DataSet>(parameters.dataset_name, exp->getDiffractometer())}; - dataset->setImageReaderParameters(parameters); + std::make_shared<ohkl::DataSet>(parameters->dataset_name, exp->getDiffractometer())}; + dataset->setImageReaderParameters(*parameters); for (const auto& filename : filenames) dataset->addRawFrame(filename.toStdString()); dataset->finishRead(); - parameters.cols = dataset->nCols(); - parameters.rows = dataset->nRows(); + parameters->cols = dataset->nCols(); + parameters->rows = dataset->nRows(); exp->addData(dataset); } ohkl::ExperimentYAML yaml(yml_path.toStdString()); - yaml.setDataReaderParameters(¶meters); + yaml.setDataReaderParameters(parameters); yaml.writeFile(yml_path.toStdString()); onDataChanged(); } catch (std::exception& e) { @@ -431,7 +431,7 @@ bool Session::loadTiffData(bool single_file /* = false */) ohkl::Experiment* exp = currentProject()->experiment(); ohkl::Detector* detector = exp->getDiffractometer()->detector(); - ohkl::DataReaderParameters params; + ohkl::DataReaderParameters* params = exp->dataReaderParameters(); try { // Get input filenames from dialog. @@ -450,9 +450,9 @@ bool Session::loadTiffData(bool single_file /* = false */) QString yml_path = QDir(path).filePath(yml_file); QFileInfo info(yml_path); if (info.exists() && info.isFile()) - params.loadFromYAML(yml_path.toStdString()); + params->loadFromYAML(yml_path.toStdString()); ImageReaderDialog dialog( - filenames, static_cast<ohkl::DataReaderParameters*>(¶ms), ohkl::DataFormat::TIFF); + filenames, static_cast<ohkl::DataReaderParameters*>(params), ohkl::DataFormat::TIFF); dialog.setWindowTitle("Tiff data parameters"); if (single_file) @@ -462,21 +462,21 @@ bool Session::loadTiffData(bool single_file /* = false */) return false; currentProject()->setDirectory(path); - params = dialog.dataReaderParameters(); - detector->setBaseline(params.baseline); - detector->setGain(params.gain); + *params = dialog.dataReaderParameters(); + detector->setBaseline(params->baseline); + detector->setGain(params->gain); if (single_file) { const std::shared_ptr<ohkl::DataSet> dataset{ - std::make_shared<ohkl::SingleFrame>(params.dataset_name, exp->getDiffractometer())}; - dataset->setImageReaderParameters(params); + std::make_shared<ohkl::SingleFrame>(params->dataset_name, exp->getDiffractometer())}; + dataset->setImageReaderParameters(*params); dataset->addTiffFrame(filenames[0].toStdString()); dataset->finishRead(); exp->addData(dataset); } else { const std::shared_ptr<ohkl::DataSet> dataset{ - std::make_shared<ohkl::DataSet>(params.dataset_name, exp->getDiffractometer())}; - dataset->setImageReaderParameters(params); + std::make_shared<ohkl::DataSet>(params->dataset_name, exp->getDiffractometer())}; + dataset->setImageReaderParameters(*params); for (const auto& filename : filenames) dataset->addTiffFrame(filename.toStdString()); dataset->finishRead(); @@ -499,7 +499,7 @@ bool Session::loadPlainTextData(bool single_file /* = false */) ohkl::Experiment* exp = currentProject()->experiment(); ohkl::Detector* detector = exp->getDiffractometer()->detector(); - ohkl::DataReaderParameters params; + ohkl::DataReaderParameters* params = exp->dataReaderParameters(); try { // Get input filenames from dialog. @@ -518,9 +518,9 @@ bool Session::loadPlainTextData(bool single_file /* = false */) QString yml_path = QDir(path).filePath(yml_file); QFileInfo info(yml_path); if (info.exists() && info.isFile()) - params.loadFromYAML(yml_path.toStdString()); + params->loadFromYAML(yml_path.toStdString()); ImageReaderDialog dialog( - filenames, static_cast<ohkl::DataReaderParameters*>(¶ms), + filenames, static_cast<ohkl::DataReaderParameters*>(params), ohkl::DataFormat::PLAINTEXT); dialog.setWindowTitle("Plain text data parameters"); @@ -531,21 +531,21 @@ bool Session::loadPlainTextData(bool single_file /* = false */) return false; currentProject()->setDirectory(path); - params = dialog.dataReaderParameters(); - detector->setBaseline(params.baseline); - detector->setGain(params.gain); + *params = dialog.dataReaderParameters(); + detector->setBaseline(params->baseline); + detector->setGain(params->gain); if (single_file) { const std::shared_ptr<ohkl::DataSet> dataset{ - std::make_shared<ohkl::SingleFrame>(params.dataset_name, exp->getDiffractometer())}; - dataset->setImageReaderParameters(params); + std::make_shared<ohkl::SingleFrame>(params->dataset_name, exp->getDiffractometer())}; + dataset->setImageReaderParameters(*params); dataset->addPlainTextFrame(filenames[0].toStdString()); dataset->finishRead(); exp->addData(dataset); } else { const std::shared_ptr<ohkl::DataSet> dataset{ - std::make_shared<ohkl::DataSet>(params.dataset_name, exp->getDiffractometer())}; - dataset->setImageReaderParameters(params); + std::make_shared<ohkl::DataSet>(params->dataset_name, exp->getDiffractometer())}; + dataset->setImageReaderParameters(*params); for (const auto& filename : filenames) dataset->addPlainTextFrame(filename.toStdString()); dataset->finishRead(); -- GitLab From fc9fd6772bd1142e1df17c0ed333f1dce32f1ad9 Mon Sep 17 00:00:00 2001 From: Zamaan Raza <z.raza@fz-juelich.de> Date: Thu, 6 Jun 2024 08:43:17 +0200 Subject: [PATCH 19/22] Disable full integrator tests --- cmake/CTestCustom.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/CTestCustom.cmake b/cmake/CTestCustom.cmake index 626014259..45f0e4844 100644 --- a/cmake/CTestCustom.cmake +++ b/cmake/CTestCustom.cmake @@ -1,4 +1,5 @@ # Tests specified in the following variable will not be executed set(CTEST_CUSTOM_TESTS_IGNORE TestProfile3DIntegration + TestPxsumIntegration ) -- GitLab From a8846386c1b998623760f1896a94d83dcbe0f71a Mon Sep 17 00:00:00 2001 From: Zamaan Raza <z.raza@fz-juelich.de> Date: Thu, 6 Jun 2024 09:00:19 +0200 Subject: [PATCH 20/22] CMake variable to toggle integration tests (off by default) --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c2b569f8..58c5e8c22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ option(BUILD_PDF_DOCUMENTATION "Build Sphinx documentation using LaTeX" ON) option(OHKL_TIDY "Upon 'make', clang-tidy will print warnings as specified in .clang-tidy" OFF) option(OHKL_FULL_WORKFLOW_TEST "Add full workflow test to ctest" OFF) option(BUILD_WITH_QT6 "Build using QT6" OFF) +option(TEST_INTEGRATORS "Build intensive integrator tests" OFF) # Path to test data directory must provided via -DOHKL_TESTDATA_DIR or from cache. set(OHKL_TESTDATA_URL https://computing.mlz-garching.de/download/testdata/nsx) @@ -226,7 +227,9 @@ if(OHKL_PYTHON AND NOT MSVC AND BUILD_TESTING) endif() # Ignore tests specified in CTestCustom.cmake -configure_file(${CMAKE_SOURCE_DIR}/cmake/CTestCustom.cmake ${CMAKE_BINARY_DIR}) +if (NOT TEST_INTEGRATORS) + configure_file(${CMAKE_SOURCE_DIR}/cmake/CTestCustom.cmake ${CMAKE_BINARY_DIR}) +endif() ################################################################################################### # Build documentation -- GitLab From 77938ddd21402f93240ec9fdb16ae8edde79180a Mon Sep 17 00:00:00 2001 From: Zamaan Raza <z.raza@fz-juelich.de> Date: Wed, 24 Jul 2024 13:01:11 +0200 Subject: [PATCH 21/22] Code review changes --- core/data/DataSet.h | 2 +- core/experiment/ExperimentYAML.cpp | 10 ++++++---- core/loader/XFileHandler.cpp | 12 ++++++------ core/peak/Peak3D.cpp | 1 - core/peak/Qs2Events.cpp | 4 ++-- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/core/data/DataSet.h b/core/data/DataSet.h index d6c2201d8..e4d9d20e2 100644 --- a/core/data/DataSet.h +++ b/core/data/DataSet.h @@ -155,7 +155,7 @@ class DataSet { void clearHistograms(); //! Maximum per pixel count for whole DataSet double maxCount(); - //! getting number of available + //! Get number of available histograms size_t getNumberHistograms() { return _histograms.size(); } //! accessing created histograms diff --git a/core/experiment/ExperimentYAML.cpp b/core/experiment/ExperimentYAML.cpp index 4ede768af..6cf1be920 100644 --- a/core/experiment/ExperimentYAML.cpp +++ b/core/experiment/ExperimentYAML.cpp @@ -2,8 +2,8 @@ // // OpenHKL: data reduction for single crystal diffraction // -//! @file base/parser/ExperimentYAML.cpp -//! @brief Defines function eigenToVector +//! @file core/experiment/ExperimentYAML.cpp +//! @brief Implements class ExperimentYAML //! //! @homepage https://openhkl.org //! @license GNU General Public License v3 or higher (see COPYING) @@ -124,7 +124,6 @@ void ExperimentYAML::grabIntegrationParameters(IntegrationParameters* params) params->fixed_peak_end = getNode<double>(branch, "fixed_peak_end"); params->fixed_bkg_begin = getNode<double>(branch, "fixed_bkg_begin"); params->fixed_bkg_end = getNode<double>(branch, "fixed_bkg_end"); - params->max_strength = getNode<double>(branch, "max_strength"); params->fit_center = getNode<bool>(branch, "fit_center"); params->fit_cov = getNode<bool>(branch, "fit_cov"); params->integrator_type = static_cast<IntegratorType>(getNode<int>(branch, "integrator_type")); @@ -138,6 +137,8 @@ void ExperimentYAML::grabIntegrationParameters(IntegrationParameters* params) params->max_strength = getNode<double>(branch, "max_strength"); params->use_max_d = getNode<bool>(branch, "use_max_d"); params->max_d = getNode<double>(branch, "max_d"); + params->use_max_width = getNode<bool>(branch, "use_max_width"); + params->max_width = getNode<double>(branch, "max_width"); params->discard_saturated = getNode<bool>(branch, "discard_saturated"); params->max_counts = getNode<double>(branch, "max_counts"); } @@ -156,7 +157,6 @@ void ExperimentYAML::setIntegrationParameters(IntegrationParameters* params) int_node["fixed_peak_end"] = params->fixed_peak_end; int_node["fixed_bkg_begin"] = params->fixed_bkg_begin; int_node["fixed_bkg_end"] = params->fixed_bkg_end; - int_node["max_strength"] = params->max_strength; int_node["fit_center"] = params->fit_center; int_node["fit_cov"] = params->fit_cov; int_node["integrator_type"] = static_cast<int>(params->integrator_type); @@ -170,6 +170,8 @@ void ExperimentYAML::setIntegrationParameters(IntegrationParameters* params) int_node["max_strength"] = params->max_strength; int_node["use_max_d"] = params->use_max_d; int_node["max_d"] = params->max_d; + int_node["use_max_width"] = params->use_max_width; + int_node["max_width"] = params->max_width; int_node["discard_saturated"] = params->discard_saturated; int_node["max_counts"] = params->max_counts; } diff --git a/core/loader/XFileHandler.cpp b/core/loader/XFileHandler.cpp index 671522665..e72cc05d3 100644 --- a/core/loader/XFileHandler.cpp +++ b/core/loader/XFileHandler.cpp @@ -51,24 +51,24 @@ void XFileHandler::readXFile(int frame) _a(1, 0) = std::stod(tokens[1]); _a(2, 0) = std::stod(tokens[2]); _u(0, 0) = std::stod(tokens[3]); - _u(1, 0) = std::stod(tokens[3]); - _u(2, 0) = std::stod(tokens[4]); + _u(1, 0) = std::stod(tokens[4]); + _u(2, 0) = std::stod(tokens[5]); std::getline(ifs, line); tokens = tokenize(line); _a(0, 1) = std::stod(tokens[0]); _a(1, 1) = std::stod(tokens[1]); _a(2, 1) = std::stod(tokens[2]); _u(0, 1) = std::stod(tokens[3]); - _u(2, 1) = std::stod(tokens[3]); - _u(1, 1) = std::stod(tokens[4]); + _u(2, 1) = std::stod(tokens[4]); + _u(1, 1) = std::stod(tokens[5]); std::getline(ifs, line); tokens = tokenize(line); _a(0, 2) = std::stod(tokens[0]); _a(1, 2) = std::stod(tokens[1]); _a(2, 2) = std::stod(tokens[2]); _u(0, 2) = std::stod(tokens[3]); - _u(1, 2) = std::stod(tokens[3]); - _u(2, 2) = std::stod(tokens[4]); + _u(1, 2) = std::stod(tokens[4]); + _u(2, 2) = std::stod(tokens[5]); // Other metadata (line 5) std::getline(ifs, line); diff --git a/core/peak/Peak3D.cpp b/core/peak/Peak3D.cpp index 000019610..27368e1eb 100644 --- a/core/peak/Peak3D.cpp +++ b/core/peak/Peak3D.cpp @@ -247,7 +247,6 @@ void Peak3D::updateIntegration( _regionType = regionType; _rockingCurve = result.rocking_curve; - _rockingCurve = result.rocking_curve; if (result.integrator_type == IntegratorType::PixelSum) _meanBkgGradient = result.bkg_gradient; diff --git a/core/peak/Qs2Events.cpp b/core/peak/Qs2Events.cpp index bda9b2666..3d18620da 100644 --- a/core/peak/Qs2Events.cpp +++ b/core/peak/Qs2Events.cpp @@ -43,7 +43,7 @@ std::vector<std::pair<MillerIndex, DetectorEvent>> algo::qMap2Events( sptrProgressHandler handler /* = nullptr */, bool thread_parallel /* = true */) { ohklLog( - Level::Debug, "algo::Qs2Events::qVectorList2Events: processing ", sample_qs.size(), + Level::Debug, "algo::Qs2Events::qMap2Events: processing ", sample_qs.size(), " q-vectors"); std::vector<std::pair<MillerIndex, DetectorEvent>> events; @@ -75,7 +75,7 @@ std::vector<std::pair<MillerIndex, DetectorEvent>> algo::qMap2Events( if (handler) handler->setProgress(100); ohklLog( - Level::Debug, "algo::Qs2Events::qVectorList2Events: finished; generated ", events.size(), + Level::Debug, "algo::Qs2Events::qMap2Events: finished; generated ", events.size(), " events"); return events; } -- GitLab From a0c9bc6d3349285494147b3d8a8d2579d3c6cbc9 Mon Sep 17 00:00:00 2001 From: Zamaan Raza <z.raza@fz-juelich.de> Date: Wed, 24 Jul 2024 14:52:07 +0200 Subject: [PATCH 22/22] Add integration maximum width to SubframeFindPeaks --- gui/subframe_find/SubframeFindPeaks.cpp | 21 ++++++++++++++++++++ gui/subframe_find/SubframeFindPeaks.h | 3 +++ gui/subframe_integrate/SubframeIntegrate.cpp | 2 +- gui/subframe_shapes/SubframeShapes.cpp | 2 +- 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/gui/subframe_find/SubframeFindPeaks.cpp b/gui/subframe_find/SubframeFindPeaks.cpp index 0df88de8b..5591cb4df 100644 --- a/gui/subframe_find/SubframeFindPeaks.cpp +++ b/gui/subframe_find/SubframeFindPeaks.cpp @@ -216,6 +216,23 @@ void SubframeFindPeaks::setIntegrateUp() "Background end", "(" + QString(QChar(0x03C3)) + ") - scaling factor for upper limit of background"); + _use_max_width = new QGroupBox("Maximum width for integration"); + _use_max_width->setAlignment(Qt::AlignLeft); + _use_max_width->setCheckable(true); + _use_max_width->setChecked(false); + _use_max_width->setToolTip("Skip integration of peaks spanning too many images"); + + _max_width = new SafeSpinBox(); + _max_width->setMaximum(100); + + QLabel* label = new QLabel("Maximum width"); + label->setToolTip("Maximum width for peak to be integrated"); + QGridLayout* grid = new QGridLayout(); + _use_max_width->setLayout(grid); + grid->addWidget(label, 0, 0, 1, 1); + grid->addWidget(_max_width, 0, 1, 1, 1); + f.addWidget(_use_max_width); + _gradient_check = f.addCheckBox( "Compute gradient", "Compute mean gradient and sigma of background region", 1); @@ -450,6 +467,8 @@ void SubframeFindPeaks::grabIntegrationParameters() _bkg_end->setValue(params->fixed_bkg_end); } + _use_max_width->setChecked(params->use_max_width); + _max_width->setValue(params->max_width); _gradient_check->setChecked(params->use_gradient); _fft_gradient_check->setChecked(params->fft_gradient); _gradient_kernel->setCurrentIndex(static_cast<int>(params->gradient_type)); @@ -472,6 +491,8 @@ void SubframeFindPeaks::setIntegrationParameters() params->fixed_bkg_begin = _bkg_begin->value(); params->fixed_bkg_end = _bkg_end->value(); } + params->use_max_width = _use_max_width->isChecked(); + params->max_width = _max_width->value(); params->use_gradient = _gradient_check->isChecked(); params->fft_gradient = _fft_gradient_check->isChecked(); params->gradient_type = static_cast<ohkl::GradientKernel>(_gradient_kernel->currentIndex()); diff --git a/gui/subframe_find/SubframeFindPeaks.h b/gui/subframe_find/SubframeFindPeaks.h index d9a1d3945..2d57304bc 100644 --- a/gui/subframe_find/SubframeFindPeaks.h +++ b/gui/subframe_find/SubframeFindPeaks.h @@ -28,6 +28,7 @@ class PeakTableView; class PeakViewWidget; class QCheckBox; class QComboBox; +class QGroupBox; class QPushButton; class QSplitter; class QTableWidget; @@ -128,6 +129,8 @@ class SubframeFindPeaks : public QWidget { SafeDoubleSpinBox* _peak_end; SafeDoubleSpinBox* _bkg_begin; SafeDoubleSpinBox* _bkg_end; + SafeSpinBox* _max_width; + QGroupBox* _use_max_width; QCheckBox* _gradient_check; QCheckBox* _fft_gradient_check; QComboBox* _gradient_kernel; diff --git a/gui/subframe_integrate/SubframeIntegrate.cpp b/gui/subframe_integrate/SubframeIntegrate.cpp index 2220098dc..8921edabf 100644 --- a/gui/subframe_integrate/SubframeIntegrate.cpp +++ b/gui/subframe_integrate/SubframeIntegrate.cpp @@ -411,7 +411,7 @@ void SubframeIntegrate::setIntegrateUp() _use_max_width->setToolTip("Skip integration of peaks spanning too many images"); _max_width = new SafeSpinBox(); - _max_width->setMaximum(20); + _max_width->setMaximum(100); label = new QLabel("Maximum width"); label->setToolTip("Maximum width for peak to be integrated"); diff --git a/gui/subframe_shapes/SubframeShapes.cpp b/gui/subframe_shapes/SubframeShapes.cpp index 15d52f9eb..c80069718 100644 --- a/gui/subframe_shapes/SubframeShapes.cpp +++ b/gui/subframe_shapes/SubframeShapes.cpp @@ -184,7 +184,7 @@ void SubframeShapes::setInputUp() _min_strength->setMaximum(10000); _min_d->setMaximum(1000); _max_d->setMaximum(10000); - _max_width->setMaximum(20); + _max_width->setMaximum(100); _peak_end->setMinimum(0.1); _peak_end->setMaximum(50); _peak_end->setSingleStep(0.1); -- GitLab