Mantid
Loading...
Searching...
No Matches
VectorHelper.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 +
7#include <cmath>
8#include <stdexcept>
9
10#include "MantidKernel/Memory.h"
12#include <algorithm>
13#include <boost/algorithm/string.hpp>
14#include <numeric>
15#include <sstream>
16
17using std::size_t;
18
19namespace {
20// edge checks
22struct EdgeCheck { // for polymorphic access to functors
24 virtual bool operator()(double, double, double) const = 0;
25 virtual ~EdgeCheck() = default;
26};
27
28// Standard check, for linear, log, and power
29struct StandardEdgeCheck : EdgeCheck {
30 StandardEdgeCheck(double lastBinCoef) : m_lastBinCoef(lastBinCoef) {}
31 bool operator()(double xcurr, double xs, double rightEdge) const override {
32 return xcurr + (1. + m_lastBinCoef) * xs <= rightEdge;
33 }
34
35private:
36 double m_lastBinCoef;
37};
38// Reverse log check, for reverse log only
39struct ReverseLogEdgeCheck : EdgeCheck {
40 bool operator()(double xcurr, double xs, double leftEdge) const override { return xcurr + 2. * xs >= leftEdge; }
41};
42
43// bin width functions
45struct BinWidth { // for polymorphic access to functors
47 virtual double operator()(double, double) const = 0;
48 virtual ~BinWidth() = default;
49};
50
51// Linear bin width is just the alpha parameter
52struct LinearBinWidth : BinWidth {
53 double operator()([[maybe_unused]] double xcurr, double alpha) const override { return alpha; }
54};
55// Log bin width is current position times the alpha parameter
56struct LogBinWidth : BinWidth {
57 double operator()(double xcurr, double alpha) const override { return xcurr * alpha; }
58};
59
60// REVERSE LOGARITHMIC
61// we go through a reverse log bin by starting from its end, and working our way back to the beginning
62// this way we can define the bins in a recurring way, and with a more obvious closeness with the usual log.
63struct ReverseLogBinWidth : BinWidth {
64 ReverseLogBinWidth(double leftEdge, double rightEdge) : m_leftEdge(leftEdge), m_rightEdge(rightEdge) {}
65 double operator()(double xcurr, double alpha) const override { return (xcurr - m_rightEdge - m_leftEdge) * alpha; }
66
67private:
68 double m_leftEdge;
69 double m_rightEdge;
70};
71// Power bin width is the alpha parameter divided by the current index to the power
72struct PowerBinWidth : BinWidth {
73 PowerBinWidth(std::size_t &currDiv, double power) : m_currDiv(&currDiv), m_power(power) {}
74 double operator()([[maybe_unused]] double xcurr, double alpha) const override {
75 return alpha * std::pow((*m_currDiv)++, -m_power);
76 }
77
78private:
79 std::size_t *const m_currDiv; // the pointed-to changes, but not the pointer
80 double m_power;
81};
82} // namespace
83
85
87void validateRebinParameters(std::vector<double> const &params, bool const isPower) {
88 size_t numParams = params.size();
89 if (numParams < 3 || numParams % 2 != 1) {
90 throw std::runtime_error("validateRebinParameters: Invalid number of rebin parameters (" +
91 std::to_string(numParams) + ")! Must be odd number of parameters, 3 or more.");
92 }
93 double previousEdge = params[0];
94 for (size_t i = 1; i < numParams - 1; i += 2) {
95 double const binWidth = params[i];
96 double const nextEdge = params[i + 1];
97 if (previousEdge >= nextEdge) {
98 throw std::runtime_error(
99 "validateRebinParameters: Bin boundary values must be given in order of increasing value! " +
100 std::to_string(previousEdge) + " >= " + std::to_string(nextEdge));
101 } else if (binWidth == 0) {
102 throw std::runtime_error("validateRebinParameters: Bin width cannot be zero! Found in range " +
103 std::to_string(previousEdge) + " to " + std::to_string(nextEdge));
104 } else if (binWidth < 0 && previousEdge <= 0) {
105 throw std::runtime_error("Bin boundaries must be positive for logarithmic binning. Got " +
106 std::to_string(previousEdge) + " to " + std::to_string(nextEdge));
107 } else if (binWidth < 0 && isPower) {
108 throw std::runtime_error(
109 "validateRebinParameters: Cannot use power binning with negative bin width! Bin width was " +
110 std::to_string(binWidth));
111 }
112 previousEdge = nextEdge;
113 }
114}
115
120std::size_t estimateNumberOfBins(std::vector<double> const &params, double const power) {
121 bool const isPower = power > 0 && power <= 1;
122 validateRebinParameters(params, isPower);
123 double numBins = 1.; // always start with 1 left edge / fencepost
124 double previousEdge = params[0];
125 for (size_t i = 1; i < params.size() - 1; i += 2) {
126 double const binWidth = params[i];
127 double const nextEdge = params[i + 1];
128 if (binWidth < 0) {
129 // LOGARITHMIC BINNING
130 // NOTE log or reverse log should have approximately the same number of bins here
131 numBins += std::ceil(std::log(nextEdge / previousEdge) / std::log(1. - binWidth));
132 } else if (isPower) {
133 // POWER BINNING
134 // formulas taken from Rebin::validateInputs
135 if (power == 1) {
136 double constexpr eulerMascheroni = 0.5772156649; // Euler-Mascheroni constant
137 numBins += std::exp((nextEdge - previousEdge) / binWidth - eulerMascheroni);
138 } else {
139 numBins += std::pow(((nextEdge - previousEdge) / binWidth) * (1 - power) + 1, 1 / (1 - power));
140 }
141 } else if (binWidth > 0) {
142 // LINEAR BINNING
143 numBins += std::ceil((nextEdge - previousEdge) / binWidth);
144 }
145 previousEdge = nextEdge;
146 }
147 if (numBins >= static_cast<double>(std::numeric_limits<std::size_t>::max())) {
148 throw std::runtime_error(
149 "estimateNumberOfBins: The number of bins requested exceeds the maximum storable by size_t.");
150 }
151 return static_cast<size_t>(numBins);
152}
153
168std::size_t createAxisFromRebinParams(const std::vector<double> &params, std::vector<double> &xnew,
169 const bool resize_xnew, const bool full_bins_only, const double xMinHint,
170 const double xMaxHint, const bool useReverseLogarithmic, const double power) {
171
172 // correctly expand the parameters
173 std::vector<double> tmp;
174 if (params.size() == 1) {
175 if (std::isnan(xMinHint) || std::isnan(xMaxHint)) {
176 throw std::runtime_error(
177 "createAxisFromRebinParams: xMinHint and xMaxHint must be supplied if params contains only the bin width.");
178 }
179 tmp = {xMinHint, params.front(), xMaxHint};
180 }
181 std::vector<double> const &fullParams = params.size() == 1 ? tmp : params;
182
183 // This coefficient represents the maximum difference between the size of the last bin and all
184 // the other bins.
185 // For full_bin_only, we want it so that last bin couldn't be smaller than the previous bin
186 double const lastBinCoef = full_bins_only ? 1.0 : 0.25;
187 // whether to use power binning
188 bool isPower = power > 0 && power <= 1;
189
190 // if resize_xnew is true, prepare the vector
191 double xcurr = fullParams[0];
192 xnew.clear();
193 if (resize_xnew) {
194 // call to estimateNumberOfBins will also validate the parameters
195 size_t neededSize = estimateNumberOfBins(fullParams, power);
196 /* detect potential memory overflows
197 * TODO find a way to check against actual available system memory
198 * NOTE use of MemoryStats.availMem() was considered, but introduces large overhead into this hot path
199 */
200 if (neededSize >= xnew.max_size()) {
201 throw std::runtime_error(
202 "createAxisFromRebinParams: The number of bins requested exceeds the maximum storable by the output vector.");
203 }
204 xnew.reserve(neededSize);
205 xnew.push_back(xcurr);
206 } else {
207 // validate the parameters, but don't calculate needed size
208 validateRebinParameters(fullParams, isPower);
209 }
210
211 std::size_t currDiv = 1;
212
213 // for each range specified, fill in the axis with correct bin edges
214 std::size_t inew = 1; // we start with 1, the leftmost edge
215 double leftEdge = fullParams[0];
216 for (std::size_t i = 1; i <= fullParams.size() - 1; i += 2) {
217 double binParam = fullParams[i];
218 double rightEdge = fullParams[i + 1];
219 double alpha = std::fabs(binParam);
220
221 // functions for calculating bin width and checking if within range
222 std::unique_ptr<BinWidth> binWidth(nullptr);
223 std::unique_ptr<EdgeCheck> edgeCheck(nullptr);
224 if (binParam < 0.0 && useReverseLogarithmic) {
225 // REVERSE LOGARITHMIC
226 binWidth = std::make_unique<ReverseLogBinWidth>(leftEdge, rightEdge);
227 edgeCheck = std::make_unique<ReverseLogEdgeCheck>();
228 // for reverse log, we start at the right edge and work back to left; to use the same loop, swap the edges
229 // the final edge will be added after the loop, fullParams[i+1]
230 xcurr = rightEdge;
231 std::swap(leftEdge, rightEdge);
232 } else if (binParam < 0.0) {
233 // LOGARITHMIC
234 binWidth = std::make_unique<LogBinWidth>();
235 edgeCheck = std::make_unique<StandardEdgeCheck>(lastBinCoef);
236 } else if (isPower) {
237 // POWER
238 binWidth = std::make_unique<PowerBinWidth>(currDiv, power);
239 edgeCheck = std::make_unique<StandardEdgeCheck>(lastBinCoef);
240 } else {
241 // LINEAR
242 binWidth = std::make_unique<LinearBinWidth>();
243 edgeCheck = std::make_unique<StandardEdgeCheck>(lastBinCoef);
244 }
245
246 double xs = (*binWidth)(xcurr, alpha);
247 while ((*edgeCheck)(xcurr, xs, rightEdge) || !std::isfinite(xs)) { // let errors fall through
248 if (xs == 0.0) {
249 // Someone gave a 0-sized step! What a dope.
250 throw std::runtime_error("Invalid binning step provided! Can't create binning axis.");
251 } else if (!std::isfinite(xs)) {
252 throw std::runtime_error("An infinite or NaN value was found in the binning parameters.");
253 }
254 xcurr = xcurr + xs;
255 if (resize_xnew) {
256 xnew.push_back(xcurr);
257 }
258 inew++;
259 // prepare for next step
260 xs = (*binWidth)(xcurr, alpha);
261 }
262
263 // handle last edge specially, depending on if full_bins_only is set
264 if (!useReverseLogarithmic && full_bins_only) {
265 // For full_bins_only, we want to add one more full bin, so that last bin is not bigger than the previous one
266 xcurr = xcurr + xs;
267 } else {
268 // For non full_bins_only, finish by adding as much as is left from the range
269 // NOTE for reverse log, the left edge was already added, so we can instead jump to the right edge of this range
270 xcurr = fullParams[i + 1];
271 }
272 if (resize_xnew) {
273 xnew.push_back(xcurr);
274 }
275 inew++;
276 // prepare for next range
277 leftEdge = xcurr;
278 }
279 // if using reverse log, some bins were added in wrong order; so sort them
280 if (useReverseLogarithmic) {
281 std::sort(xnew.begin(), xnew.end());
282 }
283 return inew;
284}
285
305void rebin(std::span<double const> xold, std::span<double const> yold, std::span<double const> eold,
306 std::span<double const> xnew, std::span<double> ynew, std::span<double> enew, bool distribution,
307 bool addition) {
308 // Make sure y and e vectors are of correct sizes
309 const size_t size_xold = xold.size();
310 if (size_xold != (yold.size() + 1) || size_xold != (eold.size() + 1))
311 throw std::runtime_error("rebin: old y and error vectors should be of same "
312 "size & 1 shorter than x");
313 const size_t size_xnew = xnew.size();
314 if (size_xnew != (ynew.size() + 1) || size_xnew != (enew.size() + 1))
315 throw std::runtime_error("rebin: new y and error vectors should be of same "
316 "size & 1 shorter than x");
317
318 size_t size_yold = yold.size();
319 size_t size_ynew = ynew.size();
320
321 if (!addition) {
322 // Make sure ynew & enew contain zeroes
323 std::fill(ynew.begin(), ynew.end(), 0.0);
324 std::fill(enew.begin(), enew.end(), 0.0);
325 }
326
327 size_t iold = 0, inew = 0;
328 double width;
329
330 while ((inew < size_ynew) && (iold < size_yold)) {
331 double xo_low = xold[iold];
332 double xo_high = xold[iold + 1];
333 double xn_low = xnew[inew];
334 double xn_high = xnew[inew + 1];
335 if (xn_high <= xo_low)
336 inew++; /* old and new bins do not overlap */
337 else if (xo_high <= xn_low)
338 iold++; /* old and new bins do not overlap */
339 else {
340 // delta is the overlap of the bins on the x axis
341 // delta = std::min(xo_high, xn_high) - std::max(xo_low, xn_low);
342 double delta = xo_high < xn_high ? xo_high : xn_high;
343 delta -= xo_low > xn_low ? xo_low : xn_low;
344 width = xo_high - xo_low;
345 if ((delta <= 0.0) || (width <= 0.0)) {
346 // No need to throw here, just return (ynew & enew will be empty)
347 // throw std::runtime_error("rebin: no bin overlap detected");
348 return;
349 }
350 /*
351 * yoldp contains counts/unit time, ynew contains counts
352 * enew contains counts**2
353 * ynew has been filled with zeros on creation
354 */
355 if (distribution) {
356 // yold/eold data is distribution
357 ynew[inew] += yold[iold] * delta;
358 // this error is calculated in the same way as opengenie
359 enew[inew] += eold[iold] * eold[iold] * delta * width;
360 } else {
361 // yold/eold data is not distribution
362 // do implicit division of yold by width in summing.... avoiding the
363 // need for temporary yold array
364 // this method is ~7% faster and uses less memory
365 ynew[inew] += yold[iold] * delta / width; // yold=yold/width
366 // eold=eold/width, so divide by width**2 compared with distribution
367 // calculation
368 enew[inew] += eold[iold] * eold[iold] * delta / width;
369 }
370 if (xn_high > xo_high) {
371 iold++;
372 } else {
373 inew++;
374 }
375 }
376 }
377
378 if (!addition) // If using the addition facility, have to do bin width and
379 // sqrt errors externally
380 {
381 if (distribution) {
382 /*
383 * convert back to counts/unit time
384 */
385 for (size_t i = 0; i < size_ynew; ++i) {
386 {
387 width = xnew[i + 1] - xnew[i];
388 if (width != 0.0) {
389 ynew[i] /= width;
390 enew[i] = sqrt(enew[i]) / width;
391 } else {
392 throw std::invalid_argument("rebin: Invalid output X array, contains consecutive X values");
393 }
394 }
395 }
396 } else {
397 // non-distribution, just square root final error value
398 using pf = double (*)(double);
399 pf uf = std::sqrt;
400 std::transform(enew.begin(), enew.end(), enew.begin(), uf);
401 }
402 }
403}
404
405//-------------------------------------------------------------------------------------------------
422void rebinHistogram(std::span<double const> xold, std::span<double const> yold, std::span<double const> eold,
423 std::span<double const> xnew, std::span<double> ynew, std::span<double> enew, bool addition) {
424 // Make sure y and e vectors are of correct sizes
425 const size_t size_yold = yold.size();
426 if (xold.size() != (size_yold + 1) || size_yold != eold.size())
427 throw std::runtime_error("rebin: y and error vectors should be of same size & 1 shorter than x");
428 const size_t size_ynew = ynew.size();
429 if (xnew.size() != (size_ynew + 1) || size_ynew != enew.size())
430 throw std::runtime_error("rebin: y and error vectors should be of same size & 1 shorter than x");
431
432 // If not adding to existing vectors, make sure ynew & enew contain zeroes
433 if (!addition) {
434 std::fill(ynew.begin(), ynew.end(), 0.0);
435 std::fill(enew.begin(), enew.end(), 0.0);
436 }
437
438 // Find the starting points to avoid wasting time processing irrelevant bins
439 size_t iold = 0, inew = 0; // iold/inew is the bin number under consideration
440 // (counting from 1, so index+1)
441 if (xnew.front() > xold.front()) {
442 auto it = std::upper_bound(xold.begin(), xold.end(), xnew.front());
443 if (it == xold.end())
444 return;
445 // throw std::runtime_error("No overlap: max of X-old < min of X-new");
446 iold = std::distance(xold.begin(), it) - 1; // Old bin to start at (counting from 0)
447 } else {
448 auto it = std::upper_bound(xnew.begin(), xnew.end(), xold.front());
449 if (it == xnew.end())
450 return;
451 // throw std::runtime_error("No overlap: max of X-new < min of X-old");
452 inew = std::distance(xnew.begin(), it) - 1; // New bin to start at (counting from 0)
453 }
454
455 double frac, fracE;
456 double oneOverWidth, overlap;
457 double temp;
458
459 // loop over old vector from starting point calculated above
460 for (; iold < size_yold; ++iold) {
461 double xold_of_iold_p_1 = xold[iold + 1]; // cache for speed
462 // If current old bin is fully enclosed by new bin, just unload the counts
463 if (xold_of_iold_p_1 <= xnew[inew + 1]) {
464 ynew[inew] += yold[iold];
465 temp = eold[iold];
466 enew[inew] += temp * temp;
467 // If the upper bin boundaries were equal, then increment inew
468 if (xold_of_iold_p_1 == xnew[inew + 1])
469 inew++;
470 } else {
471 double xold_of_iold = xold[iold]; // cache for speed
472 // This is the counts per unit X in current old bin
473 oneOverWidth = 1. / (xold_of_iold_p_1 - xold_of_iold); // cache 1/width to speed things up
474 frac = yold[iold] * oneOverWidth;
475 temp = eold[iold];
476 fracE = temp * temp * oneOverWidth;
477
478 // Now loop over bins in new vector overlapping with current 'old' bin
479 while (inew < size_ynew && xnew[inew + 1] <= xold_of_iold_p_1) {
480 overlap = xnew[inew + 1] - std::max(xnew[inew], xold_of_iold);
481 ynew[inew] += frac * overlap;
482 enew[inew] += fracE * overlap;
483 ++inew;
484 }
485
486 // Stop if at end of new X range
487 if (inew == size_ynew)
488 break;
489
490 // Unload the rest of the current old bin into the current new bin
491 overlap = xold_of_iold_p_1 - xnew[inew];
492 ynew[inew] += frac * overlap;
493 enew[inew] += fracE * overlap;
494 }
495 } // loop over old bins
496
497 if (!addition) // If this used to add at the same time then not necessary
498 // (should be done externally)
499 {
500 // Now take the root-square of the errors
501 using pf = double (*)(double);
502 pf uf = std::sqrt;
503 std::transform(enew.begin(), enew.end(), enew.begin(), uf);
504 }
505}
506
507//-------------------------------------------------------------------------------------------------
513void convertToBinCentre(std::span<double const> bin_edges, std::vector<double> &bin_centres) {
514 const std::vector<double>::size_type npoints = bin_edges.size();
515 if (bin_centres.size() != npoints) {
516 bin_centres.resize(npoints);
517 }
518
519 // The custom binary function modifies the behaviour of the algorithm to
520 // compute the average of
521 // two adjacent bin boundaries
522 std::adjacent_difference(bin_edges.begin(), bin_edges.end(), bin_centres.begin(), SimpleAverage<double>());
523 // The algorithm copies the first element of the input to the first element of
524 // the output so we need to
525 // remove the first element of the output
526 bin_centres.erase(bin_centres.begin());
527}
528
529//-------------------------------------------------------------------------------------------------
545void convertToBinBoundary(std::span<double const> bin_centers, std::vector<double> &bin_edges) {
546 const auto n = bin_centers.size();
547
548 // Special case empty input: output is also empty
549 if (n == 0) {
550 bin_edges.resize(0);
551 return;
552 }
553
554 bin_edges.resize(n + 1);
555
556 // Special case input of size one: we have no means of guessing the bin size,
557 // set it to 1.
558 if (n == 1) {
559 bin_edges[0] = bin_centers[0] - 0.5;
560 bin_edges[1] = bin_centers[0] + 0.5;
561 return;
562 }
563
564 for (size_t i = 0; i < n - 1; ++i) {
565 bin_edges[i + 1] = 0.5 * (bin_centers[i] + bin_centers[i + 1]);
566 }
567
568 bin_edges[0] = bin_centers[0] - (bin_edges[1] - bin_centers[0]);
569
570 bin_edges[n] = bin_centers[n - 1] + (bin_centers[n - 1] - bin_edges[n - 1]);
571}
572
583size_t indexOfValueFromCenters(std::span<double const> bin_centers, const double value) {
584 int index = indexOfValueFromCentersNoThrow(bin_centers, value);
585 if (index >= 0)
586 return static_cast<size_t>(index);
587 else
588 throw std::out_of_range("indexOfValue - value out of range");
589}
590
591int indexOfValueFromCentersNoThrow(std::span<double const> bin_centers, const double value) {
592 if (bin_centers.empty()) {
593 throw std::out_of_range("indexOfValue - vector is empty");
594 }
595 if (bin_centers.size() == 1) {
596 // no mean to guess bin size, assuming 1
597 if (value < bin_centers[0] - 0.5 || value > bin_centers[0] + 0.5) {
598 return -1;
599 } else {
600 return 0;
601 }
602 } else {
603 const size_t n = bin_centers.size();
604 const double firstBinLowEdge = bin_centers[0] - 0.5 * (bin_centers[1] - bin_centers[0]);
605 const double lastBinHighEdge = bin_centers[n - 1] + 0.5 * (bin_centers[n - 1] - bin_centers[n - 2]);
606 if (value < firstBinLowEdge || value > lastBinHighEdge) {
607 return -1;
608 } else {
609 const auto it = std::lower_bound(bin_centers.begin(), bin_centers.end(), value);
610 if (it == bin_centers.end()) {
611 return static_cast<int>(n - 1);
612 }
613 size_t binIndex = std::distance(bin_centers.begin(), it);
614 if (binIndex > 0 &&
615 value < bin_centers[binIndex - 1] + 0.5 * (bin_centers[binIndex] - bin_centers[binIndex - 1])) {
616 binIndex--;
617 }
618 return static_cast<int>(binIndex);
619 }
620 }
621}
622
631size_t indexOfValueFromEdges(std::span<double const> bin_edges, const double value) {
632 if (bin_edges.empty()) {
633 throw std::out_of_range("indexOfValue - vector is empty");
634 }
635 if (bin_edges.size() == 1) {
636 throw std::out_of_range("indexOfValue - requires at least two bin edges");
637 }
638 if (value < bin_edges.front()) {
639 throw std::out_of_range("indexOfValue - value out of range");
640 }
641 const auto it = std::lower_bound(bin_edges.begin(), bin_edges.end(), value);
642 if (it == bin_edges.end()) {
643 throw std::out_of_range("indexOfValue - value out of range");
644 }
645 // index of closest edge above value is distance of iterator from start
646 size_t edgeIndex = std::distance(bin_edges.begin(), it);
647 // if the element n is the first that is >= value, then the value is in (n-1)
648 // th bin
649 if (edgeIndex > 0)
650 edgeIndex--;
651 return edgeIndex;
652}
653
654//-------------------------------------------------------------------------------------------------
659bool isConstantValue(std::span<double const> arra) {
660 // make comparisons with the first value
661 auto i = arra.begin();
662
663 if (i == arra.end()) { // empty array
664 return true;
665 }
666
667 double val(*i);
668 // this loop can be entered! NAN values make comparisons difficult because nan
669 // != nan, deal with these first
670 for (; val != val;) {
671 ++i;
672 if (i == arra.end()) {
673 // all values are contant (NAN)
674 return true;
675 }
676 val = *i;
677 }
678
679 for (; i != arra.end(); ++i) {
680 if (*i != val) {
681 return false;
682 }
683 }
684 // no different value was found and so every must be equal to c
685 return true;
686}
687
688//-------------------------------------------------------------------------------------------------
697template <typename NumT>
698std::vector<NumT> splitStringIntoVector(std::string listString, const std::string &separators) {
699 // Split the string and turn it into a vector.
700 std::vector<NumT> values;
701 std::vector<std::string> strs;
702
703 boost::split(strs, listString, boost::is_any_of(separators));
704 for (auto &str : strs) {
705 if (!str.empty()) {
706 // String not empty
707 std::stringstream oneNumber(str);
708 NumT num;
709 oneNumber >> num;
710 values.emplace_back(num);
711 }
712 }
713 return values;
714}
715
716//-------------------------------------------------------------------------------------------------
725int getBinIndex(std::span<double const> bins, const double value) {
726 assert(bins.size() >= 2);
727 // Since we cast to an int below:
728 assert(bins.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
729 // If X is below the min value
730 if (value < bins.front())
731 return 0;
732
733 // upper_bound will find the right-hand bin boundary (even if the value is
734 // equal to
735 // the left-hand one) - hence we subtract 1 from the found point.
736 // Since we want to return the LH boundary of the last bin if the value is
737 // outside
738 // the upper range, we leave the last value out (i.e. bins.end()-1)
739 auto it = std::upper_bound(bins.begin(), bins.end() - 1, value) - 1;
740 assert(it >= bins.begin());
741 // Convert an iterator to an index for the return value
742 return static_cast<int>(it - bins.begin());
743}
744
745//-------------------------------------------------------------------------------------------------
746
747namespace {
760double runAverage(size_t index, size_t startIndex, size_t endIndex, const double halfWidth,
761 std::span<double const> input, std::span<double const> binBndrs) {
762
763 size_t iStart, iEnd;
764 double weight0(0), weight1(0), start(0.0), end(0.0);
765 //
766 if (!binBndrs.empty()) {
767 // identify initial and final bins to
768 // integrate over. Notice the difference
769 // between start and end bin and shift of
770 // the interpolating function into the center
771 // of each bin
772 auto const &rBndrs = binBndrs;
773 // bin0 = binBndrs[index + 1] - binBndrs[index];
774
775 double binC = 0.5 * (rBndrs[index + 1] + rBndrs[index]);
776 start = binC - halfWidth;
777 end = binC + halfWidth;
778 if (start <= rBndrs[startIndex]) {
779 iStart = startIndex;
780 start = rBndrs[iStart];
781 } else {
782 iStart = getBinIndex(binBndrs, start);
783 weight0 = (rBndrs[iStart + 1] - start) / (rBndrs[iStart + 1] - rBndrs[iStart]);
784 iStart++;
785 }
786 if (end >= rBndrs[endIndex]) {
787 iEnd = endIndex; // the signal defined up to i<iEnd
788 end = rBndrs[endIndex];
789 } else {
790 iEnd = getBinIndex(binBndrs, end);
791 weight1 = (end - rBndrs[iEnd]) / (rBndrs[iEnd + 1] - rBndrs[iEnd]);
792 }
793 if (iStart > iEnd) { // start and end get into the same bin
794 weight1 = 0;
795 weight0 = (end - start) / (rBndrs[iStart] - rBndrs[iStart - 1]);
796 }
797 } else { // integer indexes and functions defined in the bin centers
798 auto iHalfWidth = static_cast<size_t>(halfWidth);
799 iStart = index - iHalfWidth;
800 if (startIndex + iHalfWidth > index)
801 iStart = startIndex;
802 iEnd = index + iHalfWidth;
803 if (iEnd > endIndex)
804 iEnd = endIndex;
805 }
806
807 double avrg = 0;
808 size_t ic = 0;
809 for (size_t j = iStart; j < iEnd; j++) {
810 avrg += input[j];
811 ic++;
812 }
813 if (!binBndrs.empty()) { // add values at edges
814 if (iStart != startIndex)
815 avrg += input[iStart - 1] * weight0;
816 if (iEnd != endIndex)
817 avrg += input[iEnd] * weight1;
818
819 double div = end - start;
820 if (.0 == div)
821 return 0;
822 else
823 return avrg / (end - start);
824 } else {
825 if (0 == ic) {
826 return 0;
827 } else {
828 return avrg / double(ic);
829 }
830 }
831}
832} // namespace
833
858void smoothInRange(std::span<double const> input, std::vector<double> &output, const double avrgInterval,
859 std::span<double const> binBndrs, size_t startIndex, size_t endIndex,
860 std::vector<double> *const outBins) {
861 bool const haveBndrs = !binBndrs.empty();
862
863 if (endIndex == 0)
864 endIndex = input.size();
865 if (endIndex > input.size())
866 endIndex = input.size();
867
868 if (endIndex <= startIndex) {
869 output.resize(0);
870 return;
871 }
872
873 size_t max_size = input.size();
874 if (haveBndrs) {
875 if (binBndrs.size() != max_size + 1) {
876 throw std::invalid_argument("Array of bin boundaries, "
877 "if present, have to be one bigger then the input array");
878 }
879 }
880
881 size_t length = endIndex - startIndex;
882 output.resize(length);
883
884 double halfWidth = avrgInterval / 2;
885 if (!haveBndrs) {
886 if (std::floor(halfWidth) * 2 - avrgInterval > 1.e-6) {
887 halfWidth = std::floor(halfWidth) + 1;
888 }
889 }
890
891 if (outBins)
892 outBins->resize(length + 1);
893
894 // Run averaging
895 double binSize = 1;
896 for (size_t i = startIndex; i < endIndex; i++) {
897 if (haveBndrs) {
898 binSize = binBndrs[i + 1] - binBndrs[i];
899 }
900 output[i - startIndex] = runAverage(i, startIndex, endIndex, halfWidth, input, binBndrs) * binSize;
901 if (outBins && haveBndrs) {
902 outBins->operator[](i - startIndex) = binBndrs[i];
903 }
904 }
905 if (outBins && haveBndrs) {
906 outBins->operator[](endIndex - startIndex) = binBndrs[endIndex];
907 }
908}
909
911template DLLExport std::vector<int32_t> splitStringIntoVector<int32_t>(std::string listString,
912 const std::string &separator);
913template DLLExport std::vector<int64_t> splitStringIntoVector<int64_t>(std::string listString,
914 const std::string &separator);
915template DLLExport std::vector<size_t> splitStringIntoVector<size_t>(std::string listString,
916 const std::string &separator);
917template DLLExport std::vector<float> splitStringIntoVector<float>(std::string listString,
918 const std::string &separator);
919template DLLExport std::vector<double> splitStringIntoVector<double>(std::string listString,
920 const std::string &separator);
921template DLLExport std::vector<std::string> splitStringIntoVector<std::string>(std::string listString,
922 const std::string &separator);
923
924} // namespace Mantid::Kernel::VectorHelper
gsl_vector * tmp
double value
The value of the point.
Definition FitMW.cpp:51
std::map< DeltaEMode::Type, std::string > index
#define DLLExport
Definitions of the DLLImport compiler directives for MSVC.
Definition System.h:33
size_t iEnd
size_t iStart
template DLLExport std::vector< int64_t > splitStringIntoVector< int64_t >(std::string listString, const std::string &separator)
template DLLExport std::vector< std::string > splitStringIntoVector< std::string >(std::string listString, const std::string &separator)
MANTID_KERNEL_DLL void smoothInRange(std::span< double const > input, std::vector< double > &output, double avrgInterval, std::span< double const > binBndrs={}, size_t startIndex=0, size_t endIndex=0, std::vector< double > *const outBins=nullptr)
Basic running average of input vector within specified range, considering variable bin-boundaries if ...
template DLLExport std::vector< size_t > splitStringIntoVector< size_t >(std::string listString, const std::string &separator)
void MANTID_KERNEL_DLL rebin(std::span< double const > xold, std::span< double const > yold, std::span< double const > eold, std::span< double const > xnew, std::span< double > ynew, std::span< double > enew, bool distribution, bool addition=false)
The input and output ranges are taken as spans so that the size-checked histogram data types,...
std::size_t MANTID_KERNEL_DLL estimateNumberOfBins(std::vector< double > const &params, double const power=-1)
Returns a size_t with the estimated number of bins that would be needed for a rebinning operation.
template DLLExport std::vector< float > splitStringIntoVector< float >(std::string listString, const std::string &separator)
template DLLExport std::vector< double > splitStringIntoVector< double >(std::string listString, const std::string &separator)
int MANTID_KERNEL_DLL indexOfValueFromCentersNoThrow(std::span< double const > bin_centers, const double value)
Gets the bin of a value from a vector of bin centers and returns -1 if out of range.
MANTID_KERNEL_DLL int getBinIndex(std::span< double const > bins, const double value)
Return the index into a vector of bin boundaries for a particular X value.
void MANTID_KERNEL_DLL rebinHistogram(std::span< double const > xold, std::span< double const > yold, std::span< double const > eold, std::span< double const > xnew, std::span< double > ynew, std::span< double > enew, bool addition)
Rebins histogram data according to a new output X array.
void MANTID_KERNEL_DLL convertToBinBoundary(std::span< double const > bin_centers, std::vector< double > &bin_edges)
Convert an array of bin centers to bin boundary values.
template DLLExport std::vector< int32_t > splitStringIntoVector< int32_t >(std::string listString, const std::string &separator)
Declare all version of this.
bool MANTID_KERNEL_DLL isConstantValue(std::span< double const > arra)
Assess if all the values in the vector are equal or if there are some different values.
MANTID_KERNEL_DLL std::vector< NumT > splitStringIntoVector(std::string listString, const std::string &separators=", ")
Take a string of comma or space-separated values, and splits it into a vector of doubles.
void MANTID_KERNEL_DLL convertToBinCentre(std::span< double const > bin_edges, std::vector< double > &bin_centres)
Convert an array of bin boundaries to bin center values.
void MANTID_KERNEL_DLL validateRebinParameters(std::vector< double > const &, bool const =false)
Validate rebinning parameters, throwing an error if any assumptions are invalidated.
size_t MANTID_KERNEL_DLL indexOfValueFromEdges(std::span< double const > bin_edges, const double value)
Gets the bin of a value from a vector of bin edges.
size_t MANTID_KERNEL_DLL indexOfValueFromCenters(std::span< double const > bin_centers, const double value)
Gets the bin of a value from a vector of bin centers and throws exception if out of range.
std::size_t 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.
std::string to_string(const wide_integer< Bits, Signed > &n)
A binary functor to compute the simple average of 2 numbers.