Mantid
Loading...
Searching...
No Matches
DiffractionFocussing2.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 +
9#include "MantidAPI/Axis.h"
11#include "MantidAPI/ISpectrum.h"
21#include "MantidHistogramData/LogarithmicGenerator.h"
22#include "MantidIndexing/Group.h"
23#include "MantidIndexing/IndexInfo.h"
26
27#include <algorithm>
28#include <cfloat>
29#include <iterator>
30#include <numeric>
31
32using namespace Mantid::Kernel;
33using namespace Mantid::API;
34using namespace Mantid::DataObjects;
35using Mantid::HistogramData::BinEdges;
36using std::vector;
37
38namespace Mantid::Algorithms {
39
40// Register the class into the algorithm factory
41DECLARE_ALGORITHM(DiffractionFocussing2)
42
43
47
48 auto wsValidator = std::make_shared<API::RawCountValidator>();
49 declareProperty(
50 std::make_unique<API::WorkspaceProperty<MatrixWorkspace>>("InputWorkspace", "", Direction::Input, wsValidator),
51 "A 2D workspace with X values of d-spacing or Q");
52 declareProperty(std::make_unique<API::WorkspaceProperty<>>("OutputWorkspace", "", Direction::Output),
53 "The result of diffraction focussing of InputWorkspace");
54
55 declareProperty(std::make_unique<FileProperty>("GroupingFileName", "", FileProperty::OptionalLoad, ".cal"),
56 "Optional: The name of the CalFile with grouping data.");
57
58 declareProperty(std::make_unique<WorkspaceProperty<GroupingWorkspace>>("GroupingWorkspace", "", Direction::Input,
60 "Optional: GroupingWorkspace to use instead of a grouping file.");
61
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.");
67 declareProperty(std::make_unique<ArrayProperty<double>>("DMin"),
68 "Minimum x values, one value for each output specta or single value which is common to all");
69 declareProperty(std::make_unique<ArrayProperty<double>>("DMax"),
70 "Maximum x values, one value for each output specta or single value which is common to all");
71 declareProperty(std::make_unique<ArrayProperty<double>>("Delta"),
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");
75}
76
77std::map<std::string, std::string> DiffractionFocussing2::validateInputs() {
78 std::map<std::string, std::string> issues;
79
80 // can only specify grouping in a single way
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;
91 }
92
93 // validate input workspace
94 API::MatrixWorkspace_const_sptr inputWS = getProperty("InputWorkspace");
95 if (!inputWS) {
96 // Could be a workspace group
97 const auto inputProp =
98 dynamic_cast<const WorkspaceProperty<MatrixWorkspace> *>(getPointerToProperty("InputWorkspace"));
100 std::dynamic_pointer_cast<WorkspaceGroup>(AnalysisDataService::Instance().retrieve(inputProp->value()));
101 if (!wsGroup) {
102 issues["InputWorkspace"] = "InputWorksapce must be a matrix workspace or workspace group.";
103 } else {
104 for (const Workspace_sptr &ws : wsGroup->getAllItems()) {
105 inputWS = std::dynamic_pointer_cast<MatrixWorkspace>(ws);
106 validateInputWorkspaceUnit(std::move(inputWS), issues);
107 }
108 }
109 } else {
110 validateInputWorkspaceUnit(std::move(inputWS), issues);
111 }
112
113 if (isDefault("DMin") && isDefault("DMax") && isDefault("Delta"))
114 return issues;
115
116 if (isDefault("DMin") || isDefault("DMax") || isDefault("Delta")) {
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";
120 return issues;
121 }
122
123 // check that delta is finite and non-zero, mins and maxs are finite and min is less than max
124 const std::vector<double> xmins = getProperty("DMin");
125 const std::vector<double> xmaxs = getProperty("DMax");
126 const std::vector<double> deltas = getProperty("Delta");
127
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";
132
133 if (std::any_of(xmins.cbegin(), xmins.cend(), [](double x) { return !std::isfinite(x); }))
134 issues["DMin"] = "All must be finite";
135
136 if (std::any_of(xmaxs.cbegin(), xmaxs.cend(), [](double x) { return !std::isfinite(x); }))
137 issues["DMax"] = "All must be finite";
138
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;
143 }
144 } else if (xmaxs.size() == 1) {
145 if (xmaxs[0] <= *std::max_element(xmins.cbegin(), xmins.cend())) {
146 min_less_than_max = false;
147 }
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";
151 } else {
152 for (size_t i{0}; i < xmins.size(); i++) {
153 if (xmins[i] >= xmaxs[i]) {
154 min_less_than_max = false;
155 break;
156 }
157 }
158 }
159
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";
163 }
164
165 return issues;
166}
167
169 std::map<std::string, std::string> &issues) {
170 // Validate UnitID (spacing)
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();
176 }
177}
178
179//=============================================================================
184 // Clear maps and vectors to free up memory.
185 groupAtWorkspaceIndex.clear();
186 std::vector<int>().swap(groupAtWorkspaceIndex);
187 group2xvector.clear();
188 group2xvector = std::map<int, HistogramData::BinEdges>();
189 group2xstep.clear();
190 group2xstep = std::map<int, double>();
191 group2wgtvector.clear();
192 this->m_validGroups.clear();
193 std::vector<Indexing::SpectrumNumber>().swap(m_validGroups);
194 this->m_wsIndices.clear();
195 std::vector<std::vector<std::size_t>>().swap(this->m_wsIndices);
196}
197
198//=============================================================================
206 // Get the input workspace
207 m_matrixInputW = getProperty("InputWorkspace");
208 nPoints = static_cast<int>(m_matrixInputW->blocksize());
209 nHist = static_cast<int>(m_matrixInputW->getNumberHistograms());
210
211 this->getGroupingWorkspace();
212
213 const bool autoBinning = isDefault("DMin");
214
215 // Fill the map
216 progress(0.2, "Determine Rebin Params");
217 { // keep variables in relatively small scope
218 std::vector<int> udet2group; // map from udet to group
219 g_log.debug() << "(1) nGroups " << nGroups << "\n";
220 m_groupWS->makeDetectorIDToGroupVector(udet2group, nGroups);
221 if (nGroups <= 0)
222 throw std::runtime_error("No groups were specified.");
223 g_log.debug() << "(2) nGroups " << nGroups << "\n";
224
225 // This finds the rebin parameters (used in both versions)
226 // It also initializes the groupAtWorkspaceIndex[] array.
227 if (autoBinning)
228 determineRebinParameters(udet2group);
229 else {
231 nPoints = 1; // only needed for workspace init, histogram will be replaced
232 }
233 }
234
235 size_t totalHistProcess = this->setupGroupToWSIndices();
236
237 // determine event workspace min/max tof
238 double eventXMin = 0.;
239 double eventXMax = 0.;
240
241 const auto eventInputWS = std::dynamic_pointer_cast<const EventWorkspace>(m_matrixInputW);
242 if (eventInputWS) {
243 if (getProperty("PreserveEvents")) {
244 // Input workspace is an event workspace. Use the other exec method
245 this->execEvent();
246 this->cleanup();
247 return; // <- return early!!!!!!!!!!!!!
248 } else {
249 // get the full d-spacing range
250 m_matrixInputW->getXMinMax(eventXMin, eventXMax);
251 }
252 }
253
254 // Check valida detectors are found in the .Cal file
255 if (nGroups <= 0) {
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.");
259 }
260 // Check the number of points
261 if (nPoints <= 0) {
262 throw std::runtime_error("No points found in the data range.");
263 }
266 // Caching containers that are either only read from or unused. Initialize
267 // them once.
268 // Helgrind will show a race-condition but the data is completely unused so it
269 // is irrelevant
270 MantidVec weights_default(1, 1.0), emptyVec(1, 0.0);
271
272 Progress prog(this, 0.2, 1.0, static_cast<int>(totalHistProcess) + nGroups);
273
275 for (int outWorkspaceIndex = 0; outWorkspaceIndex < static_cast<int>(m_validGroups.size()); outWorkspaceIndex++) {
277 auto group = static_cast<int>(m_validGroups[outWorkspaceIndex]);
278
279 // Get the group
280 // const so that binding it to a std::span does not detach the cow_ptr
281 auto const &Xout = group2xvector.at(group);
282
283 // Assign the new X axis only once (i.e when this group is encountered the
284 // first time)
285 int nPoints_local(nPoints);
286 if (!autoBinning) {
287 nPoints_local = static_cast<int>(Xout.size() - 1);
288 out->resizeHistogram(outWorkspaceIndex, nPoints_local);
289 }
290
291 out->setBinEdges(outWorkspaceIndex, Xout);
292
293 // This is the output spectrum
294 auto &outSpec = out->getSpectrum(outWorkspaceIndex);
295 outSpec.setSpectrumNo(group);
296
297 // Get the references to Y and E output and rebin
298 auto &Yout = outSpec.mutableY();
299 auto &Eout = outSpec.mutableE();
300
301 // Initialize the group's weight vector here and the dummy vector used for
302 // accumulating errors.
303 MantidVec EOutDummy(nPoints_local);
304 MantidVec groupWgt(nPoints_local, 0.0);
305
306 // loop through the contributing histograms
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];
311 // This is the input spectrum
312 const auto &inSpec = m_matrixInputW->getSpectrum(inWorkspaceIndex);
313 // Get reference to its old X,Y,and E.
314 auto &Xin = inSpec.x();
315
316 // copy over detector ids
317 outSpec.addDetectorIDs(inSpec.getDetectorIDs());
318
319 // get histogram version of the data
320 if (eventInputWS) {
321 const EventList &el = eventInputWS->getSpectrum(inWorkspaceIndex);
322 // generateHistogram overwrites the data in Y and E so write to a temporary vector
323 MantidVec Ytemp;
324 MantidVec Etemp;
325 el.generateHistogram(group2xstep.at(group), Xout, Ytemp, Etemp);
326 // accumulate the histogram into the output
327 std::transform(Ytemp.cbegin(), Ytemp.cend(), Yout.begin(), Yout.begin(),
328 [](const auto &left, const auto &right) { return left + right; });
329 // accumulate the square of the error
330 std::transform(Etemp.cbegin(), Etemp.cend(), Eout.begin(), Eout.begin(),
331 [](const auto &left, const auto &right) { return left * left + right; });
332 } else {
333 auto const &Yin = inSpec.y();
334 auto const &Ein = inSpec.e();
335
336 try {
337 // TODO This should be implemented in Histogram as rebin
338 Mantid::Kernel::VectorHelper::rebinHistogram(Xin, Yin, Ein, Xout, Yout, Eout, true);
339 } catch (...) {
340 // Should never happen because Xout is constructed to envelop all of the
341 // Xin vectors
342 std::ostringstream mess;
343 mess << "Error in rebinning process for spectrum:" << inWorkspaceIndex;
344 throw std::runtime_error(mess.str());
345 }
346 }
347
348 // Check for masked bins in this spectrum
349 if (m_matrixInputW->hasMaskedBins(i)) {
350 MantidVec weight_bins, weights;
351 weight_bins.emplace_back(Xin.front());
352 // If there are masked bins, get a reference to the list of them
353 const API::MatrixWorkspace::MaskList &mask = m_matrixInputW->maskedBins(i);
354 // Now iterate over the list, adjusting the weights for the affected
355 // bins
356 for (const auto &bin : mask) {
357 const double currentX = Xin[bin.first];
358 // Add an intermediate bin with full weight if masked bins aren't
359 // consecutive
360 if (weight_bins.back() != currentX) {
361 weights.emplace_back(1.0);
362 weight_bins.emplace_back(currentX);
363 }
364 // The weight for this masked bin is 1 - the degree to which this bin
365 // is masked
366 weights.emplace_back(1.0 - bin.second);
367 weight_bins.emplace_back(Xin[bin.first + 1]);
368 }
369 // Add on a final bin with full weight if masking doesn't go up to the
370 // end
371 if (weight_bins.back() != Xin.back()) {
372 weights.emplace_back(1.0);
373 weight_bins.emplace_back(Xin.back());
374 }
375
376 // Create a zero vector for the errors because we don't care about them
377 // here
378 const MantidVec zeroes(weights.size(), 0.0);
379 // Rebin the weights - note that this is a distribution
380 VectorHelper::rebin(weight_bins, weights, zeroes, Xout, groupWgt, EOutDummy, true, true);
381 } else // If no masked bins we want to add 1 to the weight of the output
382 // bins that this input covers
383 {
384 // Initialized within the loop to avoid having to wrap writing to it
385 // with a PARALLEL_CRITICAL sections
386 MantidVec limits(2);
387
388 if (eventXMin > 0. && eventXMax > 0.) {
389 limits[0] = eventXMin;
390 limits[1] = eventXMax;
391 } else {
392 limits[0] = Xin.front();
393 limits[1] = Xin.back();
394 }
395
396 // Rebin the weights - note that this is a distribution
397 VectorHelper::rebin(limits, weights_default, emptyVec, Xout, groupWgt, EOutDummy, true, true);
398 }
399 prog.report();
400 } // end of loop for input spectra
401
402 // Finalize the group weights:
403 // in the case that the requested output domain interval contains all of the input intervals,
404 // there may be zero-weighted sections at the boundaries.
405 // (Note that the corresponding y-values should also be zero, but 0.0 / 0.0 is still NaN.)
406 std::transform(groupWgt.cbegin(), groupWgt.cend(), groupWgt.begin(),
407 [](double w) { return std::fabs(w) < std::numeric_limits<double>::epsilon() ? 1.0 : w; });
408
409 // Calculate the bin widths
410 std::vector<double> widths(Xout.size());
411 std::adjacent_difference(Xout.begin(), Xout.end(), widths.begin());
412
413 // Take the square root of the errors
414 std::transform(Eout.begin(), Eout.end(), Eout.begin(), static_cast<double (*)(double)>(sqrt));
415
416 // Multiply the data and errors by the bin widths because the rebin
417 // function, when used
418 // in the fashion above for the weights, doesn't put it back in
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>());
421
422 // Now need to normalise the data (and errors) by the weights.
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>());
425
426 // Now multiply by the number of spectra in the group
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); });
429
430 prog.report();
432 } // end of loop for groups
434
435 setProperty("OutputWorkspace", out);
436
437 this->cleanup();
438}
439
440//=============================================================================
448 // Create a new outputworkspace with not much in it - bin boundaries will be replaced later
449 auto eventOutputW = create<EventWorkspace>(*m_matrixInputW, m_validGroups.size(), HistogramData::BinEdges(2));
450
451 // determine if this is an in-place operation so events can be deleted from the in put while running
452 bool inPlace;
453 {
454 MatrixWorkspace_const_sptr outputWS = getProperty("OutputWorkspace");
455 inPlace = bool(m_matrixInputW == outputWS);
456 }
457 if (inPlace) {
458 g_log.debug("Focussing EventWorkspace in-place.");
459 }
460 g_log.debug() << nGroups << " groups found in .cal file (counting group 0).\n";
461
462 const auto eventinputWS = std::dynamic_pointer_cast<const EventWorkspace>(m_matrixInputW);
463 const EventType eventWtype = eventinputWS->getEventType();
464 if (inPlace) {
465 // MRU isn't needed since the workspace will be deleted soon
466 std::const_pointer_cast<EventWorkspace>(eventinputWS)->clearMRU();
467 }
468
469 std::unique_ptr<Progress> prog = std::make_unique<Progress>(this, 0.2, 0.25, nGroups);
470
471 // determine precount size
472 vector<size_t> size_required(this->m_validGroups.size(), 0);
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];
476
477 totalHistProcess += static_cast<int>(indices.size());
478 for (auto index : indices) {
479 size_required[iGroup] += eventinputWS->getSpectrum(index).getNumberEvents();
480 }
481 prog->report(1, "Pre-counting");
482 }
483
484 // ------------- Pre-allocate Event Lists ----------------------------
485 prog.reset();
486 prog = std::make_unique<Progress>(this, 0.25, 0.3, totalHistProcess);
487
488 // This creates and reserves the space required
489 for (size_t iGroup = 0; iGroup < this->m_validGroups.size(); iGroup++) {
490 const auto group = static_cast<int>(m_validGroups[iGroup]);
491 EventList &groupEL = eventOutputW->getSpectrum(iGroup);
492 groupEL.switchTo(eventWtype);
493 groupEL.clear(true); // remove detector ids
494 groupEL.reserve(size_required[iGroup]);
495 groupEL.setSpectrumNo(group);
496 prog->reportIncrement(1, "Allocating");
497 }
498
499 // ----------- Focus ---------------
500 prog.reset();
501 prog = std::make_unique<Progress>(this, 0.3, 0.9, totalHistProcess);
502
503 if (this->m_validGroups.size() == 1) {
504 g_log.information() << "Performing focussing on a single group\n";
505 // Special case of a single group - parallelize differently
506 EventList &groupEL = eventOutputW->getSpectrum(0);
507 const std::vector<size_t> &indices = this->m_wsIndices[0];
508
509 constexpr int chunkSize{200};
510
511 const int end = (totalHistProcess / chunkSize) + 1;
512
513 PRAGMA_OMP(parallel for schedule(dynamic, 1) )
514 for (int wiChunk = 0; wiChunk < end; wiChunk++) {
516
517 // Perform in chunks for more efficiency
518 int max = (wiChunk + 1) * chunkSize;
519 if (max > totalHistProcess)
520 max = totalHistProcess;
521
522 // Make a blank EventList that will accumulate the chunk.
523 EventList chunkEL;
524 chunkEL.switchTo(eventWtype);
525 // chunkEL.reserve(numEventsInChunk);
526
527 // process the chunk
528 for (int i = wiChunk * chunkSize; i < max; i++) {
529 // Accumulate the chunk
530 size_t wi = indices[i];
531 chunkEL += eventinputWS->getSpectrum(wi);
532 }
533
534 // Rejoin the chunk with the rest.
535 PARALLEL_CRITICAL(DiffractionFocussing2_JoinChunks) { groupEL += chunkEL; }
536
538 }
540 } else {
541 // ------ PARALLELIZE BY GROUPS -------------------------
542
543 auto nValidGroups = static_cast<int>(this->m_validGroups.size());
544 PARALLEL_FOR_IF(Kernel::threadSafe(*eventinputWS))
545 for (int iGroup = 0; iGroup < nValidGroups; iGroup++) {
547 const std::vector<size_t> &indices = this->m_wsIndices[iGroup];
548 for (auto wi : indices) {
549 // In workspace index iGroup, put what was in the OLD workspace index wi
550 eventOutputW->getSpectrum(iGroup) += eventinputWS->getSpectrum(wi);
551
552 prog->reportIncrement(1, "Appending Lists");
553
554 // When focussing in place, you can clear out old memory from the input
555 // one!
556 if (inPlace) {
557 std::const_pointer_cast<EventWorkspace>(eventinputWS)->getSpectrum(wi).clear(true);
558 }
559 }
561 }
563 } // (done with parallel by groups)
564
565 // Now that the data is cleaned up, go through it and set the X vectors to the
566 // input workspace we first talked about.
567 prog.reset();
568 prog = std::make_unique<Progress>(this, 0.9, 1.0, nGroups);
569 for (size_t workspaceIndex = 0; workspaceIndex < this->m_validGroups.size(); workspaceIndex++) {
570 const auto group = static_cast<int>(m_validGroups[workspaceIndex]);
571 // Now this is the workspace index of that group; simply 1 offset
572 prog->reportIncrement(1, "Setting X");
573
574 if (workspaceIndex >= eventOutputW->getNumberHistograms()) {
575 g_log.warning() << "Warning! Invalid workspace index found for group # " << group
576 << ". Histogram will be empty.\n";
577 continue;
578 }
579
580 // Now you set the X axis to the X you saved before.
581 if (!group2xvector.empty()) {
582 auto git = group2xvector.find(group);
583 if (git != group2xvector.end())
584 // Reset Histogram instead of BinEdges, the latter forbids size change.
585 eventOutputW->setHistogram(workspaceIndex, BinEdges(git->second.cowData()));
586 else
587 // Just use the 1st X vector it found, instead of nothin.
588 // Reset Histogram instead of BinEdges, the latter forbids size change.
589 eventOutputW->setHistogram(workspaceIndex, BinEdges(group2xvector.begin()->second.cowData()));
590 } else
591 g_log.warning() << "Warning! No X histogram bins were found for any "
592 "groups. Histogram will be empty.\n";
593 }
594 eventOutputW->clearMRU();
595 setProperty("OutputWorkspace", std::move(eventOutputW));
596}
597
598//=============================================================================
606int DiffractionFocussing2::validateSpectrumInGroup(const std::vector<int> &udet2group, size_t wi) {
607 const auto &dets = m_matrixInputW->getSpectrum(wi).getDetectorIDs();
608 if (dets.empty()) // Not in group
609 {
610 g_log.debug() << wi << " <- this workspace index is empty!\n";
611 return -1;
612 }
613
614 auto it = dets.cbegin();
615 if (*it < 0) // bad pixel id
616 return -1;
617
618 try { // what if index out of range?
619 const int group = udet2group.at(*it);
620 if (group <= 0)
621 return -1;
622 it++;
623 for (; it != dets.end(); ++it) // Loop other all other udets
624 {
625 if (udet2group.at(*it) != group)
626 return -1;
627 }
628 return group;
629 } catch (...) {
630 }
631
632 return -1;
633}
634
635//=============================================================================
651void DiffractionFocussing2::determineRebinParameters(const std::vector<int> &udet2group) {
652 std::ostringstream mess;
653
654 // typedef for the storage of the group ranges
655 using group2minmaxmap = std::map<int, std::pair<double, double>>;
656 // Map from group number to its associated range parameters <Xmin,Xmax,step>
657 group2minmaxmap group2minmax;
658 group2minmaxmap::iterator gpit;
659
660 const double BIGGEST = std::numeric_limits<double>::max();
661
662 // whether or not to bother checking for a mask
663 bool checkForMask = false;
664 Geometry::Instrument_const_sptr instrument = m_matrixInputW->getInstrument();
665 if (instrument != nullptr) {
666 checkForMask = ((instrument->getSource() != nullptr) && (instrument->getSample() != nullptr));
667 }
668 const auto &spectrumInfo = m_matrixInputW->spectrumInfo();
669
671 for (int wi = 0; wi < nHist; wi++) // Iterate over all histograms to find X boundaries for each group
672 {
673 const int group = validateSpectrumInGroup(udet2group, static_cast<size_t>(wi));
675 if (group == -1)
676 continue;
677
678 if (checkForMask) {
679 if (spectrumInfo.isMasked(wi)) {
680 groupAtWorkspaceIndex[wi] = -1;
681 continue;
682 }
683 }
684 gpit = group2minmax.find(group);
685
686 // Create the group range in the map if it isn't already there
687 if (gpit == group2minmax.end()) {
688 gpit = group2minmax.emplace(group, std::make_pair(BIGGEST, -1. * BIGGEST)).first;
689 }
690 const double min = (gpit->second).first;
691 const double max = (gpit->second).second;
692 auto &X = m_matrixInputW->x(wi);
693 double temp = X.front();
694 if (temp < (min)) // New Xmin found
695 (gpit->second).first = temp;
696 temp = X.back();
697 if (temp > (max)) // New Xmax found
698 (gpit->second).second = temp;
699 }
700
701 nGroups = group2minmax.size(); // Number of unique groups
702
703 const int64_t xPoints = nPoints + 1;
704 // Iterator over all groups to create the new X vectors
705 for (gpit = group2minmax.begin(); gpit != group2minmax.end(); ++gpit) {
706 double Xmin, Xmax, step;
707 Xmin = (gpit->second).first;
708 Xmax = (gpit->second).second;
709
710 // Make sure that Xmin is not 0 - since it is not possible to do log binning
711 // from 0.0.
712 if (Xmin <= 0)
713 Xmin = Xmax / nPoints;
714 if (Xmin <= 0)
715 Xmin = 1.0;
716 if (Xmin == Xmax)
717 Xmin = Xmax / 2.0;
718
719 if (Xmax < Xmin) // Should never happen
720 {
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());
724 }
725 // This log step size will give the right # of points
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;
729 // g_log.information(mess.str());
730 mess.str("");
731
732 HistogramData::BinEdges xnew(xPoints, HistogramData::LogarithmicGenerator(Xmin, step));
733 group2xvector[gpit->first] = xnew; // Register this vector in the map
734 group2xstep[gpit->first] = -step;
735 }
736}
737
739 std::set<int> groups;
740
741 // whether or not to bother checking for a mask
742 bool checkForMask = false;
743 Geometry::Instrument_const_sptr instrument = m_matrixInputW->getInstrument();
744 if (instrument != nullptr) {
745 checkForMask = ((instrument->getSource() != nullptr) && (instrument->getSample() != nullptr));
746 }
747 const auto &spectrumInfo = m_matrixInputW->spectrumInfo();
748
750 for (int wi = 0; wi < nHist; wi++) // Iterate over all histograms to find which groups are actually used
751 {
752 const int group = validateSpectrumInGroup(udet2group, static_cast<size_t>(wi));
754 if (group == -1)
755 continue;
756
757 if (checkForMask) {
758 if (spectrumInfo.isMasked(wi)) {
759 groupAtWorkspaceIndex[wi] = -1;
760 continue;
761 }
762 }
763 groups.insert(group);
764 }
765
766 nGroups = groups.size(); // Number of unique groups
767
768 const bool fullBinsOnly = getProperty("FullBinsOnly");
769
770 // only now can we check that the length of rebin parameters are correct
771 std::vector<double> xmins = getProperty("DMin");
772 std::vector<double> xmaxs = getProperty("DMax");
773 std::vector<double> deltas = getProperty("Delta");
774
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 " +
787
788 // resize vectors with only one value
789 if (numMin == 1)
790 xmins.resize(nGroups, xmins[0]);
791 if (numMax == 1)
792 xmaxs.resize(nGroups, xmaxs[0]);
793 if (numDelta == 1)
794 deltas.resize(nGroups, deltas[0]);
795
796 // Iterate over all groups to create the new X vectors
797 size_t i = 0;
798 for (auto group : groups) {
799 // NOTE must use a vector instead of a HistogramData::BinEdges here because the latter doesn't allow resizing
800 // BinEdges stores HistogramX data, and HistogramX is a type of FixedLengthVector
801 std::vector<double> xnew;
802 VectorHelper::createAxisFromRebinParams({xmins[i], deltas[i], xmaxs[i]}, xnew, true, fullBinsOnly);
803 group2xvector[group] = std::move(xnew); // Register this vector in the map
804 group2xstep[group] = deltas[i];
805 i++;
806 }
807}
808
813 m_groupWS = getProperty("GroupingWorkspace");
814
815 // Do we need to read the grouping workspace from a file?
816 if (!m_groupWS) {
817 const std::string groupingFileName = getProperty("GroupingFileName");
818 progress(0.01, "Reading grouping file");
819 auto childAlg = createChildAlgorithm("CreateGroupingWorkspace");
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");
824 }
825}
826
827/***
828 * Configure the mapping of output group to list of input workspace
829 * indices, and the list of valid group numbers.
830 *
831 * @return the total number of input histograms that will be read.
832 */
834 // set up the mapping of group to input workspace index
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++) {
839 // wi is the workspace index (of the input)
840 const int group = groupAtWorkspaceIndex[wi];
841 if (group < 1) // Not in a group, or invalid group #
842 continue;
843
844 // resize the ws_indices if it is not big enough
845 if (wsIndices.size() < static_cast<size_t>(group + 1)) {
846 wsIndices.resize(group + 1);
847 }
848
849 // Also record a list of workspace indices
850 wsIndices[group].emplace_back(wi);
851 }
852
853 // initialize a vector of the valid group numbers
854 size_t totalHistProcess = 0;
855 for (const auto &item : group2xvector) {
856 const auto group = item.first;
857 m_validGroups.emplace_back(group);
858 totalHistProcess += wsIndices[group].size(); // cppcheck-suppress containerOutOfBounds
859 }
860
861 std::transform(m_validGroups.cbegin(), m_validGroups.cend(), std::back_inserter(m_wsIndices),
862 [&wsIndices](const auto &group) { return wsIndices[static_cast<int>(group)]; });
863
864 return totalHistProcess;
865}
866
867} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:542
std::map< DeltaEMode::Type, std::string > index
double left
double right
#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.
Kernel::Logger & g_log
Definition Algorithm.h:423
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.
Definition Progress.h:25
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::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.
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.
A class for holding :
Definition EventList.h:58
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.
Definition Logger.cpp:145
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
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.
Definition IEventList.h:19
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 > &params, 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
Definition cow_ptr.h:172
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54