Mantid
Loading...
Searching...
No Matches
DataBlockComposite.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 <algorithm>
10#include <cassert>
11
12namespace {
13
15const specnum_t INVALIDINTERVALVALUE = std::numeric_limits<specnum_t>::min();
26std::vector<SpectrumPair>
27getRemovalIntervalsRelevantForTheCurrentOriginalInterval(const SpectrumPair &original,
28 const std::vector<SpectrumPair> &removeIntervals) {
29
30 auto hasOverlap = [](const SpectrumPair &original, const SpectrumPair &toRemove) {
31 return ((original.first <= toRemove.first) && (toRemove.first <= original.second)) ||
32 ((original.first <= toRemove.second) && (toRemove.second <= original.second)) ||
33 ((toRemove.first <= original.first) && (original.first <= toRemove.second));
34 };
35
36 std::vector<SpectrumPair> overlaps;
37 for (auto &removeInterval : removeIntervals) {
38 if (hasOverlap(original, removeInterval)) {
39 overlaps.emplace_back(removeInterval);
40 }
41 }
42 return overlaps;
43}
44
53void handleLeftHandSideOverlap(SpectrumPair &original, const SpectrumPair &toRemove) {
54 original.first = toRemove.second + 1;
55}
56
65SpectrumPair handleRightHandSideOverlap(SpectrumPair &original, const SpectrumPair &toRemove) {
66 auto newInterval = std::make_pair(original.first, toRemove.first - 1);
67 original.first = INVALIDINTERVALVALUE;
68 original.second = INVALIDINTERVALVALUE;
69 return newInterval;
70}
71
80SpectrumPair handleFullyContained(SpectrumPair &original, const SpectrumPair &toRemove) {
81 // It is important to first creat the new pair and then perform the cut
82 auto newPair = std::make_pair(original.first, toRemove.first - 1);
83 original.first = toRemove.second + 1;
84 return newPair;
85}
86
87std::vector<SpectrumPair> getSlicedIntervals(SpectrumPair original, const std::vector<SpectrumPair> &removeIntervals) {
88 // If there is nothing to remove return the original
89 if (removeIntervals.empty()) {
90 return std::vector<SpectrumPair>{original};
91 }
92
93 // There are several overlap scenarios.
94 // 1. Full overlap
95 // original : |-------| and |------|
96 // toRemove: ...------------... and |------|
97 // 2. Left hand side overlap
98 // original : |------... and |-----....
99 // toRemove: |------| and |---|
100 // 3. Right hand side overlap
101 // original : ...-------| and ...-----|
102 // toRemove: |-----| and |---|
103 // 4. Fully contained
104 // original : ...-------...
105 // toRemove: |---|
106
107 auto isFullOverlap = [](const SpectrumPair &original, const SpectrumPair &toRemove) {
108 return (toRemove.first <= original.first) && (original.first <= toRemove.second) &&
109 (toRemove.first <= original.second) && (original.second <= toRemove.second);
110 };
111
112 auto isLeftHandSideOverlap = [](const SpectrumPair &original, const SpectrumPair &toRemove) {
113 return (toRemove.first <= original.first) && (original.first <= toRemove.second) &&
114 (toRemove.second < original.second);
115 };
116
117 auto isRightHandSideOverlap = [](const SpectrumPair &original, const SpectrumPair &toRemove) {
118 return (original.first < toRemove.first) && (toRemove.first <= original.second) &&
119 (original.second <= toRemove.second);
120 };
121
122 auto isFullyContained = [](const SpectrumPair &original, const SpectrumPair &toRemove) {
123 return (original.first < toRemove.first) && (toRemove.first < original.second) &&
124 (original.first < toRemove.second) && (toRemove.second < original.second);
125 };
126
127 // Use that removeIntervals has oredred, non-overlapping intervals
128 // Subtract all the removeIntervals
129 std::vector<SpectrumPair> newIntervals;
130 for (auto &removeInterval : removeIntervals) {
131
132 if (isFullOverlap(original, removeInterval)) {
133 // In this case we should remove everything. At this point
134 // newIntervals
135 // should still be empty, since the remove intervals should not be
136 // overlapping
137 assert(newIntervals.empty() && "DataBlockComposite: The "
138 "newIntervals container should be "
139 "empty");
140 // Set the remainder of the original to invalid, such that we don't
141 // pick
142 // it up at the very end
143 original.first = INVALIDINTERVALVALUE;
144 original.second = INVALIDINTERVALVALUE;
145 break;
146 } else if (isRightHandSideOverlap(original, removeInterval)) {
147 auto newInterval = handleRightHandSideOverlap(original, removeInterval);
148 newIntervals.emplace_back(newInterval);
149 } else if (isLeftHandSideOverlap(original, removeInterval)) {
150 handleLeftHandSideOverlap(original, removeInterval);
151 } else if (isFullyContained(original, removeInterval)) {
152 auto newInterval = handleFullyContained(original, removeInterval);
153 newIntervals.emplace_back(newInterval);
154 } else {
155 throw std::runtime_error("DataBlockComposite: The intervals don't seem to overlap.");
156 }
157 }
158
159 // There might be some remainder in the original interval, e.g if there
160 // wasn't
161 // a full overlap removal
162 // or no righ-hand-side overlap of a removal interval
163 if ((original.first != INVALIDINTERVALVALUE) && (original.second != INVALIDINTERVALVALUE)) {
164 newIntervals.emplace_back(original);
165 }
166
167 return newIntervals;
168}
169
173template <typename T> void sortDataBlocks(T &dataBlcokCollection) {
174 // Sort the intervals. We sort them by minimum value
175 using namespace Mantid::DataHandling;
176 auto comparison = [](const DataBlock &el1, const DataBlock &el2) {
177 return el1.getMinSpectrumID() < el2.getMinSpectrumID();
178 };
179 std::sort(std::begin(dataBlcokCollection), std::end(dataBlcokCollection), comparison);
180}
181
182std::vector<SpectrumPair> spectrumIDIntervals(const std::vector<Mantid::DataHandling::DataBlock> &blocks) {
183 std::vector<SpectrumPair> intervals;
184 intervals.reserve(blocks.size());
185
186 std::transform(blocks.begin(), blocks.end(), std::back_inserter(intervals),
187 [](const auto &block) { return std::make_pair(block.getMinSpectrumID(), block.getMaxSpectrumID()); });
188 return intervals;
189}
190} // namespace
191
192namespace Mantid::DataHandling {
193
195 specnum_t min = std::numeric_limits<specnum_t>::max();
196 for (const auto &child : m_dataBlocks) {
197 auto temp = child.getMinSpectrumID();
198 if (temp < min) {
199 min = temp;
200 }
201 }
202 return min;
203}
204
206 // DO NOTHING
207}
208
210 specnum_t max = std::numeric_limits<specnum_t>::min();
211 for (const auto &child : m_dataBlocks) {
212 auto temp = child.getMaxSpectrumID();
213 if (temp > max) {
214 max = temp;
215 }
216 }
217 return max;
218}
219
221 // DO NOTHING
222}
223
224std::unique_ptr<DataBlockGenerator> DataBlockComposite::getGenerator() const {
225 const auto intervals = spectrumIDIntervals(m_dataBlocks);
226 return std::make_unique<DataBlockGenerator>(intervals);
227}
228
230 // Set the number of periods, number of spectra and number of channel
234
235 // Insert the data block
236 m_dataBlocks.emplace_back(dataBlock);
237}
238
240 return std::accumulate(m_dataBlocks.cbegin(), m_dataBlocks.cend(), static_cast<size_t>(0),
241 [](size_t sum, const auto &element) { return sum + element.getNumberOfSpectra(); });
242}
243
245 return m_dataBlocks.empty() ? 0 : m_dataBlocks[0].getNumberOfChannels();
246}
247
249 return m_dataBlocks.empty() ? 0 : m_dataBlocks[0].getNumberOfPeriods();
250}
251
253 DataBlockComposite output;
254 output.m_dataBlocks.insert(std::end(output.m_dataBlocks), std::begin(m_dataBlocks), std::end(m_dataBlocks));
255 output.m_dataBlocks.insert(std::end(output.m_dataBlocks), std::begin(other.m_dataBlocks),
256 std::end(other.m_dataBlocks));
257 return output;
258}
259
260std::vector<DataBlock> DataBlockComposite::getDataBlocks() {
261 // Sort the intervals. We sort them by minimum value
262 sortDataBlocks(m_dataBlocks);
263 return m_dataBlocks;
264}
265
267 sortDataBlocks(m_dataBlocks);
268 // Find the first data block which is not completely cut off by specMin
269 // original: |-----| |--------| |------|
270 // spec_min: | or | or | or|
271 // result: this one
272 auto isNotCompletelyCutOffFromMin = [&specMin](const DataBlock &block) {
273 return (specMin <= block.getMinSpectrumID()) || (specMin <= block.getMaxSpectrumID());
274 };
275
276 // Find the last data block which is not completely cut off by specMax
277 // original: |-----| |--------| |------|
278 // spec_min: | or | or| or |
279 // result: this one
280 auto isNotCompletelyCutOffFromMax = [&specMax](const DataBlock &block) {
281 return (block.getMinSpectrumID() <= specMax) || (block.getMaxSpectrumID() <= specMax);
282 };
283
284 auto firstDataBlock = std::find_if(std::begin(m_dataBlocks), std::end(m_dataBlocks), isNotCompletelyCutOffFromMin);
285
286 // Note that we have to start from the back.
287 auto lastDataBlockReverseIterator =
288 std::find_if(m_dataBlocks.rbegin(), m_dataBlocks.rend(), isNotCompletelyCutOffFromMax);
289
290 // Check the case where the actuall don't have any spectrum numbers in the
291 // truncation interval
292 // e.g |-----| |------| |----| or |-----| |------| |----|
293 // | | | |
294 auto isFirstDataBlockAtEnd = firstDataBlock == m_dataBlocks.end();
295 auto isLastDataBlockReverseIteratorAtREnd = lastDataBlockReverseIterator == m_dataBlocks.rend();
296
297 if (isFirstDataBlockAtEnd || isLastDataBlockReverseIteratorAtREnd) {
298 std::vector<DataBlock> newDataBlocks;
299 m_dataBlocks.swap(newDataBlocks);
300 return;
301 }
302
303 // Check if we have an empty interval in the truncation
304 auto isEmptyInterval = firstDataBlock->getMinSpectrumID() > lastDataBlockReverseIterator->getMaxSpectrumID();
305
306 if (isEmptyInterval) {
307 std::vector<DataBlock> newDataBlocks;
308 m_dataBlocks.swap(newDataBlocks);
309 return;
310 }
311
312 auto lastDataBlock = std::find(std::begin(m_dataBlocks), std::end(m_dataBlocks), *lastDataBlockReverseIterator);
313
314 // Create datablocks
315 // Increment since we want to include the last data block
316 ++lastDataBlock;
317 std::vector<DataBlock> newDataBlocks(firstDataBlock, lastDataBlock);
318
319 // Adjust the spec_min and spec_max value. Only change the block if
320 // the it cuts the block.
321 if (newDataBlocks[0].getMinSpectrumID() < specMin) {
322 auto numberOfSpectra = newDataBlocks[0].getMaxSpectrumID() - specMin + 1;
323 DataBlock block(newDataBlocks[0].getNumberOfPeriods(), numberOfSpectra, newDataBlocks[0].getNumberOfChannels());
324 block.setMinSpectrumID(specMin);
325 block.setMaxSpectrumID(newDataBlocks[0].getMaxSpectrumID());
326 newDataBlocks[0] = block;
327 }
328
329 auto lastIndex = newDataBlocks.size() - 1;
330 if (specMax < newDataBlocks[lastIndex].getMaxSpectrumID()) {
331 auto numberOfSpectra = specMax - newDataBlocks[lastIndex].getMaxSpectrumID() + 1;
332 DataBlock block(newDataBlocks[lastIndex].getNumberOfPeriods(), numberOfSpectra,
333 newDataBlocks[lastIndex].getNumberOfChannels());
334 block.setMinSpectrumID(newDataBlocks[lastIndex].getMinSpectrumID());
335 block.setMaxSpectrumID(specMax);
336 newDataBlocks[lastIndex] = block;
337 }
338
339 m_dataBlocks.swap(newDataBlocks);
340}
341
343 if (other.m_dataBlocks.size() != m_dataBlocks.size()) {
344 return false;
345 }
346
347 // Create a copy of the intervals, since the comparison operator should not
348 // have
349 // side effects!!!!! We need the vector sorted to compare though
350 auto otherDataBlocks = other.m_dataBlocks;
351 auto thisDataBlocks = m_dataBlocks;
352 sortDataBlocks(otherDataBlocks);
353 sortDataBlocks(thisDataBlocks);
354
355 auto isEqual = true;
356 auto itOther = otherDataBlocks.cbegin();
357 auto itThis = thisDataBlocks.cbegin();
358 for (; itOther != otherDataBlocks.cend(); ++itOther, ++itThis) {
359 isEqual = isEqual && *itOther == *itThis;
360 }
361 return isEqual;
362}
363
373 // Get intervals for current data blocks
374 const auto originalIntervals = spectrumIDIntervals(m_dataBlocks);
375
376 // Get intervals for the data blocks which should be removed
377 const auto removeBlocks = toRemove.getDataBlocks();
378 const auto toRemoveIntervals = spectrumIDIntervals(removeBlocks);
379
380 // Now create the new intervals which don't include the removeInterval
381 // values
382 std::vector<SpectrumPair> newIntervals;
383 for (auto &originalInterval : originalIntervals) {
384 // Find all relevant remove intervals. In principal this could
385 // be made more efficient.
386 auto currentRemovalIntervals =
387 getRemovalIntervalsRelevantForTheCurrentOriginalInterval(originalInterval, toRemoveIntervals);
388 auto slicedIntervals = getSlicedIntervals(originalInterval, currentRemovalIntervals);
389 newIntervals.insert(std::end(newIntervals), std::begin(slicedIntervals), std::end(slicedIntervals));
390 }
391
392 // Create a new set of data blocks
393 auto numberOfPeriods = m_dataBlocks[0].getNumberOfPeriods();
394 auto numberOfChannels = m_dataBlocks[0].getNumberOfChannels();
395
396 m_dataBlocks.clear();
397 for (const auto &newInterval : newIntervals) {
398 DataBlock dataBlock(numberOfPeriods, newInterval.second - newInterval.first + 1, numberOfChannels);
399 dataBlock.setMinSpectrumID(newInterval.first);
400 dataBlock.setMaxSpectrumID(newInterval.second);
401 m_dataBlocks.emplace_back(dataBlock);
402 }
403}
404
410 auto generator = getGenerator();
411 std::vector<specnum_t> allSpectra;
412
413 for (; !generator->isDone(); generator->next()) {
414 allSpectra.emplace_back(generator->getValue());
415 }
416
417 return allSpectra;
418}
419
421} // namespace Mantid::DataHandling
DataBlockComposite: The DataBlockComposite handles a collection of DataBlocks.
bool operator==(const DataBlockComposite &other) const
DataBlockComposite operator+(const DataBlockComposite &other)
void truncate(specnum_t specMin, specnum_t specMax)
std::vector< specnum_t > getAllSpectrumNumbers()
Provides a container with all spectrum numbers.
std::unique_ptr< DataBlockGenerator > getGenerator() const override
void addDataBlock(const DataBlock &dataBlock)
void removeSpectra(DataBlockComposite &toRemove)
Removes the input data blocks from the current list of data blocks.
DataBlock: The DataBlock class holds information about a contiguous block of spectrum numbers.
Definition: DataBlock.h:26
virtual specnum_t getMaxSpectrumID() const
Definition: DataBlock.cpp:33
virtual size_t getNumberOfChannels() const
Definition: DataBlock.cpp:41
virtual void setMaxSpectrumID(specnum_t minSpecID)
Definition: DataBlock.cpp:35
virtual int getNumberOfPeriods() const
Definition: DataBlock.cpp:39
virtual size_t getNumberOfSpectra() const
Definition: DataBlock.cpp:37
virtual specnum_t getMinSpectrumID() const
Definition: DataBlock.cpp:29
virtual void setMinSpectrumID(specnum_t minSpecID)
Definition: DataBlock.cpp:31
std::pair< specnum_t, specnum_t > SpectrumPair
Definition: DataBlock.h:16
int32_t specnum_t
Typedef for a spectrum Number.
Definition: IDTypes.h:16