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 auto &Xout = group2xvector.at(group);
281
282 // Assign the new X axis only once (i.e when this group is encountered the
283 // first time)
284 int nPoints_local(nPoints);
285 if (!autoBinning) {
286 nPoints_local = static_cast<int>(Xout.size() - 1);
287 out->resizeHistogram(outWorkspaceIndex, nPoints_local);
288 }
289
290 out->setBinEdges(outWorkspaceIndex, Xout);
291
292 // This is the output spectrum
293 auto &outSpec = out->getSpectrum(outWorkspaceIndex);
294 outSpec.setSpectrumNo(group);
295
296 // Get the references to Y and E output and rebin
297 // TODO can only be changed once rebin implemented in HistogramData
298 auto &Yout = outSpec.dataY();
299 auto &Eout = outSpec.dataE();
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.rawData(), 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 &Yin = inSpec.y();
334 auto &Ein = inSpec.e();
335
336 try {
337 // TODO This should be implemented in Histogram as rebin
338 Mantid::Kernel::VectorHelper::rebinHistogram(Xin.rawData(), Yin.rawData(), Ein.rawData(), Xout.rawData(),
339 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.rawData(), 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.rawData(), 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 HistogramData::BinEdges xnew(0);
801 static_cast<void>(VectorHelper::createAxisFromRebinParams({xmins[i], deltas[i], xmaxs[i]}, xnew.mutableRawData(),
802 true, fullBinsOnly));
803 group2xvector[group] = 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:538
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:422
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(const MantidVec &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:18
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.
int 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.
void MANTID_KERNEL_DLL rebin(const std::vector< double > &xold, const std::vector< double > &yold, const std::vector< double > &eold, const std::vector< double > &xnew, std::vector< double > &ynew, std::vector< double > &enew, bool distribution, bool addition=false)
Rebins data according to a new output X array.
void MANTID_KERNEL_DLL rebinHistogram(const std::vector< double > &xold, const std::vector< double > &yold, const std::vector< double > &eold, const std::vector< double > &xnew, std::vector< double > &ynew, std::vector< double > &enew, bool addition)
Rebins histogram data according to a new output X array.
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