21#include "MantidHistogramData/LogarithmicGenerator.h"
22#include "MantidIndexing/Group.h"
23#include "MantidIndexing/IndexInfo.h"
36using Mantid::HistogramData::BinEdges;
49 auto wsValidator = std::make_shared<API::RawCountValidator>();
52 "A 2D workspace with X values of d-spacing or Q");
54 "The result of diffraction focussing of InputWorkspace");
57 "Optional: The name of the CalFile with grouping data.");
61 "Optional: GroupingWorkspace to use instead of a grouping file.");
63 declareProperty(
"PreserveEvents",
true,
64 "Keep the output workspace as an EventWorkspace, if the "
65 "input has events (default).\n"
66 "If false, then the workspace gets converted to a "
67 "Workspace2D histogram.");
69 "Minimum x values, one value for each output specta or single value which is common to all");
71 "Maximum x values, one value for each output specta or single value which is common to all");
73 "Step parameters for rebin, positive values are constant step-size, negative are logorithmic. One "
74 "value for each output specta or single value which is common to all");
75 declareProperty(
"FullBinsOnly",
false,
"Omit the final bin if it's width is smaller than the step size");
79 std::map<std::string, std::string> issues;
82 const bool hasGroupingFilename = !
isDefault(
"GroupingFileName");
83 const bool hasGroupingWksp = !
isDefault(
"GroupingWorkspace");
84 if (hasGroupingFilename && hasGroupingWksp) {
85 const std::string msg =
"You must enter a GroupingFileName or a GroupingWorkspace, not both!";
86 issues[
"GroupingFileName"] = msg;
87 issues[
"GroupingWorkspace"] = msg;
88 }
else if (!(hasGroupingFilename || hasGroupingWksp)) {
89 const std::string msg =
"You must enter a GroupingFileName or a GroupingWorkspace!";
90 issues[
"GroupingFileName"] = msg;
91 issues[
"GroupingWorkspace"] = msg;
98 const auto inputProp =
101 std::dynamic_pointer_cast<WorkspaceGroup>(AnalysisDataService::Instance().retrieve(inputProp->value()));
103 issues[
"InputWorkspace"] =
"InputWorksapce must be a matrix workspace or workspace group.";
106 inputWS = std::dynamic_pointer_cast<MatrixWorkspace>(ws);
118 issues[
"DMin"] =
"Must specify values for XMin, XMax and Delta or none of them";
119 issues[
"DMax"] =
"Must specify values for XMin, XMax and Delta or none of them";
120 issues[
"Delta"] =
"Must specify values for XMin, XMax and Delta or none of them";
125 const std::vector<double> xmins =
getProperty(
"DMin");
126 const std::vector<double> xmaxs =
getProperty(
"DMax");
127 const std::vector<double> deltas =
getProperty(
"Delta");
129 if (std::any_of(deltas.cbegin(), deltas.cend(), [](
double d) { return !std::isfinite(d); }))
130 issues[
"Delta"] =
"All must be finite";
131 else if (std::any_of(deltas.cbegin(), deltas.cend(), [](
double d) { return d == 0; }))
132 issues[
"Delta"] =
"All must be nonzero";
134 if (std::any_of(xmins.cbegin(), xmins.cend(), [](
double x) { return !std::isfinite(x); }))
135 issues[
"DMin"] =
"All must be finite";
137 if (std::any_of(xmaxs.cbegin(), xmaxs.cend(), [](
double x) { return !std::isfinite(x); }))
138 issues[
"DMax"] =
"All must be finite";
140 bool min_less_than_max =
true;
141 if (xmins.size() == 1) {
142 if (xmins[0] >= *std::min_element(xmaxs.cbegin(), xmaxs.cend())) {
143 min_less_than_max =
false;
145 }
else if (xmaxs.size() == 1) {
146 if (xmaxs[0] <= *std::max_element(xmins.cbegin(), xmins.cend())) {
147 min_less_than_max =
false;
149 }
else if (xmins.size() != xmaxs.size()) {
150 issues[
"DMin"] =
"DMin is different length to DMax";
151 issues[
"DMax"] =
"DMin is different length to DMax";
153 for (
size_t i{0}; i < xmins.size(); i++) {
154 if (xmins[i] >= xmaxs[i]) {
155 min_less_than_max =
false;
161 if (!min_less_than_max) {
162 issues[
"DMin"] =
"DMin must be less than corresponding DMax";
163 issues[
"DMax"] =
"DMin must be less than corresponding DMax";
170 std::map<std::string, std::string> &issues) {
172 const std::string unitid = inputWS->getAxis(0)->unit()->unitID();
173 if (unitid !=
"dSpacing" && unitid !=
"MomentumTransfer") {
174 std::stringstream msg;
175 msg <<
"UnitID " << unitid <<
" is not a supported spacing";
176 issues[
"InputWorkspace"] = msg.str();
214 const bool autoBinning =
isDefault(
"DMin");
217 progress(0.2,
"Determine Rebin Params");
219 std::vector<int> udet2group;
223 throw std::runtime_error(
"No groups were specified.");
239 double eventXMin = 0.;
240 double eventXMax = 0.;
242 const auto eventInputWS = std::dynamic_pointer_cast<const EventWorkspace>(
m_matrixInputW);
257 throw std::runtime_error(
"No selected Detectors found in .cal file for "
258 "input range. Please ensure spectra range has "
259 "atleast one selected detector.");
263 throw std::runtime_error(
"No points found in the data range.");
271 MantidVec weights_default(1, 1.0), emptyVec(1, 0.0);
273 Progress prog(
this, 0.2, 1.0,
static_cast<int>(totalHistProcess) +
nGroups);
276 for (
int outWorkspaceIndex = 0; outWorkspaceIndex < static_cast<int>(
m_validGroups.size()); outWorkspaceIndex++) {
288 nPoints_local =
static_cast<int>(Xout.size() - 1);
289 out->resizeHistogram(outWorkspaceIndex, nPoints_local);
292 out->setBinEdges(outWorkspaceIndex, Xout);
295 auto &outSpec = out->getSpectrum(outWorkspaceIndex);
296 outSpec.setSpectrumNo(
group);
299 auto &Yout = outSpec.mutableY();
300 auto &Eout = outSpec.mutableE();
308 const std::vector<size_t> &indices =
m_wsIndices[outWorkspaceIndex];
309 const size_t groupSize = indices.size();
310 for (
size_t i = 0; i < groupSize; i++) {
311 size_t inWorkspaceIndex = indices[i];
313 const auto &inSpec =
m_matrixInputW->getSpectrum(inWorkspaceIndex);
315 auto &Xin = inSpec.x();
318 outSpec.addDetectorIDs(inSpec.getDetectorIDs());
322 const EventList &el = eventInputWS->getSpectrum(inWorkspaceIndex);
328 std::transform(Ytemp.cbegin(), Ytemp.cend(), Yout.begin(), Yout.begin(),
329 [](
const auto &
left,
const auto &
right) { return left + right; });
331 std::transform(Etemp.cbegin(), Etemp.cend(), Eout.begin(), Eout.begin(),
332 [](
const auto &
left,
const auto &
right) { return left * left + right; });
334 auto const &Yin = inSpec.y();
335 auto const &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);
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, 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) {
802 std::vector<double> xnew;
818 const std::string groupingFileName =
getProperty(
"GroupingFileName");
819 progress(0.01,
"Reading grouping file");
821 childAlg->setProperty(
"InputWorkspace", std::const_pointer_cast<MatrixWorkspace>(
m_matrixInputW));
822 childAlg->setProperty(
"OldCalFilename", groupingFileName);
823 childAlg->executeAsChildAlg();
824 m_groupWS = childAlg->getProperty(
"OutputWorkspace");
836 std::vector<std::vector<std::size_t>> wsIndices;
837 wsIndices.reserve(this->
nGroups + 1);
838 auto nHist_st =
static_cast<size_t>(
nHist);
839 for (
size_t wi = 0; wi < nHist_st; wi++) {
846 if (wsIndices.size() <
static_cast<size_t>(
group + 1)) {
847 wsIndices.resize(
group + 1);
851 wsIndices[
group].emplace_back(wi);
855 size_t totalHistProcess = 0;
857 const auto group = item.first;
859 totalHistProcess += wsIndices[
group].size();
863 [&wsIndices](
const auto &
group) { return wsIndices[static_cast<int>(group)]; });
865 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.