Skip to content

Commit 78d0301

Browse files
authored
Merge pull request #37793 from rosswhitfield/ragged_binary_operations
Add ragged workspace support for binary operations
2 parents 819eca1 + 412fd73 commit 78d0301

File tree

12 files changed

+223
-19
lines changed

12 files changed

+223
-19
lines changed

Framework/API/inc/MantidAPI/ISpectrum.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,9 @@ class MANTID_API_DLL ISpectrum {
198198
mutableHistogramRef().setSharedE(e);
199199
}
200200

201+
void resize(size_t n) { mutableHistogramRef().resize(n); }
202+
size_t size() const { return histogramRef().size(); }
203+
201204
void setMatrixWorkspace(MatrixWorkspace *matrixWorkspace, const size_t index);
202205

203206
virtual void copyDataInto(DataObjects::EventList &) const;

Framework/API/inc/MantidAPI/MatrixWorkspace.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,8 @@ class MANTID_API_DLL MatrixWorkspace : public IMDWorkspace, public ExperimentInf
233233
void setSharedE(const size_t index, const Kernel::cow_ptr<HistogramData::HistogramE> &e) & {
234234
getSpectrumWithoutInvalidation(index).setSharedE(e);
235235
}
236+
void resizeHistogram(const size_t index, size_t n) & { getSpectrum(index).resize(n); }
237+
size_t histogramSize(const size_t index) const { return getSpectrum(index).size(); }
236238
// Methods for getting read-only access to the data.
237239
// Just passes through to the virtual dataX/Y/E function (const version)
238240
/// Deprecated, use x() instead. Returns a read-only (i.e. const) reference to

Framework/Algorithms/inc/MantidAlgorithms/BinaryOperation.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,10 @@ class MANTID_ALGORITHMS_DLL BinaryOperation : public API::Algorithm {
190190
/// Cache for RHS workspace's blocksize
191191
size_t m_rhsBlocksize;
192192

193+
/// Cache for if LHS workspace's is ragged
194+
bool m_lhsRagged{false};
195+
/// Cache for if RHS workspace's is ragged
196+
bool m_rhsRagged{false};
193197
//------ Requirements -----------
194198

195199
/// matchXSize set to true if the X sizes of histograms must match.

Framework/Algorithms/src/BinaryOperation.cpp

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,20 @@ void BinaryOperation::exec() {
134134
m_rhs = getProperty(inputPropName2());
135135
m_AllowDifferentNumberSpectra = getProperty("AllowDifferentNumberSpectra");
136136

137-
m_lhsBlocksize = m_lhs->blocksize();
138-
m_rhsBlocksize = m_rhs->blocksize();
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+
}
139151

140152
// Special handling for 1-WS and 1/WS.
141153
if (this->handleSpecialDivideMinus())
@@ -167,6 +179,7 @@ void BinaryOperation::exec() {
167179
std::swap(m_lhs, m_rhs);
168180
std::swap(m_elhs, m_erhs);
169181
std::swap(m_lhsBlocksize, m_rhsBlocksize);
182+
std::swap(m_lhsRagged, m_rhsRagged);
170183
}
171184

172185
// Check that the input workspaces are compatible
@@ -210,8 +223,7 @@ void BinaryOperation::exec() {
210223

211224
// Always clear the MRUs.
212225
m_eout->clearMRU();
213-
if (m_elhs)
214-
m_elhs->clearMRU();
226+
m_elhs->clearMRU();
215227
if (m_erhs)
216228
m_erhs->clearMRU();
217229

@@ -307,7 +319,7 @@ bool BinaryOperation::checkCompatibility(const API::MatrixWorkspace_const_sptr l
307319
const std::string rhs_unitID = (rhs_unit ? rhs_unit->unitID() : "");
308320

309321
// Check the workspaces have the same units and distribution flag
310-
if (lhs_unitID != rhs_unitID && m_lhsBlocksize > 1 && m_rhsBlocksize > 1) {
322+
if (lhs_unitID != rhs_unitID && m_lhsBlocksize != 1 && m_rhsBlocksize != 1) {
311323
g_log.error("The two workspace are not compatible because they have "
312324
"different units on the X axis.");
313325
return false;
@@ -372,8 +384,8 @@ std::string BinaryOperation::checkSizeCompatibility(const API::MatrixWorkspace_c
372384
if (m_rhsBlocksize == 1 && lhs->getNumberHistograms() == rhs->getNumberHistograms())
373385
return "";
374386
// Past this point, we require the X arrays to match. Note this only checks
375-
// the first spectrum
376-
if (!WorkspaceHelpers::matchingBins(*lhs, *rhs, true)) {
387+
// the first spectrum except for ragged workspaces
388+
if (!WorkspaceHelpers::matchingBins(*lhs, *rhs, !m_lhsRagged && !m_rhsRagged)) {
377389
return "X arrays must match when performing this operation on a 2D "
378390
"workspaces.";
379391
}

Framework/Algorithms/src/Divide.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,8 @@ std::string Divide::checkSizeCompatibility(const API::MatrixWorkspace_const_sptr
200200

201201
if (m_matchXSize) {
202202
// Past this point, for a 2D WS operation, we require the X arrays to match.
203-
// Note this only checks the first spectrum
204-
if (!WorkspaceHelpers::matchingBins(*lhs, *rhs, true)) {
203+
// Note this only checks the first spectrum except for ragged workspaces
204+
if (!WorkspaceHelpers::matchingBins(*lhs, *rhs, !m_lhsRagged && !m_rhsRagged)) {
205205
return "X arrays must match when dividing 2D workspaces.";
206206
}
207207
}

Framework/Algorithms/src/Multiply.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,8 @@ std::string Multiply::checkSizeCompatibility(const API::MatrixWorkspace_const_sp
170170

171171
if (m_matchXSize) {
172172
// Past this point, for a 2D WS operation, we require the X arrays to
173-
// match. Note this only checks the first spectrum
174-
if (!WorkspaceHelpers::matchingBins(*lhs, *rhs, true)) {
173+
// match. Note this only checks the first spectrum except for ragged workspaces
174+
if (!WorkspaceHelpers::matchingBins(*lhs, *rhs, !m_lhsRagged && !m_rhsRagged)) {
175175
return "X arrays must match when multiplying 2D workspaces.";
176176
}
177177
}

Framework/Algorithms/test/MultiplyDivideTest.in.h

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "MantidFrameworkTestHelpers/WorkspaceCreationHelper.h"
1515
#include "MantidAlgorithms/Divide.h"
1616
#include "MantidAlgorithms/Multiply.h"
17+
#include "MantidHistogramData/HistogramBuilder.h"
1718
#include "MantidAPI/AnalysisDataService.h"
1819
#include "MantidDataObjects/Workspace2D.h"
1920
#include "MantidAPI/SpectrumInfo.h"
@@ -698,7 +699,12 @@ class @MULTIPLYDIVIDETEST_CLASS@ : public CxxTest::TestSuite
698699
mess << "Event";
699700
else
700701
mess << "2D";
701-
mess << "(" << ws->getNumberHistograms() << " spectra," << ws->blocksize() << " bins,";
702+
mess << "(" << ws->getNumberHistograms() << " spectra, ";
703+
if (ws->isRaggedWorkspace())
704+
mess << "Ragged";
705+
else
706+
mess << ws->blocksize();
707+
mess << " bins,";
702708
mess << "Y[0][0] = " << ws->y(0)[0] << ")";
703709
return mess.str();
704710
}
@@ -838,7 +844,11 @@ class @MULTIPLYDIVIDETEST_CLASS@ : public CxxTest::TestSuite
838844
int loopOrientation, double expectedValue=-1.0, double expectedError=-1.0)
839845
{
840846
TSM_ASSERT_LESS_THAN( message, 0, work_out1->getNumberHistograms());
841-
TSM_ASSERT_LESS_THAN( message, 0, work_out1->blocksize());
847+
if (work_out1->isRaggedWorkspace()){
848+
TSM_ASSERT_LESS_THAN( message, 0, work_out1->y(0).size());
849+
} else {
850+
TSM_ASSERT_LESS_THAN( message, 0, work_out1->blocksize());
851+
}
842852
TSM_ASSERT_EQUALS( message, work_in1->getNumberHistograms(), work_out1->getNumberHistograms());
843853

844854
if (expectedValue == -1.0 && expectedError == -1.0)
@@ -1000,6 +1010,65 @@ class @MULTIPLYDIVIDETEST_CLASS@ : public CxxTest::TestSuite
10001010
if( !replaceInput ) AnalysisDataService::Instance().remove(outputSpace);
10011011
}
10021012

1013+
MatrixWorkspace_sptr create_RaggedWorkspace()
1014+
{
1015+
// create workspace with 2 histograms
1016+
MatrixWorkspace_sptr raggedWS = WorkspaceCreationHelper::create2DWorkspace(2, 1);
1017+
1018+
// create and replace histograms with ragged ones
1019+
Mantid::MantidVec x_data{100., 200., 300., 400.};
1020+
Mantid::MantidVec y_data{2., 2., 2.};
1021+
Mantid::MantidVec e_data{2., 2., 2.};
1022+
Mantid::HistogramData::HistogramBuilder builder;
1023+
builder.setX(x_data);
1024+
builder.setY(y_data);
1025+
builder.setE(e_data);
1026+
raggedWS->setHistogram(0, builder.build());
1027+
1028+
Mantid::MantidVec x_data2{200., 400., 600.};
1029+
Mantid::MantidVec y_data2{2., 2.};
1030+
Mantid::MantidVec e_data2{2., 2.};
1031+
Mantid::HistogramData::HistogramBuilder builder2;
1032+
builder2.setX(x_data2);
1033+
builder2.setY(y_data2);
1034+
builder2.setE(e_data2);
1035+
raggedWS->setHistogram(1, builder2.build());
1036+
1037+
// quick check of the workspace
1038+
TS_ASSERT(raggedWS->isRaggedWorkspace());
1039+
TS_ASSERT_EQUALS(raggedWS->getNumberHistograms(), 2);
1040+
TS_ASSERT_EQUALS(raggedWS->x(0).size(), 4);
1041+
TS_ASSERT_EQUALS(raggedWS->x(1).size(), 3);
1042+
TS_ASSERT_EQUALS(raggedWS->y(0).size(), 3);
1043+
TS_ASSERT_EQUALS(raggedWS->y(1).size(), 2);
1044+
return raggedWS;
1045+
}
1046+
1047+
void test_RaggedWorkspace()
1048+
{
1049+
auto lhs = create_RaggedWorkspace();
1050+
auto rhs = create_RaggedWorkspace();
1051+
auto result = performTest(lhs, rhs, false, DO_DIVIDE ? 1.0 : 4.0, DO_DIVIDE ? 1.4142135625 : 5.6568542436);
1052+
TS_ASSERT(result->isRaggedWorkspace());
1053+
}
1054+
1055+
void test_RaggedWorkspace_and_single_value()
1056+
{
1057+
auto lhs = create_RaggedWorkspace();
1058+
auto rhs = WorkspaceCreationHelper::createWorkspaceSingleValue(2);
1059+
auto result = performTest(lhs, rhs, false, DO_DIVIDE ? 1.0 : 4.0, DO_DIVIDE ? 1.2247448711 : 4.8989794899);
1060+
TS_ASSERT(result->isRaggedWorkspace());
1061+
}
1062+
1063+
void test_RaggedWorkspace_not_compatible_x()
1064+
{
1065+
auto lhs = create_RaggedWorkspace();
1066+
auto rhs = WorkspaceCreationHelper::create2DWorkspace(2, 4);
1067+
performTest_fails(lhs, rhs);
1068+
}
1069+
1070+
1071+
10031072
};
10041073

10051074
//============================================================================

Framework/Algorithms/test/PlusMinusTest.in.h

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "MantidAlgorithms/Minus.h"
1414
#include "MantidAlgorithms/Plus.h"
1515
#include "MantidAlgorithms/Rebin.h"
16+
#include "MantidHistogramData/HistogramBuilder.h"
1617
#include "MantidAPI/AnalysisDataService.h"
1718
#include "MantidDataObjects/Workspace2D.h"
1819
#include "MantidAPI/WorkspaceProperty.h"
@@ -597,8 +598,12 @@ class @PLUSMINUSTEST_CLASS@ : public CxxTest::TestSuite
597598
mess << "Event";
598599
else
599600
mess << "2D";
600-
mess << "(" << ws->getNumberHistograms() << " spectra," << ws->blocksize() << " bins,";
601-
mess << "Y[0][0] = " << ws->y(0)[0] << ")";
601+
mess << "(" << ws->getNumberHistograms() << " spectra, ";
602+
if (ws->isRaggedWorkspace())
603+
mess << "Ragged";
604+
else
605+
mess << ws->blocksize();
606+
mess << " bins," << "Y[0][0] = " << ws->y(0)[0] << ")";
602607
return mess.str();
603608
}
604609

@@ -784,11 +789,19 @@ class @PLUSMINUSTEST_CLASS@ : public CxxTest::TestSuite
784789
int loopOrientation, double expectedValue=-1.0, double expectedError=-1.0 )
785790
{
786791
TSM_ASSERT_LESS_THAN( message, 0, work_out1->getNumberHistograms());
787-
TSM_ASSERT_LESS_THAN( message, 0, work_out1->blocksize());
792+
if (work_out1->isRaggedWorkspace()) {
793+
TSM_ASSERT_LESS_THAN( message, 0, work_out1->y(0).size());
794+
} else {
795+
TSM_ASSERT_LESS_THAN( message, 0, work_out1->blocksize());
796+
}
788797
TSM_ASSERT_EQUALS( message, work_in1->getNumberHistograms(), work_out1->getNumberHistograms());
789798
// Number of histograms/bins is unchanged (relative to LHS argument)
790799
TSM_ASSERT_EQUALS( message, work_out1->getNumberHistograms(), work_in1->getNumberHistograms());
791-
TSM_ASSERT_EQUALS( message, work_out1->blocksize(), work_in1->blocksize());
800+
if (work_out1->isRaggedWorkspace()) {
801+
TSM_ASSERT_EQUALS( message, work_out1->y(0).size(), work_in1->y(0).size());
802+
} else {
803+
TSM_ASSERT_EQUALS( message, work_out1->blocksize(), work_in1->blocksize());
804+
}
792805

793806
if (expectedValue == -1.0 && expectedError == -1.0)
794807
{
@@ -1081,6 +1094,62 @@ class @PLUSMINUSTEST_CLASS@ : public CxxTest::TestSuite
10811094
}
10821095

10831096

1097+
MatrixWorkspace_sptr create_RaggedWorkspace()
1098+
{
1099+
// create workspace with 2 histograms
1100+
MatrixWorkspace_sptr raggedWS = WorkspaceCreationHelper::create2DWorkspace(2, 1);
1101+
1102+
// create and replace histograms with ragged ones
1103+
MantidVec x_data{100., 200., 300., 400.};
1104+
MantidVec y_data{1., 1., 1.};
1105+
MantidVec e_data{1., 1., 1.};
1106+
Mantid::HistogramData::HistogramBuilder builder;
1107+
builder.setX(x_data);
1108+
builder.setY(y_data);
1109+
builder.setE(e_data);
1110+
raggedWS->setHistogram(0, builder.build());
1111+
1112+
MantidVec x_data2{200., 400., 600.};
1113+
MantidVec y_data2{1., 1.};
1114+
MantidVec e_data2{1., 1.};
1115+
Mantid::HistogramData::HistogramBuilder builder2;
1116+
builder2.setX(x_data2);
1117+
builder2.setY(y_data2);
1118+
builder2.setE(e_data2);
1119+
raggedWS->setHistogram(1, builder2.build());
1120+
1121+
// quick check of the workspace
1122+
TS_ASSERT(raggedWS->isRaggedWorkspace());
1123+
TS_ASSERT_EQUALS(raggedWS->getNumberHistograms(), 2);
1124+
TS_ASSERT_EQUALS(raggedWS->x(0).size(), 4);
1125+
TS_ASSERT_EQUALS(raggedWS->x(1).size(), 3);
1126+
TS_ASSERT_EQUALS(raggedWS->y(0).size(), 3);
1127+
TS_ASSERT_EQUALS(raggedWS->y(1).size(), 2);
1128+
return raggedWS;
1129+
}
1130+
1131+
void test_RaggedWorkspace()
1132+
{
1133+
auto lhs = create_RaggedWorkspace();
1134+
auto rhs = create_RaggedWorkspace();
1135+
auto result = performTest(lhs, rhs, false, false, DO_PLUS ? 2.0 : 0.0, 1.4142135625);
1136+
TS_ASSERT(result->isRaggedWorkspace());
1137+
}
1138+
1139+
void test_RaggedWorkspace_and_single_value()
1140+
{
1141+
auto lhs = create_RaggedWorkspace();
1142+
auto rhs = WorkspaceCreationHelper::createWorkspaceSingleValue(2);
1143+
auto result = performTest(lhs, rhs, false, false, DO_PLUS ? 3.0 : -1.0, 1.7320508071);
1144+
TS_ASSERT(result->isRaggedWorkspace());
1145+
}
1146+
1147+
void test_RaggedWorkspace_not_compatible_x()
1148+
{
1149+
auto lhs = create_RaggedWorkspace();
1150+
auto rhs = WorkspaceCreationHelper::create2DWorkspace(2, 4);
1151+
performTest_fails(lhs, rhs);
1152+
}
10841153

10851154
}; // end of class @PLUSMINUSTEST_CLASS@
10861155

Framework/DataObjects/inc/MantidDataObjects/WorkspaceCreation.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,26 @@ std::unique_ptr<T> create(const std::shared_ptr<const Geometry::Instrument> &ins
177177
return ws;
178178
}
179179

180+
template <class T, class P, typename std::enable_if<std::is_base_of<API::MatrixWorkspace, P>::value>::type * = nullptr>
181+
std::unique_ptr<T> createRagged(const P &parent) {
182+
const auto numHistograms = parent.getNumberHistograms();
183+
184+
// make a temporary histogram that will be used for initialization. Can't be 0 size so resize.
185+
auto histArg = HistogramData::Histogram(parent.histogram(0).xMode(), parent.histogram(0).yMode());
186+
histArg.resize(1);
187+
auto ws = create<T>(parent, numHistograms, histArg);
188+
for (size_t i = 0; i < numHistograms; ++i) {
189+
ws->resizeHistogram(i, parent.histogramSize(i));
190+
ws->setSharedX(i, parent.sharedX(i));
191+
}
192+
return ws;
193+
}
194+
180195
template <class T, class P, typename std::enable_if<std::is_base_of<API::MatrixWorkspace, P>::value>::type * = nullptr>
181196
std::unique_ptr<T> create(const P &parent) {
197+
if (parent.isRaggedWorkspace())
198+
return createRagged<T>(parent);
199+
182200
const auto numHistograms = parent.getNumberHistograms();
183201
auto ws = create<T>(parent, numHistograms, detail::stripData(parent.histogram(0)));
184202
for (size_t i = 0; i < numHistograms; ++i) {

Framework/DataObjects/test/WorkspaceCreationTest.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "MantidDataObjects/SpecialWorkspace2D.h"
1515
#include "MantidDataObjects/Workspace2D.h"
1616
#include "MantidDataObjects/WorkspaceCreation.h"
17+
#include "MantidHistogramData/HistogramBuilder.h"
1718
#include "MantidIndexing/IndexInfo.h"
1819
#include "MantidTypes/SpectrumDefinition.h"
1920

@@ -364,6 +365,32 @@ class WorkspaceCreationTest : public CxxTest::TestSuite {
364365
check_zeroed_data(*ws);
365366
}
366367

368+
void test_create_ragged_from_parent() {
369+
const auto parent = create<Workspace2D>(2, Histogram(BinEdges(3)));
370+
MantidVec x_data{1., 2., 3., 4.};
371+
MantidVec y_data{1., 2., 3.};
372+
MantidVec e_data{1., 1., 1.};
373+
HistogramBuilder builder;
374+
builder.setX(x_data);
375+
builder.setY(y_data);
376+
builder.setE(e_data);
377+
parent->setHistogram(1, builder.build());
378+
TS_ASSERT(parent->isRaggedWorkspace());
379+
380+
const auto ws = create<Workspace2D>(*parent);
381+
;
382+
TS_ASSERT(ws->isRaggedWorkspace());
383+
TS_ASSERT_EQUALS(ws->getNumberHistograms(), 2);
384+
TS_ASSERT_EQUALS(ws->x(0).size(), 3);
385+
TS_ASSERT_EQUALS(ws->x(1).size(), 4);
386+
TS_ASSERT_EQUALS(ws->y(0).size(), 2);
387+
TS_ASSERT_EQUALS(ws->y(1).size(), 3);
388+
TS_ASSERT_EQUALS(ws->x(0).rawData(), std::vector<double>({0, 0, 0}));
389+
TS_ASSERT_EQUALS(ws->x(1).rawData(), std::vector<double>({1, 2, 3, 4}));
390+
TS_ASSERT_EQUALS(ws->y(0).rawData(), std::vector<double>({0, 0}));
391+
TS_ASSERT_EQUALS(ws->y(1).rawData(), std::vector<double>({0, 0, 0})); // y is expected to be zeroed
392+
}
393+
367394
private:
368395
std::shared_ptr<Geometry::Instrument> m_instrument;
369396
};

0 commit comments

Comments
 (0)