35template <
typename MDE,
size_t nd>
struct IsFullEvent : boost::false_type {};
38template <
size_t nd>
struct IsFullEvent<
MDEvent<nd>, nd> : boost::true_type {};
44template <
typename MDE,
size_t nd>
bool isFullMDEvent(
const boost::true_type & ) {
return true; }
50template <
typename MDE,
size_t nd>
bool isFullMDEvent(
const boost::false_type & ) {
return false; }
55template <
typename MDE,
size_t nd>
bool isFullMDEvent() {
return isFullMDEvent<MDE, nd>(IsFullEvent<MDE, nd>()); }
62template <
typename MDE,
size_t nd>
65 std::cerr <<
"Box has children\n";
70 throw std::invalid_argument(
"FindPeaksMD::addDetectors - Unexpected Box "
71 "type, cannot retrieve events");
73 const auto &events = mdBox->getConstEvents();
74 auto itend = events.end();
75 for (
auto it = events.begin(); it != itend; ++it) {
82template <
typename MDE,
size_t nd>
84 const boost::false_type & ) {
85 throw std::runtime_error(
"FindPeaksMD - Workspace contains lean events, "
86 "cannot include detector information");
110 : peakWS(), peakRadiusSquared(), DensityThresholdFactor(0.0), m_maxPeaks(0), m_addDetectors(true),
111 m_densityScaleFactor(1e-6), prog(nullptr), m_inst(), m_runNumber(-1), dimType(), m_goniometer() {}
118 "An input MDEventWorkspace or MDHistoWorkspace with at least "
122 "Threshold distance for rejecting peaks that are found to be too close "
124 "This should be some multiple of the radius of a peak. Default: 0.1.");
127 "Maximum number of peaks to find. Default: 500.");
131 "Strategy for finding peaks in an MD workspace."
132 "1. VolumeNormalization: This is the default strategy. It will sort "
133 "all boxes in the workspace by deacresing order of signal density "
134 "(total weighted event sum divided by box volume).\n"
135 "2.NumberOfEventsNormalization: This option is only valid for "
136 "MDEventWorkspaces. It will use the total weighted event sum divided"
137 "by the number of events. This can improve peak finding for "
139 "raw data which has been converted to an EventWorkspace. The threshold "
141 "peak finding can be controlled by the SingalThresholdFactor property "
143 "be larger than 1. Note that this approach does not work for event-based "
147 "The overall signal density of the workspace will be "
148 "multiplied by this factor \n"
149 "to get a threshold signal density below which boxes are NOT "
150 "considered to be peaks. See the help.\n"
154 std::make_unique<EnabledWhenProperty>(
158 "The overal signal value (not density!) normalized by the "
159 "number of events is compared to the specified signal "
160 "threshold. Boxes which are below this threshold are NOT "
161 "considered to be peaks."
162 "This property is enabled when the PeakFindingStrategy has "
163 "been set to NumberOfEventsNormalization.\n"
164 "The value of boxes which contain peaks will be above 1. See "
165 "the below for more information.\n"
169 std::make_unique<EnabledWhenProperty>(
"PeakFindingStrategy",
174 "This will calculate the goniometer rotation (around y-axis "
175 "only) for a constant wavelength. This only works for Q "
176 "sample workspaces.");
178 auto nonNegativeDbl = std::make_shared<BoundedValidator<double>>();
179 nonNegativeDbl->setLower(0);
181 "Wavelength to use when calculating goniometer angle. If not"
182 "set will use the wavelength parameter on the instrument.");
184 std::make_unique<EnabledWhenProperty>(
"CalculateGoniometerForCW",
188 "Whether the goniometer to be calculated is the most inner "
189 "(phi) or most outer (omega)");
191 std::make_unique<EnabledWhenProperty>(
"CalculateGoniometerForCW",
195 "Used when calculating goniometer angle if the q_lab x value "
196 "should be negative, hence the detector of the other side "
197 "(right) of the beam");
200 std::vector<std::string> peakTypes = {
"Automatic",
"Peak",
"LeanElasticPeak"};
201 declareProperty(
"OutputType",
"Automatic", std::make_shared<StringListValidator>(peakTypes),
202 "Type of Peak in OutputWorkspace");
204 "An output PeaksWorkspace with the peaks' found positions.");
207 "If checked, then append the peaks in the output workspace "
209 "If unchecked, the output workspace is replaced (Default).");
211 auto nonNegativeInt = std::make_shared<BoundedValidator<int>>();
212 nonNegativeInt->setLower(0);
213 declareProperty(
"EdgePixels", 0, nonNegativeInt,
"Remove peaks that are at pixels this close to edge. ");
220 m_inst = ei->getInstrument();
228 }
catch (std::exception &e) {
229 g_log.
warning() <<
"Error finding goniometer matrix. It will not be set in "
230 "the peaks found.\n";
237 std::string dim0 = ws->getDimension(0)->getName();
240 throw std::runtime_error(
"Cannot find peaks in a workspace that is already in HKL space.");
241 }
else if (dim0 ==
"Q_lab_x") {
243 }
else if (dim0 ==
"Q_sample_x")
246 throw std::runtime_error(
"Unexpected dimensions: need either Q_lab_x or Q_sample_x.");
254 if (peakType ==
"Automatic") {
255 if (numExperimentInfo == 0)
257 }
else if (peakType ==
"LeanElasticPeak") {
260 if (numExperimentInfo == 0)
261 throw std::runtime_error(
"Cannot create Peak output with 0 expInfo");
276 auto p = this->
createPeak(Q, binCount, tracer);
278 if (
edgePixel(compInfo, p->getBankName(), p->getCol(), p->getRow(),
m_edge))
281 if (p->getDetectorID() != -1)
283 }
catch (std::exception &e) {
284 g_log.
notice() <<
"Error creating peak at " << Q <<
" because of '" << e.what() <<
"'. Peak will be skipped.\n";
306 std::shared_ptr<DataObjects::Peak> p;
309 p = std::make_shared<Peak>(
m_inst, Q);
314 bool calcGoniometer =
getProperty(
"CalculateGoniometerForCW");
316 if (calcGoniometer) {
319 if (wavelength == DBL_MAX) {
320 if (
m_inst->hasParameter(
"wavelength")) {
321 wavelength =
m_inst->getNumberParameter(
"wavelength").at(0);
323 throw std::runtime_error(
"Could not get wavelength, neither "
324 "Wavelength algorithm property "
325 "set nor instrument wavelength parameter");
332 g_log.
information() <<
"Found goniometer rotation to be in YZY convention [" << angles[0] <<
", " << angles[1]
333 <<
". " << angles[2] <<
"] degrees for Q sample = " << Q <<
"\n";
334 p = std::make_shared<Peak>(
m_inst, Q, goniometer.
getR());
340 throw std::invalid_argument(
"Cannot Integrate peaks unless the dimension is QLAB or QSAMPLE");
344 p->findDetector(tracer);
348 p->setBinCount(binCount);
357std::shared_ptr<DataObjects::LeanElasticPeak>
359 std::shared_ptr<DataObjects::LeanElasticPeak> p;
364 p = std::make_shared<LeanElasticPeak>(Q);
366 throw std::invalid_argument(
"Cannot find peaks unless the dimension is QSAMPLE");
369 p->setBinCount(binCount);
381 throw std::invalid_argument(
"Workspace must have at least 3 dimensions.");
383 if (isFullMDEvent<MDE, nd>()) {
387 g_log.
warning(
"Workspace contains only lean events. Resultant "
388 "PeaksWorkspaces will not contain full detector "
392 progress(0.01,
"Refreshing Centroids");
404 if (!std::isfinite(threshold)) {
405 g_log.
warning() <<
"Infinite or NaN overall density found. Your input data "
406 "may be invalid. Using a 0 threshold instead.\n";
414 typename std::vector<API::IMDNode *> boxes;
418 ws->
getBox()->getBoxes(boxes, 1000,
true);
421 using dens_box = std::pair<double, API::IMDNode *>;
425 typename std::multimap<double, API::IMDNode *> sortedBoxes;
428 progress(0.20,
"Sorting Boxes by Density");
429 auto it1 = boxes.begin();
430 auto it1_end = boxes.end();
431 for (; it1 != it1_end; it1++) {
436 if (
value > threshold)
437 sortedBoxes.insert(dens_box(
value, box));
442 std::vector<API::IMDNode *> peakBoxes;
447 bool isMDEvent(ws->
id().find(
"MDEventWorkspace") != std::string::npos);
449 int64_t numBoxesFound = 0;
452 typename std::multimap<double, boxPtr>::reverse_iterator it2;
453 auto it2_end = sortedBoxes.rend();
454 for (it2 = sortedBoxes.rbegin(); it2 != it2_end; ++it2) {
456 boxPtr box = it2->second;
457#ifndef MDBOX_TRACK_CENTROID
461 const coord_t *boxCenter = box->getCentroid();
466 for (
auto &peakBoxe : peakBoxes) {
468#ifndef MDBOX_TRACK_CENTROID
470 (*it3)->calculateCentroid(otherCenter);
472 const coord_t *otherCenter = peakBoxe->getCentroid();
477 for (
size_t d = 0;
d < nd;
d++) {
478 coord_t dist = otherCenter[
d] - boxCenter[
d];
479 distSquared += (dist * dist);
492 g_log.
notice() <<
"Number of peaks found exceeded the limit of " <<
m_maxPeaks <<
". Stopping peak finding.\n";
496 peakBoxes.emplace_back(box);
498 for (
size_t d = 0;
d < nd;
d++)
500 g_log.
debug() <<
"; Density = " << density <<
'\n';
502 prog->report(
"Finding Peaks");
506 prog->resetNumSteps(numBoxesFound, 0.95, 1.0);
510 if (numExperimentInfo == 0) {
512 for (
auto box : peakBoxes) {
514#ifndef MDBOX_TRACK_CENTROID
522 V3D Q(boxCenter[0], boxCenter[1], boxCenter[2]);
527 binCount =
static_cast<double>(box->
getNPoints());
533 prog->report(
"Adding Peaks");
544 peakWS->copyExperimentInfoFrom(ei.get());
547 for (
auto box : peakBoxes) {
549 if (numExperimentInfo > 1) {
551 const std::vector<MDE> &events = mdbox->
getEvents();
552 if (std::none_of(events.cbegin(), events.cend(), [&iexp, &numExperimentInfo](
MDE event) {
553 return event.getExpInfoIndex() == iexp ||
event.getExpInfoIndex() >= numExperimentInfo;
561 if (ei->run().getNumGoniometers() > 1) {
562 const std::vector<MDE> &events =
dynamic_cast<MDBox<MDE, nd> *
>(box)->getEvents();
565 for (
const auto &event : events) {
566 if (event.getExpInfoIndex() == iexp) {
567 sum +=
event.getGoniometerIndex();
575#ifndef MDBOX_TRACK_CENTROID
583 V3D Q(boxCenter[0], boxCenter[1], boxCenter[2]);
589 binCount =
static_cast<double>(box->
getNPoints());
595 auto p = this->
createPeak(Q, binCount, tracer);
599 throw std::runtime_error(
"Failed to cast box to MDBoxBase");
601 addDetectors(*p, *mdBox);
603 if (p->getDetectorID() != -1) {
605 if (!
edgePixel(compInfo, p->getBankName(), p->getCol(), p->getRow(),
m_edge))
611 g_log.
information() <<
"Add new peak with Q-center = " << Q[0] <<
", " << Q[1] <<
", " << Q[2] <<
"\n";
613 }
catch (std::exception &e) {
614 g_log.
notice() <<
"Error creating peak at " << Q <<
" because of '" << e.what()
615 <<
"'. Peak will be skipped.\n";
620 prog->report(
"Adding Peaks");
634 size_t nd = ws->getNumDims();
636 throw std::invalid_argument(
"Workspace must have at least 3 dimensions.");
638 g_log.
warning(
"Workspace is an MDHistoWorkspace. Resultant PeaksWorkspaces "
639 "will not contain full detector information.");
642 using dens_box = std::pair<double, size_t>;
646 std::multimap<double, size_t> sortedBoxes;
648 size_t numBoxes = ws->getNPoints();
651 progress(0.10,
"Counting Total Signal");
652 double totalSignal = 0;
653 for (
size_t i = 0; i < numBoxes; i++)
654 totalSignal += ws->getSignalAt(i);
656 double thresholdDensity =
658 if (!std::isfinite(thresholdDensity)) {
659 g_log.
warning() <<
"Infinite or NaN overall density found. Your input data "
660 "may be invalid. Using a 0 threshold instead.\n";
661 thresholdDensity = 0;
663 g_log.
information() <<
"Threshold signal density: " << thresholdDensity <<
'\n';
666 progress(0.20,
"Sorting Boxes by Density");
667 for (
size_t i = 0; i < numBoxes; i++) {
670 if (density > thresholdDensity)
671 sortedBoxes.insert(dens_box(density, i));
676 std::vector<size_t> peakBoxes;
680 int64_t numBoxesFound = 0;
683 std::multimap<double, size_t>::reverse_iterator it2;
684 auto it2_end = sortedBoxes.rend();
685 for (it2 = sortedBoxes.rbegin(); it2 != it2_end; ++it2) {
687 size_t index = it2->second;
689 VMD boxCenter = ws->getCenter(
index);
693 for (
auto &peakBoxe : peakBoxes) {
694 VMD otherCenter = ws->getCenter(peakBoxe);
698 for (
size_t d = 0;
d < nd;
d++) {
699 coord_t dist = otherCenter[
d] - boxCenter[
d];
700 distSquared += (dist * dist);
713 g_log.
notice() <<
"Number of peaks found exceeded the limit of " <<
m_maxPeaks <<
". Stopping peak finding.\n";
717 peakBoxes.emplace_back(
index);
719 g_log.
debug() <<
"; Density = " << density <<
'\n';
721 prog->report(
"Finding Peaks");
725 if (ws->getNumExperimentInfo() == 0) {
727 for (
auto index : peakBoxes) {
729 VMD boxCenter = ws->getCenter(
index);
732 V3D Q(boxCenter[0], boxCenter[1], boxCenter[2]);
741 prog->report(
"Adding Peaks");
745 for (uint16_t iexp = 0; iexp < ws->getNumExperimentInfo(); iexp++) {
752 peakWS->copyExperimentInfoFrom(ei.get());
755 for (
auto index : peakBoxes) {
757 VMD boxCenter = ws->getCenter(
index);
760 V3D Q(boxCenter[0], boxCenter[1], boxCenter[2]);
769 addPeak(compInfo, Q, binCount, tracer);
772 prog->report(
"Adding Peaks");
793 uint16_t numExperimentInfo = 0;
795 numExperimentInfo = inMDHW->getNumExperimentInfo();
797 numExperimentInfo = inMDEW->getNumExperimentInfo();
806 if (!
peakWS || !AppendPeaks) {
814 double PeakDistanceThreshold =
getProperty(
"PeakDistanceThreshold");
820 std::string strategy =
getProperty(
"PeakFindingStrategy");
832 throw std::runtime_error(
"This algorithm can only find peaks on a "
833 "MDHistoWorkspace or a MDEventWorkspace; it does "
834 "not work on a regular MatrixWorkspace.");
838 std::vector<std::pair<std::string, bool>> criteria;
839 criteria.emplace_back(
"RunNumber",
true);
840 auto isPeaksWorkspace = std::dynamic_pointer_cast<PeaksWorkspace>(
peakWS);
841 if (isPeaksWorkspace)
842 criteria.emplace_back(
"BankName",
true);
843 criteria.emplace_back(
"bincount",
false);
846 for (
auto i = 0; i !=
peakWS->getNumberPeaks(); ++i) {
858 std::map<std::string, std::string> result;
860 std::string strategy =
getProperty(
"PeakFindingStrategy");
867 if (useNumberOfEventsNormalization && !inMDEW) {
868 result[
"PeakFindingStrategy"] =
"The NumberOfEventsNormalization selection "
869 "can only be used with an MDEventWorkspace "
873 uint16_t numExperimentInfo = 0;
875 numExperimentInfo = inMDHW->getNumExperimentInfo();
877 numExperimentInfo = inMDEW->getNumExperimentInfo();
881 if (peakType ==
"Peak" && numExperimentInfo == 0)
882 result[
"OutputType"] =
"The InputWorkspace doesn't contain any experiment information so the "
883 "OutputType cannot be Peak.";
#define DECLARE_ALGORITHM(classname)
double value
The value of the point.
std::map< DeltaEMode::Type, std::string > index
#define CALL_MDEVENT_FUNCTION3(funcname, workspace)
Macro that makes it possible to call a templated method for a MDEventWorkspace using a IMDEventWorksp...
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
virtual void calculateCentroid(coord_t *) const =0
Calculate the centroid of this box and all sub-boxes.
virtual signal_t getSignalByNEvents() const
virtual size_t getNumChildren() const =0
Get the # of children MDBoxBase'es (non-recursive)
virtual coord_t * getCentroid() const =0
Get the centroid of this box and all sub-boxes.
virtual uint64_t getNPoints() const =0
Get total number of points both in memory and on file if present;.
uint16_t getNumExperimentInfo() const
ExperimentInfo_sptr getExperimentInfo(const uint16_t expInfoIndex)
Get the ExperimentInfo for the given Experiment-Info Index.
A property class for workspaces.
The class LeanElasticPeaksWorkspace stores information about a set of SCD lean peaks.
Templated super-class of a multi-dimensional event "box".
signal_t getSignalNormalized() const override
Returns the integrated signal from all points within, normalized for the cell volume.
Templated class for a multi-dimensional event "box".
std::vector< MDE > & getEvents()
Get vector of events to change.
std::shared_ptr< MDEventWorkspace< MDE, nd > > sptr
Typedef for a shared pointer of this kind of event workspace.
const std::string id() const override
Get the data type (id) of the workspace.
MDBoxBase< MDE, nd > * getBox()
Templated class holding data about a neutron detection event in N-dimensions (for example,...
Templated class holding data about a neutron detection event in N-dimensions (for example,...
Structure describing a single-crystal peak.
void addContributingDetID(const int id)
Add a detector ID that contributed to this peak.
The class PeaksWorkspace stores information about a set of SCD peaks.
ComponentInfo : Provides a component centric view on to the instrument.
Class to represent a particular goniometer setting, which is described by the rotation matrix.
std::vector< double > getEulerAngles(const std::string &convention="YZX")
Return Euler angles acording to a convention.
const Kernel::DblMatrix & getR() const
Return global rotation matrix.
void calcFromQSampleAndWavelength(const Mantid::Kernel::V3D &Q, double wavelength, bool flip_x=false, bool inner=false)
Calculate goniometer for rotation around y-asix for constant wavelength from Q Sample.
Structure describing a single-crystal peak.
virtual void setPeakNumber(int m_PeakNumber)=0
This class is responsible for tracking rays and accumulating a list of objects that are intersected a...
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void setPropertySettings(const std::string &name, std::unique_ptr< IPropertySettings const > settings)
Add a PropertySettings instance to the chain of settings for a given property.
void debug(const std::string &msg)
Logs at debug level.
void notice(const std::string &msg)
Logs at notice level.
void warning(const std::string &msg)
Logs at warning level.
void information(const std::string &msg)
Logs at information level.
The concrete, templated class for properties.
coord_t peakRadiusSquared
Estimated radius of peaks. Boxes closer than this are rejected.
eDimensionType dimType
Dimension type.
double m_signalThresholdFactor
Signal density factor.
void addPeak(const Geometry::ComponentInfo &compInfo, const Mantid::Kernel::V3D &Q, const double binCount, const Geometry::InstrumentRayTracer &tracer)
Adds a peak based on Q, bin count & a set of detector IDs.
bool m_useNumberOfEventsNormalization
Use number of events normalization for event workspaces.
std::shared_ptr< DataObjects::Peak > createPeak(const Mantid::Kernel::V3D &Q, const double binCount, const Geometry::InstrumentRayTracer &tracer)
Adds a peak based on Q, bin count.
void init() override
Initialise the properties.
void findPeaksHisto(const Mantid::DataObjects::MDHistoWorkspace_sptr &ws)
Run find peaks on a histo workspace.
void readExperimentInfo(const Mantid::API::ExperimentInfo_sptr &ei)
Read member variables from experiment info.
void exec() override
Run the algorithm.
void checkWorkspaceDims(const Mantid::API::IMDWorkspace_sptr &ws)
double DensityThresholdFactor
Thresholding factor.
signal_t m_densityScaleFactor
Arbitrary scaling factor for density to make more manageable numbers, especially for older file forma...
void determineOutputType(const std::string &peakType, const uint16_t numExperimentInfo)
FindPeaksMD()
Constructor.
int64_t m_maxPeaks
Max # of peaks.
void findPeaks(typename DataObjects::MDEventWorkspace< MDE, nd >::sptr ws)
Run find peaks on an MDEventWorkspace.
bool m_addDetectors
Flag to include the detectors within the peak.
std::unique_ptr< Mantid::API::Progress > prog
Progress reporter.
Mantid::API::IPeaksWorkspace_sptr peakWS
Output PeaksWorkspace.
static const std::string numberOfEventsNormalization
NumberOfEventNormalization.
std::map< std::string, std::string > validateInputs() override
Validate the inputs.
int m_edge
Number of edge pixels with no peaks.
Mantid::Kernel::Matrix< double > m_goniometer
Goniometer matrix.
std::shared_ptr< DataObjects::LeanElasticPeak > createLeanElasticPeak(const Mantid::Kernel::V3D &Q, const double binCount, const bool useGoniometer=false)
Adds a peak based on Q, bin count.
int m_runNumber
Run number of the peaks.
static const std::string volumeNormalization
VolumeNormalization.
Mantid::Geometry::Instrument_const_sptr m_inst
Instrument.
void addLeanElasticPeak(const Mantid::Kernel::V3D &Q, const double binCount, const bool useGoniometer=false)
Adds a peak based on Q, bin count.
std::shared_ptr< IMDEventWorkspace > IMDEventWorkspace_sptr
Shared pointer to Mantid::API::IMDEventWorkspace.
std::shared_ptr< ExperimentInfo > ExperimentInfo_sptr
Shared pointer to ExperimentInfo.
std::shared_ptr< IMDWorkspace > IMDWorkspace_sptr
Shared pointer to the IMDWorkspace base class.
void MANTID_DATAHANDLING_DLL addDetectors(H5::Group &group, const Mantid::API::MatrixWorkspace_sptr &workspace, const std::vector< std::string > &detectorNames)
Adds detector info to the sas group.
std::shared_ptr< PeaksWorkspace > PeaksWorkspace_sptr
Typedef for a shared pointer to a peaks workspace.
std::shared_ptr< LeanElasticPeaksWorkspace > LeanElasticPeaksWorkspace_sptr
Typedef for a shared pointer to a peaks workspace.
std::shared_ptr< MDHistoWorkspace > MDHistoWorkspace_sptr
A shared pointer to a MDHistoWorkspace.
MANTID_GEOMETRY_DLL bool edgePixel(ComponentInfo const &info, const std::string &bankName, int col, int row, int Edge)
Function to find peaks near detector edge.
float coord_t
Typedef for the data type to use for coordinate axes in MD objects such as MDBox, MDEventWorkspace,...
double signal_t
Typedef for the signal recorded in a MDBox, etc.
@ Input
An input workspace.
@ Output
An output workspace.