Mantid
Loading...
Searching...
No Matches
IntegrateMDHistoWorkspace.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
10
14#include "MantidAPI/Progress.h"
15
20
21#include <algorithm>
22#include <cmath>
23#include <map>
24#include <utility>
25
26#include <memory>
27
28using namespace Mantid::API;
29using namespace Mantid::Kernel;
30using namespace Mantid::Geometry;
31using namespace Mantid::DataObjects;
32
33namespace {
34
40bool emptyBinning(const std::vector<double> &binning) { return binning.empty(); }
41
47bool integrationBinning(const std::vector<double> &binning) { return binning.size() == 2 && binning[0] < binning[1]; }
48
54bool similarBinning(const std::vector<double> &binning) { return binning.size() == 3 && binning[1] == 0.0; }
55
61std::string checkBinning(const std::vector<double> &binning) {
62 std::string error; // No error.
63 if (!emptyBinning(binning)) {
64 if (binning.size() == 3) {
65 const double step = binning[1];
66 if (step != 0) {
67 error = "Only step size zero is allowed. Denotes copy of original step "
68 "size for that dimension.";
69 } else {
70 auto min = binning[0];
71 auto max = binning[2];
72 if (min >= max) {
73 error = "Min must be < max limit for binning";
74 }
75 }
76
77 } else if (binning.size() == 2) {
78 auto min = binning[0];
79 auto max = binning[1];
80 if (min >= max) {
81 error = "Min must be < max limit for binning";
82 }
83 } else {
84 error = "Unknown binning prameters for dimension.";
85 }
86 }
87 return error;
88}
89
95Mantid::coord_t getPrecisionCorrectedCoordinate(Mantid::coord_t position, Mantid::coord_t binWidth) {
96 // Find the closest integer value
97 const auto up = std::ceil(position);
98 const auto down = std::floor(position);
99 const auto diffUp = fabs(up - position);
100 const auto diffDown = fabs(down - position);
101 const auto nearest = diffUp < diffDown ? up : down;
102
103 // Check if the relative deviation is larger than 1e-5
104 const auto deviation = fabs((nearest - position) / binWidth);
105 const auto tolerance = 1e-5;
106 Mantid::coord_t coordinate(position);
107 if (deviation < tolerance) {
108 coordinate = nearest;
109 }
110 return coordinate;
111}
112
121void setMinMaxBins(Mantid::coord_t &pMin, Mantid::coord_t &pMax, size_t &numberOfBins,
122 const IMDDimension_const_sptr &dimension, Logger &logger) {
123 // Get workspace extents
124 const Mantid::coord_t width = dimension->getBinWidth();
125 const Mantid::coord_t max = dimension->getMaximum();
126
127 // Get offset between origin and next bin boundary towards the max value
128 // NOTE: GCC shows a conversion warning from double to float here. This
129 // is incorrect. Silence warning with explicit cast.
130 const auto offset = static_cast<Mantid::coord_t>(fmod(max, width));
131
132 // Create the shifted pMax and pMin
133 auto minBin = (pMin - offset) / width;
134 auto maxBin = (pMax - offset) / width;
135
136 // Make sure that we don't snap to the wrong value
137 // because of the precision of floats (which coord_t is)
138 minBin = getPrecisionCorrectedCoordinate(minBin, width);
139 maxBin = getPrecisionCorrectedCoordinate(maxBin, width);
140 auto snappedPMin = width * std::floor(minBin);
141 auto snappedPMax = width * std::ceil(maxBin);
142
143 // Shift the snappedPMax/snappedPMin values back
144 snappedPMax += offset;
145 snappedPMin += offset;
146
147 if (snappedPMin < dimension->getMinimum()) {
148 snappedPMin = dimension->getMinimum();
149 } else if (pMin != snappedPMin) {
150 std::stringstream buffer;
151 buffer << "Rounding min from: " << pMin << " to the nearest whole width at: " << snappedPMin;
152 logger.warning(buffer.str());
153 }
154
155 if (snappedPMax > dimension->getMaximum()) {
156 snappedPMax = dimension->getMaximum();
157 } else if (pMax != snappedPMax) {
158 std::stringstream buffer;
159 buffer << "Rounding max from: " << pMax << " to the nearest whole width at: " << snappedPMax;
160 logger.warning(buffer.str());
161 }
162
163 pMin = snappedPMin;
164 pMax = snappedPMax;
165
166 // Bins
167 numberOfBins = std::lround((pMax - pMin) / width); // round up to a whole number of bins.
168}
169} // namespace
170
178MDHistoWorkspace_sptr createShapedOutput(IMDHistoWorkspace const *const inWS, std::vector<std::vector<double>> pbins,
179 Logger &logger) {
180 const size_t nDims = inWS->getNumDims();
181 std::vector<Mantid::Geometry::IMDDimension_sptr> dimensions(nDims);
182 for (size_t i = 0; i < nDims; ++i) {
183
184 IMDDimension_const_sptr inDim = inWS->getDimension(i);
185 auto outDim = std::make_shared<MDHistoDimension>(inDim.get());
186 // Apply dimensions as inputs.
187 if (i < pbins.size() && integrationBinning(pbins[i])) {
188 auto binning = pbins[i];
189 outDim->setRange(1 /*single bin*/, static_cast<Mantid::coord_t>(binning.front()) /*min*/,
190 static_cast<Mantid::coord_t>(binning.back()) /*max*/); // Set custom min, max and nbins.
191 } else if (i < pbins.size() && similarBinning(pbins[i])) {
192 auto binning = pbins[i];
193
194 auto pMin = static_cast<Mantid::coord_t>(binning.front());
195 auto pMax = static_cast<Mantid::coord_t>(binning.back());
196 size_t numberOfBins;
197
198 setMinMaxBins(pMin, pMax, numberOfBins, inDim, logger);
199
200 outDim->setRange(numberOfBins, static_cast<Mantid::coord_t>(pMin) /*min*/,
201 static_cast<Mantid::coord_t>(pMax) /*max*/); // Set custom min, max and nbins.
202 }
203 dimensions[i] = outDim;
204 }
205 return std::make_shared<MDHistoWorkspace>(dimensions);
206}
207
218 double &sumSignal, double &sumSQErrors, double &sumNEvents) {
219 if (!iterator->getIsMasked()) {
220 const double weight = box.fraction(iterator->getBoxExtents());
221 if (weight != 0) {
222 sumSignal += weight * iterator->getSignal();
223 const double error = iterator->getError();
224 sumSQErrors += weight * (error * error);
225 sumNEvents += weight * double(iterator->getNumEventsFraction());
226 }
227 }
228}
229
230namespace Mantid::MDAlgorithms {
231
234
235// Register the algorithm into the AlgorithmFactory
236DECLARE_ALGORITHM(IntegrateMDHistoWorkspace)
237
238//----------------------------------------------------------------------------------------------
239
240
241const std::string IntegrateMDHistoWorkspace::name() const { return "IntegrateMDHistoWorkspace"; }
242
244int IntegrateMDHistoWorkspace::version() const { return 1; }
245
247const std::string IntegrateMDHistoWorkspace::category() const { return "MDAlgorithms\\Slicing"; }
248
250const std::string IntegrateMDHistoWorkspace::summary() const {
251 return "Performs axis aligned integration of MDHistoWorkspaces";
252}
253
254//----------------------------------------------------------------------------------------------
258 declareProperty(std::make_unique<WorkspaceProperty<IMDHistoWorkspace>>("InputWorkspace", "", Direction::Input),
259 "An input workspace.");
260
261 const std::vector<double> defaultBinning;
262 declareProperty(std::make_unique<ArrayProperty<double>>("P1Bin", defaultBinning), "Projection 1 binning.");
263 declareProperty(std::make_unique<ArrayProperty<double>>("P2Bin", defaultBinning), "Projection 2 binning.");
264 declareProperty(std::make_unique<ArrayProperty<double>>("P3Bin", defaultBinning), "Projection 3 binning.");
265 declareProperty(std::make_unique<ArrayProperty<double>>("P4Bin", defaultBinning), "Projection 4 binning.");
266 declareProperty(std::make_unique<ArrayProperty<double>>("P5Bin", defaultBinning), "Projection 5 binning.");
267
268 declareProperty(std::make_unique<WorkspaceProperty<IMDHistoWorkspace>>("OutputWorkspace", "", Direction::Output),
269 "An output workspace.");
270}
271
272//----------------------------------------------------------------------------------------------
276 IMDHistoWorkspace_sptr inWS = this->getProperty("InputWorkspace");
277 const size_t nDims = inWS->getNumDims();
278 std::vector<std::vector<double>> pbins(5);
279 pbins[0] = this->getProperty("P1Bin");
280 pbins[1] = this->getProperty("P2Bin");
281 pbins[2] = this->getProperty("P3Bin");
282 pbins[3] = this->getProperty("P4Bin");
283 pbins[4] = this->getProperty("P5Bin");
284
285 const size_t emptyCount = std::count_if(pbins.begin(), pbins.end(), emptyBinning);
286 if (emptyCount == pbins.size()) {
287 // No work to do.
288 g_log.information(this->name() + " Direct clone of input.");
289 this->setProperty("OutputWorkspace", std::shared_ptr<IMDHistoWorkspace>(inWS->clone()));
290 } else {
291
292 /* Create the output workspace in the right shape. This allows us to iterate
293 over our output
294 structure and fill it.
295 */
296 MDHistoWorkspace_sptr outWS = createShapedOutput(inWS.get(), pbins, g_log);
297
298 Progress progress(this, 0.0, 1.0, size_t(outWS->getNPoints()));
299
300 // Store in each dimension
301 std::vector<Mantid::coord_t> binWidthsOut(nDims);
302 std::vector<int> widthVector(nDims); // used for nearest neighbour search
303 for (size_t i = 0; i < nDims; ++i) {
304 binWidthsOut[i] = outWS->getDimension(i)->getBinWidth();
305
306 // Maximum width vector for region in output workspace corresponding to
307 // region in input workspace.
308
309 /* ceil(wout/win) = n_pixels in input corresponding to 1 pixel in output.
310 The width vector is the total width. So we always need to double it to
311 take account of the whole region.
312 For example, 8/4 = 2, but thats only 1 pixel on each side of the
313 center, we need 2 * that to give the correct answer of 4.
314 */
315 widthVector[i] = 2 * static_cast<int>(std::ceil(binWidthsOut[i] / inWS->getDimension(i)->getBinWidth()));
316
317 if (widthVector[i] % 2 == 0) {
318 widthVector[i] += 1; // make it odd if not already.
319 }
320 }
321
322 // Outer loop over the output workspace iterator poistions.
323 const int nThreads = Mantid::API::FrameworkManager::Instance().getNumOMPThreads(); // NThreads to Request
324
325 auto outIterators = outWS->createIterators(nThreads, nullptr);
326
328 for (int i = 0; i < int(outIterators.size()); ++i) { // NOLINT
330 auto outIterator = dynamic_cast<MDHistoWorkspaceIterator *>(outIterators[i].get());
331 if (!outIterator) {
332 throw std::logic_error("Failed to cast iterator to MDHistoWorkspaceIterator");
333 }
334
335 do {
336
337 Mantid::Kernel::VMD outIteratorCenter = outIterator->getCenter();
338
339 // Calculate the extents for this out iterator position.
340 std::vector<Mantid::coord_t> mins(nDims);
341 std::vector<Mantid::coord_t> maxs(nDims);
342 for (size_t j = 0; j < nDims; ++j) {
343 const coord_t delta = binWidthsOut[j] / 2;
344 mins[j] = outIteratorCenter[j] - delta;
345 maxs[j] = outIteratorCenter[j] + delta;
346 }
347 MDBoxImplicitFunction box(mins, maxs);
348
349 double sumSignal = 0;
350 double sumSQErrors = 0;
351 double sumNEvents = 0;
352
353 // Create a thread-local input iterator.
354
355 auto iterator = inWS->createIterator();
356 auto inIterator = dynamic_cast<MDHistoWorkspaceIterator *>(iterator.get());
357 if (!inIterator) {
358 throw std::runtime_error("Could not convert IMDIterator to a MDHistoWorkspaceIterator");
359 }
360
361 /*
362 We jump to the iterator position which is closest in the model
363 coordinates
364 to the centre of our output iterator. This allows us to consider a much
365 smaller region of space as part of our inner loop
366 rather than iterating over the full set of boxes of the input workspace.
367 */
368 inIterator->jumpToNearest(outIteratorCenter);
369 performWeightedSum(inIterator, box, sumSignal, sumSQErrors,
370 sumNEvents); // Use the present position. neighbours
371 // below exclude the current position.
372 // Look at all of the neighbours of our position. We previously
373 // calculated what the width vector would need to be.
374 auto neighbourIndexes = inIterator->findNeighbourIndexesByWidth(widthVector);
375 for (auto neighbourIndex : neighbourIndexes) {
376 inIterator->jumpTo(neighbourIndex); // Go to that neighbour
377 performWeightedSum(inIterator, box, sumSignal, sumSQErrors, sumNEvents);
378 }
379
380 const size_t iteratorIndex = outIterator->getLinearIndex();
381 outWS->setSignalAt(iteratorIndex, sumSignal);
382 outWS->setErrorSquaredAt(iteratorIndex, sumSQErrors);
383 outWS->setNumEventsAt(iteratorIndex, sumNEvents);
384
385 progress.report();
386 } while (outIterator->next());
388 }
390 outWS->setDisplayNormalization(inWS->displayNormalizationHisto());
391 this->setProperty("OutputWorkspace", outWS);
392 }
393}
394
400 // Check binning parameters
401 std::map<std::string, std::string> errors;
402
403 for (int i = 1; i < 6; ++i) {
404 std::stringstream propBuffer;
405 propBuffer << "P" << i << "Bin";
406 std::string propertyName = propBuffer.str();
407 std::vector<double> binning = this->getProperty(propertyName);
408 std::string result = checkBinning(binning);
409 if (!result.empty()) {
410 errors.emplace(propertyName, result);
411 }
412 }
413 return errors;
414}
415
416} // namespace Mantid::MDAlgorithms
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
double position
Definition: GetAllEi.cpp:154
double error
Definition: IndexPeaks.cpp:133
MDHistoWorkspace_sptr createShapedOutput(IMDHistoWorkspace const *const inWS, std::vector< std::vector< double > > pbins, Logger &logger)
Create the output workspace in the right shape.
void performWeightedSum(MDHistoWorkspaceIterator const *const iterator, const MDBoxImplicitFunction &box, double &sumSignal, double &sumSQErrors, double &sumNEvents)
Perform a weighted sum at the iterator position.
#define fabs(x)
Definition: Matrix.cpp:22
#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...
Definition: MultiThreaded.h:94
#define PARALLEL_FOR_NO_WSP_CHECK()
#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_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
double tolerance
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
Definition: Algorithm.cpp:1913
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
Kernel::Logger & g_log
Definition: Algorithm.h:451
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
Definition: Algorithm.cpp:231
Abstract interface to MDHistoWorkspace, for use in exposing to Python.
virtual std::shared_ptr< const Mantid::Geometry::IMDDimension > getDimension(size_t index) const
Get a dimension.
Definition: MDGeometry.cpp:161
virtual size_t getNumDims() const
Definition: MDGeometry.cpp:148
Helper class for reporting progress from algorithms.
Definition: Progress.h:25
A property class for workspaces.
An implementation of IMDIterator that iterates through a MDHistoWorkspace.
signal_t getSignal() const override
Returns the signal for this box, same as innerSignal.
virtual signal_t getNumEventsFraction() const
Returns the number of events/points contained in this box.
VecMDExtents getBoxExtents() const
Get the extents in n-dimensions corresponding to the position of the box of the current iterator.
signal_t getError() const override
Returns the error for this box, same as innerError.
bool getIsMasked() const override
Returns true if masking is used.
General N-dimensional box implicit function: Defines a cuboid in N dimensions that is aligned with th...
double fraction(const std::vector< boost::tuple< Mantid::coord_t, Mantid::coord_t > > &boxExtents) const
Calculate the fraction of a box residing inside this implicit function.
Support for a property that holds an array of values.
Definition: ArrayProperty.h:28
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition: Logger.h:52
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
void information(const std::string &msg)
Logs at information level.
Definition: Logger.cpp:105
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
IntegrateMDHistoWorkspace : Algorithm to perform axis aligned integration of an MDHistoWorkspace.
int version() const override
Algorithm's version for identification.
std::map< std::string, std::string > validateInputs() override
Overriden validate inputs.
const std::string category() const override
Algorithm's category for identification.
const std::string name() const override
Algorithms name for identification.
void init() override
Initialize the algorithm's properties.
const std::string summary() const override
Algorithm's summary for use in the GUI and help.
std::shared_ptr< IMDHistoWorkspace > IMDHistoWorkspace_sptr
shared pointer to Mantid::API::IMDHistoWorkspace
std::shared_ptr< MDHistoWorkspace > MDHistoWorkspace_sptr
A shared pointer to a MDHistoWorkspace.
std::shared_ptr< const IMDDimension > IMDDimension_const_sptr
Shared Pointer to const IMDDimension.
Definition: IMDDimension.h:101
float coord_t
Typedef for the data type to use for coordinate axes in MD objects such as MDBox, MDEventWorkspace,...
Definition: MDTypes.h:27
STL namespace.
Describes the direction (within an algorithm) of a Property.
Definition: Property.h:50
@ Input
An input workspace.
Definition: Property.h:53
@ Output
An output workspace.
Definition: Property.h:54