Mantid
Loading...
Searching...
No Matches
BinFinder.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 <cmath>
9#include <cstddef>
10#include <stdexcept>
11
12using std::size_t;
13
14namespace Mantid::Kernel {
15
22BinFinder::BinFinder(const std::vector<double> &binParams) {
23 boundaries.clear();
24 stepSizes.clear();
25
26 size_t n = binParams.size();
27 if (n < 3)
28 throw std::invalid_argument("BinFinder: not enough bin parameters.");
29 if (n % 2 == 0)
30 throw std::invalid_argument("BinFinder: the number of bin parameters should be odd.");
31
32 for (size_t i = 0; i < n / 2; i++) {
33 // The boundaries
34 double min = binParams[i * 2];
35 double max = binParams[i * 2 + 2];
36 // Only the first bin needs the min boundary
37 if (i == 0)
38 boundaries.emplace_back(min);
39 boundaries.emplace_back(max);
40 // The step
41 double step = binParams[i * 2 + 1];
42 stepSizes.emplace_back(step);
43 if (step == 0)
44 throw std::invalid_argument("BinFinder: step size of 0.");
45 if ((step < 0) && (min <= 0))
46 throw std::invalid_argument("BinFinder: logarithmic binning with 0.0 starting bin.");
47 if (max <= min)
48 throw std::invalid_argument("BinFinder: final bin must be > starting bin boundary.");
49
50 int numBins = 0;
51
52 // Pre-do some calculations for log binning.
53 if (step < 0) {
54 double log_step = log1p(fabs(step));
55 logSteps.emplace_back(log_step);
56 if (i == 0)
57 logBoundaries.emplace_back(log(min));
58 logBoundaries.emplace_back(log(max));
59 // How many bins is that?
60 numBins = static_cast<int>(ceil((log(max) - log(min)) / log_step));
61
62 // Check that the last bin is at least .25 x the previous step
63 // This is because the VectorHelper removes that final bin. Annoying!
64 double nextToLastValue = min * pow(1.0 + fabs(step), numBins - 1);
65 double nextToNextToLastValue = min * pow(1.0 + fabs(step), numBins - 2);
66 double lastBinSize = max - nextToLastValue;
67 double nextToLastBinSize = nextToLastValue - nextToNextToLastValue;
68 if (lastBinSize < nextToLastBinSize * 0.25)
69 numBins--;
70 if (numBins < 1)
71 numBins = 1;
72
73 } else {
74 // Empty log values; these won't be used
75 logSteps.emplace_back(0);
76 if (i == 0)
77 logBoundaries.emplace_back(0);
78 logBoundaries.emplace_back(0);
79 //# of linear bins
80 numBins = static_cast<int>(ceil((max - min) / step));
81
82 // Check that the last bin is at least .25 x the previous step
83 // This is because the VectorHelper removes that final bin. Annoying!
84 double lastBinSize = max - ((numBins - 1) * step + min);
85 if (lastBinSize < step * 0.25)
86 numBins--;
87 if (numBins < 1)
88 numBins = 1;
89 }
90
91 // Find the end bin index
92 int startBinIndex = 0;
93 if (i > 0)
94 startBinIndex = this->endBinIndex[i - 1];
95 endBinIndex.emplace_back(numBins + startBinIndex);
96 }
97 // How many binning regions?
98 numRegions = static_cast<int>(stepSizes.size());
99}
100
105 if (!endBinIndex.empty())
106 return endBinIndex.back();
107 else
108 return -1;
109}
110
115int BinFinder::bin(double x) {
116 int index;
117 double min(boundaries[0]);
118
119 // Too small?
120 if (x < boundaries[0])
121 return -1;
122
123 // Find which binning region to use
124 int i = -1;
125 for (i = 0; i < numRegions; i++) {
126 min = boundaries[i];
127 double max = boundaries[i + 1];
128 if ((x >= min) && (x < max))
129 break;
130 }
131 // Didn't find it?
132 if (i >= numRegions)
133 return -1;
134
135 // Step size in this region
136 double step = stepSizes[i];
137 if (step > 0) {
138 // Linear binning. Truncate when you divide by the step size
139 index = static_cast<int>((x - min) / step);
140 // Add bin index offset if not in the first region
141 if (i > 0)
142 index += endBinIndex[i - 1];
143 // In the event that a final bin was skipped, cap to the max
144 if (index >= endBinIndex[i])
145 index = endBinIndex[i] - 1;
146 return index;
147 } else {
155 double log_x = log(x); // Just one log to call per event!
156 double log_step = logSteps[i];
157 double log_min = logBoundaries[i];
158 index = static_cast<int>((log_x - log_min) / log_step);
159 // Add bin index offset if not in the first region
160 if (i > 0)
161 index += endBinIndex[i - 1];
162 // In the event that a final bin was skipped, cap to the max
163 if (index >= endBinIndex[i])
164 index = endBinIndex[i] - 1;
165 return index;
166 }
167}
168} // namespace Mantid::Kernel
std::map< DeltaEMode::Type, std::string > index
Definition: DeltaEMode.cpp:19
#define fabs(x)
Definition: Matrix.cpp:22
std::vector< double > boundaries
Boundaries between binning regions.
Definition: BinFinder.h:40
std::vector< double > logSteps
Log of the step size (used by log binning)
Definition: BinFinder.h:44
std::vector< double > logBoundaries
Log of the boundary (used by log binning)
Definition: BinFinder.h:46
std::vector< double > stepSizes
Step sizes in binning regions; 1 smaller than boundaries.
Definition: BinFinder.h:42
int lastBinIndex()
Returns the last bin boundary index, which should be == to the size of the X axis.
Definition: BinFinder.cpp:104
int bin(double x)
Find the bin index for a value.
Definition: BinFinder.cpp:115
BinFinder(const std::vector< double > &binParams)
Constructor.
Definition: BinFinder.cpp:22
int numRegions
How many regions?
Definition: BinFinder.h:50
std::vector< int > endBinIndex
Index of the last boundary in the bins.
Definition: BinFinder.h:48