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++) {
286 nPoints_local =
static_cast<int>(Xout.size() - 1);
287 out->resizeHistogram(outWorkspaceIndex, nPoints_local);
290 out->setBinEdges(outWorkspaceIndex, Xout);
293 auto &outSpec = out->getSpectrum(outWorkspaceIndex);
294 outSpec.setSpectrumNo(
group);
298 auto &Yout = outSpec.dataY();
299 auto &Eout = outSpec.dataE();
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 &Yin = inSpec.y();
334 auto &Ein = inSpec.e();
343 std::ostringstream mess;
344 mess <<
"Error in rebinning process for spectrum:" << inWorkspaceIndex;
345 throw std::runtime_error(mess.str());
352 weight_bins.emplace_back(Xin.front());
357 for (
const auto &bin : mask) {
358 const double currentX = Xin[bin.first];
361 if (weight_bins.back() != currentX) {
362 weights.emplace_back(1.0);
363 weight_bins.emplace_back(currentX);
367 weights.emplace_back(1.0 - bin.second);
368 weight_bins.emplace_back(Xin[bin.first + 1]);
372 if (weight_bins.back() != Xin.back()) {
373 weights.emplace_back(1.0);
374 weight_bins.emplace_back(Xin.back());
379 const MantidVec zeroes(weights.size(), 0.0);
381 VectorHelper::rebin(weight_bins, weights, zeroes, Xout.rawData(), groupWgt, EOutDummy,
true,
true);
389 if (eventXMin > 0. && eventXMax > 0.) {
390 limits[0] = eventXMin;
391 limits[1] = eventXMax;
393 limits[0] = Xin.front();
394 limits[1] = Xin.back();
398 VectorHelper::rebin(limits, weights_default, emptyVec, Xout.rawData(), groupWgt, EOutDummy,
true,
true);
407 std::transform(groupWgt.cbegin(), groupWgt.cend(), groupWgt.begin(),
408 [](
double w) { return std::fabs(w) < std::numeric_limits<double>::epsilon() ? 1.0 : w; });
411 std::vector<double> widths(Xout.size());
412 std::adjacent_difference(Xout.begin(), Xout.end(), widths.begin());
415 std::transform(Eout.begin(), Eout.end(), Eout.begin(),
static_cast<double (*)(
double)
>(sqrt));
420 std::transform(Yout.begin(), Yout.end(), widths.begin() + 1, Yout.begin(), std::multiplies<double>());
421 std::transform(Eout.begin(), Eout.end(), widths.begin() + 1, Eout.begin(), std::multiplies<double>());
424 std::transform(Yout.begin(), Yout.end(), groupWgt.begin(), Yout.begin(), std::divides<double>());
425 std::transform(Eout.begin(), Eout.end(), groupWgt.begin(), Eout.begin(), std::divides<double>());
428 std::for_each(Yout.begin(), Yout.end(), [groupSize](
double &val) { val *= static_cast<double>(groupSize); });
429 std::for_each(Eout.begin(), Eout.end(), [groupSize](
double &val) { val *= static_cast<double>(groupSize); });
459 g_log.
debug(
"Focussing EventWorkspace in-place.");
463 const auto eventinputWS = std::dynamic_pointer_cast<const EventWorkspace>(
m_matrixInputW);
464 const EventType eventWtype = eventinputWS->getEventType();
467 std::const_pointer_cast<EventWorkspace>(eventinputWS)->clearMRU();
470 std::unique_ptr<Progress> prog = std::make_unique<Progress>(
this, 0.2, 0.25,
nGroups);
474 int totalHistProcess = 0;
475 for (
size_t iGroup = 0; iGroup < this->
m_validGroups.size(); iGroup++) {
476 const vector<size_t> &indices = this->
m_wsIndices[iGroup];
478 totalHistProcess +=
static_cast<int>(indices.size());
479 for (
auto index : indices) {
480 size_required[iGroup] += eventinputWS->getSpectrum(
index).getNumberEvents();
482 prog->report(1,
"Pre-counting");
487 prog = std::make_unique<Progress>(
this, 0.25, 0.3, totalHistProcess);
490 for (
size_t iGroup = 0; iGroup < this->
m_validGroups.size(); iGroup++) {
492 EventList &groupEL = eventOutputW->getSpectrum(iGroup);
495 groupEL.
reserve(size_required[iGroup]);
497 prog->reportIncrement(1,
"Allocating");
502 prog = std::make_unique<Progress>(
this, 0.3, 0.9, totalHistProcess);
507 EventList &groupEL = eventOutputW->getSpectrum(0);
508 const std::vector<size_t> &indices = this->
m_wsIndices[0];
510 constexpr int chunkSize{200};
512 const int end = (totalHistProcess / chunkSize) + 1;
514 PRAGMA_OMP(parallel
for schedule(dynamic, 1) )
515 for (
int wiChunk = 0; wiChunk < end; wiChunk++) {
519 int max = (wiChunk + 1) * chunkSize;
520 if (max > totalHistProcess)
521 max = totalHistProcess;
529 for (
int i = wiChunk * chunkSize; i < max; i++) {
531 size_t wi = indices[i];
532 chunkEL += eventinputWS->getSpectrum(wi);
544 auto nValidGroups =
static_cast<int>(this->
m_validGroups.size());
546 for (
int iGroup = 0; iGroup < nValidGroups; iGroup++) {
548 const std::vector<size_t> &indices = this->
m_wsIndices[iGroup];
549 for (
auto wi : indices) {
551 eventOutputW->getSpectrum(iGroup) += eventinputWS->getSpectrum(wi);
553 prog->reportIncrement(1,
"Appending Lists");
558 std::const_pointer_cast<EventWorkspace>(eventinputWS)->getSpectrum(wi).
clear(
true);
569 prog = std::make_unique<Progress>(
this, 0.9, 1.0,
nGroups);
570 for (
size_t workspaceIndex = 0; workspaceIndex < this->
m_validGroups.size(); workspaceIndex++) {
573 prog->reportIncrement(1,
"Setting X");
575 if (workspaceIndex >= eventOutputW->getNumberHistograms()) {
576 g_log.
warning() <<
"Warning! Invalid workspace index found for group # " <<
group
577 <<
". Histogram will be empty.\n";
586 eventOutputW->setHistogram(workspaceIndex, BinEdges(git->second.cowData()));
590 eventOutputW->setHistogram(workspaceIndex, BinEdges(
group2xvector.begin()->second.cowData()));
592 g_log.
warning() <<
"Warning! No X histogram bins were found for any "
593 "groups. Histogram will be empty.\n";
595 eventOutputW->clearMRU();
596 setProperty(
"OutputWorkspace", std::move(eventOutputW));
608 const auto &dets =
m_matrixInputW->getSpectrum(wi).getDetectorIDs();
611 g_log.
debug() << wi <<
" <- this workspace index is empty!\n";
615 auto it = dets.cbegin();
620 const int group = udet2group.at(*it);
624 for (; it != dets.end(); ++it)
626 if (udet2group.at(*it) !=
group)
653 std::ostringstream mess;
656 using group2minmaxmap = std::map<int, std::pair<double, double>>;
658 group2minmaxmap group2minmax;
659 group2minmaxmap::iterator gpit;
661 const double BIGGEST = std::numeric_limits<double>::max();
664 bool checkForMask =
false;
666 if (instrument !=
nullptr) {
667 checkForMask = ((instrument->getSource() !=
nullptr) && (instrument->getSample() !=
nullptr));
672 for (
int wi = 0; wi <
nHist; wi++)
680 if (spectrumInfo.isMasked(wi)) {
685 gpit = group2minmax.find(
group);
688 if (gpit == group2minmax.end()) {
689 gpit = group2minmax.emplace(
group, std::make_pair(BIGGEST, -1. * BIGGEST)).first;
691 const double min = (gpit->second).first;
692 const double max = (gpit->second).second;
694 double temp =
X.front();
696 (gpit->second).first = temp;
699 (gpit->second).second = temp;
704 const int64_t xPoints =
nPoints + 1;
706 for (gpit = group2minmax.begin(); gpit != group2minmax.end(); ++gpit) {
707 double Xmin, Xmax, step;
708 Xmin = (gpit->second).first;
709 Xmax = (gpit->second).second;
722 mess <<
"Fail to determine X boundaries for group:" << gpit->first <<
"\n";
723 mess <<
"The boundaries are (Xmin,Xmax):" << Xmin <<
" " << Xmax;
724 throw std::runtime_error(mess.str());
727 step = expm1((log(Xmax) - log(Xmin)) /
nPoints);
728 mess <<
"Found Group:" << gpit->first <<
"(Xmin,Xmax,log step):" << (gpit->second).first <<
","
729 << (gpit->second).second <<
"," << step;
733 HistogramData::BinEdges xnew(xPoints, HistogramData::LogarithmicGenerator(Xmin, step));
740 std::set<int> groups;
743 bool checkForMask =
false;
745 if (instrument !=
nullptr) {
746 checkForMask = ((instrument->getSource() !=
nullptr) && (instrument->getSample() !=
nullptr));
751 for (
int wi = 0; wi <
nHist; wi++)
759 if (spectrumInfo.isMasked(wi)) {
764 groups.insert(
group);
769 const bool fullBinsOnly =
getProperty(
"FullBinsOnly");
776 const int64_t numMin = xmins.size();
777 const int64_t numMax = xmaxs.size();
778 const int64_t numDelta = deltas.size();
779 if (numMin > 1 && numMin !=
nGroups)
780 throw std::runtime_error(
"DMin must have length 1 or equal to number of output groups which is " +
782 if (numMax > 1 && numMax !=
nGroups)
783 throw std::runtime_error(
"DMax must have length 1 or equal to number of output groups which is " +
785 if (numDelta > 1 && numDelta !=
nGroups)
786 throw std::runtime_error(
"Delta must have length 1 or equal to number of output groups which is " +
791 xmins.resize(
nGroups, xmins[0]);
793 xmaxs.resize(
nGroups, xmaxs[0]);
795 deltas.resize(
nGroups, deltas[0]);
799 for (
auto group : groups) {
800 HistogramData::BinEdges xnew(0);
802 true, fullBinsOnly));
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(const MantidVec &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.
int 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.
void MANTID_KERNEL_DLL rebin(const std::vector< double > &xold, const std::vector< double > &yold, const std::vector< double > &eold, const std::vector< double > &xnew, std::vector< double > &ynew, std::vector< double > &enew, bool distribution, bool addition=false)
Rebins data according to a new output X array.
void MANTID_KERNEL_DLL rebinHistogram(const std::vector< double > &xold, const std::vector< double > &yold, const std::vector< double > &eold, const std::vector< double > &xnew, std::vector< double > &ynew, std::vector< double > &enew, bool addition)
Rebins histogram data according to a new output X array.
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.