21#include "MantidHistogramData/LogarithmicGenerator.h"
22#include "MantidIndexing/Group.h"
23#include "MantidIndexing/IndexInfo.h"
35using Mantid::HistogramData::BinEdges;
48 auto wsValidator = std::make_shared<API::RawCountValidator>();
51 "A 2D workspace with X values of d-spacing or Q");
53 "The result of diffraction focussing of InputWorkspace");
56 "Optional: The name of the CalFile with grouping data.");
60 "Optional: GroupingWorkspace to use instead of a grouping file.");
62 declareProperty(
"PreserveEvents",
true,
63 "Keep the output workspace as an EventWorkspace, if the "
64 "input has events (default).\n"
65 "If false, then the workspace gets converted to a "
66 "Workspace2D histogram.");
68 "Minimum x values, one value for each output specta or single value which is common to all");
70 "Maximum x values, one value for each output specta or single value which is common to all");
72 "Step parameters for rebin, positive values are constant step-size, negative are logorithmic. One "
73 "value for each output specta or single value which is common to all");
74 declareProperty(
"FullBinsOnly",
false,
"Omit the final bin if it's width is smaller than the step size");
78 std::map<std::string, std::string> issues;
81 const bool hasGroupingFilename = !
isDefault(
"GroupingFileName");
82 const bool hasGroupingWksp = !
isDefault(
"GroupingWorkspace");
83 if (hasGroupingFilename && hasGroupingWksp) {
84 const std::string msg =
"You must enter a GroupingFileName or a GroupingWorkspace, not both!";
85 issues[
"GroupingFileName"] = msg;
86 issues[
"GroupingWorkspace"] = msg;
87 }
else if (!(hasGroupingFilename || hasGroupingWksp)) {
88 const std::string msg =
"You must enter a GroupingFileName or a GroupingWorkspace!";
89 issues[
"GroupingFileName"] = msg;
90 issues[
"GroupingWorkspace"] = msg;
97 const auto inputProp =
100 std::dynamic_pointer_cast<WorkspaceGroup>(AnalysisDataService::Instance().retrieve(inputProp->value()));
102 issues[
"InputWorkspace"] =
"InputWorksapce must be a matrix workspace or workspace group.";
105 inputWS = std::dynamic_pointer_cast<MatrixWorkspace>(ws);
117 issues[
"DMin"] =
"Must specify values for XMin, XMax and Delta or none of them";
118 issues[
"DMax"] =
"Must specify values for XMin, XMax and Delta or none of them";
119 issues[
"Delta"] =
"Must specify values for XMin, XMax and Delta or none of them";
124 const std::vector<double> xmins =
getProperty(
"DMin");
125 const std::vector<double> xmaxs =
getProperty(
"DMax");
126 const std::vector<double> deltas =
getProperty(
"Delta");
128 if (std::any_of(deltas.cbegin(), deltas.cend(), [](
double d) { return !std::isfinite(d); }))
129 issues[
"Delta"] =
"All must be finite";
130 else if (std::any_of(deltas.cbegin(), deltas.cend(), [](
double d) { return d == 0; }))
131 issues[
"Delta"] =
"All must be nonzero";
133 if (std::any_of(xmins.cbegin(), xmins.cend(), [](
double x) { return !std::isfinite(x); }))
134 issues[
"DMin"] =
"All must be finite";
136 if (std::any_of(xmaxs.cbegin(), xmaxs.cend(), [](
double x) { return !std::isfinite(x); }))
137 issues[
"DMax"] =
"All must be finite";
139 bool min_less_than_max =
true;
140 if (xmins.size() == 1) {
141 if (xmins[0] >= *std::min_element(xmaxs.cbegin(), xmaxs.cend())) {
142 min_less_than_max =
false;
144 }
else if (xmaxs.size() == 1) {
145 if (xmaxs[0] <= *std::max_element(xmins.cbegin(), xmins.cend())) {
146 min_less_than_max =
false;
148 }
else if (xmins.size() != xmaxs.size()) {
149 issues[
"DMin"] =
"DMin is different length to DMax";
150 issues[
"DMax"] =
"DMin is different length to DMax";
152 for (
size_t i{0}; i < xmins.size(); i++) {
153 if (xmins[i] >= xmaxs[i]) {
154 min_less_than_max =
false;
160 if (!min_less_than_max) {
161 issues[
"DMin"] =
"DMin must be less than corresponding DMax";
162 issues[
"DMax"] =
"DMin must be less than corresponding DMax";
169 std::map<std::string, std::string> &issues) {
171 const std::string unitid = inputWS->getAxis(0)->unit()->unitID();
172 if (unitid !=
"dSpacing" && unitid !=
"MomentumTransfer") {
173 std::stringstream msg;
174 msg <<
"UnitID " << unitid <<
" is not a supported spacing";
175 issues[
"InputWorkspace"] = msg.str();
213 const bool autoBinning =
isDefault(
"DMin");
216 progress(0.2,
"Determine Rebin Params");
218 std::vector<int> udet2group;
222 throw std::runtime_error(
"No groups were specified.");
238 double eventXMin = 0.;
239 double eventXMax = 0.;
241 const auto eventInputWS = std::dynamic_pointer_cast<const EventWorkspace>(
m_matrixInputW);
256 throw std::runtime_error(
"No selected Detectors found in .cal file for "
257 "input range. Please ensure spectra range has "
258 "atleast one selected detector.");
262 throw std::runtime_error(
"No points found in the data range.");
270 MantidVec weights_default(1, 1.0), emptyVec(1, 0.0);
272 Progress prog(
this, 0.2, 1.0,
static_cast<int>(totalHistProcess) +
nGroups);
275 for (
int outWorkspaceIndex = 0; outWorkspaceIndex < static_cast<int>(
m_validGroups.size()); outWorkspaceIndex++) {
287 nPoints_local =
static_cast<int>(Xout.size() - 1);
288 out->resizeHistogram(outWorkspaceIndex, nPoints_local);
291 out->setBinEdges(outWorkspaceIndex, Xout);
294 auto &outSpec = out->getSpectrum(outWorkspaceIndex);
295 outSpec.setSpectrumNo(
group);
298 auto &Yout = outSpec.mutableY();
299 auto &Eout = outSpec.mutableE();
307 const std::vector<size_t> &indices =
m_wsIndices[outWorkspaceIndex];
308 const size_t groupSize = indices.size();
309 for (
size_t i = 0; i < groupSize; i++) {
310 size_t inWorkspaceIndex = indices[i];
312 const auto &inSpec =
m_matrixInputW->getSpectrum(inWorkspaceIndex);
314 auto &Xin = inSpec.x();
317 outSpec.addDetectorIDs(inSpec.getDetectorIDs());
321 const EventList &el = eventInputWS->getSpectrum(inWorkspaceIndex);
327 std::transform(Ytemp.cbegin(), Ytemp.cend(), Yout.begin(), Yout.begin(),
328 [](
const auto &
left,
const auto &
right) { return left + right; });
330 std::transform(Etemp.cbegin(), Etemp.cend(), Eout.begin(), Eout.begin(),
331 [](
const auto &
left,
const auto &
right) { return left * left + right; });
333 auto const &Yin = inSpec.y();
334 auto const &Ein = inSpec.e();
342 std::ostringstream mess;
343 mess <<
"Error in rebinning process for spectrum:" << inWorkspaceIndex;
344 throw std::runtime_error(mess.str());
351 weight_bins.emplace_back(Xin.front());
356 for (
const auto &bin : mask) {
357 const double currentX = Xin[bin.first];
360 if (weight_bins.back() != currentX) {
361 weights.emplace_back(1.0);
362 weight_bins.emplace_back(currentX);
366 weights.emplace_back(1.0 - bin.second);
367 weight_bins.emplace_back(Xin[bin.first + 1]);
371 if (weight_bins.back() != Xin.back()) {
372 weights.emplace_back(1.0);
373 weight_bins.emplace_back(Xin.back());
378 const MantidVec zeroes(weights.size(), 0.0);
388 if (eventXMin > 0. && eventXMax > 0.) {
389 limits[0] = eventXMin;
390 limits[1] = eventXMax;
392 limits[0] = Xin.front();
393 limits[1] = Xin.back();
397 VectorHelper::rebin(limits, weights_default, emptyVec, Xout, groupWgt, EOutDummy,
true,
true);
406 std::transform(groupWgt.cbegin(), groupWgt.cend(), groupWgt.begin(),
407 [](
double w) { return std::fabs(w) < std::numeric_limits<double>::epsilon() ? 1.0 : w; });
410 std::vector<double> widths(Xout.size());
411 std::adjacent_difference(Xout.begin(), Xout.end(), widths.begin());
414 std::transform(Eout.begin(), Eout.end(), Eout.begin(),
static_cast<double (*)(
double)
>(sqrt));
419 std::transform(Yout.begin(), Yout.end(), widths.begin() + 1, Yout.begin(), std::multiplies<double>());
420 std::transform(Eout.begin(), Eout.end(), widths.begin() + 1, Eout.begin(), std::multiplies<double>());
423 std::transform(Yout.begin(), Yout.end(), groupWgt.begin(), Yout.begin(), std::divides<double>());
424 std::transform(Eout.begin(), Eout.end(), groupWgt.begin(), Eout.begin(), std::divides<double>());
427 std::for_each(Yout.begin(), Yout.end(), [groupSize](
double &val) { val *= static_cast<double>(groupSize); });
428 std::for_each(Eout.begin(), Eout.end(), [groupSize](
double &val) { val *= static_cast<double>(groupSize); });
458 g_log.
debug(
"Focussing EventWorkspace in-place.");
462 const auto eventinputWS = std::dynamic_pointer_cast<const EventWorkspace>(
m_matrixInputW);
463 const EventType eventWtype = eventinputWS->getEventType();
466 std::const_pointer_cast<EventWorkspace>(eventinputWS)->clearMRU();
469 std::unique_ptr<Progress> prog = std::make_unique<Progress>(
this, 0.2, 0.25,
nGroups);
473 int totalHistProcess = 0;
474 for (
size_t iGroup = 0; iGroup < this->
m_validGroups.size(); iGroup++) {
475 const vector<size_t> &indices = this->
m_wsIndices[iGroup];
477 totalHistProcess +=
static_cast<int>(indices.size());
478 for (
auto index : indices) {
479 size_required[iGroup] += eventinputWS->getSpectrum(
index).getNumberEvents();
481 prog->report(1,
"Pre-counting");
486 prog = std::make_unique<Progress>(
this, 0.25, 0.3, totalHistProcess);
489 for (
size_t iGroup = 0; iGroup < this->
m_validGroups.size(); iGroup++) {
491 EventList &groupEL = eventOutputW->getSpectrum(iGroup);
494 groupEL.
reserve(size_required[iGroup]);
496 prog->reportIncrement(1,
"Allocating");
501 prog = std::make_unique<Progress>(
this, 0.3, 0.9, totalHistProcess);
506 EventList &groupEL = eventOutputW->getSpectrum(0);
507 const std::vector<size_t> &indices = this->
m_wsIndices[0];
509 constexpr int chunkSize{200};
511 const int end = (totalHistProcess / chunkSize) + 1;
513 PRAGMA_OMP(parallel
for schedule(dynamic, 1) )
514 for (
int wiChunk = 0; wiChunk < end; wiChunk++) {
518 int max = (wiChunk + 1) * chunkSize;
519 if (max > totalHistProcess)
520 max = totalHistProcess;
528 for (
int i = wiChunk * chunkSize; i < max; i++) {
530 size_t wi = indices[i];
531 chunkEL += eventinputWS->getSpectrum(wi);
543 auto nValidGroups =
static_cast<int>(this->
m_validGroups.size());
545 for (
int iGroup = 0; iGroup < nValidGroups; iGroup++) {
547 const std::vector<size_t> &indices = this->
m_wsIndices[iGroup];
548 for (
auto wi : indices) {
550 eventOutputW->getSpectrum(iGroup) += eventinputWS->getSpectrum(wi);
552 prog->reportIncrement(1,
"Appending Lists");
557 std::const_pointer_cast<EventWorkspace>(eventinputWS)->getSpectrum(wi).
clear(
true);
568 prog = std::make_unique<Progress>(
this, 0.9, 1.0,
nGroups);
569 for (
size_t workspaceIndex = 0; workspaceIndex < this->
m_validGroups.size(); workspaceIndex++) {
572 prog->reportIncrement(1,
"Setting X");
574 if (workspaceIndex >= eventOutputW->getNumberHistograms()) {
575 g_log.
warning() <<
"Warning! Invalid workspace index found for group # " <<
group
576 <<
". Histogram will be empty.\n";
585 eventOutputW->setHistogram(workspaceIndex, BinEdges(git->second.cowData()));
589 eventOutputW->setHistogram(workspaceIndex, BinEdges(
group2xvector.begin()->second.cowData()));
591 g_log.
warning() <<
"Warning! No X histogram bins were found for any "
592 "groups. Histogram will be empty.\n";
594 eventOutputW->clearMRU();
595 setProperty(
"OutputWorkspace", std::move(eventOutputW));
607 const auto &dets =
m_matrixInputW->getSpectrum(wi).getDetectorIDs();
610 g_log.
debug() << wi <<
" <- this workspace index is empty!\n";
614 auto it = dets.cbegin();
619 const int group = udet2group.at(*it);
623 for (; it != dets.end(); ++it)
625 if (udet2group.at(*it) !=
group)
652 std::ostringstream mess;
655 using group2minmaxmap = std::map<int, std::pair<double, double>>;
657 group2minmaxmap group2minmax;
658 group2minmaxmap::iterator gpit;
660 const double BIGGEST = std::numeric_limits<double>::max();
663 bool checkForMask =
false;
665 if (instrument !=
nullptr) {
666 checkForMask = ((instrument->getSource() !=
nullptr) && (instrument->getSample() !=
nullptr));
671 for (
int wi = 0; wi <
nHist; wi++)
679 if (spectrumInfo.isMasked(wi)) {
684 gpit = group2minmax.find(
group);
687 if (gpit == group2minmax.end()) {
688 gpit = group2minmax.emplace(
group, std::make_pair(BIGGEST, -1. * BIGGEST)).first;
690 const double min = (gpit->second).first;
691 const double max = (gpit->second).second;
693 double temp =
X.front();
695 (gpit->second).first = temp;
698 (gpit->second).second = temp;
703 const int64_t xPoints =
nPoints + 1;
705 for (gpit = group2minmax.begin(); gpit != group2minmax.end(); ++gpit) {
706 double Xmin, Xmax, step;
707 Xmin = (gpit->second).first;
708 Xmax = (gpit->second).second;
721 mess <<
"Fail to determine X boundaries for group:" << gpit->first <<
"\n";
722 mess <<
"The boundaries are (Xmin,Xmax):" << Xmin <<
" " << Xmax;
723 throw std::runtime_error(mess.str());
726 step = expm1((log(Xmax) - log(Xmin)) /
nPoints);
727 mess <<
"Found Group:" << gpit->first <<
"(Xmin,Xmax,log step):" << (gpit->second).first <<
","
728 << (gpit->second).second <<
"," << step;
732 HistogramData::BinEdges xnew(xPoints, HistogramData::LogarithmicGenerator(Xmin, step));
739 std::set<int> groups;
742 bool checkForMask =
false;
744 if (instrument !=
nullptr) {
745 checkForMask = ((instrument->getSource() !=
nullptr) && (instrument->getSample() !=
nullptr));
750 for (
int wi = 0; wi <
nHist; wi++)
758 if (spectrumInfo.isMasked(wi)) {
763 groups.insert(
group);
768 const bool fullBinsOnly =
getProperty(
"FullBinsOnly");
775 const int64_t numMin = xmins.size();
776 const int64_t numMax = xmaxs.size();
777 const int64_t numDelta = deltas.size();
778 if (numMin > 1 && numMin !=
nGroups)
779 throw std::runtime_error(
"DMin must have length 1 or equal to number of output groups which is " +
781 if (numMax > 1 && numMax !=
nGroups)
782 throw std::runtime_error(
"DMax must have length 1 or equal to number of output groups which is " +
784 if (numDelta > 1 && numDelta !=
nGroups)
785 throw std::runtime_error(
"Delta must have length 1 or equal to number of output groups which is " +
790 xmins.resize(
nGroups, xmins[0]);
792 xmaxs.resize(
nGroups, xmaxs[0]);
794 deltas.resize(
nGroups, deltas[0]);
798 for (
auto group : groups) {
801 std::vector<double> xnew;
817 const std::string groupingFileName =
getProperty(
"GroupingFileName");
818 progress(0.01,
"Reading grouping file");
820 childAlg->setProperty(
"InputWorkspace", std::const_pointer_cast<MatrixWorkspace>(
m_matrixInputW));
821 childAlg->setProperty(
"OldCalFilename", groupingFileName);
822 childAlg->executeAsChildAlg();
823 m_groupWS = childAlg->getProperty(
"OutputWorkspace");
835 std::vector<std::vector<std::size_t>> wsIndices;
836 wsIndices.reserve(this->
nGroups + 1);
837 auto nHist_st =
static_cast<size_t>(
nHist);
838 for (
size_t wi = 0; wi < nHist_st; wi++) {
845 if (wsIndices.size() <
static_cast<size_t>(
group + 1)) {
846 wsIndices.resize(
group + 1);
850 wsIndices[
group].emplace_back(wi);
854 size_t totalHistProcess = 0;
856 const auto group = item.first;
858 totalHistProcess += wsIndices[
group].size();
862 [&wsIndices](
const auto &
group) { return wsIndices[static_cast<int>(group)]; });
864 return totalHistProcess;
#define DECLARE_ALGORITHM(classname)
std::map< DeltaEMode::Type, std::string > index
#define PARALLEL_START_INTERRUPT_REGION
Begins a block to skip processing is the algorithm has been interupted Note the end of the block if n...
#define PARALLEL_CRITICAL(name)
#define PARALLEL_END_INTERRUPT_REGION
Ends a block to skip processing is the algorithm has been interupted Note the start of the block if n...
#define PARALLEL_FOR_IF(condition)
Empty definitions - to enable set your complier to enable openMP.
#define PRAGMA_OMP(expression)
#define PARALLEL_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
Kernel::Property * getPointerToProperty(const std::string &name) const override
Get a property by name.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
bool isDefault(const std::string &name) const
@ OptionalLoad
to specify a file to read but the file doesn't have to exist
void setSpectrumNo(specnum_t num)
Sets the spectrum number of this spectrum.
std::map< size_t, double > MaskList
Masked bins for each spectrum are stored as a set of pairs containing <bin index, weight>
Helper class for reporting progress from algorithms.
A property class for workspaces.
Algorithm to focus powder diffraction data into a number of histograms according to a grouping scheme...
Mantid::DataObjects::GroupingWorkspace_sptr m_groupWS
Grouping workspace with groups to build.
void determineRebinParameters(const std::vector< int > &udet2group)
Loop over the workspace and determine the rebin parameters (Xmin,Xmax,step) for each group.
int nPoints
Number of points in the 2D workspace.
int validateSpectrumInGroup(const std::vector< int > &udet2group, size_t wi)
Verify that all the contributing detectors to a spectrum belongs to the same group.
void execEvent()
Executes the algorithm in the case of an Event input workspace.
void determineRebinParametersFromParameters(const std::vector< int > &udet2group)
std::map< int, HistogramData::BinEdges > group2xvector
Map from the group number to the group's X vector.
std::size_t setupGroupToWSIndices()
std::map< int, double > group2xstep
std::vector< int > groupAtWorkspaceIndex
The list of group numbers.
std::vector< std::vector< std::size_t > > m_wsIndices
Mapping of group number to vector of inputworkspace indices.
int nHist
Number of histograms.
void exec() override
Executes the algorithm.
std::vector< Indexing::SpectrumNumber > m_validGroups
List of valid group numbers.
void getGroupingWorkspace()
Initialize the pointer to the grouping workspace based on input properties.
group2vectormap group2wgtvector
Map from the group number to the group's summed weight vector.
void validateInputWorkspaceUnit(API::MatrixWorkspace_const_sptr inputWS, std::map< std::string, std::string > &issues)
API::MatrixWorkspace_const_sptr m_matrixInputW
Shared pointer to the input workspace.
void cleanup()
Perform clean-up of memory after execution but before destructor.
int64_t nGroups
The number of (used) groups.
std::map< std::string, std::string > validateInputs() override
Method checking errors on ALL the inputs, before execution.
void generateHistogram(std::span< double const > X, MantidVec &Y, MantidVec &E, bool skipError=false) const override
Generates both the Y and E (error) histograms w.r.t TOF for an EventList with or without WeightedEven...
void reserve(size_t num) override
Reserve a certain number of entries in event list of the specified eventType.
void switchTo(Mantid::API::EventType newType) override
Switch the EventList to use the given EventType (TOF, WEIGHTED, or WEIGHTED_NOTIME)
void clear(const bool removeDetIDs=true) override
Clear the list of events and any associated detector ID's.
Support for a property that holds an array of values.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void debug(const std::string &msg)
Logs at debug level.
void warning(const std::string &msg)
Logs at warning level.
void information(const std::string &msg)
Logs at information level.
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
std::shared_ptr< const WorkspaceGroup > WorkspaceGroup_const_sptr
shared pointer to Mantid::API::WorkspaceGroup, pointer to const version
EventType
What kind of event list is being stored.
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
void swap(MDLeanEvent< nd > &first, MDLeanEvent< nd > &second)
std::shared_ptr< const Instrument > Instrument_const_sptr
Shared pointer to an const instrument object.
void MANTID_KERNEL_DLL rebin(std::span< double const > xold, std::span< double const > yold, std::span< double const > eold, std::span< double const > xnew, std::span< double > ynew, std::span< double > enew, bool distribution, bool addition=false)
The input and output ranges are taken as spans so that the size-checked histogram data types,...
void MANTID_KERNEL_DLL rebinHistogram(std::span< double const > xold, std::span< double const > yold, std::span< double const > eold, std::span< double const > xnew, std::span< double > ynew, std::span< double > enew, bool addition)
Rebins histogram data according to a new output X array.
std::size_t MANTID_KERNEL_DLL createAxisFromRebinParams(const std::vector< double > ¶ms, std::vector< double > &xnew, const bool resize_xnew=true, const bool full_bins_only=false, const double xMinHint=std::nan(""), const double xMaxHint=std::nan(""), const bool useReverseLogarithmic=false, const double power=-1)
Creates a new output X array given a 'standard' set of rebinning parameters.
std::enable_if< std::is_pointer< Arg >::value, bool >::type threadSafe(Arg workspace)
Thread-safety check Checks the workspace to ensure it is suitable for multithreaded access.
std::vector< double > MantidVec
typedef for the data storage used in Mantid matrix workspaces
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
@ Output
An output workspace.