Mantid
Loading...
Searching...
No Matches
BinMD.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 +
22#include "MantidKernel/Utils.h"
23#include <boost/algorithm/string.hpp>
24
25namespace Mantid::MDAlgorithms {
26
27// Register the algorithm into the AlgorithmFactory
29
30using namespace Mantid::Kernel;
31using namespace Mantid::API;
32using namespace Mantid::Geometry;
33using namespace Mantid::DataObjects;
34
35//----------------------------------------------------------------------------------------------
39 : SlicingAlgorithm(), outWS(), implicitFunction(), indexMultiplier(), signals(nullptr), errors(nullptr),
40 numEvents(nullptr) {}
41
42//----------------------------------------------------------------------------------------------
46 declareProperty(std::make_unique<WorkspaceProperty<IMDWorkspace>>("InputWorkspace", "", Direction::Input),
47 "An input MDWorkspace.");
48
49 // Properties for specifying the slice to perform.
50 this->initSlicingProps();
51
52 // --------------- Processing methods and options
53 // ---------------------------------------
54 std::string grp = "Methods";
55 declareProperty(std::make_unique<PropertyWithValue<std::string>>("ImplicitFunctionXML", "", Direction::Input),
56 "XML string describing the implicit function determining "
57 "which bins to use.");
58 setPropertyGroup("ImplicitFunctionXML", grp);
59
60 declareProperty(std::make_unique<PropertyWithValue<bool>>("IterateEvents", true, Direction::Input),
61 "Alternative binning method where you iterate through every event, "
62 "placing them in the proper bin.\n"
63 "This may be faster for workspaces with few events and lots of output "
64 "bins.");
65 setPropertyGroup("IterateEvents", grp);
66
67 declareProperty(std::make_unique<PropertyWithValue<bool>>("Parallel", false, Direction::Input),
68 "Temporary parameter: true to run in parallel. This is ignored for "
69 "file-backed workspaces, where running in parallel makes things slower "
70 "due to disk thrashing.");
71 setPropertyGroup("Parallel", grp);
72
73 declareProperty(std::make_unique<WorkspaceProperty<IMDHistoWorkspace>>("TemporaryDataWorkspace", "", Direction::Input,
75 "An input MDHistoWorkspace used to accumulate results from "
76 "multiple MDEventWorkspaces. If unspecified a blank "
77 "MDHistoWorkspace will be created.");
78
79 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("OutputWorkspace", "", Direction::Output),
80 "A name for the output MDHistoWorkspace.");
81}
82
83//----------------------------------------------------------------------------------------------
92template <typename MDE, size_t nd>
93inline void BinMD::binMDBox(MDBox<MDE, nd> *box, const size_t *const chunkMin, const size_t *const chunkMax) {
94 // An array to hold the rotated/transformed coordinates
95 auto outCenter = std::vector<coord_t>(m_outD);
96
97 // Evaluate whether the entire box is in the same bin
98 if (box->getNPoints() > (1 << nd) * 2) {
99 // There is a check that the number of events is enough for it to make sense
100 // to do all this processing.
101 size_t numVertexes = 0;
102 auto vertexes = box->getVertexesArray(numVertexes);
103
104 // All vertexes have to be within THE SAME BIN = have the same linear index.
105 size_t lastLinearIndex = 0;
106 bool badOne = false;
107
108 for (size_t i = 0; i < numVertexes; i++) {
109 // Cache the center of the event (again for speed)
110 const coord_t *inCenter = vertexes.get() + i * nd;
111
112 // Now transform to the output dimensions
113 m_transform->apply(inCenter, outCenter.data());
114
115 // To build up the linear index
116 size_t linearIndex = 0;
117 // To mark VERTEXES outside range
118 badOne = false;
119
121 for (size_t bd = 0; bd < m_outD; bd++) {
122 // What is the bin index in that dimension
123 coord_t x = outCenter[bd];
124 auto ix = size_t(x);
125 // Within range (for this chunk)?
126 if ((x >= 0) && (ix >= chunkMin[bd]) && (ix < chunkMax[bd])) {
127 // Build up the linear index
128 linearIndex += indexMultiplier[bd] * ix;
129 } else {
130 // Outside the range
131 badOne = true;
132 break;
133 }
134 } // (for each dim in MDHisto)
135
136 // Is the vertex at the same place as the last one?
137 if (!badOne) {
138 if ((i > 0) && (linearIndex != lastLinearIndex)) {
139 // Change of index
140 badOne = true;
141 break;
142 }
143 lastLinearIndex = linearIndex;
144 }
145
146 // Was the vertex completely outside the range?
147 if (badOne)
148 break;
149 } // (for each vertex)
150
151 if (!badOne) {
152 // Yes, the entire box is within a single bin
153 // std::cout << "Box at " << box->getExtentsStr() << " is within a
154 // single bin.\n";
155 // Add the CACHED signal from the entire box
156 signals[lastLinearIndex] += box->getSignal();
157 errors[lastLinearIndex] += box->getErrorSquared();
158 // TODO: If DataObjects get a weight, this would need to get the summed
159 // weight.
160 numEvents[lastLinearIndex] += static_cast<signal_t>(box->getNPoints());
161
162 // And don't bother looking at each event. This may save lots of time
163 // loading from disk.
164 return;
165 }
166 }
167
168 // If you get here, you could not determine that the entire box was in the
169 // same bin.
170 // So you need to iterate through events.
171 const std::vector<MDE> &events = box->getConstEvents();
172 for (auto it = events.begin(); it != events.end(); ++it) {
173 // Cache the center of the event (again for speed)
174 const coord_t *inCenter = it->getCenter();
175
176 // Now transform to the output dimensions
177 m_transform->apply(inCenter, outCenter.data());
178
179 // To build up the linear index
180 size_t linearIndex = 0;
181 // To mark events outside range
182 bool badOne = false;
183
185 for (size_t bd = 0; bd < m_outD; bd++) {
186 // What is the bin index in that dimension
187 coord_t x = outCenter[bd];
188 auto ix = size_t(x);
189 // Within range (for this chunk)?
190 if ((x >= 0) && (ix >= chunkMin[bd]) && (ix < chunkMax[bd])) {
191 // Build up the linear index
192 linearIndex += indexMultiplier[bd] * ix;
193 } else {
194 // Outside the range
195 badOne = true;
196 break;
197 }
198 } // (for each dim in MDHisto)
199
200 if (!badOne) {
201 // Sum the signals as doubles to preserve precision
202 signals[linearIndex] += static_cast<signal_t>(it->getSignal());
203 errors[linearIndex] += static_cast<signal_t>(it->getErrorSquared());
204 // TODO: If DataObjects get a weight, this would need to get the summed
205 // weight.
206 numEvents[linearIndex] += 1.0;
207 }
208 }
209 // Done with the events list
210 box->releaseEvents();
211}
212
213//----------------------------------------------------------------------------------------------
219template <typename MDE, size_t nd> void BinMD::binByIterating(typename MDEventWorkspace<MDE, nd>::sptr ws) {
221 // store exisiting write buffer size for the future
222 // uint64_t writeBufSize =bc->getDiskBuffer().getWriteBufferSize();
223 // and disable write buffer (if any) for input MD Events for this algorithm
224 // purposes;
225 // bc->setCacheParameters(1,0);
226
227 // Cache some data to speed up accessing them a bit
228 indexMultiplier.resize(m_outD);
229 for (size_t d = 0; d < m_outD; d++) {
230 if (d > 0)
231 indexMultiplier[d] = outWS->getIndexMultiplier()[d - 1];
232 else
233 indexMultiplier[d] = 1;
234 }
235 signals = outWS->mutableSignalArray();
236 errors = outWS->mutableErrorSquaredArray();
237 numEvents = outWS->mutableNumEventsArray();
238
239 if (!m_accumulate) {
240 // Start with signal/error/numEvents at 0.0
241 outWS->setTo(0.0, 0.0, 0.0);
242 }
243
244 // The dimension (in the output workspace) along which we chunk for parallel
245 // processing
246 // TODO: Find the smartest dimension to chunk against
247 size_t chunkDimension = 0;
248
249 // How many bins (in that dimension) per chunk.
250 // Try to split it so each core will get 2 tasks:
251 auto chunkNumBins = int(m_binDimensions[chunkDimension]->getNBins() / (PARALLEL_GET_MAX_THREADS * 2));
252 if (chunkNumBins < 1)
253 chunkNumBins = 1;
254
255 // Do we actually do it in parallel?
256 bool doParallel = getProperty("Parallel");
257 // Not if file-backed!
258 if (bc->isFileBacked())
259 doParallel = false;
260 if (!doParallel)
261 chunkNumBins = int(m_binDimensions[chunkDimension]->getNBins());
262
263 // Total number of steps
264 size_t progNumSteps = 0;
265 if (prog) {
266 prog->setNotifyStep(0.1);
267 prog->resetNumSteps(100, 0.00, 1.0);
268 }
269
270 // Run the chunks in parallel. There is no overlap in the output workspace so
271 // it is thread safe to write to it..
272
273 PRAGMA_OMP( parallel for schedule(dynamic,1) if (doParallel) )
274 for (int chunk = 0; chunk < int(m_binDimensions[chunkDimension]->getNBins()); chunk += chunkNumBins) {
276 // Region of interest for this chunk.
277 std::vector<size_t> chunkMin(m_outD);
278 std::vector<size_t> chunkMax(m_outD);
279 for (size_t bd = 0; bd < m_outD; bd++) {
280 // Same limits in the other dimensions
281 chunkMin[bd] = 0;
282 chunkMax[bd] = m_binDimensions[bd]->getNBins();
283 }
284 // Parcel out a chunk in that single dimension dimension
285 chunkMin[chunkDimension] = size_t(chunk);
286 if (size_t(chunk + chunkNumBins) > m_binDimensions[chunkDimension]->getNBins())
287 chunkMax[chunkDimension] = m_binDimensions[chunkDimension]->getNBins();
288 else
289 chunkMax[chunkDimension] = size_t(chunk + chunkNumBins);
290
291 // Build an implicit function (it needs to be in the space of the
292 // MDEventWorkspace)
293 auto function = this->getImplicitFunctionForChunk(chunkMin.data(), chunkMax.data());
294
295 // Use getBoxes() to get an array with a pointer to each box
296 std::vector<API::IMDNode *> boxes;
297 // Leaf-only; no depth limit; with the implicit function passed to it.
298 ws->getBox()->getBoxes(boxes, 1000, true, function.get());
299
300 // Sort boxes by file position IF file backed. This reduces seeking time,
301 // hopefully.
302 if (bc->isFileBacked())
304
305 // For progress reporting, the # of boxes
306 if (prog) {
307 PARALLEL_CRITICAL(BinMD_progress) {
308 g_log.debug() << "Chunk " << chunk << ": found " << boxes.size() << " boxes within the implicit function.\n";
309 progNumSteps += boxes.size();
310 prog->setNumSteps(progNumSteps);
311 }
312 }
313
314 // Go through every box for this chunk.
315 for (auto &boxe : boxes) {
316 auto *box = dynamic_cast<MDBox<MDE, nd> *>(boxe);
317 // Perform the binning in this separate method.
318 if (box && !box->getIsMasked())
319 this->binMDBox(box, chunkMin.data(), chunkMax.data());
320
321 // Progress reporting
322 if (prog)
323 prog->report();
324 // For early cancelling of the loop
325 if (this->m_cancel)
326 break;
327 } // for each box in the vector
329 } // for each chunk in parallel
331
332 // Now the implicit function
333 if (implicitFunction) {
334 if (prog)
335 prog->report("Applying implicit function.");
336 signal_t nan = std::numeric_limits<signal_t>::quiet_NaN();
337 outWS->applyImplicitFunction(implicitFunction.get(), nan, nan);
338 }
339}
340
341//----------------------------------------------------------------------------------------------
345 // Input MDEventWorkspace/MDHistoWorkspace
346 m_inWS = getProperty("InputWorkspace");
347 // Look at properties, create either axis-aligned or general transform.
348 // This (can) change m_inWS
349 this->createTransform();
350
351 // De serialize the implicit function
352 std::string ImplicitFunctionXML = getPropertyValue("ImplicitFunctionXML");
353 implicitFunction = nullptr;
354 if (!ImplicitFunctionXML.empty())
355 implicitFunction = std::unique_ptr<MDImplicitFunction>(
356 Mantid::API::ImplicitFunctionFactory::Instance().createUnwrapped(ImplicitFunctionXML));
357
358 // This gets deleted by the thread pool; don't delete it in here.
359 prog = std::make_unique<Progress>(this, 0.0, 1.0, 1);
360
361 // Create the dense histogram. This allocates the memory
362 std::shared_ptr<IMDHistoWorkspace> tmp = this->getProperty("TemporaryDataWorkspace");
363 outWS = std::dynamic_pointer_cast<MDHistoWorkspace>(tmp);
364 if (!outWS) {
365 outWS = std::make_shared<MDHistoWorkspace>(m_binDimensions);
366 } else {
367 m_accumulate = true;
368 }
369
370 // Saves the geometry transformation from original to binned in the workspace
371 outWS->setTransformFromOriginal(this->m_transformFromOriginal.release(), 0);
372 outWS->setTransformToOriginal(this->m_transformToOriginal.release(), 0);
373 for (size_t i = 0; i < m_bases.size(); i++)
374 outWS->setBasisVector(i, m_bases[i]);
375 outWS->setOrigin(this->m_translation);
376 outWS->setOriginalWorkspace(m_inWS, 0);
377
378 // And the intermediate WS one too, if any
379 if (m_intermediateWS) {
380 outWS->setOriginalWorkspace(m_intermediateWS, 1);
381 outWS->setTransformFromOriginal(m_transformFromIntermediate.release(), 1);
382 outWS->setTransformToOriginal(m_transformToIntermediate.release(), 1);
383 }
384
385 // Wrapper to cast to MDEventWorkspace then call the function
386 bool IterateEvents = getProperty("IterateEvents");
387 if (!IterateEvents) {
388 g_log.warning() << "IterateEvents=False is no longer supported. Setting "
389 "IterateEvents=True.\n";
390 IterateEvents = true;
391 }
392
393 /*
394 We should fail noisily here. CALL_MDEVENT_FUNCTION will silently allow
395 IMDHistoWorkspaces to cascade through to the end
396 and result in an empty output. The only way we allow InputWorkspaces to be
397 IMDHistoWorkspaces is if they also happen to contain original workspaces
398 that are MDEventWorkspaces.
399 */
400 if (m_inWS->isMDHistoWorkspace()) {
401 throw std::runtime_error("Cannot rebin a workspace that is histogrammed and has no original "
402 "workspace that is an MDEventWorkspace. "
403 "Reprocess the input so that it contains full MDEvents.");
404 }
405
407
408 // Copy the coordinate system & experiment infos to the output
409 IMDEventWorkspace_sptr inEWS = std::dynamic_pointer_cast<IMDEventWorkspace>(m_inWS);
410 if (inEWS) {
411 outWS->setCoordinateSystem(inEWS->getSpecialCoordinateSystem());
412 try {
413 outWS->copyExperimentInfos(*inEWS);
414 } catch (std::runtime_error &) {
415 g_log.warning() << this->name() << " was not able to copy experiment info to output workspace "
416 << outWS->getName() << '\n';
417 }
418 }
419
420 // Pass on the display normalization from the input workspace
421 outWS->setDisplayNormalization(m_inWS->displayNormalizationHisto());
422
423 outWS->updateSum();
424 // Save the output
425 setProperty("OutputWorkspace", std::dynamic_pointer_cast<Workspace>(outWS));
426}
427
428} // namespace Mantid::MDAlgorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
gsl_vector * tmp
#define CALL_MDEVENT_FUNCTION(funcname, workspace)
Macro that makes it possible to call a templated method for a MDEventWorkspace using a IMDEventWorksp...
#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 PRAGMA_OMP(expression)
#define PARALLEL_GET_MAX_THREADS
#define PARALLEL_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
std::atomic< bool > m_cancel
Set to true to stop execution.
Definition Algorithm.h:404
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Kernel::Logger & g_log
Definition Algorithm.h:422
static void sortObjByID(std::vector< IMDNode * > &boxes)
Static method for sorting a list of MDBoxBase pointers by their file position, ascending.
Definition IMDNode.h:292
A property class for workspaces.
std::unique_ptr< coord_t[]> getVertexesArray(size_t &numVertices) const override
Return the vertices of every corner of the box, but as a bare array of length numVertices * nd.
signal_t getSignal() const override
Returns the integrated signal from all points within.
Definition MDBoxBase.h:232
signal_t getErrorSquared() const override
Returns the integrated error squared from all points within.
Definition MDBoxBase.h:242
Templated class for a multi-dimensional event "box".
Definition MDBox.h:45
const std::vector< MDE > & getConstEvents() const
Get vector of constant events to use.
Definition MDBox.hxx:269
uint64_t getNPoints() const override
Returns the total number of points (events) in this box either they are all in memory,...
Definition MDBox.hxx:225
void releaseEvents()
For file-backed MDBoxes, this marks that the data vector is no longer "busy", and so it is safe for t...
Definition MDBox.hxx:294
std::shared_ptr< MDEventWorkspace< MDE, nd > > sptr
Typedef for a shared pointer of this kind of event workspace.
Mantid::API::BoxController_sptr getBoxController() override
Returns the BoxController used in this workspace.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void setPropertyGroup(const std::string &name, const std::string &group)
Set the group for a given property.
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
The concrete, templated class for properties.
void binMDBox(DataObjects::MDBox< MDE, nd > *box, const size_t *const chunkMin, const size_t *const chunkMax)
Method to bin a single MDBox.
Definition BinMD.cpp:93
std::unique_ptr< Mantid::Geometry::MDImplicitFunction > implicitFunction
ImplicitFunction used.
Definition BinMD.h:73
std::vector< size_t > indexMultiplier
Cached values for speed up.
Definition BinMD.h:76
void exec() override
Run the algorithm.
Definition BinMD.cpp:344
const std::string name() const override
Algorithm's name for identification.
Definition BinMD.h:42
std::unique_ptr< Mantid::API::Progress > prog
Progress reporting.
Definition BinMD.h:71
void binByIterating(typename DataObjects::MDEventWorkspace< MDE, nd >::sptr ws)
Helper method.
Definition BinMD.cpp:219
void init() override
Initialise the properties.
Definition BinMD.cpp:45
Mantid::DataObjects::MDHistoWorkspace_sptr outWS
The output MDHistoWorkspace.
Definition BinMD.h:69
Abstract Algorithm class that will be used by: BinMD and SliceMD and shares code for getting a slice ...
std::unique_ptr< API::CoordTransform > m_transformFromOriginal
Coordinate transformation to save in the output workspace (original->binned)
Mantid::API::IMDWorkspace_sptr m_intermediateWS
Intermediate original workspace.
std::unique_ptr< Mantid::Geometry::MDImplicitFunction > getImplicitFunctionForChunk(const size_t *const chunkMin, const size_t *const chunkMax)
Create an implicit function for picking boxes, based on the indexes in the output MDHistoWorkspace.
std::unique_ptr< API::CoordTransform > m_transform
Coordinate transformation to apply.
void createTransform()
Read the algorithm properties and creates the appropriate transforms for slicing the MDEventWorkspace...
Mantid::API::IMDWorkspace_sptr m_inWS
Input workspace.
std::unique_ptr< API::CoordTransform > m_transformToOriginal
Coordinate transformation to save in the output workspace (binned->original)
std::unique_ptr< DataObjects::CoordTransformAffine > m_transformFromIntermediate
Coordinate transformation to save in the output WS, from the intermediate WS.
size_t m_outD
Number of dimensions in the output (binned) workspace.
std::vector< Mantid::Kernel::VMD > m_bases
Basis vectors of the output dimensions, normalized to unity length.
void initSlicingProps()
Initialise the properties.
Mantid::Kernel::VMD m_translation
Translation from the OUTPUT to the INPUT workspace i.e.
std::unique_ptr< DataObjects::CoordTransformAffine > m_transformToIntermediate
Coordinate transformation to save in the intermediate WS.
std::vector< Mantid::Geometry::MDHistoDimension_sptr > m_binDimensions
Bin dimensions to actually use.
std::shared_ptr< IMDEventWorkspace > IMDEventWorkspace_sptr
Shared pointer to Mantid::API::IMDEventWorkspace.
std::shared_ptr< BoxController > BoxController_sptr
Shared ptr to BoxController.
std::size_t numEvents(Nexus::File &file, bool &hasTotalCounts, bool &oldNeXusFileNames, const std::string &prefix, const Nexus::NexusDescriptor &descriptor)
Get the number of events in the currently opened group.
float coord_t
Typedef for the data type to use for coordinate axes in MD objects such as MDBox, MDEventWorkspace,...
Definition MDTypes.h:27
double signal_t
Typedef for the signal recorded in a MDBox, etc.
Definition MDTypes.h:36
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54