9 RooRealVar &mu, RooRealVar &sigma) {
10 return new RooGaussian(name.Data(), name.Data(), x, mu, sigma);
14 RooRealVar &mu, RooRealVar &sigma) {
15 return new RooStepShelf(name.Data(), name.Data(), x, mu, sigma);
19 RooRealVar &mu, RooRealVar &sigma,
20 RooRealVar &tau_ratio) {
21 return new RooLowExpTail(name.Data(), name.Data(), x, mu, sigma, tau_ratio);
25 RooRealVar &mu, RooRealVar &sigma,
27 return new RooLowLinTail(name.Data(), name.Data(), x, mu, sigma, slope);
31 RooRealVar &mu, RooRealVar &sigma,
32 RooRealVar &tau_ratio) {
33 return new RooHighExpTail(name.Data(), name.Data(), x, mu, sigma, tau_ratio);
39 return new RooPolynomial(name.Data(), name.Data(), x, RooArgList(slope));
42void RooFitUtils::RegisterOwned(RooAbsArg *arg) { owned_args_.push_back(arg); }
44void RooFitUtils::InitState() {
45 working_hist_ =
nullptr;
49 display_bin_width_kev_ = 0;
50 use_flat_background_ = kFALSE;
52 use_low_exp_tail_ = kFALSE;
53 use_low_lin_tail_ = kFALSE;
54 use_high_exp_tail_ = kFALSE;
55 use_manual_init_ = kFALSE;
56 interactive_ = kFALSE;
60 unbinned_data_ =
nullptr;
64 sim_category_ =
nullptr;
66 sim_combined_data_ =
nullptr;
69 RooMsgService::instance().setGlobalKillBelow(RooFit::WARNING);
70 for (Int_t i = 0; i < RooMsgService::instance().numStreams(); i++) {
71 RooMsgService::instance().getStream(i).removeTopic(RooFit::InputArguments);
73 RooAbsReal::setEvalErrorLoggingMode(RooAbsReal::Ignore);
74 RooRealVar::enableSilentClipping();
79 std::vector<Double_t> out;
83 Double_t energy_d = 0;
84 TBranch *br = tree->GetBranch(branch_name.Data());
86 std::cerr <<
"ERROR: LoadEventsFromTree: branch '" << branch_name
87 <<
"' not found" << std::endl;
90 TLeaf *leaf = br->GetLeaf(branch_name.Data());
91 Bool_t is_double = (leaf && TString(leaf->GetTypeName()) ==
"Double_t");
93 tree->SetBranchAddress(branch_name.Data(), &energy_d);
95 tree->SetBranchAddress(branch_name.Data(), &energy_f);
97 Long64_t n = tree->GetEntries();
99 for (Long64_t i = 0; i < n; i++) {
101 out.push_back(is_double ? energy_d : (Double_t)energy_f);
103 tree->ResetBranchAddresses();
108 const std::vector<Double_t> &events, Float_t fit_range_low,
109 Float_t fit_range_high, Float_t display_bin_width_kev) {
110 Float_t hist_lo = 0.85f * fit_range_low;
111 Float_t hist_hi = 1.15f * fit_range_high;
112 Int_t nbins = TMath::Max(
113 1, (Int_t)TMath::Nint((hist_hi - hist_lo) / display_bin_width_kev));
114 hist_hi = hist_lo + nbins * display_bin_width_kev;
116 TH1F *hist =
new TH1F(hname,
117 TString::Format(
"; Energy [keV]; Counts / %.0f eV",
118 display_bin_width_kev * 1000.0),
119 nbins, hist_lo, hist_hi);
120 hist->SetDirectory(0);
121 for (
size_t i = 0; i < events.size(); i++) {
122 Double_t e = events[i];
123 if (e >= hist_lo && e < hist_hi)
130 const std::vector<Double_t> &events,
131 Float_t fit_range_low,
132 Float_t fit_range_high,
133 Float_t display_bin_width_kev) {
134 Float_t hist_lo = 0.85f * fit_range_low;
135 Float_t hist_hi = 1.15f * fit_range_high;
136 Int_t nbins = TMath::Max(
137 1, (Int_t)TMath::Nint((hist_hi - hist_lo) / display_bin_width_kev));
138 hist_hi = hist_lo + nbins * display_bin_width_kev;
139 hist->SetBins(nbins, hist_lo, hist_hi);
141 for (
size_t i = 0; i < events.size(); i++) {
142 Double_t e = events[i];
143 if (e >= hist_lo && e < hist_hi)
149RooFitUtils::BuildUnbinnedDataFrom(
const std::vector<Double_t> &events,
152 RooDataSet *ds =
new RooDataSet(
"unbinned_data",
"unbinned_data", vars);
153 Double_t xmin = x->getMin();
154 Double_t xmax = x->getMax();
155 for (
size_t i = 0; i < events.size(); i++) {
156 Double_t e = events[i];
157 if (e < xmin || e > xmax)
165void RooFitUtils::BuildDisplayHistogram() {
166 delete working_hist_;
168 events_, fit_range_low_, fit_range_high_, display_bin_width_kev_);
171void RooFitUtils::BuildUnbinnedData() {
172 delete unbinned_data_;
173 unbinned_data_ = BuildUnbinnedDataFrom(events_, x_);
182 Float_t fit_range_low, Float_t fit_range_high,
183 Float_t display_bin_width_kev,
184 Bool_t use_flat_background, Bool_t use_step,
185 Bool_t use_low_exp_tail, Bool_t use_low_lin_tail,
186 Bool_t use_high_exp_tail) {
189 fit_range_low_ = fit_range_low;
190 fit_range_high_ = fit_range_high;
191 display_bin_width_kev_ = display_bin_width_kev;
192 use_flat_background_ = use_flat_background;
193 use_step_ = use_step;
194 use_low_exp_tail_ = use_low_exp_tail;
195 use_low_lin_tail_ = use_low_lin_tail;
196 use_high_exp_tail_ = use_high_exp_tail;
198 BuildDisplayHistogram();
200 std::cout <<
"Fit configuration:" << std::endl;
201 std::cout << std::endl;
202 if (use_flat_background_) {
203 std::cout <<
"Background: FLAT" << std::endl;
205 std::cout <<
"Background: LINEAR" << std::endl;
207 std::cout <<
"Step function: " << (use_step_ ?
"ENABLED" :
"DISABLED")
209 std::cout <<
"Low exponential tail: "
210 << (use_low_exp_tail_ ?
"ENABLED" :
"DISABLED") << std::endl;
211 std::cout <<
"Low linear tail: "
212 << (use_low_lin_tail_ ?
"ENABLED" :
"DISABLED") << std::endl;
213 std::cout <<
"High exponential tail: "
214 << (use_high_exp_tail_ ?
"ENABLED" :
"DISABLED") << std::endl;
215 std::cout <<
"Unbinned events: " << events_.size() << std::endl;
219 for (
size_t i = 0; i < owned_args_.size(); i++) {
220 delete owned_args_[i];
223 delete unbinned_data_;
224 delete sim_combined_data_;
226 delete sim_category_;
227 std::map<TString, RooDataSet *>::iterator dit;
228 for (dit = sim_channel_data_.begin(); dit != sim_channel_data_.end(); ++dit) {
231 sim_channel_data_.clear();
232 for (
size_t i = 0; i < sim_channels_.size(); i++) {
233 delete sim_channels_[i].hist;
235 delete working_hist_;
239 Int_t expected = num_peaks_ * 10 + 2;
240 if (num_peaks_ == 0 || params.size() != (
size_t)expected) {
241 std::cerr <<
"ERROR: Manual parameters size (" << params.size()
242 <<
") doesn't match expected (" << expected <<
")" << std::endl;
246 manual_params_ = params;
247 use_manual_init_ = kTRUE;
249 std::vector<RooRealVar *> all = CollectAllParams();
250 for (
size_t i = 0; i < all.size(); i++) {
251 all[i]->setVal(params[i]);
252 all[i]->setConstant(kTRUE);
255 std::cout <<
"Manual parameters set:" << std::endl;
256 for (
size_t i = 0; i < all.size(); i++) {
257 std::cout <<
" Par[" << i <<
"] " << all[i]->GetName() <<
" = "
258 << params[i] << std::endl;
263 std::vector<RooRealVar *> all = CollectAllParams();
264 if (index < 0 || index >= (Int_t)all.size()) {
265 std::cerr <<
"ERROR: Parameter index " << index <<
" out of range [0, "
266 << (Int_t)all.size() - 1 <<
"]" << std::endl;
270 if (!use_manual_init_) {
271 manual_params_.resize(all.size(), 0.0);
272 use_manual_init_ = kTRUE;
275 manual_params_[index] = value;
276 all[index]->setVal(value);
277 all[index]->setConstant(kTRUE);
279 std::cout <<
"Set Par[" << index <<
"] " << all[index]->GetName() <<
" = "
280 << value << std::endl;
283Double_t RooFitUtils::EstimateBackground() {
284 Int_t left_bin = working_hist_->FindBin(fit_range_low_);
285 Int_t right_bin = working_hist_->FindBin(fit_range_high_);
287 Int_t n_sideband = (right_bin - left_bin) / 10;
288 Double_t left_avg = 0;
289 Double_t right_avg = 0;
291 for (Int_t i = 0; i < n_sideband; i++) {
292 left_avg += working_hist_->GetBinContent(left_bin + i);
293 right_avg += working_hist_->GetBinContent(right_bin - i);
296 return (left_avg + right_avg) / (2.0 * n_sideband);
299void RooFitUtils::BuildPeak(Int_t peak_idx, Double_t mu_init,
300 Double_t sigma_init, Double_t peak_height,
301 Double_t range_width) {
303 TString suffix = TString::Format(
"%d", peak_idx + 1);
305 p.
mu =
new RooRealVar(
"Mu" + suffix,
"Mu" + suffix, mu_init, fit_range_low_,
307 Double_t sigma_lo = working_hist_->GetBinWidth(1);
308 Double_t sigma_hi = range_width * 0.5;
309 if (sigma_init < sigma_lo)
310 sigma_init = 2.0 * sigma_lo;
311 p.
sigma =
new RooRealVar(
"Sigma" + suffix,
"Sigma" + suffix, sigma_init,
314 Int_t mu_bin = working_hist_->FindBin(mu_init);
315 Double_t local_height = working_hist_->GetBinContent(mu_bin);
316 Double_t bkg_floor = EstimateBackground();
317 Double_t net_height = local_height - bkg_floor;
318 if (net_height < 0.1 * local_height)
319 net_height = 0.1 * local_height;
320 Double_t total_init =
321 net_height * sigma_init * TMath::Sqrt(2.0 * TMath::Pi());
323 new RooRealVar(
"GausAmplitude" + suffix,
"GausAmplitude" + suffix,
324 total_init, 0, peak_height * range_width * 10.0);
326 p.
ratio_step =
new RooRealVar(
"StepAmplitude" + suffix,
327 "StepAmplitude" + suffix, 0.0, 0.0, 0.5);
329 new RooRealVar(
"LowExpTailAmplitude" + suffix,
330 "LowExpTailAmplitude" + suffix, 0.0, 0.0, 0.5);
332 new RooRealVar(
"LowExpTailRatio" + suffix,
"LowExpTailRatio" + suffix,
333 1.5, 1.0, tail_ratio_max_);
335 new RooRealVar(
"LowLinTailAmplitude" + suffix,
336 "LowLinTailAmplitude" + suffix, 0.0, 0.0, 0.5);
338 "LowLinTailSlope" + suffix, 0.0, -0.1, 0.1);
340 new RooRealVar(
"HighExpTailAmplitude" + suffix,
341 "HighExpTailAmplitude" + suffix, 0.0, 0.0, 0.5);
343 new RooRealVar(
"HighExpTailRatio" + suffix,
"HighExpTailRatio" + suffix,
344 1.5, 1.0, tail_ratio_max_);
347 RegisterOwned(p.
sigma);
374 p.
step_yield =
new RooFormulaVar(
"step_yield" + suffix,
"@0*@1",
377 new RooFormulaVar(
"low_exp_yield" + suffix,
"@0*@1",
380 new RooFormulaVar(
"low_lin_yield" + suffix,
"@0*@1",
383 new RooFormulaVar(
"high_exp_yield" + suffix,
"@0*@1",
394void RooFitUtils::BuildBackground(Double_t bkg_estimate, Double_t peak_height,
395 Double_t range_width) {
397 new RooRealVar(
"BkgConstant",
"BkgConstant", bkg_estimate * range_width,
398 0, peak_height * range_width * 10.0);
402 Double_t slope_lo = -0.9 / fit_range_high_;
403 Double_t slope_hi = 5.0 / (fit_range_high_ - fit_range_low_);
405 new RooRealVar(
"BkgSlope",
"BkgSlope", 0.0, slope_lo, slope_hi);
407 RegisterOwned(bkg_.bkg_yield);
408 RegisterOwned(bkg_.bkg_slope);
412 if (use_flat_background_) {
413 bkg_.bkg_slope->setVal(0.0);
414 bkg_.bkg_slope->setConstant(kTRUE);
416 RegisterOwned(bkg_.bkg_pdf);
419void RooFitUtils::BuildTotalModel() {
421 RooArgList coef_list;
422 for (
size_t pi = 0; pi < peaks_.size(); pi++) {
423 pdf_list.add(*peaks_[pi].gauss_pdf);
424 coef_list.add(*peaks_[pi].gaus_yield);
425 pdf_list.add(*peaks_[pi].step_pdf);
426 coef_list.add(*peaks_[pi].step_yield);
427 pdf_list.add(*peaks_[pi].low_exp_pdf);
428 coef_list.add(*peaks_[pi].low_exp_yield);
429 pdf_list.add(*peaks_[pi].low_lin_pdf);
430 coef_list.add(*peaks_[pi].low_lin_yield);
431 pdf_list.add(*peaks_[pi].high_exp_pdf);
432 coef_list.add(*peaks_[pi].high_exp_yield);
434 pdf_list.add(*bkg_.bkg_pdf);
435 coef_list.add(*bkg_.bkg_yield);
437 total_pdf_ =
new RooAddPdf(
"total_pdf",
"total_pdf", pdf_list, coef_list);
438 RegisterOwned(total_pdf_);
441void RooFitUtils::ConfigureComponentFlagsForPeak(Int_t peak_idx) {
442 RooFitPeakModel &p = peaks_[peak_idx];
452 if (use_low_exp_tail_) {
464 if (use_low_lin_tail_) {
476 if (use_high_exp_tail_) {
489void RooFitUtils::FixComponent(Int_t peak_idx,
const TString &component) {
490 RooFitPeakModel &p = peaks_[peak_idx];
491 if (component ==
"step") {
494 }
else if (component ==
"low_exp") {
499 }
else if (component ==
"low_lin") {
504 }
else if (component ==
"high_exp") {
512void RooFitUtils::ReleaseComponent(Int_t peak_idx,
const TString &component) {
513 RooFitPeakModel &p = peaks_[peak_idx];
514 if (component ==
"step") {
517 }
else if (component ==
"low_exp") {
522 }
else if (component ==
"low_lin") {
527 }
else if (component ==
"high_exp") {
535std::vector<RooRealVar *> RooFitUtils::CollectAllParams() {
536 std::vector<RooRealVar *> out;
537 for (
size_t pi = 0; pi < peaks_.size(); pi++) {
538 out.push_back(peaks_[pi].mu);
539 out.push_back(peaks_[pi].sigma);
540 out.push_back(peaks_[pi].gaus_yield);
541 out.push_back(peaks_[pi].ratio_step);
542 out.push_back(peaks_[pi].ratio_low_exp);
543 out.push_back(peaks_[pi].tau_ratio_low_exp);
544 out.push_back(peaks_[pi].ratio_low_lin);
545 out.push_back(peaks_[pi].slope_low_lin);
546 out.push_back(peaks_[pi].ratio_high_exp);
547 out.push_back(peaks_[pi].tau_ratio_high_exp);
549 out.push_back(bkg_.bkg_yield);
550 out.push_back(bkg_.bkg_slope);
554std::vector<RooRealVar *> RooFitUtils::CollectFloatingParams() {
555 std::vector<RooRealVar *> all = CollectAllParams();
556 std::vector<RooRealVar *> out;
557 for (
size_t i = 0; i < all.size(); i++) {
558 if (!all[i]->isConstant()) {
559 out.push_back(all[i]);
565RooFitResult *RooFitUtils::RunFit(Bool_t quiet) {
566 Int_t print_level = quiet ? -1 : 0;
567 RooFitResult *result = total_pdf_->fitTo(
568 *unbinned_data_, RooFit::Save(kTRUE), RooFit::Extended(kTRUE),
570 RooFit::PrintLevel(print_level), RooFit::PrintEvalErrors(-1),
571 RooFit::Strategy(2), RooFit::Minimizer(
"Minuit2",
"migrad"),
576Double_t RooFitUtils::ComputeReducedChi2(RooFitResult *fit_result,
579 Double_t total_exp = total_pdf_->expectedEvents(&nset);
580 Double_t bin_width = working_hist_->GetBinWidth(1);
581 Double_t saved = x_->getVal();
584 Int_t nbins_in_range = 0;
585 Int_t nbins_hist = working_hist_->GetNbinsX();
586 for (Int_t i = 1; i <= nbins_hist; i++) {
587 Double_t xv = working_hist_->GetBinCenter(i);
588 if (xv < fit_range_low_ || xv > fit_range_high_)
590 Double_t data = working_hist_->GetBinContent(i);
591 Double_t error = working_hist_->GetBinError(i);
592 if (error <= 0 || data <= 0)
595 Double_t fit_val = total_exp * total_pdf_->getVal(&nset) * bin_width;
596 Double_t residual = (data - fit_val) / error;
597 chi2 += residual * residual;
602 Int_t npars = fit_result ? fit_result->floatParsFinal().size()
603 : (Int_t)CollectFloatingParams().size();
604 ndof = nbins_in_range - npars;
610void RooFitUtils::SnapshotParams(std::vector<Double_t> &vals,
611 std::vector<Double_t> &errs,
612 std::vector<Bool_t> &consts) {
613 std::vector<RooRealVar *> all = CollectAllParams();
614 vals.resize(all.size());
615 errs.resize(all.size());
616 consts.resize(all.size());
617 for (
size_t i = 0; i < all.size(); i++) {
618 vals[i] = all[i]->getVal();
619 errs[i] = all[i]->getError();
620 consts[i] = all[i]->isConstant();
624void RooFitUtils::RestoreParams(
const std::vector<Double_t> &vals,
625 const std::vector<Double_t> &errs,
626 const std::vector<Bool_t> &consts) {
627 std::vector<RooRealVar *> all = CollectAllParams();
628 for (
size_t i = 0; i < all.size() && i < vals.size(); i++) {
629 all[i]->setVal(vals[i]);
630 all[i]->setError(errs[i]);
631 all[i]->setConstant(consts[i]);
635void RooFitUtils::TestLowSideGroup(Int_t peak_idx, Double_t &best_chi2,
636 std::vector<Double_t> &best_vals,
637 std::vector<Double_t> &best_errs,
638 std::vector<Bool_t> &best_const) {
639 Bool_t any_low_side = use_step_ || use_low_exp_tail_ || use_low_lin_tail_;
643 std::cout <<
"Testing low-side component group for peak " << peak_idx + 1
644 <<
"..." << std::endl;
646 ReleaseComponent(peak_idx,
"step");
647 if (use_low_exp_tail_)
648 ReleaseComponent(peak_idx,
"low_exp");
649 if (use_low_lin_tail_)
650 ReleaseComponent(peak_idx,
"low_lin");
652 RooFitResult *group_fit = RunFit(kTRUE);
653 Bool_t group_ok = group_fit && group_fit->status() == 0;
655 Double_t chi2_group = group_ok ? ComputeReducedChi2(group_fit, tmp_ndof) : -1;
658 if (group_ok && chi2_group < best_chi2) {
659 std::cout <<
"Low-side group peak " << peak_idx + 1
660 <<
" ACCEPTED, pruning..." << std::endl;
661 best_chi2 = chi2_group;
662 SnapshotParams(best_vals, best_errs, best_const);
664 const TString comps[3] = {
"step",
"low_exp",
"low_lin"};
665 Bool_t enabled[3] = {use_step_, use_low_exp_tail_, use_low_lin_tail_};
666 for (Int_t ci = 0; ci < 3; ci++) {
669 FixComponent(peak_idx, comps[ci]);
670 RooFitResult *pf = RunFit(kTRUE);
671 Bool_t ok = pf && pf->status() == 0;
673 Double_t c2 = ok ? ComputeReducedChi2(pf, nd) : -1;
675 if (ok && c2 <= best_chi2) {
676 std::cout <<
" " << comps[ci] <<
" peak " << peak_idx + 1 <<
" pruned"
679 SnapshotParams(best_vals, best_errs, best_const);
681 std::cout <<
" " << comps[ci] <<
" peak " << peak_idx + 1
682 <<
" retained" << std::endl;
683 ReleaseComponent(peak_idx, comps[ci]);
684 RestoreParams(best_vals, best_errs, best_const);
688 std::cout <<
"Low-side group peak " << peak_idx + 1 <<
" REJECTED"
691 FixComponent(peak_idx,
"step");
692 if (use_low_exp_tail_)
693 FixComponent(peak_idx,
"low_exp");
694 if (use_low_lin_tail_)
695 FixComponent(peak_idx,
"low_lin");
696 RestoreParams(best_vals, best_errs, best_const);
700void RooFitUtils::TestHighTailIndependent(Int_t peak_idx, Double_t &best_chi2,
701 std::vector<Double_t> &best_vals,
702 std::vector<Double_t> &best_errs,
703 std::vector<Bool_t> &best_const) {
704 if (!use_high_exp_tail_)
707 std::cout <<
"Testing high exponential tail for peak " << peak_idx + 1
708 <<
"..." << std::endl;
709 ReleaseComponent(peak_idx,
"high_exp");
710 RooFitResult *htail_fit = RunFit(kTRUE);
711 Bool_t htail_ok = htail_fit && htail_fit->status() == 0;
713 Double_t chi2_htail = htail_ok ? ComputeReducedChi2(htail_fit, tmp_ndof) : -1;
715 if (htail_ok && chi2_htail < best_chi2) {
716 std::cout <<
"High exp tail peak " << peak_idx + 1 <<
" ACCEPTED"
718 best_chi2 = chi2_htail;
719 SnapshotParams(best_vals, best_errs, best_const);
721 std::cout <<
"High exp tail peak " << peak_idx + 1 <<
" REJECTED"
723 FixComponent(peak_idx,
"high_exp");
724 RestoreParams(best_vals, best_errs, best_const);
728PeakFitResult RooFitUtils::ExtractPeakResult(Int_t peak_idx) {
729 RooFitPeakModel &p = peaks_[peak_idx];
730 PeakFitResult result;
731 result.
mu = p.
mu->getVal();
756void RooFitUtils::SortPeaksByMu(Int_t num_peaks) {
757 for (Int_t i = 0; i < num_peaks - 1; i++) {
758 for (Int_t j = 0; j < num_peaks - i - 1; j++) {
759 Double_t mu_j = peaks_[j].mu->getVal();
760 Double_t mu_next = peaks_[j + 1].mu->getVal();
761 if (mu_j > mu_next) {
762 std::cout <<
"Sorting peaks: swapping peak " << j + 1 <<
" (mu=" << mu_j
763 <<
") and peak " << j + 2 <<
" (mu=" << mu_next <<
")"
765 RooFitPeakModel tmp = peaks_[j];
766 peaks_[j] = peaks_[j + 1];
773void RooFitUtils::AdoptSavedSimRange(
const TString &input_name,
774 const TString &base_label) {
778 "_" + input_name +
".simroofits";
779 std::ifstream in(filename.Data());
783 if (!(in >> token) || token !=
"RANGE")
786 if (!(in >> rlo >> rhi))
790 for (
size_t i = 0; i < sim_channels_.size(); i++) {
791 sim_channels_[i].fit_range_low = rlo;
792 sim_channels_[i].fit_range_high = rhi;
793 delete sim_channels_[i].hist;
794 sim_channels_[i].hist =
796 sim_channels_[i].display_bin_width_kev);
800void RooFitUtils::AdoptSavedRange(
const TString &input_name,
801 const TString &peak_name) {
805 "_" + input_name +
".roofits";
806 std::ifstream in(filename.Data());
810 if (!(in >> token) || token !=
"RANGE")
813 if (!(in >> rlo >> rhi))
817 fit_range_low_ = rlo;
818 fit_range_high_ = rhi;
819 BuildDisplayHistogram();
822void RooFitUtils::SaveInteractiveParams(
const TString &input_name,
823 const TString &peak_name) {
825 gSystem->mkdir(fits_dir, kTRUE);
826 TString filename = fits_dir +
"/" + peak_name +
"_" + input_name +
".roofits";
827 std::ofstream out(filename.Data());
828 if (!out.is_open()) {
829 std::cerr <<
"WARNING: Could not save interactive params to " << filename
833 out << std::setprecision(17);
834 out <<
"RANGE " << fit_range_low_ <<
" " << fit_range_high_;
836 std::vector<RooRealVar *> all = CollectAllParams();
837 for (
size_t i = 0; i < all.size(); i++) {
838 out << all[i]->GetName() <<
" " << all[i]->getVal() <<
" "
839 << all[i]->getError() <<
" " << (all[i]->isConstant() ? 1 : 0);
843 std::cout <<
"Saved interactive params to " << filename << std::endl;
846Bool_t RooFitUtils::LoadInteractiveParams(
const TString &input_name,
847 const TString &peak_name) {
849 "_" + input_name +
".roofits";
850 std::ifstream in(filename.Data());
854 std::vector<RooRealVar *> all = CollectAllParams();
858 if (token ==
"RANGE") {
861 fit_range_low_ = rlo;
862 fit_range_high_ = rhi;
863 x_->setRange(rlo, rhi);
865 BuildDisplayHistogram();
869 std::map<std::string, RooRealVar *> by_name;
870 for (
size_t i = 0; i < all.size(); i++)
871 by_name[std::string(all[i]->GetName())] = all[i];
873 Double_t value, error;
875 Int_t n_set = 0, n_skipped_fixed = 0, n_unknown = 0;
876 while (in >> token >> value >> error >> fixed) {
877 std::map<std::string, RooRealVar *>::iterator it = by_name.find(token);
878 if (it == by_name.end()) {
879 std::cerr <<
"WARNING: param " << token <<
" from " << filename
880 <<
" not found in model" << std::endl;
885 if (it->second->isConstant()) {
891 it->second->setVal(value);
892 it->second->setError(error);
898 std::cerr <<
"WARNING: no usable parameters in " << filename << std::endl;
902 std::cout <<
"Loaded interactive params from " << filename << std::endl;
903 if (n_skipped_fixed > 0)
904 std::cout <<
" (" << n_skipped_fixed
905 <<
" saved params ignored: fixed by model configuration)"
908 std::cout <<
" (" << n_unknown <<
" saved params not in model)"
913void RooFitUtils::SaveSimInteractiveParams(
const TString &input_name,
914 const TString &base_label) {
916 gSystem->mkdir(fits_dir, kTRUE);
918 fits_dir +
"/" + base_label +
"_" + input_name +
".simroofits";
919 std::ofstream out(filename.Data());
920 if (!out.is_open()) {
921 std::cerr <<
"WARNING: Could not save sim interactive params to "
922 << filename << std::endl;
925 out << std::setprecision(17);
930 std::set<RooRealVar *> seen;
931 std::vector<RooRealVar *> ordered;
932 for (
size_t ci = 0; ci < sim_channels_.size(); ci++) {
933 const TString &cname = sim_channels_[ci].name;
934 std::vector<RooFitPeakModel> &peaks = sim_channel_peaks_[cname];
935 for (
size_t pi = 0; pi < peaks.size(); pi++) {
936 RooFitPeakModel &p = peaks[pi];
937 RooRealVar *vars[10] = {p.
mu,
947 for (Int_t k = 0; k < 10; k++) {
948 if (seen.insert(vars[k]).second)
949 ordered.push_back(vars[k]);
952 RooFitBackgroundModel &bkg = sim_channel_bkg_[cname];
959 for (
size_t i = 0; i < ordered.size(); i++) {
960 out << ordered[i]->GetName() <<
" " << ordered[i]->getVal() <<
" "
961 << ordered[i]->getError() <<
" " << (ordered[i]->isConstant() ? 1 : 0);
965 std::cout <<
"Saved sim interactive params to " << filename << std::endl;
968Bool_t RooFitUtils::LoadSimInteractiveParams(
const TString &input_name,
969 const TString &base_label) {
971 "_" + input_name +
".simroofits";
972 std::ifstream in(filename.Data());
976 std::map<std::string, RooRealVar *> by_name;
977 for (
size_t ci = 0; ci < sim_channels_.size(); ci++) {
978 const TString &cname = sim_channels_[ci].name;
979 std::vector<RooFitPeakModel> &peaks = sim_channel_peaks_[cname];
980 for (
size_t pi = 0; pi < peaks.size(); pi++) {
981 RooFitPeakModel &p = peaks[pi];
982 RooRealVar *vars[10] = {p.
mu,
992 for (Int_t k = 0; k < 10; k++) {
993 by_name[vars[k]->GetName()] = vars[k];
996 RooFitBackgroundModel &bkg = sim_channel_bkg_[cname];
1003 if (token ==
"RANGE") {
1006 x_->setRange(rlo, rhi);
1010 Double_t value, error;
1012 Int_t n_set = 0, n_skipped_fixed = 0;
1013 while (in >> token >> value >> error >> fixed) {
1014 std::map<std::string, RooRealVar *>::iterator it = by_name.find(token);
1015 if (it == by_name.end()) {
1016 std::cerr <<
"WARNING: param " << token
1017 <<
" from .simroofits not found in model" << std::endl;
1021 if (it->second->isConstant()) {
1027 it->second->setVal(value);
1028 it->second->setError(error);
1034 std::cerr <<
"WARNING: no usable parameters in " << filename << std::endl;
1038 std::cout <<
"Loaded sim interactive params from " << filename << std::endl;
1039 if (n_skipped_fixed > 0)
1040 std::cout <<
" (" << n_skipped_fixed
1041 <<
" saved params ignored: fixed by model configuration)"
1046void RooFitUtils::AppendPeakGraphs(std::vector<TGraph *> &components,
1047 Int_t peak_idx, Style_t line_style,
1048 RooAbsPdf *background_pdf,
1049 Double_t bkg_yield_val, Int_t npts,
1050 Double_t x_step, Double_t bin_width) {
1051 RooFitPeakModel &p = peaks_[peak_idx];
1053 RooArgSet nset(*x_);
1055 TGraph *peak_graph =
new TGraph(npts);
1057 for (Int_t i = 0; i < npts; i++) {
1058 Double_t xv = fit_range_low_ + i * x_step;
1060 Double_t y = gy * p.
gauss_pdf->getVal(&nset) * bin_width;
1061 Double_t bkg_v = bkg_yield_val * background_pdf->getVal(&nset) * bin_width;
1062 peak_graph->SetPoint(i, xv, y + bkg_v);
1064 peak_graph->SetLineColor(kBlack);
1065 peak_graph->SetLineStyle(line_style);
1066 peak_graph->SetLineWidth(line_width);
1067 components.push_back(peak_graph);
1069 if (TMath::Abs(p.
ratio_step->getVal()) > 1e-6) {
1070 TGraph *step_graph =
new TGraph(npts);
1072 for (Int_t i = 0; i < npts; i++) {
1073 Double_t xv = fit_range_low_ + i * x_step;
1075 Double_t y = sy * p.
step_pdf->getVal(&nset) * bin_width;
1077 bkg_yield_val * background_pdf->getVal(&nset) * bin_width;
1078 step_graph->SetPoint(i, xv, y + bkg_v);
1080 step_graph->SetLineColor(kGray);
1081 step_graph->SetLineStyle(line_style);
1082 step_graph->SetLineWidth(line_width);
1083 components.push_back(step_graph);
1088 TGraph *low_tail_graph =
new TGraph(npts);
1091 for (Int_t i = 0; i < npts; i++) {
1092 Double_t xv = fit_range_low_ + i * x_step;
1094 Double_t y_exp = lexp_y * p.
low_exp_pdf->getVal(&nset) * bin_width;
1095 Double_t y_lin = llin_y * p.
low_lin_pdf->getVal(&nset) * bin_width;
1097 bkg_yield_val * background_pdf->getVal(&nset) * bin_width;
1098 low_tail_graph->SetPoint(i, xv, y_exp + y_lin + bkg_v);
1100 low_tail_graph->SetLineColor(kRed);
1101 low_tail_graph->SetLineStyle(line_style);
1102 low_tail_graph->SetLineWidth(line_width);
1103 components.push_back(low_tail_graph);
1107 TGraph *high_tail_graph =
new TGraph(npts);
1109 for (Int_t i = 0; i < npts; i++) {
1110 Double_t xv = fit_range_low_ + i * x_step;
1112 Double_t y = hexp_y * p.
high_exp_pdf->getVal(&nset) * bin_width;
1114 bkg_yield_val * background_pdf->getVal(&nset) * bin_width;
1115 high_tail_graph->SetPoint(i, xv, y + bkg_v);
1117 high_tail_graph->SetLineColor(kOrange);
1118 high_tail_graph->SetLineStyle(line_style);
1119 high_tail_graph->SetLineWidth(line_width);
1120 components.push_back(high_tail_graph);
1125 const TString peak_name,
1126 const TString label) {
1128 Double_t x_step = (fit_range_high_ - fit_range_low_) / (npts - 1);
1130 Double_t bin_width = working_hist_->GetBinWidth(1);
1131 RooArgSet nset(*x_);
1133 TGraph *total_graph =
new TGraph(npts);
1134 Double_t total_exp = total_pdf_->expectedEvents(&nset);
1135 for (Int_t i = 0; i < npts; i++) {
1136 Double_t xv = fit_range_low_ + i * x_step;
1138 Double_t y = total_exp * total_pdf_->getVal(&nset) * bin_width;
1139 total_graph->SetPoint(i, xv, y);
1141 total_graph->SetLineColor(kAzure);
1142 total_graph->SetLineWidth(line_width);
1144 Double_t bkg_yield_val = bkg_.bkg_yield->getVal();
1145 TGraph *background_graph =
new TGraph(npts);
1146 for (Int_t i = 0; i < npts; i++) {
1147 Double_t xv = fit_range_low_ + i * x_step;
1149 Double_t y = bkg_yield_val * bkg_.bkg_pdf->getVal(&nset) * bin_width;
1150 background_graph->SetPoint(i, xv, y);
1152 background_graph->SetLineColor(kGreen);
1153 background_graph->SetLineWidth(line_width);
1155 std::vector<TGraph *> components;
1156 components.push_back(background_graph);
1157 AppendPeakGraphs(components, 0, 1, bkg_.bkg_pdf, bkg_yield_val, npts, x_step,
1161 working_hist_, total_graph, components, fit_range_low_, fit_range_high_,
1162 peak_name +
"_" + input_name,
"fits", label, kTRUE);
1165 for (Int_t i = 0; i < (Int_t)components.size(); i++) {
1166 delete components[i];
1171 const TString peak_name,
1172 const TString label) {
1174 Double_t x_step = (fit_range_high_ - fit_range_low_) / (npts - 1);
1176 Double_t bin_width = working_hist_->GetBinWidth(1);
1177 RooArgSet nset(*x_);
1179 TGraph *total_graph =
new TGraph(npts);
1180 Double_t total_exp = total_pdf_->expectedEvents(&nset);
1181 for (Int_t i = 0; i < npts; i++) {
1182 Double_t xv = fit_range_low_ + i * x_step;
1184 Double_t y = total_exp * total_pdf_->getVal(&nset) * bin_width;
1185 total_graph->SetPoint(i, xv, y);
1187 total_graph->SetLineColor(kAzure);
1188 total_graph->SetLineWidth(line_width);
1190 Double_t bkg_yield_val = bkg_.bkg_yield->getVal();
1191 TGraph *background_graph =
new TGraph(npts);
1192 for (Int_t i = 0; i < npts; i++) {
1193 Double_t xv = fit_range_low_ + i * x_step;
1195 Double_t y = bkg_yield_val * bkg_.bkg_pdf->getVal(&nset) * bin_width;
1196 background_graph->SetPoint(i, xv, y);
1198 background_graph->SetLineColor(kGreen);
1199 background_graph->SetLineWidth(line_width);
1201 std::vector<TGraph *> components;
1202 components.push_back(background_graph);
1203 AppendPeakGraphs(components, 0, 1, bkg_.bkg_pdf, bkg_yield_val, npts, x_step,
1205 AppendPeakGraphs(components, 1, 3, bkg_.bkg_pdf, bkg_yield_val, npts, x_step,
1209 working_hist_, total_graph, components, fit_range_low_, fit_range_high_,
1210 peak_name +
"_" + input_name,
"fits", label, kTRUE);
1213 for (Int_t i = 0; i < (Int_t)components.size(); i++) {
1214 delete components[i];
1219 const TString peak_name,
1220 const TString label) {
1222 Double_t x_step = (fit_range_high_ - fit_range_low_) / (npts - 1);
1224 Double_t bin_width = working_hist_->GetBinWidth(1);
1225 RooArgSet nset(*x_);
1227 TGraph *total_graph =
new TGraph(npts);
1228 Double_t total_exp = total_pdf_->expectedEvents(&nset);
1229 for (Int_t i = 0; i < npts; i++) {
1230 Double_t xv = fit_range_low_ + i * x_step;
1232 Double_t y = total_exp * total_pdf_->getVal(&nset) * bin_width;
1233 total_graph->SetPoint(i, xv, y);
1235 total_graph->SetLineColor(kAzure);
1236 total_graph->SetLineWidth(line_width);
1238 Double_t bkg_yield_val = bkg_.bkg_yield->getVal();
1239 TGraph *background_graph =
new TGraph(npts);
1240 for (Int_t i = 0; i < npts; i++) {
1241 Double_t xv = fit_range_low_ + i * x_step;
1243 Double_t y = bkg_yield_val * bkg_.bkg_pdf->getVal(&nset) * bin_width;
1244 background_graph->SetPoint(i, xv, y);
1246 background_graph->SetLineColor(kGreen);
1247 background_graph->SetLineWidth(line_width);
1249 std::vector<TGraph *> components;
1250 components.push_back(background_graph);
1251 AppendPeakGraphs(components, 0, 1, bkg_.bkg_pdf, bkg_yield_val, npts, x_step,
1253 AppendPeakGraphs(components, 1, 3, bkg_.bkg_pdf, bkg_yield_val, npts, x_step,
1255 AppendPeakGraphs(components, 2, 4, bkg_.bkg_pdf, bkg_yield_val, npts, x_step,
1259 working_hist_, total_graph, components, fit_range_low_, fit_range_high_,
1260 peak_name +
"_" + input_name,
"fits", label, kTRUE);
1263 for (Int_t i = 0; i < (Int_t)components.size(); i++) {
1264 delete components[i];
1269 const TString peak_name) {
1271 results.
peaks.emplace_back();
1273 AdoptSavedRange(input_name, peak_name);
1276 Double_t range_width = fit_range_high_ - fit_range_low_;
1277 Double_t mu_init = (fit_range_low_ + fit_range_high_) / 2;
1278 Double_t sigma_init = range_width * 0.01;
1279 Double_t peak_height =
1280 working_hist_->GetBinContent(working_hist_->GetMaximumBin());
1281 Double_t bkg_estimate = EstimateBackground();
1283 Double_t hist_xmin = working_hist_->GetXaxis()->GetXmin();
1284 Double_t hist_xmax = working_hist_->GetXaxis()->GetXmax();
1285 x_ =
new RooRealVar(
"x",
"x", hist_xmin, hist_xmax);
1286 x_->setRange(
kFitRangeName, fit_range_low_, fit_range_high_);
1289 BuildPeak(0, mu_init, sigma_init, peak_height, range_width);
1290 BuildBackground(bkg_estimate, peak_height, range_width);
1293 BuildUnbinnedData();
1294 x_->setRange(fit_range_low_, fit_range_high_);
1296 ConfigureComponentFlagsForPeak(0);
1298 Bool_t fit_valid = kFALSE;
1299 Double_t final_chi2 = 0;
1300 Int_t final_ndof = 0;
1303 if (LoadInteractiveParams(input_name, peak_name)) {
1304 RooFitResult *refit = RunFit(kTRUE);
1305 final_chi2 = ComputeReducedChi2(refit, final_ndof);
1306 std::cout <<
"Refit from saved params chi2/ndf = " << final_chi2
1311 Bool_t was_batch = gROOT->IsBatch();
1312 gROOT->SetBatch(kFALSE);
1314 working_hist_, &events_, display_bin_width_kev_, total_pdf_, x_,
1315 unbinned_data_, &peaks_, &bkg_, fit_range_low_, fit_range_high_,
1316 peak_name +
" / " + input_name)) {
1319 BuildDisplayHistogram();
1320 final_chi2 = ComputeReducedChi2(
nullptr, final_ndof);
1321 std::cout <<
"Interactive chi2/ndf = " << final_chi2 << std::endl;
1322 SaveInteractiveParams(input_name, peak_name);
1325 gROOT->SetBatch(was_batch);
1328 FixComponent(0,
"step");
1329 FixComponent(0,
"low_exp");
1330 FixComponent(0,
"low_lin");
1331 FixComponent(0,
"high_exp");
1333 if (use_manual_init_) {
1334 std::cout <<
"Using manually initialized parameters" << std::endl;
1335 std::vector<RooRealVar *> all = CollectAllParams();
1336 for (
size_t i = 0; i < manual_params_.size() && i < all.size(); i++) {
1337 all[i]->setVal(manual_params_[i]);
1341 RooFitResult *initial_fit = RunFit(kTRUE);
1342 if (!initial_fit || initial_fit->status() != 0) {
1343 std::cout <<
"ERROR: Initial fit failed" << std::endl;
1349 Double_t best_chi2 = ComputeReducedChi2(initial_fit, tmp_ndof);
1350 std::cout <<
"Initial chi2/ndf = " << best_chi2 << std::endl;
1353 std::vector<Double_t> best_vals;
1354 std::vector<Double_t> best_errs;
1355 std::vector<Bool_t> best_const;
1356 SnapshotParams(best_vals, best_errs, best_const);
1358 TestLowSideGroup(0, best_chi2, best_vals, best_errs, best_const);
1359 TestHighTailIndependent(0, best_chi2, best_vals, best_errs, best_const);
1361 std::cout <<
"Final fit with selected components..." << std::endl;
1362 RestoreParams(best_vals, best_errs, best_const);
1363 RooFitResult *final_fit = RunFit(kFALSE);
1364 if (final_fit && final_fit->status() == 0) {
1365 final_chi2 = ComputeReducedChi2(final_fit, final_ndof);
1367 std::cout <<
"Final chi2/ndf = " << final_chi2 << std::endl;
1373 TString chi2label = Form(
"#chi^{2}/ndf = %.3f", final_chi2);
1376 results.
peaks[0] = ExtractPeakResult(0);
1382 results.
valid = kTRUE;
1384 std::cout <<
"ERROR: Fit did not converge" << std::endl;
1391 const TString peak_name, Double_t mu1_init,
1392 Double_t mu2_init, Bool_t link_sigma) {
1394 results.
peaks.emplace_back();
1395 results.
peaks.emplace_back();
1397 AdoptSavedRange(input_name, peak_name);
1399 if (mu1_init > mu2_init) {
1400 std::cout <<
"WARNING: mu1_init > mu2_init, swapping initial values"
1402 Double_t tmp = mu1_init;
1403 mu1_init = mu2_init;
1408 Double_t range_width = fit_range_high_ - fit_range_low_;
1409 Double_t sigma_init = range_width * 0.01;
1410 Double_t peak_height =
1411 working_hist_->GetBinContent(working_hist_->GetMaximumBin());
1412 Double_t bkg_estimate = EstimateBackground();
1414 Double_t hist_xmin = working_hist_->GetXaxis()->GetXmin();
1415 Double_t hist_xmax = working_hist_->GetXaxis()->GetXmax();
1416 x_ =
new RooRealVar(
"x",
"x", hist_xmin, hist_xmax);
1417 x_->setRange(
kFitRangeName, fit_range_low_, fit_range_high_);
1420 BuildPeak(0, mu1_init, sigma_init, peak_height, range_width);
1421 BuildPeak(1, mu2_init, sigma_init, peak_height, range_width);
1428 RooRealVar *s = peaks_[0].sigma;
1430 p.
sigma->setVal(s->getVal());
1431 p.
sigma->setConstant(kTRUE);
1449 BuildBackground(bkg_estimate, peak_height, range_width);
1452 Double_t mu_midpoint = 0.5 * (mu1_init + mu2_init);
1453 peaks_[0].mu->setMax(mu_midpoint);
1454 peaks_[1].mu->setMin(mu_midpoint);
1456 BuildUnbinnedData();
1457 x_->setRange(fit_range_low_, fit_range_high_);
1459 ConfigureComponentFlagsForPeak(0);
1460 ConfigureComponentFlagsForPeak(1);
1462 Bool_t fit_valid = kFALSE;
1463 Double_t final_chi2 = 0;
1464 Int_t final_ndof = 0;
1467 if (LoadInteractiveParams(input_name, peak_name)) {
1468 RooFitResult *refit = RunFit(kTRUE);
1469 final_chi2 = ComputeReducedChi2(refit, final_ndof);
1470 std::cout <<
"Refit from saved params chi2/ndf = " << final_chi2
1475 Bool_t was_batch = gROOT->IsBatch();
1476 gROOT->SetBatch(kFALSE);
1478 working_hist_, &events_, display_bin_width_kev_, total_pdf_, x_,
1479 unbinned_data_, &peaks_, &bkg_, fit_range_low_, fit_range_high_,
1480 peak_name +
" / " + input_name)) {
1483 BuildDisplayHistogram();
1484 final_chi2 = ComputeReducedChi2(
nullptr, final_ndof);
1485 std::cout <<
"Interactive chi2/ndf = " << final_chi2 << std::endl;
1486 SaveInteractiveParams(input_name, peak_name);
1489 gROOT->SetBatch(was_batch);
1492 FixComponent(0,
"step");
1493 FixComponent(0,
"low_exp");
1494 FixComponent(0,
"low_lin");
1495 FixComponent(0,
"high_exp");
1496 FixComponent(1,
"step");
1497 FixComponent(1,
"low_exp");
1498 FixComponent(1,
"low_lin");
1499 FixComponent(1,
"high_exp");
1501 RooFitResult *initial_fit = RunFit(kTRUE);
1502 if (!initial_fit || initial_fit->status() != 0) {
1503 std::cout <<
"ERROR: Initial double peak fit failed" << std::endl;
1509 Double_t best_chi2 = ComputeReducedChi2(initial_fit, tmp_ndof);
1510 std::cout <<
"Initial chi2/ndf = " << best_chi2 << std::endl;
1513 std::vector<Double_t> best_vals;
1514 std::vector<Double_t> best_errs;
1515 std::vector<Bool_t> best_const;
1516 SnapshotParams(best_vals, best_errs, best_const);
1518 TestLowSideGroup(0, best_chi2, best_vals, best_errs, best_const);
1519 TestHighTailIndependent(1, best_chi2, best_vals, best_errs, best_const);
1523 <<
"Testing inter-peak group (peak1 high tail + peak2 low-side)..."
1525 if (use_high_exp_tail_)
1526 ReleaseComponent(0,
"high_exp");
1528 ReleaseComponent(1,
"step");
1529 if (use_low_exp_tail_)
1530 ReleaseComponent(1,
"low_exp");
1531 if (use_low_lin_tail_)
1532 ReleaseComponent(1,
"low_lin");
1534 RooFitResult *group_fit = RunFit(kTRUE);
1535 Bool_t ok = group_fit && group_fit->status() == 0;
1537 Double_t c2 = ok ? ComputeReducedChi2(group_fit, nd) : -1;
1540 if (ok && c2 < best_chi2) {
1541 std::cout <<
"Inter-peak group ACCEPTED, pruning..." << std::endl;
1543 SnapshotParams(best_vals, best_errs, best_const);
1551 {0,
"high_exp", use_high_exp_tail_},
1552 {1,
"step", use_step_},
1553 {1,
"low_exp", use_low_exp_tail_},
1554 {1,
"low_lin", use_low_lin_tail_},
1556 for (Int_t ri = 0; ri < 4; ri++) {
1557 if (!refs[ri].enabled)
1559 FixComponent(refs[ri].peak_idx, refs[ri].comp);
1560 RooFitResult *pf = RunFit(kTRUE);
1561 Bool_t pok = pf && pf->status() == 0;
1563 Double_t pc2 = pok ? ComputeReducedChi2(pf, pnd) : -1;
1565 if (pok && pc2 <= best_chi2) {
1566 std::cout <<
" " << refs[ri].comp <<
" peak "
1567 << refs[ri].peak_idx + 1 <<
" pruned" << std::endl;
1569 SnapshotParams(best_vals, best_errs, best_const);
1571 std::cout <<
" " << refs[ri].comp <<
" peak "
1572 << refs[ri].peak_idx + 1 <<
" retained" << std::endl;
1573 ReleaseComponent(refs[ri].peak_idx, refs[ri].comp);
1574 RestoreParams(best_vals, best_errs, best_const);
1578 std::cout <<
"Inter-peak group REJECTED" << std::endl;
1579 if (use_high_exp_tail_)
1580 FixComponent(0,
"high_exp");
1582 FixComponent(1,
"step");
1583 if (use_low_exp_tail_)
1584 FixComponent(1,
"low_exp");
1585 if (use_low_lin_tail_)
1586 FixComponent(1,
"low_lin");
1587 RestoreParams(best_vals, best_errs, best_const);
1591 std::cout <<
"Final fit with selected components..." << std::endl;
1592 RestoreParams(best_vals, best_errs, best_const);
1593 RooFitResult *final_fit = RunFit(kFALSE);
1594 if (final_fit && final_fit->status() == 0) {
1595 final_chi2 = ComputeReducedChi2(final_fit, final_ndof);
1597 std::cout <<
"Double peak fit converged successfully" << std::endl;
1598 std::cout <<
"Final chi2/ndf = " << final_chi2 << std::endl;
1600 std::cout <<
"ERROR: Double peak fit failed to converge" << std::endl;
1607 TString chi2label = Form(
"#chi^{2}/ndf = %.3f", final_chi2);
1610 results.
peaks[0] = ExtractPeakResult(0);
1611 results.
peaks[1] = ExtractPeakResult(1);
1617 results.
valid = kTRUE;
1619 std::cout <<
"ERROR: Double peak fit failed" << std::endl;
1626 const TString peak_name,
1628 Double_t mu2_init) {
1630 results.
peaks.emplace_back();
1631 results.
peaks.emplace_back();
1633 AdoptSavedRange(input_name, peak_name);
1636 Double_t range_width = fit_range_high_ - fit_range_low_;
1637 Double_t sigma_init = range_width * 0.01;
1638 Double_t peak_height =
1639 working_hist_->GetBinContent(working_hist_->GetMaximumBin());
1640 Double_t bkg_estimate = EstimateBackground();
1642 Double_t hist_xmin = working_hist_->GetXaxis()->GetXmin();
1643 Double_t hist_xmax = working_hist_->GetXaxis()->GetXmax();
1644 x_ =
new RooRealVar(
"x",
"x", hist_xmin, hist_xmax);
1645 x_->setRange(
kFitRangeName, fit_range_low_, fit_range_high_);
1648 BuildPeak(0, constrained_peak.
mu, constrained_peak.
sigma, peak_height,
1650 BuildPeak(1, mu2_init, sigma_init, peak_height, range_width);
1651 BuildBackground(bkg_estimate, peak_height, range_width);
1654 BuildUnbinnedData();
1655 x_->setRange(fit_range_low_, fit_range_high_);
1660 p.
mu->setVal(constrained_peak.
mu);
1661 p.
mu->setConstant(kTRUE);
1663 p.
sigma->setConstant(kTRUE);
1665 p.
gaus_yield->setRange(0, peak_height * range_width * 10.0);
1686 ConfigureComponentFlagsForPeak(1);
1688 Bool_t fit_valid = kFALSE;
1689 Double_t final_chi2 = 0;
1690 Int_t final_ndof = 0;
1693 if (LoadInteractiveParams(input_name, peak_name)) {
1694 RooFitResult *refit = RunFit(kTRUE);
1695 final_chi2 = ComputeReducedChi2(refit, final_ndof);
1699 Bool_t was_batch = gROOT->IsBatch();
1700 gROOT->SetBatch(kFALSE);
1702 working_hist_, &events_, display_bin_width_kev_, total_pdf_, x_,
1703 unbinned_data_, &peaks_, &bkg_, fit_range_low_, fit_range_high_,
1704 peak_name +
" / " + input_name)) {
1707 BuildDisplayHistogram();
1708 final_chi2 = ComputeReducedChi2(
nullptr, final_ndof);
1709 SaveInteractiveParams(input_name, peak_name);
1712 gROOT->SetBatch(was_batch);
1715 FixComponent(1,
"step");
1716 FixComponent(1,
"low_exp");
1717 FixComponent(1,
"low_lin");
1718 FixComponent(1,
"high_exp");
1720 RooFitResult *initial_fit = RunFit(kTRUE);
1721 if (!initial_fit || initial_fit->status() != 0) {
1722 std::cout <<
"ERROR: Initial constrained double peak fit failed"
1729 Double_t best_chi2 = ComputeReducedChi2(initial_fit, tmp_ndof);
1730 std::cout <<
"Initial chi2/ndf = " << best_chi2 << std::endl;
1733 std::vector<Double_t> best_vals;
1734 std::vector<Double_t> best_errs;
1735 std::vector<Bool_t> best_const;
1736 SnapshotParams(best_vals, best_errs, best_const);
1738 TestLowSideGroup(1, best_chi2, best_vals, best_errs, best_const);
1739 TestHighTailIndependent(1, best_chi2, best_vals, best_errs, best_const);
1741 std::cout <<
"Final fit with selected components..." << std::endl;
1742 RestoreParams(best_vals, best_errs, best_const);
1743 RooFitResult *final_fit = RunFit(kFALSE);
1744 if (final_fit && final_fit->status() == 0) {
1745 final_chi2 = ComputeReducedChi2(final_fit, final_ndof);
1747 std::cout <<
"Final chi2/ndf = " << final_chi2 << std::endl;
1749 std::cout <<
"ERROR: Constrained double peak fit failed" << std::endl;
1756 TString chi2label = Form(
"#chi^{2}/ndf = %.3f", final_chi2);
1759 results.
peaks[0] = ExtractPeakResult(0);
1760 results.
peaks[1] = ExtractPeakResult(1);
1766 results.
valid = kTRUE;
1773 const TString peak_name,
1775 Double_t mu3_init) {
1777 results.
peaks.emplace_back();
1778 results.
peaks.emplace_back();
1779 results.
peaks.emplace_back();
1781 AdoptSavedRange(input_name, peak_name);
1784 Double_t range_width = fit_range_high_ - fit_range_low_;
1785 Double_t sigma_init = range_width * 0.01;
1786 Double_t peak_height =
1787 working_hist_->GetBinContent(working_hist_->GetMaximumBin());
1788 Double_t bkg_estimate = EstimateBackground();
1790 Double_t hist_xmin = working_hist_->GetXaxis()->GetXmin();
1791 Double_t hist_xmax = working_hist_->GetXaxis()->GetXmax();
1792 x_ =
new RooRealVar(
"x",
"x", hist_xmin, hist_xmax);
1793 x_->setRange(
kFitRangeName, fit_range_low_, fit_range_high_);
1796 for (Int_t pi = 0; pi < 2; pi++) {
1798 BuildPeak(pi, cp.
mu, cp.
sigma, peak_height, range_width);
1800 BuildPeak(2, mu3_init, sigma_init, peak_height, range_width);
1801 BuildBackground(bkg_estimate, peak_height, range_width);
1804 BuildUnbinnedData();
1805 x_->setRange(fit_range_low_, fit_range_high_);
1807 for (Int_t pi = 0; pi < 2; pi++) {
1811 p.
mu->setVal(cp.
mu);
1812 p.
mu->setConstant(kTRUE);
1814 p.
sigma->setConstant(kTRUE);
1816 p.
gaus_yield->setRange(0, peak_height * range_width * 10.0);
1835 ConfigureComponentFlagsForPeak(2);
1837 Bool_t fit_valid = kFALSE;
1838 Double_t final_chi2 = 0;
1839 Int_t final_ndof = 0;
1842 if (LoadInteractiveParams(input_name, peak_name)) {
1843 RooFitResult *refit = RunFit(kTRUE);
1844 final_chi2 = ComputeReducedChi2(refit, final_ndof);
1848 Bool_t was_batch = gROOT->IsBatch();
1849 gROOT->SetBatch(kFALSE);
1851 working_hist_, &events_, display_bin_width_kev_, total_pdf_, x_,
1852 unbinned_data_, &peaks_, &bkg_, fit_range_low_, fit_range_high_,
1853 peak_name +
" / " + input_name)) {
1856 BuildDisplayHistogram();
1857 final_chi2 = ComputeReducedChi2(
nullptr, final_ndof);
1858 SaveInteractiveParams(input_name, peak_name);
1861 gROOT->SetBatch(was_batch);
1864 FixComponent(2,
"step");
1865 FixComponent(2,
"low_exp");
1866 FixComponent(2,
"low_lin");
1867 FixComponent(2,
"high_exp");
1869 RooFitResult *initial_fit = RunFit(kTRUE);
1870 if (!initial_fit || initial_fit->status() != 0) {
1871 std::cout <<
"ERROR: Initial triple peak fit failed" << std::endl;
1877 Double_t best_chi2 = ComputeReducedChi2(initial_fit, tmp_ndof);
1878 std::cout <<
"Initial chi2/ndf = " << best_chi2 << std::endl;
1881 std::vector<Double_t> best_vals;
1882 std::vector<Double_t> best_errs;
1883 std::vector<Bool_t> best_const;
1884 SnapshotParams(best_vals, best_errs, best_const);
1886 TestLowSideGroup(2, best_chi2, best_vals, best_errs, best_const);
1887 TestHighTailIndependent(2, best_chi2, best_vals, best_errs, best_const);
1889 std::cout <<
"Final fit with selected components..." << std::endl;
1890 RestoreParams(best_vals, best_errs, best_const);
1891 RooFitResult *final_fit = RunFit(kFALSE);
1892 if (final_fit && final_fit->status() == 0) {
1893 final_chi2 = ComputeReducedChi2(final_fit, final_ndof);
1895 std::cout <<
"Triple peak fit converged successfully" << std::endl;
1896 std::cout <<
"Final chi2/ndf = " << final_chi2 << std::endl;
1898 std::cout <<
"ERROR: Triple peak fit failed to converge" << std::endl;
1905 TString chi2label = Form(
"#chi^{2}/ndf = %.3f", final_chi2);
1908 results.
peaks[0] = ExtractPeakResult(0);
1909 results.
peaks[1] = ExtractPeakResult(1);
1910 results.
peaks[2] = ExtractPeakResult(2);
1916 results.
valid = kTRUE;
1923 Int_t peak_lo, Double_t delta,
1926 std::cerr <<
"ERROR: ConstrainPeakSeparation needs sigma > 0 (got " << sigma
1927 <<
"); a hard equality would have to fix a centroid instead."
1937 sim_sep_constraints_.push_back(c);
1943void RooFitUtils::BuildSeparationConstraints() {
1944 sim_constraint_set_.removeAll();
1945 for (
size_t i = 0; i < sim_sep_constraints_.size(); i++) {
1947 std::map<TString, std::vector<RooFitPeakModel>>::iterator it =
1948 sim_channel_peaks_.find(c.
channel);
1949 if (it == sim_channel_peaks_.end()) {
1950 std::cerr <<
"WARNING: separation constraint names unknown channel '"
1951 << c.
channel <<
"'; ignored." << std::endl;
1954 const std::vector<RooFitPeakModel> &pk = it->second;
1956 c.
peak_lo >= (Int_t)pk.size()) {
1957 std::cerr <<
"WARNING: separation constraint on '" << c.
channel
1959 <<
" but the channel has " << pk.size() <<
"; ignored."
1963 RooRealVar *mu_hi = pk[c.
peak_hi].mu;
1964 RooRealVar *mu_lo = pk[c.
peak_lo].mu;
1965 if (!mu_hi || !mu_lo)
1969 if (mu_hi == mu_lo) {
1970 std::cerr <<
"WARNING: separation constraint on '" << c.
channel
1971 <<
"' targets two peaks sharing one mu; ignored." << std::endl;
1983 RooFormulaVar *target =
new RooFormulaVar(
1984 base +
"_target", TString::Format(
"@0 + %.10g", c.
delta),
1985 RooArgList(*mu_lo));
1986 RooRealVar *width =
new RooRealVar(base +
"_sigma",
"", c.
sigma);
1987 width->setConstant(kTRUE);
1989 new RooGaussian(base +
"_pdf",
"", *mu_hi, *target, *width);
1990 RegisterOwned(target);
1991 RegisterOwned(width);
1993 sim_constraint_set_.add(*pen);
1994 std::cout <<
"Separation constraint on " << c.
channel <<
": mu" << c.
peak_hi
2000TString RooFitUtils::ParamFullName(
const TString &channel,
2001 const TString ¶m) {
2002 return channel +
":" + param;
2005TString RooFitUtils::SourceForTarget(
const TString &target) {
2006 for (
size_t i = 0; i < sim_links_.size(); i++) {
2007 TString full_target =
2008 ParamFullName(sim_links_[i].target_channel, sim_links_[i].target_param);
2009 if (full_target == target) {
2010 return ParamFullName(sim_links_[i].source_channel,
2011 sim_links_[i].source_param);
2018RooFitUtils::ResolveOrCreate(
const TString &channel,
const TString ¶m_name,
2019 std::map<TString, RooRealVar *> ®istry,
2020 Double_t init_val, Double_t lo, Double_t hi) {
2021 TString full = ParamFullName(channel, param_name);
2022 TString source = SourceForTarget(full);
2023 if (source.Length() > 0) {
2024 std::map<TString, RooRealVar *>::iterator it = registry.find(source);
2025 if (it == registry.end()) {
2026 std::cerr <<
"ERROR: source param " << source <<
" for link target "
2027 << full <<
" not yet built; check channel order." << std::endl;
2030 registry[full] = it->second;
2033 RooRealVar *v =
new RooRealVar(full.Data(), full.Data(), init_val, lo, hi);
2040 const TString &name,
const std::vector<Double_t> &events,
2041 Float_t fit_range_low, Float_t fit_range_high,
2042 Float_t display_bin_width_kev, Int_t num_peaks,
2043 const std::vector<Double_t> &mu_inits, Bool_t use_flat_background,
2044 Bool_t use_step, Bool_t use_low_exp_tail, Bool_t use_low_lin_tail,
2045 Bool_t use_high_exp_tail,
const std::vector<Bool_t> &mu_fixed,
2046 Bool_t bkg_yield_fixed, Bool_t bkg_slope_fixed,
2047 Bool_t lock_shape_after_seed,
const std::vector<Bool_t> &use_step_per_peak,
2048 const std::vector<Bool_t> &shape_lock_per_peak) {
2050 std::cerr <<
"ERROR: AddChannel called on a single-channel RooFitUtils "
2051 "instance; construct with the default ctor for sim mode."
2055 if ((Int_t)mu_inits.size() != num_peaks) {
2056 std::cerr <<
"ERROR: mu_inits size (" << mu_inits.size()
2057 <<
") must match num_peaks (" << num_peaks <<
")" << std::endl;
2060 if (!mu_fixed.empty() && (Int_t)mu_fixed.size() != num_peaks) {
2061 std::cerr <<
"ERROR: mu_fixed size (" << mu_fixed.size()
2062 <<
") must match num_peaks (" << num_peaks <<
")" << std::endl;
2065 if (!use_step_per_peak.empty() &&
2066 (Int_t)use_step_per_peak.size() != num_peaks) {
2067 std::cerr <<
"ERROR: use_step_per_peak size (" << use_step_per_peak.size()
2068 <<
") must match num_peaks (" << num_peaks <<
")" << std::endl;
2075 display_bin_width_kev);
2082 mu_fixed.empty() ? std::vector<Bool_t>(num_peaks, kFALSE) : mu_fixed;
2093 sim_channels_.push_back(cfg);
2097 Ssiz_t t_sep = target.Index(
":");
2098 Ssiz_t s_sep = source.Index(
":");
2099 if (t_sep < 0 || s_sep < 0) {
2100 std::cerr <<
"ERROR: LinkParameter expects 'channel:Param' format"
2106 lk.
target_param = TString(target(t_sep + 1, target.Length() - t_sep - 1));
2108 lk.
source_param = TString(source(s_sep + 1, source.Length() - s_sep - 1));
2109 sim_links_.push_back(lk);
2114 const TString &source_channel,
2115 Int_t source_peak) {
2116 TString t_suffix = TString::Format(
"%d", target_peak + 1);
2117 TString s_suffix = TString::Format(
"%d", source_peak + 1);
2118 const char *shape_params[9] = {
"Mu",
2121 "LowExpTailAmplitude",
2123 "LowLinTailAmplitude",
2125 "HighExpTailAmplitude",
2126 "HighExpTailRatio"};
2127 for (Int_t i = 0; i < 9; i++) {
2128 LinkParameter(target_channel +
":" + shape_params[i] + t_suffix,
2129 source_channel +
":" + shape_params[i] + s_suffix);
2135 sim_seeds_[channel_name] = result;
2140 std::map<TString, RooRealVar *> ®istry) {
2142 Double_t sigma_lo = cfg.
hist->GetBinWidth(1);
2143 Double_t sigma_hi = range_width * 0.1;
2144 Double_t sigma_init = range_width * 0.05;
2145 if (sigma_init < sigma_lo)
2146 sigma_init = 2.0 * sigma_lo;
2147 if (sigma_init > sigma_hi)
2148 sigma_init = 0.5 * sigma_hi;
2149 Double_t peak_height = cfg.
hist->GetBinContent(cfg.
hist->GetMaximumBin());
2150 Double_t hist_xmin = cfg.
hist->GetXaxis()->GetXmin();
2151 Double_t hist_xmax = cfg.
hist->GetXaxis()->GetXmax();
2153 if (x_ ==
nullptr) {
2154 Double_t global_xmin = hist_xmin;
2155 Double_t global_xmax = hist_xmax;
2156 for (
size_t i = 0; i < sim_channels_.size(); i++) {
2157 Double_t cmin = sim_channels_[i].hist->GetXaxis()->GetXmin();
2158 Double_t cmax = sim_channels_[i].hist->GetXaxis()->GetXmax();
2159 if (cmin < global_xmin)
2161 if (cmax > global_xmax)
2164 x_ =
new RooRealVar(
"x",
"x", global_xmin, global_xmax);
2168 TString range_name = TString(
"fitrange_") + cfg.
name;
2170 sim_channel_range_names_[cfg.
name] = range_name;
2172 std::vector<RooFitPeakModel> peaks;
2173 for (Int_t pi = 0; pi < cfg.
num_peaks; pi++) {
2175 TString suffix = TString::Format(
"%d", pi + 1);
2176 Double_t mu_init = cfg.
mu_inits[pi];
2178 Int_t mu_bin = cfg.
hist->FindBin(mu_init);
2179 Double_t local_height = cfg.
hist->GetBinContent(mu_bin);
2180 Double_t bkg_left = 0;
2181 Double_t bkg_right = 0;
2184 Int_t nside = (rbin - lbin) / 10;
2187 for (Int_t i = 0; i < nside; i++) {
2188 bkg_left += cfg.
hist->GetBinContent(lbin + i);
2189 bkg_right += cfg.
hist->GetBinContent(rbin - i);
2191 Double_t bkg_floor = (bkg_left + bkg_right) / (2.0 * nside);
2192 Double_t net_height = local_height - bkg_floor;
2193 if (net_height < 0.1 * local_height)
2194 net_height = 0.1 * local_height;
2195 Double_t total_init =
2196 net_height * sigma_init * TMath::Sqrt(2.0 * TMath::Pi());
2198 p.
mu = ResolveOrCreate(cfg.
name,
"Mu" + suffix, registry, mu_init,
2200 p.
sigma = ResolveOrCreate(cfg.
name,
"Sigma" + suffix, registry, sigma_init,
2201 sigma_lo, sigma_hi);
2203 ResolveOrCreate(cfg.
name,
"GausAmplitude" + suffix, registry,
2204 total_init, 0, peak_height * range_width * 10.0);
2205 p.
ratio_step = ResolveOrCreate(cfg.
name,
"StepAmplitude" + suffix, registry,
2208 registry, 0.0, 0.0, 0.5);
2210 registry, 1.5, 1.0, tail_ratio_max_);
2212 registry, 0.0, 0.0, 0.5);
2214 registry, 0.0, -0.1, 0.1);
2216 cfg.
name,
"HighExpTailAmplitude" + suffix, registry, 0.0, 0.0, 0.5);
2218 ResolveOrCreate(cfg.
name,
"HighExpTailRatio" + suffix, registry, 1.5,
2219 1.0, tail_ratio_max_);
2231 RooRealVar *required[] = {p.
mu,
2241 const char *required_names[] = {
"Mu",
2245 "LowExpTailAmplitude",
2247 "LowLinTailAmplitude",
2249 "HighExpTailAmplitude",
2250 "HighExpTailRatio"};
2251 for (
size_t ri = 0; ri <
sizeof(required) /
sizeof(required[0]); ri++) {
2252 if (!required[ri]) {
2253 std::cerr <<
"ERROR: channel '" << cfg.
name <<
"' peak " << suffix
2254 <<
": could not resolve " << required_names[ri] << suffix
2255 <<
" (see the link error above). Aborting the channel build "
2256 "rather than dereferencing null."
2262 TString pdf_suffix =
"_" + cfg.
name +
"_" + suffix;
2272 "high_exp_pdf" + pdf_suffix, *x_, *p.
mu, *p.
sigma,
2281 new RooFormulaVar((
"step_yield" + pdf_suffix).Data(),
"@0*@1",
2284 new RooFormulaVar((
"low_exp_yield" + pdf_suffix).Data(),
"@0*@1",
2287 new RooFormulaVar((
"low_lin_yield" + pdf_suffix).Data(),
"@0*@1",
2290 new RooFormulaVar((
"high_exp_yield" + pdf_suffix).Data(),
"@0*@1",
2297 Bool_t peak_use_step = cfg.
use_step;
2300 if (!peak_use_step) {
2327 std::vector<Int_t> sorted_idx(cfg.
num_peaks);
2328 for (Int_t i = 0; i < cfg.
num_peaks; i++)
2330 std::sort(sorted_idx.begin(), sorted_idx.end(), [&](Int_t a, Int_t b) {
2331 return cfg.mu_inits[a] < cfg.mu_inits[b];
2333 for (Int_t k = 0; k < cfg.
num_peaks - 1; k++) {
2334 Int_t left = sorted_idx[k];
2335 Int_t right = sorted_idx[k + 1];
2337 peaks[left].mu->setMax(midpoint);
2338 peaks[right].mu->setMin(midpoint);
2342 RooFitBackgroundModel bkg;
2343 Double_t bkg_estimate = 0;
2346 Int_t nside = (rbin - lbin) / 10;
2349 Double_t bkg_left = 0;
2350 Double_t bkg_right = 0;
2351 for (Int_t i = 0; i < nside; i++) {
2352 bkg_left += cfg.
hist->GetBinContent(lbin + i);
2353 bkg_right += cfg.
hist->GetBinContent(rbin - i);
2355 bkg_estimate = (bkg_left + bkg_right) / (2.0 * nside);
2357 bkg.
bkg_yield = ResolveOrCreate(cfg.
name,
"BkgConstant", registry,
2358 bkg_estimate * range_width, 0,
2359 peak_height * range_width * 10.0);
2361 Double_t slope_hi = 5.0 / range_width;
2363 ResolveOrCreate(cfg.
name,
"BkgSlope", registry, 0.0, slope_lo, slope_hi);
2365 std::cerr <<
"ERROR: channel '" << cfg.
name
2366 <<
"': could not resolve background parameters (see the link "
2367 "error above). Aborting the channel build rather than "
2368 "dereferencing null."
2372 TString bkg_pdf_name =
"bkg_pdf_" + cfg.
name;
2381 RooArgList pdf_list;
2382 RooArgList coef_list;
2383 for (
size_t pi = 0; pi < peaks.size(); pi++) {
2384 pdf_list.add(*peaks[pi].gauss_pdf);
2385 coef_list.add(*peaks[pi].gaus_yield);
2386 pdf_list.add(*peaks[pi].step_pdf);
2387 coef_list.add(*peaks[pi].step_yield);
2388 pdf_list.add(*peaks[pi].low_exp_pdf);
2389 coef_list.add(*peaks[pi].low_exp_yield);
2390 pdf_list.add(*peaks[pi].low_lin_pdf);
2391 coef_list.add(*peaks[pi].low_lin_yield);
2392 pdf_list.add(*peaks[pi].high_exp_pdf);
2393 coef_list.add(*peaks[pi].high_exp_yield);
2398 TString sum_name =
"total_pdf_" + cfg.
name;
2400 new RooAddPdf(sum_name.Data(), sum_name.Data(), pdf_list, coef_list);
2402 sim_channel_pdfs_[cfg.
name] = sum;
2403 sim_channel_peaks_[cfg.
name] = peaks;
2404 sim_channel_bkg_[cfg.
name] = bkg;
2406 RooArgSet vars(*x_);
2407 RooDataSet *ds =
new RooDataSet((
"data_" + cfg.
name).Data(),
2408 (
"data_" + cfg.
name).Data(), vars);
2409 Double_t xmin = x_->getMin();
2410 Double_t xmax = x_->getMax();
2411 for (
size_t i = 0; i < cfg.
events.size(); i++) {
2412 Double_t e = cfg.
events[i];
2413 if (e < xmin || e > xmax)
2418 sim_channel_data_[cfg.
name] = ds;
2422void RooFitUtils::ApplySeedToChannel(
const TString &channel) {
2423 std::map<TString, FitResult>::iterator it = sim_seeds_.find(channel);
2424 if (it == sim_seeds_.end())
2426 const FitResult &seed = it->second;
2427 std::vector<RooFitPeakModel> &peaks = sim_channel_peaks_[channel];
2428 RooFitBackgroundModel &bkg = sim_channel_bkg_[channel];
2429 for (
size_t pi = 0; pi < peaks.size() && pi < seed.
peaks.size(); pi++) {
2430 const PeakFitResult &cp = seed.
peaks[pi];
2431 RooFitPeakModel &p = peaks[pi];
2434 p.
mu->setVal(cp.
mu);
2460void RooFitUtils::ApplyChannelMuLocks() {
2461 for (
size_t ci = 0; ci < sim_channels_.size(); ci++) {
2462 const RooFitChannelConfig &cfg = sim_channels_[ci];
2463 std::vector<RooFitPeakModel> &peaks = sim_channel_peaks_[cfg.
name];
2464 Int_t n_lock = TMath::Min((Int_t)peaks.size(), (Int_t)cfg.
mu_fixed.size());
2465 for (Int_t pi = 0; pi < n_lock; pi++) {
2468 Double_t target = cfg.
mu_inits[pi];
2469 peaks[pi].mu->setVal(target);
2470 peaks[pi].mu->setConstant(kTRUE);
2475void RooFitUtils::ApplyChannelBkgLocks() {
2476 for (
size_t ci = 0; ci < sim_channels_.size(); ci++) {
2477 const RooFitChannelConfig &cfg = sim_channels_[ci];
2478 RooFitBackgroundModel &bkg = sim_channel_bkg_[cfg.
name];
2486void RooFitUtils::ApplyChannelShapeLocks() {
2487 for (
size_t ci = 0; ci < sim_channels_.size(); ci++) {
2488 const RooFitChannelConfig &cfg = sim_channels_[ci];
2491 std::vector<RooFitPeakModel> &peaks = sim_channel_peaks_[cfg.
name];
2492 for (
size_t pi = 0; pi < peaks.size(); pi++) {
2495 Bool_t lock_peak = kTRUE;
2504 RooFitPeakModel &p = peaks[pi];
2506 p.
sigma->setConstant(kTRUE);
2527Double_t RooFitUtils::ComputeChannelChi2(
2528 const TString &channel,
const std::vector<RooFitPeakModel> & ,
2530 RooAbsPdf *pdf = sim_channel_pdfs_[channel];
2531 RooFitChannelConfig
const *cfg =
nullptr;
2532 for (
size_t i = 0; i < sim_channels_.size(); i++) {
2533 if (sim_channels_[i].name == channel) {
2534 cfg = &sim_channels_[i];
2542 Double_t saved_val = x_->getVal();
2544 RooArgSet nset(*x_);
2545 Double_t total_exp = pdf->expectedEvents(&nset);
2546 Double_t bin_width = cfg->
hist->GetBinWidth(1);
2549 Int_t nbins_in_range = 0;
2550 Int_t nbins_hist = cfg->
hist->GetNbinsX();
2551 for (Int_t i = 1; i <= nbins_hist; i++) {
2552 Double_t xv = cfg->
hist->GetBinCenter(i);
2555 Double_t data = cfg->
hist->GetBinContent(i);
2556 Double_t error = cfg->
hist->GetBinError(i);
2557 if (error <= 0 || data <= 0)
2560 Double_t fit_val = total_exp * pdf->getVal(&nset) * bin_width;
2561 Double_t residual = (data - fit_val) / error;
2562 chi2 += residual * residual;
2565 x_->setVal(saved_val);
2568 RooArgSet *params = pdf->getParameters(RooArgSet(*x_));
2569 for (Int_t i = 0; i < (Int_t)params->size(); i++) {
2570 RooRealVar *v =
dynamic_cast<RooRealVar *
>((*params)[i]);
2571 if (v && !v->isConstant())
2575 ndof = nbins_in_range - npars;
2583 r.
mu = p.
mu->getVal();
2618RooFitExtractParamDiagnostic(RooRealVar *rv,
const std::string &name,
2619 std::vector<FitParameterDiagnostic> &diags) {
2623 d.
value = rv->getVal();
2624 d.
error = rv->getError();
2625 d.
lo = rv->getMin();
2626 d.
hi = rv->getMax();
2628 Double_t range = d.
hi - d.
lo;
2630 Double_t abs_tol = 1e-4;
2631 Double_t frac_tol = 1e-3;
2632 Double_t margin = TMath::Max(abs_tol, frac_tol * range);
2643std::vector<FitParameterDiagnostic>
2644RooFitUtils::ExtractParameterDiagnostics(
const TString &channel) {
2645 std::vector<FitParameterDiagnostic> diags;
2648 const RooFitChannelConfig *cfg =
nullptr;
2649 for (
size_t i = 0; i < sim_channels_.size(); i++) {
2650 if (sim_channels_[i].name == channel) {
2651 cfg = &sim_channels_[i];
2658 const std::vector<RooFitPeakModel> &peaks = sim_channel_peaks_[channel];
2659 const RooFitBackgroundModel &bkg = sim_channel_bkg_[channel];
2662 for (
size_t pi = 0; pi < peaks.size(); pi++) {
2663 TString suffix = TString::Format(
"%d", pi + 1);
2664 TString cname = channel +
":";
2665 RooFitExtractParamDiagnostic(peaks[pi].mu, (cname +
"Mu" + suffix).Data(),
2667 RooFitExtractParamDiagnostic(peaks[pi].sigma,
2668 (cname +
"Sigma" + suffix).Data(), diags);
2669 RooFitExtractParamDiagnostic(
2670 peaks[pi].gaus_yield, (cname +
"GausAmplitude" + suffix).Data(), diags);
2671 RooFitExtractParamDiagnostic(
2672 peaks[pi].ratio_step, (cname +
"StepAmplitude" + suffix).Data(), diags);
2673 RooFitExtractParamDiagnostic(
2674 peaks[pi].ratio_low_exp,
2675 (cname +
"LowExpTailAmplitude" + suffix).Data(), diags);
2676 RooFitExtractParamDiagnostic(peaks[pi].tau_ratio_low_exp,
2677 (cname +
"LowExpTailRatio" + suffix).Data(),
2679 RooFitExtractParamDiagnostic(
2680 peaks[pi].ratio_low_lin,
2681 (cname +
"LowLinTailAmplitude" + suffix).Data(), diags);
2682 RooFitExtractParamDiagnostic(peaks[pi].slope_low_lin,
2683 (cname +
"LowLinTailSlope" + suffix).Data(),
2685 RooFitExtractParamDiagnostic(
2686 peaks[pi].ratio_high_exp,
2687 (cname +
"HighExpTailAmplitude" + suffix).Data(), diags);
2688 RooFitExtractParamDiagnostic(peaks[pi].tau_ratio_high_exp,
2689 (cname +
"HighExpTailRatio" + suffix).Data(),
2694 RooFitExtractParamDiagnostic(bkg.
bkg_yield,
2695 TString(channel +
":BkgConstant").Data(), diags);
2696 RooFitExtractParamDiagnostic(bkg.
bkg_slope,
2697 TString(channel +
":BkgSlope").Data(), diags);
2702std::vector<FitParameterDiagnostic>
2703RooFitUtils::ExtractParameterDiagnosticsSingle() {
2704 std::vector<FitParameterDiagnostic> diags;
2706 for (
size_t pi = 0; pi < peaks_.size(); pi++) {
2707 TString suffix = TString::Format(
"%d", pi + 1);
2708 RooFitExtractParamDiagnostic(peaks_[pi].mu, (
"Mu" + suffix).Data(), diags);
2709 RooFitExtractParamDiagnostic(peaks_[pi].sigma, (
"Sigma" + suffix).Data(),
2711 RooFitExtractParamDiagnostic(peaks_[pi].gaus_yield,
2712 (
"GausAmplitude" + suffix).Data(), diags);
2713 RooFitExtractParamDiagnostic(peaks_[pi].ratio_step,
2714 (
"StepAmplitude" + suffix).Data(), diags);
2715 RooFitExtractParamDiagnostic(peaks_[pi].ratio_low_exp,
2716 (
"LowExpTailAmplitude" + suffix).Data(),
2718 RooFitExtractParamDiagnostic(peaks_[pi].tau_ratio_low_exp,
2719 (
"LowExpTailRatio" + suffix).Data(), diags);
2720 RooFitExtractParamDiagnostic(peaks_[pi].ratio_low_lin,
2721 (
"LowLinTailAmplitude" + suffix).Data(),
2723 RooFitExtractParamDiagnostic(peaks_[pi].slope_low_lin,
2724 (
"LowLinTailSlope" + suffix).Data(), diags);
2725 RooFitExtractParamDiagnostic(peaks_[pi].ratio_high_exp,
2726 (
"HighExpTailAmplitude" + suffix).Data(),
2728 RooFitExtractParamDiagnostic(peaks_[pi].tau_ratio_high_exp,
2729 (
"HighExpTailRatio" + suffix).Data(), diags);
2731 RooFitExtractParamDiagnostic(bkg_.bkg_yield,
"BkgConstant", diags);
2732 RooFitExtractParamDiagnostic(bkg_.bkg_slope,
"BkgSlope", diags);
2737void RooFitUtils::PlotChannel(
const TString &channel, Int_t num_peaks,
2738 const std::vector<RooFitPeakModel> &peaks,
2740 const TString &input_name,
2741 const TString &base_label,
2742 const TString &chi2_label) {
2743 RooFitChannelConfig
const *cfg =
nullptr;
2744 for (
size_t i = 0; i < sim_channels_.size(); i++) {
2745 if (sim_channels_[i].name == channel) {
2746 cfg = &sim_channels_[i];
2753 TH1 *saved_hist = working_hist_;
2754 Float_t saved_lo = fit_range_low_;
2755 Float_t saved_hi = fit_range_high_;
2756 std::vector<RooFitPeakModel> saved_peaks = peaks_;
2757 RooFitBackgroundModel saved_bkg = bkg_;
2758 Int_t saved_np = num_peaks_;
2759 RooAddPdf *saved_total = total_pdf_;
2761 working_hist_ = cfg->
hist;
2766 num_peaks_ = num_peaks;
2767 total_pdf_ =
static_cast<RooAddPdf *
>(sim_channel_pdfs_[channel]);
2769 TString plot_name = base_label +
"_" + channel;
2772 else if (num_peaks == 2)
2774 else if (num_peaks == 3)
2777 working_hist_ = saved_hist;
2778 fit_range_low_ = saved_lo;
2779 fit_range_high_ = saved_hi;
2780 peaks_ = saved_peaks;
2782 num_peaks_ = saved_np;
2783 total_pdf_ = saved_total;
2787 const TString &csv_path, Int_t npts) {
2788 std::map<TString, RooAbsPdf *>::iterator pit =
2789 sim_channel_pdfs_.find(channel);
2790 std::map<TString, RooFitBackgroundModel>::iterator bit =
2791 sim_channel_bkg_.find(channel);
2793 for (
size_t i = 0; i < sim_channels_.size(); i++)
2794 if (sim_channels_[i].name == channel) {
2795 cfg = &sim_channels_[i];
2798 if (pit == sim_channel_pdfs_.end() || bit == sim_channel_bkg_.end() || !cfg ||
2800 std::cerr <<
"ERROR: DumpChannelCSV: channel '" << channel
2801 <<
"' not built; call after FitSimultaneous." << std::endl;
2804 RooAddPdf *total =
static_cast<RooAddPdf *
>(pit->second);
2806 TH1 *hist = cfg->
hist;
2807 Double_t bin_width = hist->GetBinWidth(1);
2810 RooArgSet nset(*x_);
2811 Double_t total_exp = total->expectedEvents(&nset);
2817 TString base = csv_path;
2818 if (base.EndsWith(
".csv"))
2819 base.Remove(base.Length() - 4);
2823 TString spec_path = base +
"_spectrum.csv";
2824 std::ofstream spec(spec_path.Data());
2825 if (!spec.is_open()) {
2826 std::cerr <<
"ERROR: DumpChannelCSV: cannot open " << spec_path
2830 spec <<
"energy_keV,data_counts,fit_total,fit_background,residual_pull"
2832 Int_t lbin = hist->FindBin(lo);
2833 Int_t rbin = hist->FindBin(hi);
2834 for (Int_t b = lbin; b <= rbin; b++) {
2835 Double_t xc = hist->GetBinCenter(b);
2836 if (xc < lo || xc > hi)
2838 Double_t data = hist->GetBinContent(b);
2840 Double_t fit = total_exp * total->getVal(&nset) * bin_width;
2842 bkg.
bkg_pdf ? bkg_yield * bkg.
bkg_pdf->getVal(&nset) * bin_width : 0.0;
2843 Double_t pull = (fit > 0) ? (data - fit) / std::sqrt(fit) : 0.0;
2844 spec << std::fixed << std::setprecision(6) << xc <<
"," << data <<
","
2845 << fit <<
"," << fb <<
"," << pull << std::endl;
2848 std::cout <<
"Wrote CSV: " << spec_path << std::endl;
2851 TString curve_path = base +
"_fitcurve.csv";
2852 std::ofstream curve(curve_path.Data());
2853 if (!curve.is_open()) {
2854 std::cerr <<
"ERROR: DumpChannelCSV: cannot open " << curve_path
2858 curve <<
"energy_keV,fit_total,fit_background" << std::endl;
2859 Double_t x_step = (hi - lo) / (npts - 1);
2860 for (Int_t i = 0; i < npts; i++) {
2861 Double_t xv = lo + i * x_step;
2863 Double_t yt = total_exp * total->getVal(&nset) * bin_width;
2865 bkg.
bkg_pdf ? bkg_yield * bkg.
bkg_pdf->getVal(&nset) * bin_width : 0.0;
2866 curve << std::fixed << std::setprecision(6) << xv <<
"," << yt <<
"," << yb
2870 std::cout <<
"Wrote CSV: " << curve_path << std::endl;
2874 const TString &base_label) {
2875 std::vector<FitResult> results;
2876 if (sim_channels_.empty()) {
2877 std::cerr <<
"ERROR: FitSimultaneous called with no channels" << std::endl;
2881 AdoptSavedSimRange(input_name, base_label);
2883 std::map<TString, RooRealVar *> registry;
2884 for (
size_t i = 0; i < sim_channels_.size(); i++) {
2889 if (!BuildChannelModel(sim_channels_[i], registry)) {
2890 std::cerr <<
"ERROR: aborting simultaneous fit '" << base_label
2891 <<
"': channel '" << sim_channels_[i].name
2892 <<
"' could not be built." << std::endl;
2893 return std::vector<FitResult>();
2897 Float_t union_lo = sim_channels_[0].fit_range_low;
2898 Float_t union_hi = sim_channels_[0].fit_range_high;
2899 for (
size_t i = 1; i < sim_channels_.size(); i++) {
2900 if (sim_channels_[i].fit_range_low < union_lo)
2901 union_lo = sim_channels_[i].fit_range_low;
2902 if (sim_channels_[i].fit_range_high > union_hi)
2903 union_hi = sim_channels_[i].fit_range_high;
2905 x_->setRange(union_lo, union_hi);
2908 for (
size_t i = 0; i < sim_channels_.size(); i++) {
2909 ApplySeedToChannel(sim_channels_[i].name);
2911 ApplyChannelMuLocks();
2912 ApplyChannelBkgLocks();
2913 ApplyChannelShapeLocks();
2914 BuildSeparationConstraints();
2916 sim_category_ =
new RooCategory(
"channel",
"channel");
2917 for (
size_t i = 0; i < sim_channels_.size(); i++) {
2918 sim_category_->defineType(sim_channels_[i].name.Data());
2921 std::map<std::string, RooDataSet *> data_map;
2922 for (
size_t i = 0; i < sim_channels_.size(); i++) {
2923 data_map[sim_channels_[i].name.Data()] =
2924 sim_channel_data_[sim_channels_[i].name];
2926 sim_combined_data_ =
2927 new RooDataSet(
"combined_data",
"combined_data", RooArgSet(*x_),
2928 RooFit::Index(*sim_category_), RooFit::Import(data_map));
2930 sim_pdf_ =
new RooSimultaneous(
"sim_pdf",
"sim_pdf", *sim_category_);
2931 for (
size_t i = 0; i < sim_channels_.size(); i++) {
2932 sim_pdf_->addPdf(*sim_channel_pdfs_[sim_channels_[i].name],
2933 sim_channels_[i].name.Data());
2936 std::cout <<
"Running simultaneous fit over " << sim_channels_.size()
2937 <<
" channels" << std::endl;
2939 RooFitResult *fit_result =
nullptr;
2940 Bool_t sim_valid = kFALSE;
2943 if (LoadSimInteractiveParams(input_name, base_label)) {
2947 for (
size_t i = 0; i < sim_channels_.size(); i++) {
2948 sim_channels_[i].fit_range_low = loaded_lo;
2949 sim_channels_[i].fit_range_high = loaded_hi;
2950 delete sim_channels_[i].hist;
2952 sim_channels_[i].events, loaded_lo, loaded_hi,
2953 sim_channels_[i].display_bin_width_kev);
2955 ApplyChannelMuLocks();
2956 ApplyChannelBkgLocks();
2957 ApplyChannelShapeLocks();
2963 if (refit_after_load_) {
2964 Int_t load_print_level = fit_debug_ ? 1 : 0;
2965 Int_t load_eval_errors = fit_debug_ ? 10 : -1;
2966 fit_result = sim_pdf_->fitTo(
2967 *sim_combined_data_, RooFit::Save(kTRUE), RooFit::Extended(kTRUE),
2969 RooFit::SumW2Error(kFALSE), RooFit::PrintLevel(load_print_level),
2970 RooFit::PrintEvalErrors(load_eval_errors), RooFit::Strategy(1),
2971 RooFit::Minimizer(
"Minuit2",
"migrad"),
2972 RooFit::ExternalConstraints(sim_constraint_set_),
2979 Double_t refit_edm = fit_result ? fit_result->edm() : -1.0;
2980 Int_t refit_covq = fit_result ? fit_result->covQual() : -1;
2981 Bool_t edm_converged =
2982 (refit_edm >= 0.0 && refit_edm < 10.0 && refit_covq >= 2);
2984 (fit_result && (fit_result->status() == 0 || edm_converged));
2986 std::cout <<
"WARNING: simultaneous refit from saved params did not "
2987 "converge cleanly (edm = "
2988 << refit_edm <<
")" << std::endl;
2991 std::vector<SimEditorChannelView> views;
2992 for (
size_t i = 0; i < sim_channels_.size(); i++) {
2997 v.
events = &sim_channels_[i].events;
2999 v.
pdf = sim_channel_pdfs_[cfg.
name];
3000 v.
data = sim_channel_data_[cfg.
name];
3001 v.
peaks = &sim_channel_peaks_[cfg.
name];
3002 v.
bkg = &sim_channel_bkg_[cfg.
name];
3006 Bool_t was_batch = gROOT->IsBatch();
3007 gROOT->SetBatch(kFALSE);
3008 TString info = base_label +
" / " + input_name;
3010 sim_pdf_, sim_combined_data_, x_, views, union_lo, union_hi, info,
3012 gROOT->SetBatch(was_batch);
3013 sim_valid = accepted;
3017 for (
size_t i = 0; i < sim_channels_.size(); i++) {
3018 sim_channels_[i].fit_range_low = edited_lo;
3019 sim_channels_[i].fit_range_high = edited_hi;
3020 delete sim_channels_[i].hist;
3022 sim_channels_[i].events, edited_lo, edited_hi,
3023 sim_channels_[i].display_bin_width_kev);
3025 ApplyChannelMuLocks();
3026 ApplyChannelBkgLocks();
3027 ApplyChannelShapeLocks();
3028 SaveSimInteractiveParams(input_name, base_label);
3030 std::cout <<
"Interactive sim fit cancelled" << std::endl;
3037 RooAbsReal::setEvalErrorLoggingMode(RooAbsReal::PrintErrors);
3038 RooAbsReal *nll = sim_pdf_->createNLL(
3039 *sim_combined_data_, RooFit::Extended(kTRUE),
3041 Double_t nll_seed = (nll != 0) ? nll->getVal() : 0.0;
3042 std::cout <<
"=== AU_ROOFIT_FIT_DEBUG: seed NLL = " << nll_seed
3043 << (std::isfinite(nll_seed) ?
"" :
" <== NON-FINITE")
3044 <<
" ===" << std::endl;
3055 Int_t print_level = fit_debug_ ? 1 : 0;
3056 Int_t eval_errors = fit_debug_ ? 10 : -1;
3057 fit_result = sim_pdf_->fitTo(
3058 *sim_combined_data_, RooFit::Save(kTRUE), RooFit::Extended(kTRUE),
3060 RooFit::SumW2Error(kFALSE), RooFit::PrintLevel(print_level),
3061 RooFit::PrintEvalErrors(eval_errors), RooFit::Strategy(1),
3062 RooFit::Minimizer(
"Minuit2",
"migrad"),
3063 RooFit::ExternalConstraints(sim_constraint_set_),
3066 Double_t cold_edm = fit_result ? fit_result->edm() : -1.0;
3067 Int_t cold_covq = fit_result ? fit_result->covQual() : -1;
3068 sim_valid = (fit_result &&
3069 (fit_result->status() == 0 ||
3070 (cold_edm >= 0.0 && cold_edm < 10.0 && cold_covq >= 2)));
3072 std::cout <<
"WARNING: simultaneous fit did not converge cleanly"
3080 Bool_t has_diag = kFALSE;
3081 Int_t diag_status = -999;
3082 Int_t diag_cov_qual = -999;
3083 Double_t diag_edm = -1;
3084 Double_t diag_min_nll = -1;
3088 diag_status = fit_result->status();
3089 diag_cov_qual = fit_result->covQual();
3090 diag_edm = fit_result->edm();
3091 diag_min_nll = fit_result->minNll();
3095 std::cout <<
"=== Simultaneous fit diagnostics ===" << std::endl;
3096 std::cout <<
" status = " << diag_status << std::endl;
3097 std::cout <<
" covQual = " << diag_cov_qual << std::endl;
3098 std::cout <<
" edm = " << diag_edm << std::endl;
3099 std::cout <<
" minNLL = " << diag_min_nll
3100 << (std::isfinite(diag_min_nll) ?
"" :
" <== NON-FINITE")
3101 <<
" ===" << std::endl;
3104 for (
size_t i = 0; i < sim_channels_.size(); i++) {
3107 Double_t chi2 = ComputeChannelChi2(cfg.
name, sim_channel_peaks_[cfg.
name],
3108 sim_channel_bkg_[cfg.
name], ndof);
3109 std::cout <<
"Channel '" << cfg.
name <<
"' chi2/ndf = " << chi2
3110 <<
" (ndof=" << ndof <<
")" << std::endl;
3113 for (Int_t pi = 0; pi < cfg.
num_peaks; pi++) {
3115 ExtractPeakResultFor(sim_channel_peaks_[cfg.
name][pi]));
3122 cr.
valid = sim_valid;
3129 ExtractParameterDiagnostics(sim_channels_[i].name);
3130 results.push_back(cr);
3132 TString chi2_label = Form(
"#chi^{2}/ndf = %.3f", chi2);
3134 sim_channel_bkg_[cfg.
name], input_name, base_label, chi2_label);
Bool_t LaunchInteractiveRooFitEditor(TH1 *hist, const std::vector< Double_t > *events, Float_t display_bin_width_kev, RooAbsPdf *total_pdf, RooRealVar *x, RooAbsData *data, std::vector< RooFitPeakModel > *peaks, RooFitBackgroundModel *bkg, Double_t range_low, Double_t range_high, const TString &info_label)
Open the RooFit editor and pump its event loop until the user is done.
Bool_t LaunchInteractiveSimultaneousFitEditor(RooSimultaneous *sim_pdf, RooAbsData *combined_data, RooRealVar *x, std::vector< SimEditorChannelView > &channel_views, Double_t range_low, Double_t range_high, const TString &info_label, Bool_t fit_debug=kFALSE)
Open the simultaneous editor and pump its event loop.
RooCmdArg BestAvailableBackend()
The best RooFit evaluation backend this build supports.
static TString GetRandomName()
Generate a name unlikely to collide with existing ROOT objects.
static Width_t GetLineWidth()
Line width the Configure* methods apply.
static void PlotFitWithResiduals(TH1 *hist, TGraph *total_graph, const std::vector< TGraph * > &component_graphs, Float_t fit_range_low, Float_t fit_range_high, const TString &output_name, const TString &output_subdirectory="fits", const TString &label="", Bool_t logy=kTRUE)
Draw a fit over its data with a residual panel and save it.
static TString GetPlotsBaseDir()
Current base directory for saved figures, without trailing slash.
void LinkParameter(const TString &target, const TString &source)
Tie one parameter to another, fitting them as one degree of freedom.
RooFitUtils()
Construct in simultaneous mode.
void ConstrainPeakSeparation(const TString &channel, Int_t peak_hi, Int_t peak_lo, Double_t delta, Double_t sigma)
Constrain the spacing between two peaks to a known value.
void PlotFitSinglePeak(const TString input_name, const TString peak_name, const TString label="")
Draw and save the single-peak fit with its residual panel.
void LinkPeakShape(const TString &target_channel, Int_t target_peak, const TString &source_channel, Int_t source_peak)
Tie a peak's whole shape to another peak's.
void SeedChannel(const TString &channel_name, const FitResult &result)
Use a prior single-channel fit as starting values for a channel.
void DumpChannelCSV(const TString &channel, const TString &csv_path, Int_t npts=1000)
Export a converged channel to CSV for plotting elsewhere.
void PlotFitTriplePeak(const TString input_name, const TString peak_name, const TString label="")
Draw and save the triple-peak fit with its residual panel.
static std::vector< Double_t > LoadEventsFromTree(TTree *tree, const TString &branch_name)
Read a branch into the event vector the constructor expects.
FitResult FitSinglePeak(const TString input_name, const TString peak_name)
Fit one peak, pruning components that do not earn their place.
FitResult FitTriplePeak(const TString input_name, const TString peak_name, const FitResult &constrained_peaks, Double_t mu3_init)
Fit three peaks with the first two constrained by an earlier fit.
void PlotFitDoublePeak(const TString input_name, const TString peak_name, const TString label="")
Draw and save the double-peak fit with its residual panel.
static void RefillDisplayHistogram(TH1 *hist, const std::vector< Double_t > &events, Float_t fit_range_low, Float_t fit_range_high, Float_t display_bin_width_kev)
Rebin an existing display histogram in place.
static TH1F * BuildDisplayHistogramFrom(const std::vector< Double_t > &events, Float_t fit_range_low, Float_t fit_range_high, Float_t display_bin_width_kev)
Bin events into a display histogram.
std::vector< FitResult > FitSimultaneous(const TString &input_name, const TString &base_label)
Run one joint extended-likelihood fit across every channel.
void AddChannel(const TString &name, const std::vector< Double_t > &events, Float_t fit_range_low, Float_t fit_range_high, Float_t display_bin_width_kev, Int_t num_peaks, const std::vector< Double_t > &mu_inits, Bool_t use_flat_background=kFALSE, Bool_t use_step=kFALSE, Bool_t use_low_exp_tail=kFALSE, Bool_t use_low_lin_tail=kFALSE, Bool_t use_high_exp_tail=kFALSE, const std::vector< Bool_t > &mu_fixed=std::vector< Bool_t >(), Bool_t bkg_yield_fixed=kFALSE, Bool_t bkg_slope_fixed=kFALSE, Bool_t lock_shape_after_seed=kFALSE, const std::vector< Bool_t > &use_step_per_peak=std::vector< Bool_t >(), const std::vector< Bool_t > &shape_lock_per_peak=std::vector< Bool_t >())
Register one spectrum as a channel of the simultaneous fit.
FitResult FitDoublePeak(const TString input_name, const TString peak_name, Double_t mu1_init, Double_t mu2_init, Bool_t link_sigma=kFALSE)
Fit two peaks from centroid guesses.
static constexpr const char * kFitRangeName
Name of the RooFit range this class fits over.
~RooFitUtils()
Destroys every RooFit object this instance created.
void SetManualParameter(Int_t index, Double_t value)
Override one starting value.
void SetManualParameters(const std::vector< Double_t > ¶ms)
Supply explicit starting values for every parameter.
Exponential tail above the photopeak, convolved with the resolution.
Exponential tail below the photopeak, convolved with the resolution.
Linear tail below the photopeak, convolved with the resolution.
Resolution-smeared step on the low side of the photopeak.
RooAbsPdf * MakeLowExpTail(const TString &name, RooRealVar &x, RooRealVar &mu, RooRealVar &sigma, RooRealVar &tau_ratio)
Build a LowExpTail component.
RooAbsPdf * MakeLowLinTail(const TString &name, RooRealVar &x, RooRealVar &mu, RooRealVar &sigma, RooRealVar &slope)
Build a LowLinTail component.
RooAbsPdf * MakeGaussian(const TString &name, RooRealVar &x, RooRealVar &mu, RooRealVar &sigma)
Build a Gaussian component.
RooAbsPdf * MakeLinearBackground(const TString &name, RooRealVar &x, RooRealVar &slope)
Build a linear background component.
RooAbsPdf * MakeHighExpTail(const TString &name, RooRealVar &x, RooRealVar &mu, RooRealVar &sigma, RooRealVar &tau_ratio)
Build a HighExpTail component.
RooAbsPdf * MakeStepShelf(const TString &name, RooRealVar &x, RooRealVar &mu, RooRealVar &sigma)
Build a StepShelf component.
One fitted parameter's value, error and proximity to its limits.
Bool_t has_limits
Whether the parameter was bounded.
Double_t error
Fitted uncertainty.
std::string name
Parameter name as RooFit knows it.
Bool_t near_lower
Sits within a small margin of lo.
Bool_t near_limit
Either of the two above.
Bool_t near_upper
Sits within a small margin of hi.
Double_t lo
Lower bound, if has_limits.
Double_t value
Fitted value.
Double_t hi
Upper bound, if has_limits.
Result of one fit: peaks, background, quality, and diagnostics.
Int_t cov_qual
Covariance quality: 0 Exact, 1 NotPositiveDefinite, 2 Approximate, 3 External.
Int_t fit_status
Minuit migrad status; 0 means success.
Double_t edm
Estimated distance to the minimum.
Double_t min_nll
Minimised negative log-likelihood.
std::vector< PeakFitResult > peaks
One entry per peak; 1 to 3 supported.
Bool_t valid
In the RooFit backend this is computed post hoc against the display binning, and drives component pru...
std::vector< FitParameterDiagnostic > parameter_diagnostics
One entry per fitted RooRealVar, peak and background alike.
Float_t lin_bkg_slope_error
Background slope.
Float_t reduced_chi2
Chi-squared per degree of freedom.
Float_t bkg_constant_error
Background offset.
Bool_t has_fit_diagnostics
Whether a RooFitResult was available, and hence whether the fields in this group mean anything.
Fitted parameters and errors for one peak.
Float_t mu_error
Centroid, in the histogram's x units.
Float_t high_exp_tail_amplitude
High-energy exponential tail scale.
Float_t low_lin_tail_amplitude
Low-energy linear tail scale.
Float_t high_exp_tail_ratio
High-energy exponential decay constant, in units of sigma.
Float_t low_exp_tail_amplitude
Low-energy exponential tail scale.
Float_t sigma_error
Gaussian resolution.
Float_t low_exp_tail_ratio
Low-energy exponential decay constant, in units of sigma.
Float_t low_lin_tail_slope
Slope of the linear tail factor.
Float_t gaus_amplitude_error
Gaussian scale.
Float_t low_lin_tail_slope_error
Float_t low_exp_tail_ratio_error
Float_t step_amplitude_error
Step shelf scale.
Float_t low_lin_tail_amplitude_error
Float_t high_exp_tail_amplitude_error
Float_t high_exp_tail_ratio_error
Float_t low_exp_tail_amplitude_error
The RooFit objects making up one channel's background.
One channel of a simultaneous fit: its data, model and locks.
std::vector< Bool_t > use_step_per_peak
std::vector< Double_t > mu_inits
std::vector< Bool_t > shape_lock_per_peak
std::vector< Bool_t > mu_fixed
Float_t display_bin_width_kev
std::vector< Double_t > events
Bool_t use_flat_background
Bool_t lock_shape_after_seed
A tie between two parameters, fitted as one degree of freedom.
Every RooFit object making up one peak.
RooFormulaVar * high_exp_yield
RooRealVar * ratio_low_exp
RooRealVar * ratio_low_lin
RooRealVar * slope_low_lin
RooRealVar * tau_ratio_low_exp
RooFormulaVar * low_exp_yield
RooRealVar * tau_ratio_high_exp
RooFormulaVar * low_lin_yield
RooRealVar * ratio_high_exp
RooFormulaVar * step_yield
A known spacing between two peaks, imposed as a Gaussian penalty.
Int_t peak_hi
Index of the upper peak.
Double_t delta
Known separation, in the observable's units.
Int_t peak_lo
Index of the lower peak.
Double_t sigma
Uncertainty on delta; must be positive.
TString channel
Channel the two peaks belong to.
Everything the simultaneous editor needs to show one channel.
Float_t display_bin_width_kev
Display bin width.
const std::vector< Double_t > * events
Event-level values behind hist, so the display can be rebinned live as the fit range changes.
RooAbsData * data
This channel's unbinned dataset.
RooFitBackgroundModel * bkg
Background model.
TH1 * hist
Display histogram for this channel.
RooAbsPdf * pdf
This channel's summed model.
Int_t num_peaks
Peaks in this channel.
std::vector< RooFitPeakModel > * peaks
Per-peak parameter models.
TString name
Channel name, shown on the tab.