Newer
Older
Janik Zikovsky
committed
#include "MantidKernel/BinFinder.h"
#include "MantidKernel/Exception.h"
Peterson, Peter
committed
using std::size_t;
Janik Zikovsky
committed
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();
Janik Zikovsky
committed
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.");
Peterson, Peter
committed
for (size_t i=0; i<n/2; i++)
Janik Zikovsky
committed
{
//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 );
Janik Zikovsky
committed
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.");
Janik Zikovsky
committed
//Pre-do some calculations for log binning.
if (step < 0)
{
double log_step = log(1.0 + fabs(step));
logSteps.push_back( log_step );
if (i==0) logBoundaries.push_back( log(min) );
Janik Zikovsky
committed
logBoundaries.push_back( log(max) );
//How many bins is that?
Gigg, Martyn Anthony
committed
numBins = static_cast<int>(ceil ( (log(max) - log(min)) / log_step ));
Janik Zikovsky
committed
//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;
Janik Zikovsky
committed
}
else
{
//Empty log values; these won't be used
logSteps.push_back( 0 );
if (i==0) logBoundaries.push_back( 0 );
Janik Zikovsky
committed
logBoundaries.push_back( 0 );
Gigg, Martyn Anthony
committed
numBins = static_cast<int>(ceil( (max-min) / step ));
Janik Zikovsky
committed
//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;
Janik Zikovsky
committed
}
//Find the end bin index
int startBinIndex = 0;
if (i > 0) startBinIndex = this->endBinIndex[i-1];
endBinIndex.push_back( numBins + startBinIndex );
Janik Zikovsky
committed
}
//How many binning regions?
numRegions = 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;
}
Janik Zikovsky
committed
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/** 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, max;
//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];
max = boundaries[i+1];
if ( (x >= min) && (x < max) )
break;
}
//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
Gigg, Martyn Anthony
committed
index = static_cast<int>((x - min) / step);
//Add bin index offset if not in the first region
if (i > 0)
index += endBinIndex[i-1];
Janik Zikovsky
committed
//In the event that a final bin was skipped, cap to the max
if (index >= endBinIndex[i]) index = endBinIndex[i]-1;
Janik Zikovsky
committed
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];
Gigg, Martyn Anthony
committed
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];
Janik Zikovsky
committed
//In the event that a final bin was skipped, cap to the max
if (index >= endBinIndex[i]) index = endBinIndex[i]-1;