Mantid
Loading...
Searching...
No Matches
BinaryOperation.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 +
8#include "MantidAPI/Axis.h"
21#include "MantidHistogramData/Histogram.h"
22#include "MantidKernel/Timer.h"
23#include "MantidKernel/Unit.h"
24#include <memory>
25
26using namespace Mantid::Geometry;
27using namespace Mantid::API;
28using namespace Mantid::Kernel;
29using namespace Mantid::DataObjects;
30using namespace Mantid::HistogramData;
31using std::size_t;
32
33namespace Mantid::Algorithms {
40 "The name of the input workspace on the left hand side of the operation");
42 "The name of the input workspace on the right hand side of "
43 "the operation");
45 "The name to call the output workspace");
46 declareProperty(std::make_unique<PropertyWithValue<bool>>("AllowDifferentNumberSpectra", false, Direction::Input),
47 "Are workspaces with different number of spectra allowed? "
48 "For example, the LHSWorkspace might have one spectrum per detector, "
49 "but the RHSWorkspace could have its spectra averaged per bank. If true, "
50 "then matching between the LHS and RHS spectra is performed (all "
51 "detectors "
52 "in a LHS spectrum have to be in the corresponding RHS) in order to "
53 "apply the RHS spectrum to the LHS.");
54
55 declareProperty(std::make_unique<PropertyWithValue<bool>>("ClearRHSWorkspace", false, Direction::Input),
56 "For EventWorkspaces only. This will clear out event lists "
57 "from the RHS workspace as the binary operation is applied. "
58 "This can prevent excessive memory use, e.g. when subtracting "
59 "an EventWorkspace from another: memory use will be approximately "
60 "constant instead of increasing by 50%. At completion, the RHS workspace "
61 "will be empty.");
62}
63
69 // Is the LHS operand a single number?
70 WorkspaceSingleValue_const_sptr lhs_singleVal = std::dynamic_pointer_cast<const WorkspaceSingleValue>(m_lhs);
71 WorkspaceSingleValue_const_sptr rhs_singleVal = std::dynamic_pointer_cast<const WorkspaceSingleValue>(m_rhs);
72
73 if (lhs_singleVal) {
74 MatrixWorkspace_sptr out = getProperty("OutputWorkspace");
75 if (this->name() == "Divide" && !bool(rhs_singleVal)) {
76 // x / workspace = Power(workspace, -1) * x
77 // workspace ^ -1
78 auto pow = createChildAlgorithm("Power", 0.0, 0.5, true);
79 pow->setProperty("InputWorkspace", std::const_pointer_cast<MatrixWorkspace>(m_rhs));
80 pow->setProperty("Exponent", -1.0);
81 pow->setProperty("OutputWorkspace", out);
82 pow->executeAsChildAlg();
83 out = pow->getProperty("OutputWorkspace");
84
85 // Multiply by x
86 auto mult = createChildAlgorithm("Multiply", 0.5, 1.0, true);
87 mult->setProperty(inputPropName1(), out); //(workspace^-1)
88 mult->setProperty(inputPropName2(),
89 std::const_pointer_cast<MatrixWorkspace>(m_lhs)); // (1.0) or other number
90 mult->setProperty(outputPropName(), out);
91 mult->executeAsChildAlg();
92 out = mult->getProperty("OutputWorkspace");
93 setProperty("OutputWorkspace", out);
94 return true;
95 } else if (this->name() == "Minus") {
96 // x - workspace = x + (workspace * -1)
97 MatrixWorkspace_sptr minusOne = create<WorkspaceSingleValue>(1, Points(1));
98 minusOne->dataY(0)[0] = -1.0;
99 minusOne->dataE(0)[0] = 0.0;
100
101 // workspace * -1
102 auto mult = createChildAlgorithm("Multiply", 0.0, 0.5, true);
103 mult->setProperty(inputPropName1(), std::const_pointer_cast<MatrixWorkspace>(m_rhs));
104 mult->setProperty(inputPropName2(), minusOne);
105 mult->setProperty("OutputWorkspace", out);
106 mult->executeAsChildAlg();
107 out = mult->getProperty("OutputWorkspace");
108
109 // Multiply by x
110 auto plus = createChildAlgorithm("Plus", 0.5, 1.0, true);
111 plus->setProperty(inputPropName1(), out); //(workspace^-1)
112 plus->setProperty(inputPropName2(),
113 std::const_pointer_cast<MatrixWorkspace>(m_lhs)); // (1.0) or other number
114 plus->setProperty(outputPropName(), out);
115 plus->executeAsChildAlg();
116 out = plus->getProperty("OutputWorkspace");
117 setProperty("OutputWorkspace", out);
118 return true;
119 }
120
121 } // lhs_singleVal
122
123 // Process normally
124 return false;
125}
126
132 // get input workspace, dynamic cast not needed
135 m_AllowDifferentNumberSpectra = getProperty("AllowDifferentNumberSpectra");
136
137 try {
138 m_lhsBlocksize = m_lhs->blocksize();
139 m_lhsRagged = false;
140 } catch (std::length_error &) {
141 m_lhsBlocksize = 0;
142 m_lhsRagged = true;
143 }
144 try {
145 m_rhsBlocksize = m_rhs->blocksize();
146 m_rhsRagged = false;
147 } catch (std::length_error &) {
148 m_rhsBlocksize = 0;
149 m_rhsRagged = true;
150 }
151
152 // Special handling for 1-WS and 1/WS.
153 if (this->handleSpecialDivideMinus())
154 return;
155
156 // Cast to EventWorkspace pointers
157 m_elhs = std::dynamic_pointer_cast<const EventWorkspace>(m_lhs);
158 m_erhs = std::dynamic_pointer_cast<const EventWorkspace>(m_rhs);
159
160 // We can clear the RHS workspace if it is an event,
161 // and we are not doing mismatched spectra (in which case you might clear it
162 // too soon!)
163 // and lhs is not rhs.
164 // and out is not rhs.
165 m_ClearRHSWorkspace = getProperty("ClearRHSWorkspace");
167 if (m_AllowDifferentNumberSpectra || (!m_erhs) || (m_rhs == m_lhs) || (m_out == m_rhs)) {
168 // std::cout << "m_ClearRHSWorkspace = false\n";
169 m_ClearRHSWorkspace = false;
170 }
171 }
172
173 // Make a check of what will be needed to setup the workspaces, based on the
174 // input types.
175 this->checkRequirements();
176
177 if (m_flipSides) {
178 // Flip the workspaces left and right
179 std::swap(m_lhs, m_rhs);
180 std::swap(m_elhs, m_erhs);
181 std::swap(m_lhsBlocksize, m_rhsBlocksize);
182 std::swap(m_lhsRagged, m_rhsRagged);
183 }
184
185 // Check that the input workspaces are compatible
187 std::ostringstream ostr;
188 ostr << "The two workspaces are not compatible for algorithm " << this->name();
189 g_log.error() << ostr.str() << '\n';
190 throw std::invalid_argument(ostr.str());
191 }
192
193 // Get the output workspace
195 if (m_elhs) {
196 m_eout = std::dynamic_pointer_cast<EventWorkspace>(m_out);
197 }
198
199 // Is the output going to be an EventWorkspace?
201 // The output WILL be EventWorkspace (this implies lhs is EW or rhs is EW +
202 // it gets flipped)
203 if (!m_elhs)
204 throw std::runtime_error("BinaryOperation:: the output was set to be an "
205 "EventWorkspace (m_keepEventWorkspace == true), "
206 "but the lhs is not an EventWorkspace. There "
207 "must be a mistake in the algorithm. Contact "
208 "the developers.");
209
210 if (m_out == m_lhs) {
211 // Will be modifying the EventWorkspace in-place on the lhs. Good.
212 if (!m_eout)
213 throw std::runtime_error("BinaryOperation:: the output was set to be lhs, and to be an "
214 "EventWorkspace (m_keepEventWorkspace == true), but the output is "
215 "not an EventWorkspace. There must be a mistake in the algorithm. "
216 "Contact the developers.");
217 } else {
218 // You HAVE to copy the data from lhs to to the output!
219 m_out = m_lhs->clone();
220 // Make sure m_eout still points to the same as m_out;
221 m_eout = std::dynamic_pointer_cast<EventWorkspace>(m_out);
222 }
223
224 // Always clear the MRUs.
225 m_eout->clearMRU();
226 m_elhs->clearMRU();
227 if (m_erhs)
228 m_erhs->clearMRU();
229
230 } else {
231 // ---- Output will be WS2D -------
232
233 // We need to create a new workspace for the output if:
234 // (a) the output workspace hasn't been set to one of the input ones, or
235 // (b) it has been, but it's not the correct dimensions
236 if ((m_out != m_lhs && m_out != m_rhs) || (m_out == m_rhs && (m_lhs->size() > m_rhs->size()))) {
237 // if the input workspace are specialworkspace2d, then we need to ensure
238 // the map is set
239 auto specialLHS = dynamic_cast<const SpecialWorkspace2D *>(m_lhs.get());
240 auto specialRHS = dynamic_cast<const SpecialWorkspace2D *>(m_rhs.get());
241 if (specialLHS && specialRHS) {
242 m_out = create<SpecialWorkspace2D>(*specialLHS);
243 } else {
244 m_out = create<HistoWorkspace>(*m_lhs);
245 }
246 }
247 }
248
249 // only overridden for some operations (plus and minus at the time of writing)
250 operateOnRun(m_lhs->run(), m_rhs->run(), m_out->mutableRun());
251
252 // Initialise the progress reporting object
253 m_progress = std::make_unique<Progress>(this, 0.0, 1.0, m_lhs->getNumberHistograms());
254
255 // There are now 4 possible scenarios, shown schematically here:
256 // xxx x xxx xxx xxx xxx xxx x
257 // xxx , xxx xxx , xxx , xxx x
258 // xxx , xxx xxx xxx xxx x
259 // So work out which one we have and call the appropriate function
260
261 // Single value workspace on the right : if it is an EventWorkspace with 1
262 // spectrum, 1 bin, it is treated as a scalar
263 if ((m_rhs->size() == 1) && !m_do2D_even_for_SingleColumn_on_rhs) {
264 doSingleValue(); // m_lhs,m_rhs,m_out
265 } else if (m_rhs->getNumberHistograms() == 1) // Single spectrum on rhs
266 {
268 }
269 // Single column on rhs; if the RHS is an event workspace with one bin, it is
270 // treated as a scalar.
273 } else // The two are both 2D and should be the same size (except if LHS is an
274 // event workspace)
275 {
276 bool mismatchedSpectra =
277 (m_AllowDifferentNumberSpectra && (m_rhs->getNumberHistograms() != m_lhs->getNumberHistograms()));
278 do2D(mismatchedSpectra);
279 }
280
282
283 // Assign the result to the output workspace property
285}
286
294 UNUSED_ARG(lhs);
296 // This should never happen
297 throw Exception::NotImplementedError("BinaryOperation::execEvent() is not implemented for this operation.");
298}
299
309 Unit_const_sptr lhs_unit;
310 Unit_const_sptr rhs_unit;
311 if (lhs->axes() && rhs->axes()) // If one of these is a WorkspaceSingleValue
312 // then we don't want to check units match
313 {
314 lhs_unit = lhs->getAxis(0)->unit();
315 rhs_unit = rhs->getAxis(0)->unit();
316 }
317
318 const std::string lhs_unitID = (lhs_unit ? lhs_unit->unitID() : "");
319 const std::string rhs_unitID = (rhs_unit ? rhs_unit->unitID() : "");
320
321 // Check the workspaces have the same units and distribution flag
322 if (lhs_unitID != rhs_unitID && m_lhsBlocksize != 1 && m_rhsBlocksize != 1) {
323 g_log.error("The two workspace are not compatible because they have "
324 "different units on the X axis.");
325 return false;
326 }
327
328 // Check the size compatibility
329 const std::string checkSizeCompatibilityResult = checkSizeCompatibility(lhs, rhs);
330 if (!checkSizeCompatibilityResult.empty()) {
331 throw std::invalid_argument(checkSizeCompatibilityResult);
332 }
333
334 return true;
335}
336
350
363 const size_t lhsSize = lhs->size();
364 const size_t rhsSize = rhs->size();
365 // A SingleValueWorkspace on the right matches anything
366 if (rhsSize == 1)
367 return "";
368 // The lhs must not be smaller than the rhs
369 if (lhsSize < rhsSize)
370 return "Left hand side smaller than right hand side.";
371
372 // Did checkRequirements() tell us that the X histogram size did not matter?
373 if (!m_matchXSize) {
374 // If so, only the vertical # needs to match
375
376 if (lhs->getNumberHistograms() == rhs->getNumberHistograms()) {
377 return "";
378 } else {
379 return "Number of histograms not identical.";
380 }
381 }
382 // Otherwise they must match both ways, or horizontally or vertically with the
383 // other rhs dimension=1
384 if (m_rhsBlocksize == 1 && lhs->getNumberHistograms() == rhs->getNumberHistograms())
385 return "";
386 // Past this point, we require the X arrays to match. Note this only checks
387 // the first spectrum except for ragged workspaces
389 return "X arrays must match when performing this operation on a 2D "
390 "workspaces.";
391 }
392
393 const size_t rhsSpec = rhs->getNumberHistograms();
394
396 if (rhsSpec == 1 || lhs->getNumberHistograms() == rhsSpec) {
397 return "";
398 } else {
399 // can't be more specific as if this is reached both failed and only one
400 // or both are needed
401 return "Left and right sides should contain the same amount of spectra "
402 "or the right side should contain only one spectra.";
403 }
404 } else {
405 // blocksize check failed, but still check the number of spectra to see if
406 // that was wrong too
407 if (rhsSpec == 1 || lhs->getNumberHistograms() == rhsSpec) {
408 return "Number of y values not equal on left and right sides.";
409 } else {
410 // can't be more specific as if this is reached both failed and only one
411 // or both are needed
412 return "Number of y values not equal on left and right sides and the "
413 "right side contained neither only one spectra or the same amount "
414 "of spectra as the left.";
415 }
416 }
417}
418
431bool BinaryOperation::propagateSpectraMask(const SpectrumInfo &lhsSpectrumInfo, const SpectrumInfo &rhsSpectrumInfo,
432 const int64_t index, MatrixWorkspace &out, SpectrumInfo &outSpectrumInfo) {
433 bool continueOp(true);
434
435 if ((lhsSpectrumInfo.hasDetectors(index) && lhsSpectrumInfo.isMasked(index)) ||
436 (rhsSpectrumInfo.hasDetectors(index) && rhsSpectrumInfo.isMasked(index))) {
437 continueOp = false;
439 PARALLEL_CRITICAL(setMasked) { outSpectrumInfo.setMasked(index, true); }
440 }
441 return continueOp;
442}
443
450 // Don't propate masking from the rhs here - it would be decidedly odd if the
451 // single value was masked
452
453 // Pull out the single value and its error
454 const double rhsY = m_rhs->y(0)[0];
455 const double rhsE = m_rhs->e(0)[0];
456
457 // Now loop over the spectra of the left hand side calling the virtual
458 // function
459 const int64_t numHists = m_lhs->getNumberHistograms();
460
461 if (m_eout) {
462 // ---- The output is an EventWorkspace ------
464 for (int64_t i = 0; i < numHists; ++i) {
466 m_out->setSharedX(i, m_lhs->sharedX(i));
467 performEventBinaryOperation(m_eout->getSpectrum(i), rhsY, rhsE);
468 m_progress->report(this->name());
470 }
472 } else {
473 // ---- Histogram Output -----
475 for (int64_t i = 0; i < numHists; ++i) {
477 m_out->setSharedX(i, m_lhs->sharedX(i));
478 // Get reference to output vectors here to break any sharing outside the
479 // function call below
480 // where the order of argument evaluation is not guaranteed (if it's L->R
481 // there would be a data race)
482 HistogramData::HistogramY &outY = m_out->mutableY(i);
483 HistogramData::HistogramE &outE = m_out->mutableE(i);
484 performBinaryOperation(m_lhs->histogram(i), rhsY, rhsE, outY, outE);
485 m_progress->report(this->name());
487 }
489 }
490}
491
497 // Don't propate masking from the m_rhs here - it would be decidedly odd if
498 // the single bin was masked
499
500 // Now loop over the spectra of the left hand side pulling m_out the single
501 // value from each m_rhs 'spectrum'
502 // and then calling the virtual function
503 const int64_t numHists = m_lhs->getNumberHistograms();
504 auto &outSpectrumInfo = m_out->mutableSpectrumInfo();
505 auto &lhsSpectrumInfo = m_lhs->spectrumInfo();
506 auto &rhsSpectrumInfo = m_rhs->spectrumInfo();
507 if (m_eout) {
508 // ---- The output is an EventWorkspace ------
510 for (int64_t i = 0; i < numHists; ++i) {
512 const double rhsY = m_rhs->y(i)[0];
513 const double rhsE = m_rhs->e(i)[0];
514 if (propagateSpectraMask(lhsSpectrumInfo, rhsSpectrumInfo, i, *m_out, outSpectrumInfo)) {
515 performEventBinaryOperation(m_eout->getSpectrum(i), rhsY, rhsE);
516 }
517 m_progress->report(this->name());
519 }
521 } else {
522 // ---- Histogram Output -----
524 for (int64_t i = 0; i < numHists; ++i) {
526 const double rhsY = m_rhs->y(i)[0];
527 const double rhsE = m_rhs->e(i)[0];
528
529 m_out->setSharedX(i, m_lhs->sharedX(i));
530 if (propagateSpectraMask(lhsSpectrumInfo, rhsSpectrumInfo, i, *m_out, outSpectrumInfo)) {
531 // Get reference to output vectors here to break any sharing outside the
532 // function call below
533 // where the order of argument evaluation is not guaranteed (if it's
534 // L->R there would be a data race)
535 HistogramData::HistogramY &outY = m_out->mutableY(i);
536 HistogramData::HistogramE &outE = m_out->mutableE(i);
537 performBinaryOperation(m_lhs->histogram(i), rhsY, rhsE, outY, outE);
538 }
539 m_progress->report(this->name());
541 }
543 }
544}
545
550
551 // Propagate any masking first or it could mess up the numbers
552 // TODO: Check if this works for event workspaces...
554
555 if (m_eout) {
556 // ----------- The output is an EventWorkspace -------------
557
558 if (m_erhs) {
559 // -------- The rhs is ALSO an EventWorkspace --------
560
561 // Pull out the single eventList on the right
562 const EventList &rhs_spectrum = m_erhs->getSpectrum(0);
563
564 // Now loop over the spectra of the left hand side calling the virtual
565 // function
566 const int64_t numHists = m_lhs->getNumberHistograms();
568 for (int64_t i = 0; i < numHists; ++i) {
570 // Perform the operation on the event list on the output (== lhs)
571 performEventBinaryOperation(m_eout->getSpectrum(i), rhs_spectrum);
572 m_progress->report(this->name());
574 }
576 } else {
577 // -------- The rhs is a histogram ---------
578 // Pull m_out the m_rhs spectrum
579 const MantidVec &rhsX = m_rhs->readX(0);
580 const MantidVec &rhsY = m_rhs->readY(0);
581 const MantidVec &rhsE = m_rhs->readE(0);
582
583 // Now loop over the spectra of the left hand side calling the virtual
584 // function
585 const int64_t numHists = m_lhs->getNumberHistograms();
586
588 for (int64_t i = 0; i < numHists; ++i) {
590 // Perform the operation on the event list on the output (== lhs)
591 performEventBinaryOperation(m_eout->getSpectrum(i), rhsX, rhsY, rhsE);
592 m_progress->report(this->name());
594 }
596 }
597
598 } else {
599 // -------- The output is a histogram ----------
600 // (inputs can be EventWorkspaces, but their histogram representation
601 // will be used instead)
602
603 // Pull m_out the m_rhs spectrum
604 const auto rhs = m_rhs->histogram(0);
605
606 // Now loop over the spectra of the left hand side calling the virtual
607 // function
608 const int64_t numHists = m_lhs->getNumberHistograms();
609
611 for (int64_t i = 0; i < numHists; ++i) {
613 m_out->setSharedX(i, m_lhs->sharedX(i));
614 // Get reference to output vectors here to break any sharing outside the
615 // function call below
616 // where the order of argument evaluation is not guaranteed (if it's L->R
617 // there would be a data race)
618 HistogramData::HistogramY &outY = m_out->mutableY(i);
619 HistogramData::HistogramE &outE = m_out->mutableE(i);
620 performBinaryOperation(m_lhs->histogram(i), rhs, outY, outE);
621 m_progress->report(this->name());
623 }
625 }
626}
627
636void BinaryOperation::do2D(bool mismatchedSpectra) {
638 if (mismatchedSpectra) {
640 }
641
642 // Propagate any masking first or it could mess up the numbers
643 // TODO: Check if this works for event workspaces...
645
646 auto &outSpectrumInfo = m_out->mutableSpectrumInfo();
647 auto &lhsSpectrumInfo = m_lhs->spectrumInfo();
648 auto &rhsSpectrumInfo = m_rhs->spectrumInfo();
649
650 if (m_eout) {
651 // ----------- The output is an EventWorkspace -------------
652
654 // ------------ The rhs is ALSO an EventWorkspace ---------------
655 // Now loop over the spectra of each one calling the virtual function
656 const int64_t numHists = m_lhs->getNumberHistograms();
658 for (int64_t i = 0; i < numHists; ++i) {
660 m_progress->report(this->name());
661
662 int64_t rhs_wi = i;
663 if (mismatchedSpectra && table) {
664 rhs_wi = (*table)[i];
665 if (rhs_wi < 0)
666 continue;
667 } else {
668 // Check for masking except when mismatched sizes
669 if (!propagateSpectraMask(lhsSpectrumInfo, rhsSpectrumInfo, i, *m_out, outSpectrumInfo))
670 continue;
671 }
672 // Reach here? Do the division
673 // Perform the operation on the event list on the output (== lhs)
674 performEventBinaryOperation(m_eout->getSpectrum(i), m_erhs->getSpectrum(rhs_wi));
675
676 // Free up memory on the RHS if that is possible
678 const_cast<EventList &>(m_erhs->getSpectrum(rhs_wi)).clear();
680 }
682 } else {
683 // -------- The rhs is a histogram, or we want to use the histogram
684 // representation of it ---------
685
686 // Now loop over the spectra of each one calling the virtual function
687 const int64_t numHists = m_lhs->getNumberHistograms();
688
690 for (int64_t i = 0; i < numHists; ++i) {
692 m_progress->report(this->name());
693 int64_t rhs_wi = i;
694 if (mismatchedSpectra && table) {
695 rhs_wi = (*table)[i];
696 if (rhs_wi < 0)
697 continue;
698 } else {
699 // Check for masking except when mismatched sizes
700 if (!propagateSpectraMask(lhsSpectrumInfo, rhsSpectrumInfo, i, *m_out, outSpectrumInfo))
701 continue;
702 }
703
704 // Reach here? Do the division
705 performEventBinaryOperation(m_eout->getSpectrum(i), m_rhs->readX(rhs_wi), m_rhs->readY(rhs_wi),
706 m_rhs->readE(rhs_wi));
707
708 // Free up memory on the RHS if that is possible
710 const_cast<EventList &>(m_erhs->getSpectrum(rhs_wi)).clear();
711
713 }
715 }
716
717 } else {
718 // -------- The output is a histogram ----------
719 // (inputs can be EventWorkspaces, but their histogram representation
720 // will be used instead)
721
722 // Now loop over the spectra of each one calling the virtual function
723 const int64_t numHists = m_lhs->getNumberHistograms();
724
726 for (int64_t i = 0; i < numHists; ++i) {
728 m_progress->report(this->name());
729 m_out->setSharedX(i, m_lhs->sharedX(i));
730 int64_t rhs_wi = i;
731 if (mismatchedSpectra && table) {
732 rhs_wi = (*table)[i];
733 if (rhs_wi < 0)
734 continue;
735 } else {
736 // Check for masking except when mismatched sizes
737 if (!propagateSpectraMask(lhsSpectrumInfo, rhsSpectrumInfo, i, *m_out, outSpectrumInfo))
738 continue;
739 }
740 // Reach here? Do the division
741 // Get reference to output vectors here to break any sharing outside the
742 // function call below
743 // where the order of argument evaluation is not guaranteed (if it's L->R
744 // there would be a data race)
745 HistogramData::HistogramY &outY = m_out->mutableY(i);
746 HistogramData::HistogramE &outE = m_out->mutableE(i);
747 performBinaryOperation(m_lhs->histogram(i), m_rhs->histogram(rhs_wi), outY, outE);
748
749 // Free up memory on the RHS if that is possible
751 const_cast<EventList &>(m_erhs->getSpectrum(rhs_wi)).clear();
752
754 }
756 }
757 // Make sure we don't use outdated MRU
759 m_erhs->clearMRU();
760}
761
769 const API::MatrixWorkspace_sptr &out) {
770 const int64_t outHists = out->getNumberHistograms();
771 const int64_t rhsHists = rhs->getNumberHistograms();
772 for (int64_t i = 0; i < outHists; ++i) {
773 // Copy over masks from the rhs, if any exist.
774 // If rhs is single spectrum, copy masks from that to all spectra in the
775 // output.
776 if (rhs->hasMaskedBins((rhsHists == 1) ? 0 : i)) {
777 const MatrixWorkspace::MaskList &masks = rhs->maskedBins((rhsHists == 1) ? 0 : i);
778 MatrixWorkspace::MaskList::const_iterator it;
779 for (it = masks.begin(); it != masks.end(); ++it) {
780 out->flagMasked(i, it->first, it->second);
781 }
782 }
783 }
784}
785
786// ------- Default implementations of Event binary operations --------
787
797 UNUSED_ARG(lhs);
799 throw Exception::NotImplementedError("BinaryOperation::performEventBinaryOperation() not implemented.");
800}
801
812 const MantidVec &rhsY, const MantidVec &rhsE) {
813 UNUSED_ARG(lhs);
814 UNUSED_ARG(rhsX);
815 UNUSED_ARG(rhsY);
816 UNUSED_ARG(rhsE);
817 throw Exception::NotImplementedError("BinaryOperation::performEventBinaryOperation() not implemented.");
818}
819
828void BinaryOperation::performEventBinaryOperation(DataObjects::EventList &lhs, const double &rhsY, const double &rhsE) {
829 UNUSED_ARG(lhs);
830 UNUSED_ARG(rhsY);
831 UNUSED_ARG(rhsE);
832 throw Exception::NotImplementedError("BinaryOperation::performEventBinaryOperation() not implemented.");
833}
834
841 // An event workspace?
842 EventWorkspace_const_sptr ews = std::dynamic_pointer_cast<const EventWorkspace>(ws);
843 if (ews)
844 return eEventList;
845
846 // If the workspace has no axes, then it is a WorkspaceSingleValue
847 if (!ws->axes())
848 return eNumber;
849
850 // TODO: Check if it is a single-colum one, then
851 // return Number;
852
853 // Otherwise, we take it as a histogram (workspace 2D)
854 return eHistogram;
855}
856
864
865 // In general, the X sizes have to match.
866 // (only for some EventWorkspace cases does it NOT matter...)
867 m_matchXSize = true;
868
869 // Operations are not always commutative! Don't flip sides.
870 m_flipSides = false;
871
872 // And in general, EventWorkspaces get turned to Workspace2D
873 m_keepEventWorkspace = false;
874
875 // This will be set to true for Divide/Multiply
877}
878
893 // An addition table is a list of pairs:
894 // First int = workspace index in the EW being added
895 // Second int = workspace index to which it will be added in the OUTPUT EW.
896 // -1 if it should add a new entry at the end.
897 auto table = std::make_shared<BinaryOperationTable>();
898
899 auto rhs_nhist = static_cast<int>(rhs->getNumberHistograms());
900 auto lhs_nhist = static_cast<int>(lhs->getNumberHistograms());
901
902 // Initialize the table; filled with -1 meaning no match
903 table->resize(lhs_nhist, -1);
904
905 const detid2index_map rhs_det_to_wi = rhs->getDetectorIDToWorkspaceIndexMap();
906
908 for (int lhsWI = 0; lhsWI < lhs_nhist; lhsWI++) {
909 bool done = false;
910
911 // List of detectors on lhs side
912 const auto &lhsDets = lhs->getSpectrum(lhsWI).getDetectorIDs();
913
914 // ----------------- Matching Workspace Indices and Detector IDs
915 // --------------------------------------
916 // First off, try to match the workspace indices. Most times, this will be
917 // ok right away.
918 int64_t rhsWI = lhsWI;
919 if (rhsWI < rhs_nhist) // don't go out of bounds
920 {
921 // Get the detector IDs at that workspace index.
922 const auto &rhsDets = rhs->getSpectrum(rhsWI).getDetectorIDs();
923
924 // Checks that lhsDets is a subset of rhsDets
925 if (std::includes(rhsDets.begin(), rhsDets.end(), lhsDets.begin(), lhsDets.end())) {
926 // We found the workspace index right away. No need to keep looking
927 (*table)[lhsWI] = rhsWI;
928 done = true;
929 }
930 }
931
932 // ----------------- Scrambled Detector IDs with one Detector per Spectrum
933 // --------------------------------------
934 if (!done && (lhsDets.size() == 1)) {
935 // Didn't find it. Try to use the RHS map.
936
937 // First, we have to get the (single) detector ID of the LHS
938 auto lhsDets_it = lhsDets.cbegin();
939 detid_t lhs_detector_ID = *lhsDets_it;
940
941 // Now we use the RHS map to find it. This only works if both the lhs and
942 // rhs have 1 detector per pixel
943 auto map_it = rhs_det_to_wi.find(lhs_detector_ID);
944 if (map_it != rhs_det_to_wi.end()) {
945 rhsWI = map_it->second; // This is the workspace index in the RHS that
946 // matched lhs_detector_ID
947 } else {
948 // Did not find it!
949 rhsWI = -1; // Marker to mean its not in the LHS.
950
951 // std::ostringstream mess;
952 // mess << "BinaryOperation: cannot find a RHS spectrum that
953 // contains the detectors in LHS workspace index " << lhsWI
954 // << "\n";
955 // throw std::runtime_error(mess.str());
956 }
957 (*table)[lhsWI] = rhsWI;
958 done = true; // Great, we did it.
959 }
960
961 // ----------------- LHS detectors are subset of RHS, which are Grouped
962 // --------------------------------------
963 if (!done) {
964
965 // Didn't find it? Now we need to iterate through the output workspace to
966 // match the detector ID.
967 // NOTE: This can be SUPER SLOW!
968 for (rhsWI = 0; rhsWI < static_cast<int64_t>(rhs_nhist); rhsWI++) {
969 const auto &rhsDets = rhs->getSpectrum(rhsWI).getDetectorIDs();
970
971 // Checks that lhsDets is a subset of rhsDets
972 if (std::includes(rhsDets.begin(), rhsDets.end(), lhsDets.begin(), lhsDets.end())) {
973 // This one is right. Now we can stop looking.
974 (*table)[lhsWI] = rhsWI;
975 done = true;
976 continue;
977 }
978 }
979 }
980
981 // ------- Still nothing ! -----------
982 if (!done) {
983 (*table)[lhsWI] = -1;
984
985 // std::ostringstream mess;
986 // mess << "BinaryOperation: cannot find a RHS spectrum that
987 // contains the detectors in LHS workspace index " << lhsWI <<
988 // "\n";
989 // throw std::runtime_error(mess.str());
990 }
991 }
992
993 return table;
994}
995} // namespace Mantid::Algorithms
const std::vector< double > & rhs
std::map< DeltaEMode::Type, std::string > index
#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_FOR_NO_WSP_CHECK()
#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 PARALLEL_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
#define UNUSED_ARG(x)
Function arguments are sometimes unused in certain implmentations but are required for documentation ...
Definition System.h:48
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
void clear() override
Clears all properties under management.
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
const std::string name() const override=0
function to return a name of the algorithm, must be overridden in all algorithms
virtual void clearData()=0
void setSharedX(const Kernel::cow_ptr< HistogramData::HistogramX > &x) &
Definition ISpectrum.h:189
Base MatrixWorkspace Abstract Class.
virtual ISpectrum & getSpectrum(const size_t index)=0
Return the underlying ISpectrum ptr at the given workspace index.
std::map< size_t, double > MaskList
Masked bins for each spectrum are stored as a set of pairs containing <bin index, weight>
API::SpectrumInfo is an intermediate step towards a SpectrumInfo that is part of Instrument-2....
void setMasked(const size_t index, bool masked)
Set the mask flag of the spectrum with given index.
bool hasDetectors(const size_t index) const
Returns true if the spectrum is associated with detectors in the instrument.
bool isMasked(const size_t index) const
Returns true if the detector(s) associated with the spectrum are masked.
A property class for workspaces.
bool m_do2D_even_for_SingleColumn_on_rhs
Special case for plus/minus: if there is only one bin on the RHS, use the 2D method (appending event ...
bool m_lhsRagged
Cache for if LHS workspace's is ragged.
void doSingleSpectrum()
Called when the m_rhs operand is a single spectrum.
virtual void operateOnRun(const API::Run &lhs, const API::Run &rhs, API::Run &ans) const
Only overridden by operations that affect the properties of the run (e.g.
size_t m_rhsBlocksize
Cache for RHS workspace's blocksize.
virtual bool propagateSpectraMask(const API::SpectrumInfo &lhsSpectrumInfo, const API::SpectrumInfo &rhsSpectrumInfo, const int64_t index, API::MatrixWorkspace &out, API::SpectrumInfo &outSpectrumInfo)
Checks if the spectra at the given index of either input workspace is masked.
void doSingleValue()
Called when the rhs operand is a single value.
void propagateBinMasks(const API::MatrixWorkspace_const_sptr &rhs, const API::MatrixWorkspace_sptr &out)
Copies any bin masking from the smaller/rhs input workspace to the output.
virtual void setOutputUnits(const API::MatrixWorkspace_const_sptr lhs, const API::MatrixWorkspace_const_sptr rhs, API::MatrixWorkspace_sptr out)
Should be overridden by operations that need to manipulate the units of the output workspace.
static BinaryOperationTable_sptr buildBinaryOperationTable(const API::MatrixWorkspace_const_sptr &lhs, const API::MatrixWorkspace_const_sptr &rhs)
Build up an BinaryOperationTable for performing a binary operation e.g.
bool m_keepEventWorkspace
Variable set to true if the operation allows the output to stay as an EventWorkspace.
std::shared_ptr< BinaryOperationTable > BinaryOperationTable_sptr
virtual bool checkCompatibility(const API::MatrixWorkspace_const_sptr lhs, const API::MatrixWorkspace_const_sptr rhs) const
Checks the compatibility of the two workspaces.
DataObjects::EventWorkspace_sptr m_eout
Output EventWorkspace.
virtual std::string checkSizeCompatibility(const API::MatrixWorkspace_const_sptr lhs, const API::MatrixWorkspace_const_sptr rhs) const
Checks the overall size compatibility of two workspaces.
void init() override
Initialisation method.
void doSingleColumn()
Called when the m_rhs operand is a 2D workspace of single values.
API::MatrixWorkspace_sptr m_out
Output workspace.
virtual void execEvent(DataObjects::EventWorkspace_const_sptr lhs, DataObjects::EventWorkspace_const_sptr rhs)
Execution method for event workspaces, to be overridden as needed.
bool handleSpecialDivideMinus()
Special handling for 1-WS and 1/WS.
OperandType getOperandType(const API::MatrixWorkspace_const_sptr &ws)
Get the type of operand from a workspace.
bool m_rhsRagged
Cache for if RHS workspace's is ragged.
bool m_AllowDifferentNumberSpectra
The property value.
bool m_useHistogramForRhsEventWorkspace
Are we going to use the histogram representation of the RHS event list when performing the operation?...
bool m_matchXSize
matchXSize set to true if the X sizes of histograms must match.
bool m_flipSides
flipSides set to true if the rhs and lhs operands should be flipped - for commutative binary operatio...
virtual std::string outputPropName() const
The name of the output workspace property.
DataObjects::EventWorkspace_const_sptr m_erhs
Right-hand side EventWorkspace.
virtual bool checkEventCompatibility(const API::MatrixWorkspace_const_sptr lhs, const API::MatrixWorkspace_const_sptr rhs)
Checks the compatibility of event-based processing of the two workspaces.
std::unique_ptr< API::Progress > m_progress
Progress reporting.
virtual void performEventBinaryOperation(DataObjects::EventList &lhs, const DataObjects::EventList &rhs)
Carries out the binary operation IN-PLACE on a single EventList, with another EventList as the right-...
virtual void performBinaryOperation(const HistogramData::Histogram &lhs, const HistogramData::Histogram &rhs, HistogramData::HistogramY &YOut, HistogramData::HistogramE &EOut)=0
Carries out the binary operation on a single spectrum, with another spectrum as the right-hand operan...
void do2D(bool mismatchedSpectra)
Called when the two workspaces are the same size.
size_t m_lhsBlocksize
Cache for LHS workspace's blocksize.
void exec() override
Executes the algorithm.
virtual std::string inputPropName1() const
The name of the first input workspace property.
DataObjects::EventWorkspace_const_sptr m_elhs
Left-hand side EventWorkspace.
API::MatrixWorkspace_const_sptr m_rhs
Right-hand side workspace.
bool m_ClearRHSWorkspace
Flag to clear RHS workspace in binary operation.
virtual void checkRequirements()
Check what operation will be needed in order to apply the operation to these two types of workspaces.
virtual std::string inputPropName2() const
The name of the second input workspace property.
API::MatrixWorkspace_const_sptr m_lhs
Left-hand side workspace.
A class for holding :
Definition EventList.h:57
Marks code as not implemented yet.
Definition Exception.h:138
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
The concrete, templated class for properties.
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::shared_ptr< const EventWorkspace > EventWorkspace_const_sptr
shared pointer to a const Workspace2D
std::shared_ptr< const WorkspaceSingleValue > WorkspaceSingleValue_const_sptr
std::shared_ptr< const Unit > Unit_const_sptr
Shared pointer to the Unit base class (const version)
Definition Unit.h:196
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.
int32_t detid_t
Typedef for a detector ID.
std::unordered_map< detid_t, size_t > detid2index_map
Map with key = detector ID, value = workspace index.
std::vector< double > MantidVec
typedef for the data storage used in Mantid matrix workspaces
Definition cow_ptr.h:172
static bool matchingBins(const std::shared_ptr< const MatrixWorkspace > &ws1, const std::shared_ptr< const MatrixWorkspace > &ws2, const bool firstOnly=false)
Checks whether the bins (X values) of two workspace are the same.
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54