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