16 res_factor_(res_factor),
17 poly_order_(poly_order),
18 toy_poly_order_(toy_poly_order),
19 asymptotic_limit_(asymptotic_limit),
20 res_scale_(res_scale),
29 ROOT::Math::MinimizerOptions::SetDefaultStrategy(2);
32 std::cout <<
"[ BumpHunter ]: Shifting mass hypothesis to nearest bin center "
33 << mass_hypothesis <<
" ---> ";
34 int mbin = histogram->GetXaxis()->FindBin(mass_hypothesis);
36 std::cout <<
"[ BumpHunter ]: Histogram Bin Width is " <<
bin_width_ << std::endl;
37 mass_hypothesis = histogram->GetXaxis()->GetBinCenter(mbin);
38 std::cout << mass_hypothesis <<
" GeV" << std::endl;
43 throw std::runtime_error(
"Mass hypothesis less than the lower bound!");
52 std::cout <<
"[ BumpHunter ]: Mass resolution: " <<
mass_resolution_ <<
" GeV" << std::endl;
64 int window_start_bin = histogram->GetXaxis()->FindBin(
window_start_);
65 window_start_ = histogram->GetXaxis()->GetBinLowEdge(window_start_bin);
67 std::cout <<
"[ BumpHunter ]: Starting edge of window (" <<
window_start_
68 <<
" MeV) is below lower bound." << std::endl;
69 window_start_bin = histogram->GetXaxis()->FindBin(
lower_bound_);
70 window_start_ = histogram->GetXaxis()->GetBinLowEdge(window_start_bin);
79 int window_end_bin = histogram->GetXaxis()->FindBin(
window_end_);
80 window_end_ = histogram->GetXaxis()->GetBinUpEdge(window_end_bin);
82 std::cout <<
"[ BumpHunter ]: Upper edge of window (" <<
window_end_
83 <<
" GeV) is above upper bound." << std::endl;
84 window_end_bin = histogram->GetXaxis()->FindBin(
upper_bound_);
86 int last_bin_above = histogram->FindLastBinAbove();
87 if(window_end_bin > last_bin_above) window_end_bin = last_bin_above;
89 window_end_ = histogram->GetXaxis()->GetBinUpEdge(window_end_bin);
91 window_start_ = histogram->GetXaxis()->GetBinLowEdge(window_start_bin);
94 bins_ = window_end_bin - window_start_bin + 1;
95 std::cout <<
"[ BumpHunter ]: Setting starting edge of fit window to "
96 <<
window_start_ <<
" GeV. Bin " << window_start_bin << std::endl;
97 std::cout <<
"[ BumpHunter ]: Setting upper edge of fit window to "
98 <<
window_end_ <<
" GeV. Bin " << window_end_bin << std::endl;
99 std::cout <<
"[ BumpHunter ]: Total Number of Bins: " <<
bins_ << std::endl;
104 integral_ = histogram->Integral(window_start_bin, window_end_bin);
105 std::cout <<
"[ BumpHunter ]: Integral within window: " <<
integral_ << std::endl;
122 TF1* bkg_toys{
nullptr};
124 std::cout <<
"Defining fit functions." << std::endl;
125 std::cout <<
" Model :: ";
131 std::cout <<
" Background Order :: ";
134 std::cout <<
"1" << std::endl;
137 std::cout <<
"3" << std::endl;
140 std::cout <<
"5" << std::endl;
144 std::cout <<
" Toy Generator Order :: ";
147 std::cout <<
"1" << std::endl;
150 std::cout <<
"3" << std::endl;
153 std::cout <<
"5" << std::endl;
156 std::cout <<
"7" << std::endl;
164 std::cout <<
"*************************************************" << std::endl;
165 std::cout <<
"*************************************************" << std::endl;
166 std::cout <<
"[ BumpHunter ]: Performing background only fits." << std::endl;
167 std::cout <<
"*************************************************" << std::endl;
168 std::cout <<
"*************************************************" << std::endl;
173 bkg =
new TF1(
"bkg", bkg_func, -1, 1,
poly_order_ + 1);
176 bkg =
new TF1(
"bkg", bkg_func, -1, 1,
poly_order_ + 1);
178 bkg->SetParameter(0, initNorm);
179 bkg->SetParName(0,
"pol0");
181 bkg->SetParameter(i, 0);
182 bkg->SetParName(i, Form(
"pol%i", i));
188 bkg_toys =
new TF1(
"bkg_toys", bkg_toy_func, -1, 1,
toy_poly_order_ + 1);
191 bkg_toys =
new TF1(
"bkg_toys", bkg_toy_func, -1, 1,
toy_poly_order_ + 1);
193 bkg_toys->SetParameter(0, initNorm);
194 bkg_toys->SetParName(0,
"pol0");
196 bkg_toys->SetParameter(i, 0);
197 bkg_toys->SetParName(i, Form(
"pol%i", i));
204 std::cout <<
"*************************************************" << std::endl;
205 std::cout <<
"*************************************************" << std::endl;
206 std::cout <<
"[ BumpHunter ]: Performing background Toy fit." << std::endl;
207 std::cout <<
"*************************************************" << std::endl;
208 std::cout <<
"*************************************************" << std::endl;
215 std::cout <<
"***************************************************" << std::endl;
216 std::cout <<
"***************************************************" << std::endl;
217 std::cout <<
"[ BumpHunter ]: Performing a signal+background fit." << std::endl;
218 std::cout <<
"***************************************************" << std::endl;
219 std::cout <<
"***************************************************" << std::endl;
226 full =
new TF1(
"full", full_func, -1, 1,
poly_order_ + 4);
229 full =
new TF1(
"full", full_func, -1, 1,
poly_order_ + 4);
231 full->SetParameter(0, initNorm);
232 full->SetParName(0,
"pol0");
238 full->SetParameter(i, 0);
239 full->SetParName(i, Form(
"pol%i", i));
241 full->FixParameter(
poly_order_ + 2, mass_hypothesis);
244 for(
int parI = 0; parI <
poly_order_ + 1; parI++) {
245 full->SetParameter(parI, bkg->GetParameter(parI));
251 std::cout <<
"[ BumpHunter ]: Bkg Fit Status: " << fit_result->
getBkgFitResult()->IsValid() << std::endl;
252 std::cout <<
"[ BumpHunter ]: Bkg Toys Fit Status: " << fit_result->
getBkgToysFitResult()->IsValid() << std::endl;
253 std::cout <<
"[ BumpHunter ]: Full Fit Status: " << fit_result->
getCompFitResult()->IsValid() << std::endl;
254 if((!skip_ul) && full_result->IsValid()) {
getUpperLimit(histogram, fit_result); }
272 std::cout <<
"[ BumpHunter ]: Calculating p-value: " << std::endl;
275 printDebug(
"Signal yield: " + std::to_string(signal_yield));
290 printDebug(
"NLL when mu = " + std::to_string(signal_yield) +
": " + std::to_string(mle_nll));
294 printDebug(
"NLL when mu = 0: " + std::to_string(bkg_nll));
303 double r0 = -2.0*(mle_nll - bkg_nll);
304 if(signal_yield < 0.0 ) r0 = 2.0*(mle_nll - bkg_nll);
306 printDebug(
"Z0: " + std::to_string(r0*TMath::Sqrt(fabs(r0))/fabs(r0)));
307 p_value = 1 - ROOT::Math::gaussian_cdf( r0*TMath::Sqrt(fabs(r0))/fabs(r0) );
309 std::cout <<
"[ BumpHunter ]: p-value: " << p_value << std::endl;
317 std::cout <<
"[ BumpHunter ]: " << message << std::endl;
329 std::cout <<
"[ BumpHunter ]: Calculating upper limit." << std::endl;
333 if(signal_yield < 0) { signal_yield = 0; }
337 std::cout <<
"Signal Yield :: " << signal_yield << std::endl;
338 std::cout <<
"Signal Yield Error :: " << signal_yield_error << std::endl;
343 double upper_limit = signal_yield + 1.64 * signal_yield_error;
346 double p_value = 0.05;
349 std::cout <<
"Upper Limit :: " << upper_limit << std::endl;
350 std::cout <<
"p-Value :: " << p_value << std::endl;
371 comp =
new TF1(
"comp_ul", comp_func, -1, 1,
poly_order_ + 4);
374 comp =
new TF1(
"comp_ul", comp_func, -1, 1,
poly_order_ + 4);
376 comp->SetParameter(0, initNorm);
377 comp->SetParName(0,
"pol0");
383 comp->SetParameter(i, 0);
384 comp->SetParName(i, Form(
"pol%i", i));
393 std::cout <<
"[ BumpHunter ]: Calculating upper limit." << std::endl;
398 printDebug(
"Signal yield: " + std::to_string(mu_hat));
399 printDebug(
"Sigma: " + std::to_string(sigma));
407 printDebug(
"MLE NLL: " + std::to_string(mle_nll));
408 printDebug(
"mu=0 NLL: " + std::to_string(bkg_nll));
410 double mu95up = fabs(mu_hat + 1.64*sigma);
415 double r_mu = -9999999.9;
422 double mu_nll = full_result->MinFcnValue();
424 double nllamb_mu = mu_nll - mle_nll;
425 if(mu_hat < 0.0) nllamb_mu = mu_nll - bkg_nll;
427 r_mu = 2.0*nllamb_mu;
428 if(mu_hat > mu95up) r_mu = -2.0*nllamb_mu;
432 p_mu = 1.0 - ROOT::Math::gaussian_cdf( -1.0*TMath::Sqrt(-1.0*r_mu) );
433 p_b = 1.0 - ROOT::Math::gaussian_cdf( -1.0*TMath::Sqrt(-1.0*r_mu) - (mu95up/sigma) );
435 else if(r_mu > mu95up*mu95up/(sigma*sigma))
437 p_mu = 1.0 - ROOT::Math::gaussian_cdf( (r_mu + mu95up*mu95up/(sigma*sigma))/(2.0*mu95up/sigma) );
438 p_b = 1.0 - ROOT::Math::gaussian_cdf( (r_mu - mu95up*mu95up/(sigma*sigma))/(2.0*mu95up/sigma) );
442 p_mu = 1.0 - ROOT::Math::gaussian_cdf( TMath::Sqrt(r_mu) );
443 p_b = 1.0 - ROOT::Math::gaussian_cdf( TMath::Sqrt(r_mu) - (mu95up/sigma) );
449 std::cout <<
"[ BumpHunter ]: mu: " << mu95up << std::endl;
450 std::cout <<
"[ BumpHunter ]: mle_nll: " << mle_nll << std::endl;
451 std::cout <<
"[ BumpHunter ]: bkg_nll: " << bkg_nll << std::endl;
452 std::cout <<
"[ BumpHunter ]: mu_nll: " << mu_nll << std::endl;
453 std::cout <<
"[ BumpHunter ]: nllamb_mu: " << nllamb_mu << std::endl;
454 std::cout <<
"[ BumpHunter ]: r_mu: " << r_mu << std::endl;
455 std::cout <<
"[ BumpHunter ]: p_mu: " << p_mu << std::endl;
456 std::cout <<
"[ BumpHunter ]: p_b: " << p_b << std::endl;
457 std::cout <<
"[ BumpHunter ]: CLs: " << CLs << std::endl;
460 std::cout <<
"[ BumpHunter ]: Upper limit aborting, too many tries" << std::endl;
467 if((CLs <= 0.051 && CLs > 0.049))
469 std::cout <<
"[ BumpHunter ]: Upper limit: " << mu95up << std::endl;
470 std::cout <<
"[ BumpHunter ]: CLs: " << CLs << std::endl;
477 else if(CLs <= 1e-10) { mu95up = mu95up*0.1; }
478 else if(CLs <= 1e-8) { mu95up = mu95up*0.5; }
479 else if(CLs <= 1e-4) { mu95up = mu95up*0.8; }
480 else if(CLs <= 0.01) { mu95up = mu95up*0.9; }
481 else if(CLs <= 0.04) { mu95up = mu95up*0.99; }
482 else if(CLs <= 0.049) { mu95up = mu95up*0.999; }
483 else if(CLs <= 0.1) { mu95up = mu95up*1.01; }
484 else { mu95up = mu95up*1.1; }
485 printDebug(
"Setting mu to: " + std::to_string(mu95up));
503 comp =
new TF1(
"comp_ul", comp_func, -1, 1,
poly_order_ + 4);
506 comp =
new TF1(
"comp_ul", comp_func, -1, 1,
poly_order_ + 4);
508 comp->SetParameter(0, initNorm);
509 comp->SetParName(0,
"pol0");
515 comp->SetParameter(i, 0);
516 comp->SetParName(i, Form(
"pol%i", i));
522 std::cout <<
"[ BumpHunter ]: Calculating upper limit." << std::endl;
526 printDebug(
"Signal yield: " + std::to_string(signal_yield));
533 if(signal_yield < 0) {
534 printDebug(
"Signal yield @ min NLL is < 0. Using NLL when signal yield = 0");
541 printDebug(
"MLE NLL: " + std::to_string(mle_nll));
543 signal_yield = floor(signal_yield) + 1;
548 printDebug(
"Setting signal yield to: " + std::to_string(signal_yield));
553 double cond_nll = full_result->MinFcnValue();
559 printDebug(
"p-value after fit : " + std::to_string(p_value));
560 std::cout <<
"[ BumpHunter ]: Current Signal Yield: " << signal_yield << std::endl;
561 std::cout <<
"[ BumpHunter ]: Current p-value: " << p_value << std::endl;
563 if((p_value <= 0.0455 && p_value > 0.044)) {
564 std::cout <<
"[ BumpHunter ]: Upper limit: " << signal_yield << std::endl;
565 std::cout <<
"[ BumpHunter ]: p-value: " << p_value << std::endl;
571 }
else if (signal_yield <= 0 && p_value < 0.044) {
572 std::cout <<
"[ BumpHunter ]: Caution Background Model suspicious!" << std::endl;
573 std::cout <<
"[ BumpHunter ]: Upper limit: " << signal_yield << std::endl;
574 std::cout <<
"[ BumpHunter ]: p-value: " << p_value << std::endl;
581 else if(p_value <= 0.044) { signal_yield -= 1; }
582 else if(p_value <= 0.059) { signal_yield += 10; }
583 else if(p_value <= 0.10) { signal_yield += 20; }
584 else if(p_value <= 0.2) { signal_yield += 40; }
585 else { signal_yield += 100; }
589std::vector<TH1*>
BumpHunter::generateToys(TH1* histogram,
double n_toys,
int seed,
int toy_sig_samples,
int bkg_mult, TH1* signal_hist) {
590 gRandom->SetSeed(seed);
592 TF1* bkg_toys = histogram->GetFunction(
"bkg_toys");
596 std::vector<TH1*> hists;
597 int bkg_events = bkg_mult * int(
integral_);
598 for(
int itoy = 0; itoy < n_toys; ++itoy) {
599 std::string name =
"invariant_mass_" + std::to_string(itoy);
601 std::cout <<
"Generating Toy " << itoy << std::endl;
604 for(
int i = 0; i < bkg_events; ++i) {
607 for(
int i = 0; i < toy_sig_samples; i++) {
608 double sig_sample = 0;
609 if(signal_hist != NULL) { sig_sample = signal_hist->GetRandom(); }
611 hist->Fill(sig_sample);
613 hists.push_back(hist);
589std::vector<TH1*>
BumpHunter::generateToys(TH1* histogram,
double n_toys,
int seed,
int toy_sig_samples,
int bkg_mult, TH1* signal_hist) {
…}
622 double diff = cond_nll - mle_nll;
628 p_value = ROOT::Math::chisquared_cdf_c(q0, 1);
641 double offset = -1.19892320e4 * pow(mass, 3) + 1.50196798e3 * pow(mass, 2) - 8.38873712e1 * mass + 6.23215746;
643 printDebug(
"Offset: " + std::to_string(offset));
644 double cmass = mass - mass * offset;
645 printDebug(
"Corrected Mass: " + std::to_string(cmass));
void getChi2Prob(double min_nll_null, double min_nll, double &q0, double &p_value)
description
void printDebug(std::string message)
Print debug statement.
double correctMass(double mass)
description
void calculatePValue(HpsFitResult *result)
description
void getUpperLimitPower(TH1 *histogram, HpsFitResult *result)
description
void getUpperLimitAsymptotic(TH1 *histogram, HpsFitResult *result)
description
void getUpperLimitAsymCLs(TH1 *histogram, HpsFitResult *result)
description
void initialize(TH1 *histogram, double &mass_hypothesis)
Given the mass of interest, setup the window parameters and initialize the fit parameters.
double getMassResolution(double mass)
BumpHunter(FitFunction::BkgModel model, int poly_order, int toy_poly_order, int res_factor, double res_scale=1.00, bool asymptotic_limit=true)
Default Constructor.
bool window_use_res_scale_
FitFunction::BkgModel bkg_model_
void setBounds(double low_bound, double high_bound)
Set the histogram bounds.
std::vector< TH1 * > generateToys(TH1 *histogram, double n_toys, int seed, int toy_sig_samples, int bkg_mult=1, TH1 *signal_hist=nullptr)
description
void getUpperLimit(TH1 *histogram, HpsFitResult *result)
Get the signal upper limit.
HpsFitResult * performSearch(TH1 *histogram, double mass_hypothesis, bool skip_bkg_fit, bool skip_ul)
Perform a search for a resonance at the given mass hypothesis.
void setCompFitResult(TFitResultPtr comp_result)
Set the result from the signal+background only fit.
void setIntegral(double integral)
Set the integral.
TFitResultPtr getBkgFitResult()
Get background fit result.
void setBkgModelType(FitFunction::BkgModel bkg_model)
Sets the type of background fit function used by the fitter.
void setQ0(double q0)
Set Q0.
TFitResultPtr getCompFitResult()
Get the complete fit result.
void setPolyOrder(int poly_order)
Set the order polynomial used by the fitter.
void setCorrectedMass(double cmass)
Set the corrected mass.
void setPValue(double p_value)
Set the p-value.
void setMass(double mass)
Set mass hypothesis used for this fit.
void setBkgToysFitResult(TFitResultPtr bkg_toy_result)
Set the result from the background only fit for generating toys.
void setUpperLimit(double upper_limit)
Set the 2 sigma upper limit.
void setBkgFitResult(TFitResultPtr bkg_result)
Set the result from the background only fit.
void setUpperLimitPValue(double upper_limit_p_value)
Set the p-value at the end of the upper limit calculation.
TFitResultPtr getBkgToysFitResult()
Get background toy model fit result.
void setWindowSize(double window_size)
Set the size of the fit window used to get this results.