Newer
Older
#include "MantidKernel/BinFinder.h"
#include "MantidKernel/Exception.h"
using std::size_t;
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
namespace Mantid {
namespace Kernel {
/** Constructor. Sets up the calculation for later.
*
* @param binParams: the binning parameters, as a vector of doubles. E.g.
* 0, 1.0, 100, -0.5, 1e7:
* bins in step to 1.0 from 0 to 100; then log steps up to 1e7
*/
BinFinder::BinFinder(const std::vector<double> &binParams) {
boundaries.clear();
stepSizes.clear();
size_t n = binParams.size();
if (n < 3)
throw std::invalid_argument("BinFinder: not enough bin parameters.");
if (n % 2 == 0)
throw std::invalid_argument(
"BinFinder: the number of bin parameters should be odd.");
for (size_t i = 0; i < n / 2; i++) {
// The boundaries
double min = binParams[i * 2];
double max = binParams[i * 2 + 2];
// Only the first bin needs the min boundary
if (i == 0)
boundaries.push_back(min);
boundaries.push_back(max);
// The step
double step = binParams[i * 2 + 1];
stepSizes.push_back(step);
if (step == 0)
throw std::invalid_argument("BinFinder: step size of 0.");
if ((step < 0) && (min <= 0))
throw std::invalid_argument(
"BinFinder: logarithmic binning with 0.0 starting bin.");
if (max <= min)
throw std::invalid_argument(
"BinFinder: final bin must be > starting bin boundary.");
int numBins = 0;
// Pre-do some calculations for log binning.
if (step < 0) {
double log_step = log1p(fabs(step));
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
logSteps.push_back(log_step);
if (i == 0)
logBoundaries.push_back(log(min));
logBoundaries.push_back(log(max));
// How many bins is that?
numBins = static_cast<int>(ceil((log(max) - log(min)) / log_step));
// Check that the last bin is at least .25 x the previous step
// This is because the VectorHelper removes that final bin. Annoying!
double nextToLastValue = min * pow(1.0 + fabs(step), numBins - 1);
double nextToNextToLastValue = min * pow(1.0 + fabs(step), numBins - 2);
double lastBinSize = max - nextToLastValue;
double nextToLastBinSize = nextToLastValue - nextToNextToLastValue;
if (lastBinSize < nextToLastBinSize * 0.25)
numBins--;
if (numBins < 1)
numBins = 1;
} else {
// Empty log values; these won't be used
logSteps.push_back(0);
if (i == 0)
logBoundaries.push_back(0);
logBoundaries.push_back(0);
//# of linear bins
numBins = static_cast<int>(ceil((max - min) / step));
// Check that the last bin is at least .25 x the previous step
// This is because the VectorHelper removes that final bin. Annoying!
double lastBinSize = max - ((numBins - 1) * step + min);
if (lastBinSize < step * 0.25)
numBins--;
if (numBins < 1)
numBins = 1;
// Find the end bin index
int startBinIndex = 0;
if (i > 0)
startBinIndex = this->endBinIndex[i - 1];
endBinIndex.push_back(numBins + startBinIndex);
// How many binning regions?
numRegions = static_cast<int>(stepSizes.size());
}
/// Destructor
BinFinder::~BinFinder() {}
/** Returns the last bin boundary index,
* which should be == to the size of the X axis.
*/
int BinFinder::lastBinIndex() {
if (endBinIndex.size() > 0)
return endBinIndex[endBinIndex.size() - 1];
else
return -1;
}
/** Find the bin index for a value.
* @param x: x-value to histogram
* @return an int corresponding to the bin index to use, or -1 if out of bounds.
*/
int BinFinder::bin(double x) {
int index;
double min(boundaries[0]);
// Too small?
if (x < boundaries[0])
return -1;
// Find which binning region to use
int i = -1;
for (i = 0; i < numRegions; i++) {
min = boundaries[i];
double max = boundaries[i + 1];
if ((x >= min) && (x < max))
break;
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Didn't find it?
if (i >= numRegions)
return -1;
// Step size in this region
double step = stepSizes[i];
if (step > 0) {
// Linear binning. Truncate when you divide by the step size
index = static_cast<int>((x - min) / step);
// Add bin index offset if not in the first region
if (i > 0)
index += endBinIndex[i - 1];
// In the event that a final bin was skipped, cap to the max
if (index >= endBinIndex[i])
index = endBinIndex[i] - 1;
return index;
} else {
/** Log binning formula for bin index n:
* x_n = min * ( 1 + |step| ) ^ n
* log(x_n) = log(min) + n * log(1+|step|)
* therefore
* n = (log(x_n) - log(min))/log(1+|step|)
*/
double log_x = log(x); // Just one log to call per event!
double log_step = logSteps[i];
double log_min = logBoundaries[i];
index = static_cast<int>((log_x - log_min) / log_step);
// Add bin index offset if not in the first region
if (i > 0)
index += endBinIndex[i - 1];
// In the event that a final bin was skipped, cap to the max
if (index >= endBinIndex[i])
index = endBinIndex[i] - 1;
return index;