17#include "MantidTypes/SpectrumDefinition.h"
19#define BOOST_ALLOW_DEPRECATED_HEADERS
20#include <boost/graph/adjacency_list.hpp>
21#undef BOOST_ALLOW_DEPRECATED_HEADERS
23#include <boost/graph/connected_components.hpp>
28const double TWO_PI = 2 * M_PI;
30bool isDifferenceLargerThanTolerance(
const double angle1,
const double angle2,
const double tolerance) {
31 auto difference = std::abs(angle1 - angle2);
35 if (difference > TWO_PI) {
36 difference = std::fmod(difference, TWO_PI);
40 if (difference > M_PI) {
41 difference = TWO_PI - difference;
69SXPeak::SXPeak(
double t,
double phi,
double intensity,
const std::vector<int> &spectral,
const size_t wsIndex,
71 :
m_tof(t),
m_phi(phi), m_intensity(intensity), m_spectra(spectral), m_wsIndex(wsIndex) {
74 throw std::invalid_argument(
"SXPeak: Cannot have an intensity < 0");
76 if (spectral.empty()) {
77 throw std::invalid_argument(
"SXPeak: Cannot have zero sized spectral list");
80 throw std::invalid_argument(
"SXPeak: Spectrum at ws index " +
std::to_string(wsIndex) +
" doesn't have detectors");
83 const auto l1 = spectrumInfo.
l1();
89 throw std::invalid_argument(
"SXPeak: Cannot have detector distance < 0");
95 const auto unit = Mantid::Kernel::UnitFactory::Instance().create(
"dSpacing");
98 unit->initialize(l1, 0,
pmap);
101 }
catch (std::exception &) {
109 const auto beamDir =
normalize(samplePos - sourcePos);
111 const auto detDir =
normalize(detPos - samplePos);
113 m_qConvention = Kernel::ConfigService::Instance().getString(
"Q.convention");
137 if (std::abs(x_1 - x_2) > xTolerance) {
141 if (isDifferenceLargerThanTolerance(
m_phi,
rhs.m_phi, phiTolerance)) {
144 if (isDifferenceLargerThanTolerance(
m_twoTheta,
rhs.m_twoTheta, thetaTolerance)) {
165 double wvi = 1.0 / wi;
211 : m_y(
y), m_startIndex(0), m_stopIndex(m_y.size() - 1), m_maxIndex(0) {}
259 const HistogramData::HistogramY & )
const {
264 : m_backgroundMultiplier(backgroundMultiplier) {}
267 const HistogramData::HistogramY &
y)
const {
268 auto background = 0.5 * (1.0 +
y.front() +
y.back());
270 return intensity < background;
280 const double maxValue,
const XAxisUnit units)
281 : m_backgroundStrategy(backgroundStrategy), m_minValue(minValue), m_maxValue(maxValue),
282 m_spectrumInfo(spectrumInfo), m_units(units) {}
285 const HistogramData::HistogramE &e,
const int workspaceIndex)
const {
290 auto lowit = boundsIterator.first;
291 auto highit = boundsIterator.second;
294 if (lowit ==
x.end() || highit ==
x.begin()) {
314 for (
auto inputIt = inputPeakList.begin(); inputIt != inputPeakList.end();) {
315 if (
static_cast<int>((*inputIt)->getNumberOfPointsInPeak()) <
m_minNBinsPerPeak) {
316 inputIt = inputPeakList.erase(inputIt);
326 using std::placeholders::_1;
329 : std::find_if(lowit,
x.end(), std::bind(std::greater<double>(), _1,
m_maxValue));
331 return std::make_pair(lowit, highit);
346 const auto numberOfDetectors = spectrumDefinition.size();
348 if (numberOfDetectors == 1) {
353 if (!detectorGroup) {
354 throw std::runtime_error(
"Could not cast to detector group");
356 phi = detectorGroup->getPhi();
366 auto leftBinPosition =
x.begin() + peakLocation;
367 const double leftBinEdge = *leftBinPosition;
368 const double rightBinEdge = *std::next(leftBinPosition);
369 return 0.5 * (leftBinEdge + rightBinEdge);
377 const auto unit = UnitFactory::Instance().create(
"dSpacing");
383 return unit->singleToTOF(xValue);
388 const std::vector<std::unique_ptr<PeakContainer>> &foundPeaks,
389 const int workspaceIndex)
const {
392 if (foundPeaks.empty()) {
397 peaks = std::vector<FindSXPeaksHelper::SXPeak>();
399 for (
const auto &peak : foundPeaks) {
401 auto maxY = peak->getMaxIterator();
402 const auto distance = std::distance(
y.begin(), maxY);
407 std::vector<int> specs(1, workspaceIndex);
408 (*peaks).emplace_back(tof, phi, *maxY, specs, workspaceIndex,
m_spectrumInfo);
416 const double maxValue,
const XAxisUnit units)
420 [[maybe_unused]]
const HistogramData::HistogramE &e,
Bound low,
421 Bound high,
const int workspaceIndex)
const {
422 auto distmin = std::distance(
x.begin(), low);
423 auto distmax = std::distance(
x.begin(), high);
426 auto maxY = (
y.size() > 1) ? std::max_element(
y.begin() + distmin,
y.begin() + distmax) :
y.begin();
429 double intensity = (*maxY);
435 const auto distance = std::distance(
y.begin(), maxY);
440 std::vector<int> specs(1, workspaceIndex);
441 std::vector<SXPeak> peaks;
442 peaks.emplace_back(tof, phi, *maxY, specs, workspaceIndex,
m_spectrumInfo);
447 const double minValue,
const double maxValue,
const XAxisUnit units)
451 throw std::invalid_argument(
"The AllPeaksStrategy has to be initialized "
452 "with the AbsoluteBackgroundStrategy.");
457 [[maybe_unused]]
const HistogramData::HistogramE &e,
Bound low,
Bound high,
458 const int workspaceIndex)
const {
471std::vector<std::unique_ptr<PeakContainer>>
480 bool isRecording =
false;
482 std::unique_ptr<PeakContainer> currentPeak =
nullptr;
483 std::vector<std::unique_ptr<PeakContainer>> peaks;
487 if (high !=
x.end()) {
490 auto distanceMin = std::distance(
x.begin(), low);
491 auto distanceMax = std::distance(
x.begin(), high);
493 const auto lowY =
y.begin() + distanceMin;
494 auto highY = distanceMax < static_cast<int>(
y.size()) ?
y.begin() + distanceMax :
y.end();
496 for (
auto it = lowY; it != highY; ++it) {
497 const auto signal = *it;
505 if (!isRecording && (!std::isfinite(signal) || !isAboveThreshold)) {
507 }
else if (!isRecording && isAboveThreshold && std::isfinite(signal)) {
509 currentPeak = std::make_unique<PeakContainer>(
y);
510 currentPeak->startRecord(it);
512 }
else if (isRecording && !isAboveThreshold) {
513 currentPeak->stopRecord(it);
514 peaks.emplace_back(std::move(currentPeak));
515 currentPeak =
nullptr;
519 currentPeak->record(it);
525 if (highY ==
y.end()) {
528 currentPeak->stopRecord(highY);
529 peaks.emplace_back(std::move(currentPeak));
530 currentPeak =
nullptr;
537 const double minValue,
const double maxValue,
const XAxisUnit units)
538 :
PeakFindingStrategy(nullptr, spectrumInfo, minValue, maxValue, units), m_nsigma(nsigma) {}
541 const HistogramData::HistogramE &e,
Bound low,
Bound high,
542 const int workspaceIndex)
const {
553 const HistogramData::HistogramY &
y,
554 const HistogramData::HistogramE &e,
561 bool isRecording =
false;
562 std::unique_ptr<PeakContainer> currentPeak =
nullptr;
563 std::vector<std::unique_ptr<PeakContainer>> peaks;
566 if (high !=
x.end()) {
569 auto distanceMin = std::distance(
x.begin(), low);
570 auto distanceMax = std::distance(
x.begin(), high);
572 const auto lowY =
y.begin() + distanceMin;
573 auto highY = distanceMax < static_cast<int>(
y.size()) ?
y.begin() + distanceMax :
y.end();
575 const auto lowE = e.begin() + distanceMin;
576 const auto highE = distanceMax < static_cast<int>(e.size()) ? e.begin() + distanceMax : e.begin();
581 for (;
yIt != highY && eIt != highE; ++
yIt, ++eIt) {
582 const auto signalDiff = *
yIt - *(
yIt - 1);
585 const auto isEndPeak = (currentPeak !=
nullptr ? (isSigDropSignificant || currentPeak->getStartingSignal() > *
yIt)
586 : isSigDropSignificant);
594 if (!isRecording && isStartPeak) {
595 currentPeak = std::make_unique<PeakContainer>(
y);
596 currentPeak->startRecord(
yIt);
598 }
else if (isRecording && !isEndPeak) {
599 currentPeak->record(
yIt);
600 }
else if (isRecording && isEndPeak) {
601 currentPeak->stopRecord(
yIt);
602 peaks.emplace_back(std::move(currentPeak));
603 currentPeak =
nullptr;
612 if (highY ==
y.end()) {
615 currentPeak->stopRecord(highY);
616 peaks.emplace_back(std::move(currentPeak));
617 currentPeak =
nullptr;
628 : m_compareStrategy(compareStrategy) {}
648 std::vector<SXPeak> finalPeaks;
649 for (
const auto ¤tPeak : peaks) {
650 auto pos = std::find_if(finalPeaks.begin(), finalPeaks.end(), [¤tPeak,
this](
SXPeak &peak) {
651 auto result = this->m_compareStrategy->compare(currentPeak, peak);
658 if (pos == finalPeaks.end()) {
659 finalPeaks.emplace_back(currentPeak);
673 for (
auto peakIt = inputPeaks.begin(); peakIt != inputPeaks.end();) {
678 peakIt = inputPeaks.erase(peakIt);
703using PeakGraph = adjacency_list<vecS, vecS, undirectedS, SXPeak *>;
704using Vertex = boost::graph_traits<PeakGraph>::vertex_descriptor;
705using Edge = boost::graph_traits<PeakGraph>::edge_descriptor;
712 std::vector<SXPeak *> peaks;
713 peaks.reserve(peakList.size());
714 std::transform(peakList.cbegin(), peakList.cend(), std::back_inserter(peaks),
715 [](
const auto &peak) { return &const_cast<SXPeak &>(peak); });
720 PeakGraph::vertex_iterator vertexIt, vertexEnd;
723 const size_t numberOfPeaksFound = peaks.size();
724 if (numberOfPeaksFound > 500) {
725 std::string warningMessage = std::string(
"There are ") +
std::to_string(numberOfPeaksFound) +
726 std::string(
" peaks being processed. This might take a long time. "
727 "Please check that the cutoff of the background that "
728 "you have selected is high enough, else the algorithm will"
729 " mistake background noise for peaks. The instrument view "
730 "allows you to easily inspect the typical background level.");
734 std::string message = std::string(
"There are ") +
std::to_string(numberOfPeaksFound) +
735 std::string(
" peaks. Investigating peak number ");
738 for (
auto peak : peaks) {
742 auto vertex = add_vertex(peak, graph);
746 std::tie(vertexIt, vertexEnd) = vertices(graph);
749 if (peakCounter > 50) {
753 for (; vertexIt != vertexEnd; ++vertexIt) {
756 if (vertex == *vertexIt) {
761 if (boost::edge(vertex, *vertexIt, graph).second) {
766 const auto toCheck = graph[*vertexIt];
769 add_edge(vertex, *vertexIt, graph);
775 std::vector<int> components(boost::num_vertices(graph));
776 const int numberOfPeaks = connected_components(graph, &components[0]);
778 std::vector<std::vector<SXPeak *>> peakGroups(numberOfPeaks);
780 for (
auto i = 0u; i < components.size(); ++i) {
781 auto index = components[i];
782 peakGroups[
index].emplace_back(graph[i]);
789 std::vector<SXPeak> peaks;
793 for (
const auto &
group : peakGroups) {
802 SXPeak *maxPeak =
nullptr;
803 double maxIntensity = std::numeric_limits<double>::min();
805 for (
auto *element :
group) {
806 if (element->getIntensity() > maxIntensity) {
807 maxIntensity = element->getIntensity();
815 peaks.emplace_back(*maxPeak);
833 const double twoThetaResolution,
const XAxisUnit units)
834 : m_xUnitResolution(xUnitResolution), m_phiResolution(phiResolution), m_twoThetaResolution(twoThetaResolution),
837 constexpr double rad2deg = M_PI / 180.;
std::vector< float > m_tof
sum of all time-of-flight within the bin
const std::vector< double > & rhs
#define NSIGMA_COMPARISON_THRESHOLD
API::SpectrumInfo is an intermediate step towards a SpectrumInfo that is part of Instrument-2....
Kernel::V3D sourcePosition() const
Returns the source position.
Kernel::V3D samplePosition() const
Returns the sample position.
bool hasDetectors(const size_t index) const
Returns true if the spectrum is associated with detectors in the instrument.
double twoTheta(const size_t index) const
Returns the scattering angle 2 theta in radians (angle w.r.t.
Kernel::V3D position(const size_t index) const
Returns the position of the spectrum with given index.
double l2(const size_t index) const
Returns L2 (distance from sample to spectrum).
const SpectrumDefinition & spectrumDefinition(const size_t index) const
Returns a const reference to the SpectrumDefinition of the spectrum.
const Geometry::IDetector & detector(const size_t index) const
Return a const reference to the detector or detector group of the spectrum with given index.
double l1() const
Returns L1 (distance from source to sample).
void getDetectorValues(const Kernel::Unit &inputUnit, const Kernel::Unit &outputUnit, const Kernel::DeltaEMode::Type emode, const bool signedTheta, int64_t wsIndex, Kernel::UnitParametersMap &pmap) const
Get the detector values relevant to unit conversion for a workspace index.
AbsoluteCompareStrategy(const double tofResolution, const double phiResolution, const double twoThetaResolution, const XAxisUnit units=XAxisUnit::TOF)
double m_twoThetaResolution
bool compare(const SXPeak &lhs, const SXPeak &rhs) const override
const double m_xUnitResolution
PeakList dofindSXPeaks(const HistogramData::HistogramX &x, const HistogramData::HistogramY &y, const HistogramData::HistogramE &e, Bound low, Bound high, const int workspaceIndex) const override
std::vector< std::unique_ptr< PeakContainer > > getAllPeaks(const HistogramData::HistogramX &x, const HistogramData::HistogramY &y, Bound low, Bound high, const Mantid::Crystal::FindSXPeaksHelper::BackgroundStrategy *backgroundStrategy) const
AllPeaksStrategy(const BackgroundStrategy *backgroundStrategy, const API::SpectrumInfo &spectrumInfo, const double minValue=EMPTY_DBL(), const double maxValue=EMPTY_DBL(), const XAxisUnit units=XAxisUnit::TOF)
virtual bool compare(const SXPeak &lhs, const SXPeak &rhs) const =0
FindMaxReduceStrategy(const CompareStrategy *compareStrategy)
std::vector< SXPeak > reduce(const std::vector< SXPeak > &peaks, Mantid::Kernel::ProgressBase &progress) const override
std::vector< std::vector< SXPeak * > > getPeakGroups(const std::vector< SXPeak > &peakList, Mantid::Kernel::ProgressBase &progress) const
std::vector< SXPeak > getFinalPeaks(const std::vector< std::vector< SXPeak * > > &peakGroups) const
PeakList dofindSXPeaks(const HistogramData::HistogramX &x, const HistogramData::HistogramY &y, const HistogramData::HistogramE &e, Bound low, Bound high, const int workspaceIndex) const override
NSigmaPeaksStrategy(const API::SpectrumInfo &spectrumInfo, const double nsigma=EMPTY_DBL(), const double minValue=EMPTY_DBL(), const double maxValue=EMPTY_DBL(), const XAxisUnit units=XAxisUnit::TOF)
std::vector< std::unique_ptr< PeakContainer > > getAllNSigmaPeaks(const HistogramData::HistogramX &x, const HistogramData::HistogramY &y, const HistogramData::HistogramE &e, Bound low, Bound high) const
PeakContainer(const HistogramData::HistogramY &y)
void startRecord(yIt item)
yIt getMaxIterator() const
const HistogramData::HistogramY & m_y
void stopRecord(yIt item)
double getStartingSignal() const
size_t getNumberOfPointsInPeak() const
virtual PeakList dofindSXPeaks(const HistogramData::HistogramX &x, const HistogramData::HistogramY &y, const HistogramData::HistogramE &e, Bound low, Bound high, const int workspaceIndex) const =0
void setMinNBinsPerPeak(int minBinsPerPeak)
double calculatePhi(size_t workspaceIndex) const
Calculates the average phi value if the workspace contains multiple detectors per spectrum,...
const BackgroundStrategy * m_backgroundStrategy
double getXValue(const HistogramData::HistogramX &x, const size_t peakLocation) const
void filterPeaksForMinBins(std::vector< std::unique_ptr< PeakContainer > > &inputPeakList) const
PeakList findSXPeaks(const HistogramData::HistogramX &x, const HistogramData::HistogramY &y, const HistogramData::HistogramE &e, const int workspaceIndex) const
PeakFindingStrategy(const BackgroundStrategy *backgroundStrategy, const API::SpectrumInfo &spectrumInfo, const double minValue=EMPTY_DBL(), const double maxValue=EMPTY_DBL(), const XAxisUnit units=XAxisUnit::TOF)
PeakList convertToSXPeaks(const HistogramData::HistogramX &x, const HistogramData::HistogramY &y, const std::vector< std::unique_ptr< PeakContainer > > &peaks, const int workspaceIndex) const
BoundsIterator getBounds(const HistogramData::HistogramX &x) const
double convertToTOF(const double xValue, const size_t workspaceIndex) const
const API::SpectrumInfo & m_spectrumInfo
const CompareStrategy * m_compareStrategy
ReducePeakListStrategy(const CompareStrategy *compareStrategy)
void setMinNSpectraPerPeak(int minSpectrasForPeak)
void setMaxNSpectraPerPeak(int maxSpectrasForPeak)
bool compare(const SXPeak &lhs, const SXPeak &rhs) const override
const double m_resolution
RelativeCompareStrategy(const double resolution)
void reducePeaksFromNumberOfSpectras(std::vector< SXPeak > &inputPeaks) const
std::vector< SXPeak > reduce(const std::vector< SXPeak > &peaks, Mantid::Kernel::ProgressBase &progress) const override
SimpleReduceStrategy(const CompareStrategy *compareStrategy)
PeakList dofindSXPeaks(const HistogramData::HistogramX &x, const HistogramData::HistogramY &y, const HistogramData::HistogramE &e, Bound low, Bound high, const int workspaceIndex) const override
StrongestPeaksStrategy(const BackgroundStrategy *backgroundStrategy, const API::SpectrumInfo &spectrumInfo, const double minValue=EMPTY_DBL(), const double maxValue=EMPTY_DBL(), const XAxisUnit units=XAxisUnit::TOF)
Holds a collection of detectors.
virtual double getPhi() const =0
Gives the phi of this detector object in radians.
virtual detid_t getID() const =0
Get the detector ID.
The Logger class is in charge of the publishing messages from the framework through various channels.
void warning(const std::string &msg)
Logs at warning level.
virtual void doReport(const std::string &msg="")=0
Pure virtual method that does the progress reporting, to be overridden.
Time of flight in microseconds.
Mantid::HistogramData::HistogramY::const_iterator yIt
boost::graph_traits< PeakGraph >::vertex_descriptor Vertex
adjacency_list< vecS, vecS, undirectedS, SXPeak * > PeakGraph
Mantid::Kernel::Logger g_log("FindSXPeaksHelper")
boost::graph_traits< PeakGraph >::edge_descriptor Edge
HistogramData::HistogramX::const_iterator Bound
XAxisUnit
enum to determine the units of the workspaces X axis we are searching in
std::optional< std::vector< SXPeak > > PeakList
std::pair< Bound, Bound > BoundsIterator
MANTID_KERNEL_DLL bool withinRelativeDifference(T const x, T const y, S const tolerance)
Test whether x, y are within relative tolerance tol.
std::unordered_map< UnitParams, double > UnitParametersMap
SingletonHolder< UnitFactoryImpl > UnitFactory
MANTID_KERNEL_DLL V3D normalize(V3D v)
Normalizes a V3D.
static constexpr double NeutronMass
Mass of the neutron in kg.
static constexpr double h_bar
Planck constant in J*s, divided by 2 PI.
constexpr int EMPTY_INT() noexcept
Returns what we consider an "empty" integer within a property.
int32_t detid_t
Typedef for a detector ID.
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Generate a tableworkspace to store the calibration results.
std::string to_string(const wide_integer< Bits, Signed > &n)
AbsoluteBackgroundStrategy(const double background)
bool isBelowBackground(const double intensity, const HistogramData::HistogramY &) const override
virtual bool isBelowBackground(const double intensity, const HistogramData::HistogramY &y) const =0
PerSpectrumBackgroundStrategy(const double backgroundMultiplier)
double m_backgroundMultiplier
bool isBelowBackground(const double intensity, const HistogramData::HistogramY &y) const override
int m_nPixels
Number of contributing pixels.
detid_t getDetectorId() const
Getter for the detector Id.
double m_phi
Phi angle for the centre detector of the peak.
double m_tof
TOF for the peak centre.
std::vector< int > m_spectra
Contributing spectra to this peak.
Kernel::V3D m_unitWaveVector
Unit vector in the direction of the wavevector.
detid_t m_detId
Detector ID.
double m_LTotal
Detector-sample distance.
const std::vector< int > & getPeakSpectras() const
Getter for spectrum indexes of a peak.
SXPeak(double t, double phi, double intensity, const std::vector< int > &spectral, const size_t wsIndex, const Mantid::API::SpectrumInfo &spectrumInfo)
Constructor.
bool compare(const SXPeak &rhs, double tolerance) const
Object comparison.
double m_intensity
Measured intensity of centre of the peak.
double m_dSpacing
d-spacing at the peak centre
void reduce()
Normalise by number of pixels.
SXPeak & operator+=(const SXPeak &rhs)
Operator addition overload.
std::string m_qConvention
Q Convention.
Mantid::Kernel::V3D getQ() const
Getter for LabQ.
const double & getIntensity() const
Getter for the intensity.
double m_twoTheta
2 * theta angle for then centre detector of the peak
size_t m_wsIndex
Detector workspace index.
Peak indexing algorithm, which works by assigning multiple possible HKL values to each peak and then ...