1 #include "RayleighHist.h" 5 #include "FourierBuffer.h" 15 Acclaim::RayleighHist::RayleighHist(FourierBuffer* fb,
16 const char* name,
const char* title) :
17 TH1D(name, title, 10, 0, 1), amplitudes(fb ? fb->bufferSize : 1000), fNx(10), fNumEvents(0), fNumNonEmptyBins(0),
19 theFitParams(
std::vector<double>(fNumFitParams, 0)),
20 theFitParamsSteps(
std::vector<double>(fNumFitParams, 1e-3)),
21 fChiSquaredFunc(this, &
Acclaim::RayleighHist::getRayleighChiSquare, fNumFitParams),
22 fBinWidth(1), fFitEveryNAdds(10), fNumAddsMod10(fFitEveryNAdds), fRayleighNorm(0){
24 binValues.resize(fNx, 0);
25 squaredBinErrors.resize(fNx, 0),
30 fracOfEventsWanted = 0.999;
32 fitMethod = kAdaptive;
34 chiSquareErrorMethod = kPoisson;
39 fRay = fParent ? (TF1*) fParent->fRay->Clone(TString::Format(
"%s_fit", name)) : NULL;
40 fParamsTF1.resize(2, 0);
43 grLastAddedAmp =
new TGraph(2);
44 grLastAddedAmp->SetName(
"grLastAddedAmplitude");
45 grLastAddedAmp->SetTitle(
"Last added amplitude");
47 fMinimizer = ROOT::Math::Factory::CreateMinimizer(
"Minuit2",
"");
49 fMinimizer->SetMaxFunctionCalls(1000);
50 fMinimizer->SetMaxIterations(1000);
51 fMinimizer->SetTolerance(0.001);
52 fMinimizer->SetPrintLevel(0);
57 fMinimizer->SetFunction(fChiSquaredFunc);
68 Acclaim::RayleighHist::~RayleighHist(){
81 delete grLastAddedAmp;
82 grLastAddedAmp = NULL;
87 void Acclaim::RayleighHist::guessMaxBinLimitAndSigmaFromMean(
double meanAmp,
double& upperLimit,
double& sigmaGuess,
double fracOfEventsInsideMaxAmp){
88 upperLimit = meanAmp*TMath::Sqrt(-(4./TMath::Pi())*TMath::Log(1.0 - fracOfEventsInsideMaxAmp));
89 sigmaGuess = TMath::Sqrt(2./TMath::Pi())*meanAmp;
95 double mean = amplitudes.getMean();
96 double maxAmp, sigmaGuess;
97 guessMaxBinLimitAndSigmaFromMean(mean, maxAmp, sigmaGuess, fracOfEventsWanted);
101 double xup = fBinWidth*(NUM_BINS+1);
102 if(xup/maxAmp > 0.9 && xup/maxAmp < 1.1){
113 const double desiredMaxAmp = meanAmp*TMath::Sqrt(-(4./TMath::Pi())*TMath::Log(1.0 - fracOfEventsWanted));
119 fBinWidth = desiredMaxAmp/NUM_BINS;
123 binCentres.resize(NUM_BINS, 0);
124 squaredBinCentres.resize(NUM_BINS, 0);
125 binValues.resize(NUM_BINS, 0);
126 squaredBinErrors.resize(NUM_BINS, 0);
128 SetBins(NUM_BINS, 0, desiredMaxAmp);
131 for(
int bx = 0; bx <= NUM_BINS + 1; bx++){
132 SetBinContent(bx, 0);
135 for(
int bin=0; bin < NUM_BINS; bin++){
142 if(amp < desiredMaxAmp){
143 int bin = floor(amp/fBinWidth);
156 fNumNonEmptyBins = 0;
157 for(
int bx=1; bx <= NUM_BINS; bx++){
159 double y = binValues[bx-1];
161 SetBinContent(bx, y);
162 SetBinError(bx, TMath::Sqrt(y));
165 binCentres[bx-1] = fBinWidth*0.5 + (bx-1)*fBinWidth;
166 squaredBinCentres[bx-1] = binCentres[bx-1]*binCentres[bx-1];
168 squaredBinErrors[bx-1] = binValues[bx-1];
169 fNumNonEmptyBins += y > 0 ? 1 : 0;
172 fRayleighNorm = amplitudes.size()*fBinWidth;
182 sign = sign >= 0 ? 1 : -1;
183 int bx = TH1D::Fill(amp, sign);
184 double n = GetBinContent(bx);
188 fNumNonEmptyBins += n == 1 ? 1 : 0;
190 SetBinError(bx, TMath::Sqrt(n));
191 if(bx > 0 && bx <= NUM_BINS){
193 squaredBinErrors[bx-1] = binValues[bx-1];
206 bool needRemoveOld = amplitudes.numElements() == amplitudes.size();
207 double oldAmp = amplitudes.insert(newAmp);
208 bool removedOld =
false;
217 double mean = amplitudes.getMean();
218 fRayleighAmpGuess = TMath::Sqrt(2./TMath::Pi())*mean;
220 rebinAndRefill(mean);
222 fRayleighNorm = fBinWidth*fNumEvents;
224 bool updatedFit =
false;
225 if(fNumAddsMod10 == fFitEveryNAdds){
232 grLastAddedAmp->SetPoint(0, newAmp, 0);
233 grLastAddedAmp->SetPoint(1, newAmp, 100000000);
243 rayAmp = fRayleighAmplitude;
244 chiSquare = fChiSquare;
250 double Acclaim::RayleighHist::getRayleighChiSquare(
const double* params){
252 double amplitude = params[0];
256 chiSquare = 9999 + fabs(amplitude);
259 double invAmpSquare = 1./(amplitude*amplitude);
260 double minusHalfInvAmpSquare = -0.5*invAmpSquare;
261 double exponentPreFactorPart = fRayleighNorm*invAmpSquare;
263 for(
int i=0; i < NUM_BINS; i++){
264 if(squaredBinErrors[i] > 0){
265 double yTheoretical = exponentPreFactorPart*binCentres[i]*exp(minusHalfInvAmpSquare*squaredBinCentres[i]);
266 double yMeasured = binValues[i];
267 double dy = yTheoretical - yMeasured;
268 if(chiSquareErrorMethod==kPoisson){
269 chiSquare += dy*dy/squaredBinErrors[i];
271 else if(chiSquareErrorMethod==kPearson){
272 chiSquare += dy*dy/yTheoretical;
275 std::cerr <<
"Error in " << __PRETTY_FUNCTION__
276 <<
". Unknown chiSquareErrorMethod" << std::endl;
287 void Acclaim::RayleighHist::fitRayleighAdaptive(
bool forGuiUpdateTF1){
289 fitRayleighJustEvalGuess(forGuiUpdateTF1);
291 if(fChiSquare / fNDF >= 2){
292 fitRayleighScan(forGuiUpdateTF1);
297 std::cout << __PRETTY_FUNCTION__ <<
"\t" << fChiSquare <<
"\t" << fNDF <<
"\t" << fNumNonEmptyBins <<
"\t" << std::endl;
304 void Acclaim::RayleighHist::fitRayleighScan(
bool forGuiUpdateTF1){
305 fNDF = fNumNonEmptyBins - 1;
308 const int nStep = 100;
309 double ds = 2*fRayleighAmpGuess/nStep;
311 double minChiSquare = 1e9;
312 for(
int s=1; s < nStep; s++){
314 double cs = getRayleighChiSquare(&);
317 if(cs < minChiSquare){
322 fRayleighAmplitude = bestAmp;
323 fChiSquare = minChiSquare;
327 std::cout << __PRETTY_FUNCTION__ <<
"\t" << fChiSquare <<
"\t" << fNDF <<
"\t" << fNumNonEmptyBins <<
"\t" << std::endl;
332 void Acclaim::RayleighHist::fitRayleighJustEvalGuess(
bool forGuiUpdateTF1){
334 fNDF = fNumNonEmptyBins;
335 fChiSquare = getRayleighChiSquare(&fRayleighAmplitude);
338 std::cout << __PRETTY_FUNCTION__ <<
"\t" << fChiSquare <<
"\t" << fNDF <<
"\t" << fNumNonEmptyBins <<
"\t" << std::endl;
344 void Acclaim::RayleighHist::fitRayleighTF1(){
345 fRay->FixParameter(0, fRayleighNorm);
346 fRay->SetParameter(1, fRayleighAmpGuess);
347 fRay->SetParLimits(1, 0, fRayleighAmpGuess*2);
348 this->Fit(fRay,
"Q0");
349 fChiSquare = fRay->GetChisquare();
350 fNDF = fRay->GetNDF();
351 fRayleighAmplitude = fRay->GetParameter(1);
352 std::cout << __PRETTY_FUNCTION__ <<
"\t" << fChiSquare <<
"\t" << fNDF <<
"\t" << fNumNonEmptyBins <<
"\t" << std::endl;
357 void Acclaim::RayleighHist::fitRayleighMinuit(
bool forGuiUpdateTF1){
358 theFitParams.at(0) = fRayleighAmpGuess;
359 fMinimizer->SetVariable(0,
"amplitude", fRayleighAmpGuess, theFitParamsSteps.at(0));
360 bool successfulMinimization = fMinimizer->Minimize();
361 if(!successfulMinimization){
362 std::cerr << fTitle <<
" failed the minimization, amplitude = " << theFitParams.at(0)
363 <<
"\t" <<
", intial guess was " << fRayleighAmpGuess << std::endl << std::endl;
365 fRayleighAmplitude = theFitParams.at(0);
367 fChiSquare = getRayleighChiSquare(&fRayleighAmplitude);
368 fNDF = fNumNonEmptyBins - 1;
371 std::cout << __PRETTY_FUNCTION__ <<
"\t" << fChiSquare <<
"\t" << fNDF <<
"\t" << fNumNonEmptyBins <<
"\t" << std::endl;
377 void Acclaim::RayleighHist::fitRayleigh(
bool forGuiUpdateTF1){
380 fRayleighAmplitude = fRayleighAmpGuess;
384 if(fitMethod==kAdaptive){
385 fitRayleighAdaptive(forGuiUpdateTF1);
387 else if(fitMethod==kScan){
388 fitRayleighScan(forGuiUpdateTF1);
390 else if(fitMethod==kJustEvalGuess){
391 fitRayleighJustEvalGuess(forGuiUpdateTF1);
393 else if(fitMethod==kTF1){
396 else if(fitMethod==kMinuit){
397 fitRayleighMinuit(forGuiUpdateTF1);
400 std::cerr <<
"Warning in " << __PRETTY_FUNCTION__
401 <<
", you asked for an unknown fit method so I'm not doing anything." << std::endl;
406 void Acclaim::RayleighHist::updateParamsOfTF1(){
408 if(fParamsTF1.at(0) != fRayleighNorm){
409 fParamsTF1.at(0) = fRayleighNorm;
410 fRay->SetParameter(0, fParamsTF1.at(0));
412 if(fParamsTF1.at(1) != fRayleighAmplitude){
413 fParamsTF1.at(1) = fRayleighAmplitude;
414 fRay->SetParameter(1, fParamsTF1.at(1));
420 void Acclaim::RayleighHist::Draw(Option_t* opt){
429 grLastAddedAmp->SetLineColor(kMagenta);
430 grLastAddedAmp->Draw(
"lsame");
void getRayleighFitParams(double &rayAmp, double &chiSquare, int &ndf)
Output Rayleigh distribution parameters.
virtual bool add(double newAmp)
Input amplitudes events.
Namespace which wraps everything in the library.
virtual int Fill(double amp, double sign=1)
Fill the histogram, this is called by add(double)
void rebinAndRefill(double meanAmp)
Dynamically rebin and refill histogram with contents of RingBuffer of amplitudes. ...
bool axisRangeOK() const
Checks current axis range is reasonable.