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